diff --git a/db/bill.ts b/db/bill.ts index 99c0b39..7ded2fe 100644 --- a/db/bill.ts +++ b/db/bill.ts @@ -1,5 +1,5 @@ import * as mongoose from 'mongoose' -import { BILL_STATUS, MILAGE_RATES } from './enums' +import { BILL_STATUS, MILAGE_RATES, getMilageRateValue } from './enums' import { BookingDocument } from './booking' export interface AdditionalCosts { @@ -15,7 +15,6 @@ export interface BillDocument milageEnd: number milage?: number rate: MILAGE_RATES - total: number status: BILL_STATUS additionalCosts: AdditionalCosts[] } @@ -27,6 +26,7 @@ const BillSchema = new mongoose.Schema( booking: { type: mongoose.Schema.Types.ObjectId, ref: 'Booking', + unique: true, required: true, }, milageStart: { @@ -57,7 +57,7 @@ const BillSchema = new mongoose.Schema( rate: { type: String, enum: Object.values(MILAGE_RATES), - default: MILAGE_RATES.EXTERN_UP_TO_200, + default: MILAGE_RATES.EXTERN_LTE_200, required: true, }, additionalCosts: [ @@ -66,17 +66,17 @@ const BillSchema = new mongoose.Schema( value: { type: Number, required: true }, }, ], - total: { - type: Number, - required: true, - }, status: { type: String, enum: Object.values(BILL_STATUS), default: BILL_STATUS.UNINVOICED, }, }, - { timestamps: true, collation: { locale: 'de', strength: 1 } } + { + timestamps: true, + toJSON: { virtuals: true, getters: true }, + toObject: { virtuals: true, getters: true }, + } ) BillSchema.virtual('milage').get(function () { @@ -87,7 +87,8 @@ BillSchema.virtual('milage').get(function () { BillSchema.virtual('total').get(function () { const bill = this as BillDocument - const milageCosts = bill.milage * bill.rate + const milageCosts = + Math.round(bill.milage * getMilageRateValue(bill.rate) * 100) / 100 const additionalCostSum = bill.additionalCosts .map(({ value }) => value) .reduce((acc, value) => acc + value, 0) diff --git a/db/enums.ts b/db/enums.ts index b45d620..c108206 100644 --- a/db/enums.ts +++ b/db/enums.ts @@ -12,12 +12,27 @@ export enum BILL_STATUS { } export enum MILAGE_RATES { - INTERN_UP_TO_200 = 0.37, - INTERN_200_1000 = 0.22, - INTERN_1001_2000 = 0.15, - INTERN_OVER_2000 = 0.13, - EXTERN_UP_TO_200 = 0.42, - EXTERN_200_1000 = 0.25, - EXTERN_1001_2000 = 0.2, - EXTERN_OVER_2000 = 0.18, + INTERN_LTE_200 = 'intern_lte_200_km', + INTERN_200_1000 = 'intern_201_1000_km', + INTERN_1001_2000 = 'intern_1001_2000_km', + INTERN_GTE_2001 = 'intern_gte_2001_km', + EXTERN_LTE_200 = 'extern_lte_200_km', + EXTERN_200_1000 = 'extern_201_1000_km', + EXTERN_1001_2000 = 'extern_1001_2000_km', + EXTERN_GTE_2001 = 'extern_gte_2001_km', +} + +const rates = { + [MILAGE_RATES.INTERN_LTE_200]: 0.37, + [MILAGE_RATES.INTERN_200_1000]: 0.22, + [MILAGE_RATES.INTERN_1001_2000]: 0.15, + [MILAGE_RATES.INTERN_GTE_2001]: 0.13, + [MILAGE_RATES.EXTERN_LTE_200]: 0.42, + [MILAGE_RATES.EXTERN_200_1000]: 0.25, + [MILAGE_RATES.EXTERN_1001_2000]: 0.2, + [MILAGE_RATES.EXTERN_GTE_2001]: 0.18, +} + +export function getMilageRateValue(milageRate: MILAGE_RATES): number { + return rates[milageRate] } diff --git a/db/index.ts b/db/index.ts index da7ba49..f8609d8 100644 --- a/db/index.ts +++ b/db/index.ts @@ -86,12 +86,15 @@ export async function createBooking({ export async function createBill(bookingUUID: string, billData: BillDocument) { await connect() - const bill = new Bill(billData) - const booking = await getBookingByUUID(bookingUUID) - bill.booking = booking._id + const bill = + (await Bill.findOne({ booking: booking._id })) || + new Bill({ booking: booking._id }) + + bill.set(billData) await bill.save() await bill.populate('booking').execPopulate() + return bill.toJSON() } diff --git a/pages/booking/[uuid]/bill.tsx b/pages/booking/[uuid]/bill.tsx index f331ae2..5afd720 100644 --- a/pages/booking/[uuid]/bill.tsx +++ b/pages/booking/[uuid]/bill.tsx @@ -3,9 +3,13 @@ import React, { useEffect, useState } from 'react' import Footer from '../../../components/footer' import Header from '../../../components/header' import Input from '../../../components/wizard/input' -import { BillDocument } from '../../../db/bill' +import Bill, { AdditionalCosts, BillDocument } from '../../../db/bill' import { BookingDocument } from '../../../db/booking' -import { BOOKING_STATUS, MILAGE_RATES } from '../../../db/enums' +import { + BILL_STATUS, + MILAGE_RATES, + getMilageRateValue, +} from '../../../db/enums' import { getBookingByUUID } from '../../../db/index' import { dateFormatFrontend } from '../../../helpers/date' @@ -22,30 +26,64 @@ export const getServerSideProps: GetServerSideProps = async (context) => { res.end() return { props: {} } } + const bill = + (await Bill.findOne({ booking: booking._id })) || + new Bill({ booking: booking._id }) // TODO: hack, not sure why _id is not serilizable const bookingJSON = JSON.parse(JSON.stringify(booking.toJSON())) + const billJSON = JSON.parse(JSON.stringify(bill?.toJSON())) return { - props: { booking: bookingJSON }, + props: { booking: bookingJSON, bill: billJSON }, } } -function getBookingStatus(booking: BookingDocument) { - switch (booking.status) { - case BOOKING_STATUS.REQUESTED: - return 'In Bearbeitung' - case BOOKING_STATUS.CONFIRMED: - return 'Best�tigt' - case BOOKING_STATUS.REJECTED: - return 'Abgewiesen' - case BOOKING_STATUS.CANCELED: - return 'Storniert' +function getRateLabel(rate: MILAGE_RATES) { + switch (rate) { + case MILAGE_RATES.INTERN_LTE_200: + return 'Intern bis zu 200km' + case MILAGE_RATES.INTERN_200_1000: + return 'Intern 200-1.000km' + case MILAGE_RATES.INTERN_1001_2000: + return 'Intern 1.001-2.000km' + case MILAGE_RATES.INTERN_GTE_2001: + return 'Intern ab 2.001km' + case MILAGE_RATES.EXTERN_LTE_200: + return 'Extern bis zu 200km' + case MILAGE_RATES.EXTERN_200_1000: + return 'Extern 200-1.000km' + case MILAGE_RATES.EXTERN_1001_2000: + return 'Extern 1.001-2.000km' + case MILAGE_RATES.EXTERN_GTE_2001: + return 'Extern ab 2.001km' default: - return 'Unbekannt - bitte kontaktieren Sie uns!' + return 'Keine' } } -async function saveBill(booking: BookingDocument, bill: BillDocument) { +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!!!' + } +} + +async function saveBill( + booking: BookingDocument, + bill: { + milageStart: number + milageEnd: number + milage?: number + rate: MILAGE_RATES + additionalCosts?: AdditionalCosts[] + } +) { const response = await fetch(`/api/booking/${booking.uuid}/bill/`, { method: 'POST', mode: 'cors', @@ -60,21 +98,28 @@ async function saveBill(booking: BookingDocument, bill: BillDocument) { return response.json() } -export default function Bill({ +export default function BillPage({ booking: bookingProp, + bill: billProp, }: { booking: BookingDocument + bill: BillDocument }) { const [booking, setBooking] = useState(bookingProp) - const [milageStart, setMilageStart] = useState(0) - const [milageEnd, setMilageEnd] = useState(0) - const [rate, setRate] = useState(MILAGE_RATES.EXTERN_UP_TO_200) + const [bill, setBill] = useState(billProp) + const [milageStart, setMilageStart] = useState(bill.milageStart) + const [milageEnd, setMilageEnd] = useState(bill.milageEnd) + const [rate, setRate] = useState(bill.rate) + const [status, setStatus] = useState(bill.status) const milage = (0 < milageStart && milageStart < milageEnd && milageEnd - milageStart) || 0 - const total = (milage && rate && milage * rate) || 0 + const total = + Math.round(milage && rate && milage * getMilageRateValue(rate) * 100) / + 100 || 0 // in case the props change, update the internal state useEffect(() => setBooking(bookingProp), [bookingProp]) + useEffect(() => setBill(billProp), [billProp]) return (
@@ -89,14 +134,10 @@ export default function Bill({ milageStart, milageEnd, milage, - total, rate: MILAGE_RATES[rate], }) }} > -
- Buchungsstatus: {getBookingStatus(booking)} -
Buchungszeitraum:{' '} {dateFormatFrontend(new Date(booking.startDate))} -{' '} @@ -134,34 +175,50 @@ export default function Bill({ +
+ + + +
+
+
+ +
+
+ +
+