From d5ac2f09dc2163c663885556eb03f1b381b97cc0 Mon Sep 17 00:00:00 2001 From: Thomas Ruoff Date: Wed, 26 Mar 2025 23:14:35 +0100 Subject: [PATCH] remove all bill related stuff --- db/bill.ts | 94 --------- db/booking.ts | 14 +- db/enums.ts | 12 -- db/index.ts | 57 +----- helpers/bill.ts | 92 --------- lib/getServerSideProps.ts | 2 - pages/admin/bookings/[uuid]/bill.tsx | 267 -------------------------- pages/admin/bookings/[uuid]/index.tsx | 5 - pages/api/bookings/[uuid]/bill.ts | 43 ----- 9 files changed, 9 insertions(+), 577 deletions(-) delete mode 100644 db/bill.ts delete mode 100644 helpers/bill.ts delete mode 100644 pages/admin/bookings/[uuid]/bill.tsx delete mode 100644 pages/api/bookings/[uuid]/bill.ts diff --git a/db/bill.ts b/db/bill.ts deleted file mode 100644 index f0a0abe..0000000 --- a/db/bill.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as mongoose from 'mongoose' -import { BILL_STATUS, MILAGE_TARIFS } from './enums' -import { getBillTotal } from '../helpers/bill' - -export interface IAdditionalCost { - name: string - value: number -} - -export interface IBill { - milageStart: number - milageEnd: number - milage?: number - tarif: MILAGE_TARIFS - status: BILL_STATUS - additionalCosts: IAdditionalCost[] - - createdAt?: string - updatedAt?: string -} - -export type BillDocument = IBill & - mongoose.SchemaTimestampsConfig & - mongoose.Document - -export type BillModel = mongoose.Model - -const BillSchema = new mongoose.Schema( - { - milageStart: { - type: Number, - required: true, - validate: { - validator: function (v: number): boolean { - const bill = this as BillDocument - - return v <= bill.milageEnd - }, - message: (props: { value: Number }) => - `${props.value} is bigger than milageEnd!`, - }, - }, - milageEnd: { - type: Number, - required: true, - - validate: { - validator: function (v: number): boolean { - const bill = this as BillDocument - - return v >= bill.milageStart - }, - message: (props: { value: Number }) => - `${props.value} is smaller than milageStart!`, - }, - }, - tarif: { - type: String, - enum: Object.values(MILAGE_TARIFS), - default: MILAGE_TARIFS.EXTERN, - required: true, - }, - additionalCosts: [ - { - name: { type: String, required: true }, - value: { type: Number, required: true }, - }, - ], - status: { - type: String, - enum: Object.values(BILL_STATUS), - default: BILL_STATUS.UNINVOICED, - }, - }, - { - timestamps: true, - toJSON: { virtuals: true, getters: true }, - toObject: { virtuals: true, getters: true }, - } -) - -BillSchema.virtual('milage').get(function (): number { - const bill = this as BillDocument - return bill.milageEnd - bill.milageStart -}) - -BillSchema.virtual('total').get(function (): number { - const bill = this as BillDocument - - return getBillTotal(bill) -}) - -export default (mongoose.models.Bill || - mongoose.model('Bill', BillSchema)) as BillModel \ No newline at end of file diff --git a/db/booking.ts b/db/booking.ts index ec7bb26..8e03cab 100644 --- a/db/booking.ts +++ b/db/booking.ts @@ -8,18 +8,16 @@ import { } from '../helpers/date' import { createCalendarEvent, deleteCalendarEvent } from '../lib/googlecalendar' -import { IBill } from './bill' import { BOOKING_STATUS, VALIDATION_ERRORS } from './enums' export interface IBooking { uuid: string name: string email: string - phone: string + phone?: string street: string zip: string city: string - bill?: IBill // format YYYY-MM-DD startDate: string // format YYYY-MM-DD @@ -29,10 +27,7 @@ export interface IBooking { org?: string destination?: string days?: string[] - calendarEventId: string - - createdAt?: string - updatedAt?: string + calendarEventId?: string toJSON?: () => IBooking } @@ -54,11 +49,6 @@ const BookingSchema = new mongoose.Schema( street: { type: String, required: true }, zip: { type: String, required: true }, city: { type: String, required: true }, - bill: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Bill', - required: false, - }, startDate: { type: String, required: true, diff --git a/db/enums.ts b/db/enums.ts index 2e57aba..36b5804 100644 --- a/db/enums.ts +++ b/db/enums.ts @@ -5,18 +5,6 @@ export enum BOOKING_STATUS { CANCELED = 'canceled', } -export enum BILL_STATUS { - UNINVOICED = 'uninvoiced', - INVOICED = 'invoiced', - PAID = 'paid', -} - -export enum MILAGE_TARIFS { - INTERN = 'intern', - EXTERN = 'extern', - NOCHARGE = 'nocharge', -} - export enum VALIDATION_ERRORS { AT_LEAST_ONE_DAY_BOOKED = 'atLeastOneDayBooked', } diff --git a/db/index.ts b/db/index.ts index 2c9d01b..c5caa03 100644 --- a/db/index.ts +++ b/db/index.ts @@ -1,13 +1,12 @@ import * as mongoose from 'mongoose' import BookingModel, { IBooking } from './booking' -import BillModel, { IBill } from './bill' import { getBookedDays as calendarGetBookedDays } from '../lib/googlecalendar' import { BOOKING_STATUS } from './enums' import { uniqueFilter } from '../helpers/array' export const MONGO_URI = process.env.MONGO_URI -mongoose.set('strictQuery', false); +mongoose.set('strictQuery', false) mongoose.connect(MONGO_URI, { serverSelectionTimeoutMS: 3000, @@ -23,7 +22,9 @@ export async function getBookedDays( return [...bookedInDatabase, ...bookedInCalendar].filter(uniqueFilter).sort() } -export async function getBookingByUUID(uuid: string): Promise> { +export async function getBookingByUUID( + uuid: string +): Promise> { return BookingModel.findOne({ uuid }) } @@ -53,7 +54,9 @@ export async function createBooking({ street, zip, city, -}: IBooking): Promise> { +}: IBooking): Promise< + mongoose.FlattenMaps +> { const booking = new BookingModel({ startDate, endDate, @@ -83,49 +86,3 @@ export async function patchBooking( return { current: booking.toJSON(), previous: oldBooking } } - -export async function createBill( - bookingUUID: string, - billData: IBill -): Promise { - const booking = await getBookingByUUID(bookingUUID) - - const bill = new BillModel() - bill.set(billData) - - await bill.save() - - booking.bill = bill - await booking.save() - - return bill.toJSON() -} - -export async function patchBill( - bookingUUID: string, - billData: IBill -): Promise { - const booking = await getBookingByUUID(bookingUUID) - const bill = - (booking.bill && (await BillModel.findById(booking.bill))) || - (await BillModel.create({})) - - bill.set(billData) - await bill.save() - - if (booking.bill !== bill) { - booking.bill = bill - await booking.save() - } - - return bill.toJSON() -} - -export async function getMilageMax(): Promise { - const billMaxMilageEnd = await BillModel.findOne({}) - .sort('-milageEnd') - .select('milageEnd') - .exec() - - return billMaxMilageEnd?.milageEnd || 0 -} \ No newline at end of file diff --git a/helpers/bill.ts b/helpers/bill.ts deleted file mode 100644 index ecd1c2d..0000000 --- a/helpers/bill.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { MILAGE_TARIFS } from '../db/enums' -import { IAdditionalCost, IBill } from '../db/bill' -import fetch from './fetch' - -function roundToCent(amount: number): number { - return Math.round(amount * 100) / 100 -} - -export function getMilageCosts({ - tarif, - km, -}: { - tarif: MILAGE_TARIFS - km: number -}): number { - if (tarif === MILAGE_TARIFS.NOCHARGE) { - return 0 - } - - if (km <= 0) { - return 0 - } - - let rate: number - - if (tarif === MILAGE_TARIFS.EXTERN) { - if (km <= 200) { - rate = 0.42 - } else if (km <= 1000) { - rate = 0.25 - } else if (km <= 2000) { - rate = 0.2 - } else { - rate = 0.18 - } - } - - if (tarif === MILAGE_TARIFS.INTERN) { - if (km <= 200) { - rate = 0.37 - } else if (km <= 1000) { - rate = 0.22 - } else if (km <= 2000) { - rate = 0.15 - } else { - rate = 0.13 - } - } - - if (rate === undefined) { - throw Error('Unable to determine rate') - } - - return roundToCent(km * rate) -} - -export function getBillTotal({ - tarif, - milage, - additionalCosts, -}: { - tarif: MILAGE_TARIFS - milage?: number - additionalCosts: IAdditionalCost[] -}): number { - const milageCosts = getMilageCosts({ tarif, km: milage }) - const additionalCostsSum = additionalCosts - .map(({ value }) => value) - .reduce((acc: number, value: number) => acc + value, 0) - - return roundToCent(milageCosts + additionalCostsSum) -} - -export async function createBill( - bookingUuid: string, - bill: IBill -): Promise { - return fetch(`/api/bookings/${bookingUuid}/bill`, { - method: 'POST', - body: bill, - }) -} - -export async function patchBill( - bookingUuid: string, - bill: IBill -): Promise { - return fetch(`/api/bookings/${bookingUuid}/bill`, { - method: 'POST', - body: bill, - }) -} diff --git a/lib/getServerSideProps.ts b/lib/getServerSideProps.ts index 5fdfd7a..0191e5b 100644 --- a/lib/getServerSideProps.ts +++ b/lib/getServerSideProps.ts @@ -47,8 +47,6 @@ export const getServerSideBooking = async ( return { props: { booking: null } } } - await booking.populate('bill') - // TODO: hack, not sure why _id is not serilizable const bookingJSON = JSON.parse(JSON.stringify(booking.toJSON())) as object return { diff --git a/pages/admin/bookings/[uuid]/bill.tsx b/pages/admin/bookings/[uuid]/bill.tsx deleted file mode 100644 index 0b917cb..0000000 --- a/pages/admin/bookings/[uuid]/bill.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import React, { useEffect, useState } from 'react' -import Input from '../../../../components/input' -import Select from '../../../../components/select' -import { IBooking } from '../../../../db/booking' -import { BILL_STATUS, MILAGE_TARIFS } from '../../../../db/enums' -import { getMilageMax } from '../../../../db/index' -import { daysFormatFrontend } from '../../../../helpers/date' -import { log } from '../../../../helpers/log' -import { getBillTotal, createBill, patchBill } from '../../../../helpers/bill' -import { getBookingStatus } from '../../../../helpers/booking' -import { getServerSideBooking } from '../../../../lib/getServerSideProps' -import withAuth from '../../../../helpers/withAuth' - -export const getServerSideProps = async (context) => { - const milageMax = await getMilageMax() - const serverSideBookingProps = await getServerSideBooking(context) - return { - props: { - ...serverSideBookingProps.props, - milageMax, - }, - } -} - -const milageTarifOptions = Object.values(MILAGE_TARIFS).map((tarif) => { - return ( - - ) -}) - -const billStatusOptions = Object.values(BILL_STATUS).map((status) => { - return ( - - ) -}) - -function getTarifLabel(tarif: MILAGE_TARIFS) { - switch (tarif) { - case MILAGE_TARIFS.EXTERN: - return 'Extern' - case MILAGE_TARIFS.INTERN: - return 'Intern' - case MILAGE_TARIFS.NOCHARGE: - return 'Frei von Kosten' - default: - return 'Keine' - } -} - -function getBillStatusLabel(status: BILL_STATUS) { - switch (status) { - case BILL_STATUS.UNINVOICED: - return 'Nicht gestellt' - case BILL_STATUS.INVOICED: - return 'Gestellt' - case BILL_STATUS.PAID: - return 'Bezahlt' - default: - return 'Unbekannt!!!' - } -} - -function BookingBillPage({ - booking: bookingProp, - milageMax, -}: { - booking: IBooking - milageMax: number -}) { - const [booking, setBooking] = useState(bookingProp) - const [milageStart, setMilageStart] = useState( - booking?.bill?.milageStart || milageMax - ) - const [milageEnd, setMilageEnd] = useState(booking?.bill?.milageEnd) - const [tarif, setTarif] = useState( - booking?.bill?.tarif || MILAGE_TARIFS.EXTERN - ) - const [status, setStatus] = useState(booking?.bill?.status) - const [additionalCosts, setAdditionalCosts] = useState([]) - const [storingInProgress, setStoringInProgress] = useState(false) - const [storingError, setStoringError] = useState(null) - const milage = - (0 < milageStart && milageStart < milageEnd && milageEnd - milageStart) || 0 - const total = getBillTotal({ tarif, milage, additionalCosts }) - - // in case the props change, update the internal state - useEffect(() => setBooking(bookingProp), [bookingProp]) - - const onSubmit = async (event: React.FormEvent) => { - event.preventDefault() - setStoringInProgress(true) - setStoringError(null) - - try { - const saveBill = !!booking.bill ? createBill : patchBill - const bill = await saveBill(booking.uuid, { - milageStart, - milageEnd, - milage, - tarif, - status, - additionalCosts, - }) - - booking.bill = bill - setBooking(booking) - } catch (error) { - setStoringError('Buchung konnte nicht gespeichert werden!') - log.error('Failed to store booking', error) - } - setStoringInProgress(false) - } - - const onAddAdditionalCost = function ( - event: React.MouseEvent - ) { - event.preventDefault() - setAdditionalCosts([...additionalCosts, { name: '', value: 0 }]) - } - - const onRemoveAdditionalCost = function ( - event: React.MouseEvent, - index: number - ) { - event.preventDefault() - setAdditionalCosts([ - ...additionalCosts.slice(0, index), - ...additionalCosts.slice(index + 1), - ]) - } - - return ( - <> - {booking && ( -
-
- Buchungszeitraum:{' '} - {daysFormatFrontend(booking.days)} -
-
- Bucher: {booking.name} -
-
- Buchungsstatus: {getBookingStatus(booking.status)} -
-
- >) => - setMilageStart(Number(e.target.value)) - } - /> - >) => - setMilageEnd(Number(e.target.value)) - } - /> - - -
- - -
- {additionalCosts.map((_, index) => { - return ( - <> -
- - -
-
- { - const newAdditonalCosts = [...additionalCosts] - newAdditonalCosts[index] = { - value: newAdditonalCosts[index].value, - name: event.target.value, - } - setAdditionalCosts(newAdditonalCosts) - }} - /> - { - const newAdditonalCosts = [...additionalCosts] - newAdditonalCosts[index] = { - name: newAdditonalCosts[index].name, - value: Number(event.target.value), - } - setAdditionalCosts(newAdditonalCosts) - }} - /> -
- - ) - })} - -
- - {storingError && ( -
{storingError}
- )} - -
- )} - - ) -} - -BookingBillPage.authenticationRequired = true - -export default withAuth(BookingBillPage) diff --git a/pages/admin/bookings/[uuid]/index.tsx b/pages/admin/bookings/[uuid]/index.tsx index c0254df..2fc5b11 100644 --- a/pages/admin/bookings/[uuid]/index.tsx +++ b/pages/admin/bookings/[uuid]/index.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' -import Link from 'next/link' import Calendar from '../../../../components/calendar' import { getServerSideBooking } from '../../../../lib/getServerSideProps' import { IBooking } from '../../../../db/booking' @@ -13,7 +12,6 @@ import withAuth from '../../../../helpers/withAuth' export const getServerSideProps = getServerSideBooking function ShowBookingAdmin({ booking: bookingProp }: { booking: IBooking }) { - const router = useRouter() const [booking, setBooking] = useState(bookingProp) const [storingBooking, setStoringBooking] = useState(false) const [storingBookingError, setStoringBookingError] = useState(null) @@ -64,9 +62,6 @@ function ShowBookingAdmin({ booking: bookingProp }: { booking: IBooking }) { > Buchung Abweisen - - Rechnung - ) diff --git a/pages/api/bookings/[uuid]/bill.ts b/pages/api/bookings/[uuid]/bill.ts deleted file mode 100644 index 3e77dc9..0000000 --- a/pages/api/bookings/[uuid]/bill.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next' -import { IBill } from '../../../../db/bill' -import { createBill, patchBill } from '../../../../db/index' -import { log } from '../../../../helpers/log' - -export default async function billHandler( - req: NextApiRequest, - res: NextApiResponse -): Promise { - const { - method, - query: { uuid: uuids }, - } = req - const bookingUUID = Array.isArray(uuids) ? uuids[0] : uuids - - let bill: IBill - - switch (method) { - case 'POST': - try { - bill = await createBill(bookingUUID, req.body) - res.status(200).json(bill) - } catch (e) { - log.error('Failed to store bill', e) - res.status(500).end(`Internal Server Error...Guru is meditating...`) - return - } - break - case 'PATCH': - try { - bill = await patchBill(bookingUUID, req.body) - res.status(200).json(bill) - } catch (e) { - log.error('Failed to patch bill', e) - res.status(500).end(`Internal Server Error...Guru is meditating...`) - return - } - break - default: - res.setHeader('Allow', ['POST', 'PATCH']) - res.status(405).end(`Method ${method} Not Allowed`) - } -} \ No newline at end of file