mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-04 06:57:12 +01:00
further work on billing
This commit is contained in:
committed by
Thomas Ruoff
parent
f8434233d9
commit
c396cdcbf9
@@ -1,6 +1,5 @@
|
|||||||
import * as mongoose from 'mongoose'
|
import * as mongoose from 'mongoose'
|
||||||
import { BILL_STATUS, MILAGE_RATES, getMilageRateValue } from './enums'
|
import { BILL_STATUS, MILAGE_RATES, getMilageRateValue } from './enums'
|
||||||
import { BookingDocument } from './booking'
|
|
||||||
|
|
||||||
export interface AdditionalCosts {
|
export interface AdditionalCosts {
|
||||||
name: string
|
name: string
|
||||||
@@ -10,7 +9,6 @@ export interface AdditionalCosts {
|
|||||||
export interface BillDocument
|
export interface BillDocument
|
||||||
extends mongoose.SchemaTimestampsConfig,
|
extends mongoose.SchemaTimestampsConfig,
|
||||||
mongoose.Document {
|
mongoose.Document {
|
||||||
booking: BookingDocument
|
|
||||||
milageStart: number
|
milageStart: number
|
||||||
milageEnd: number
|
milageEnd: number
|
||||||
milage?: number
|
milage?: number
|
||||||
@@ -23,12 +21,6 @@ export interface BillModel extends mongoose.Model<BillDocument> {}
|
|||||||
|
|
||||||
const BillSchema = new mongoose.Schema<BillDocument>(
|
const BillSchema = new mongoose.Schema<BillDocument>(
|
||||||
{
|
{
|
||||||
booking: {
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: 'Booking',
|
|
||||||
unique: true,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
milageStart: {
|
milageStart: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as mongoose from 'mongoose'
|
import * as mongoose from 'mongoose'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { dateFormatBackend, getDays } from '../helpers/date'
|
import { dateFormatBackend, getDays } from '../helpers/date'
|
||||||
|
import { BillDocument } from './bill'
|
||||||
import { BookerDocument } from './booker'
|
import { BookerDocument } from './booker'
|
||||||
import { BOOKING_STATUS } from './enums'
|
import { BOOKING_STATUS } from './enums'
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ export interface BookingDocument
|
|||||||
mongoose.SchemaTimestampsConfig {
|
mongoose.SchemaTimestampsConfig {
|
||||||
uuid: string
|
uuid: string
|
||||||
booker: BookerDocument
|
booker: BookerDocument
|
||||||
|
bill: BillDocument
|
||||||
startDate: Date
|
startDate: Date
|
||||||
endDate: Date
|
endDate: Date
|
||||||
status: BOOKING_STATUS
|
status: BOOKING_STATUS
|
||||||
@@ -35,6 +37,11 @@ const BookingSchema = new mongoose.Schema<BookingDocument>(
|
|||||||
ref: 'Booker',
|
ref: 'Booker',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
bill: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'Bill',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
startDate: {
|
startDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
required: true,
|
required: true,
|
||||||
@@ -56,11 +63,6 @@ const BookingSchema = new mongoose.Schema<BookingDocument>(
|
|||||||
purpose: { type: String, required: false },
|
purpose: { type: String, required: false },
|
||||||
org: { type: String, required: false },
|
org: { type: String, required: false },
|
||||||
destination: { type: String, required: false },
|
destination: { type: String, required: false },
|
||||||
bill: {
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: 'bill',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true,
|
timestamps: true,
|
||||||
|
|||||||
30
db/index.ts
30
db/index.ts
@@ -27,8 +27,8 @@ export async function getBookedDays() {
|
|||||||
|
|
||||||
export async function getBookingByUUID(uuid: string) {
|
export async function getBookingByUUID(uuid: string) {
|
||||||
await connect()
|
await connect()
|
||||||
const booking = await Booking.findOne({ uuid })
|
return Booking.findOne({ uuid })
|
||||||
return booking?.populate('booker').execPopulate()
|
//return booking.populate('bill').populate('booker').execPopulate()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBookings() {
|
export async function getBookings() {
|
||||||
@@ -87,14 +87,32 @@ export async function createBooking({
|
|||||||
export async function createBill(bookingUUID: string, billData: BillDocument) {
|
export async function createBill(bookingUUID: string, billData: BillDocument) {
|
||||||
await connect()
|
await connect()
|
||||||
const booking = await getBookingByUUID(bookingUUID)
|
const booking = await getBookingByUUID(bookingUUID)
|
||||||
const bill =
|
|
||||||
(await Bill.findOne({ booking: booking._id })) ||
|
|
||||||
new Bill({ booking: booking._id })
|
|
||||||
|
|
||||||
|
const bill = new Bill()
|
||||||
bill.set(billData)
|
bill.set(billData)
|
||||||
|
|
||||||
await bill.save()
|
await bill.save()
|
||||||
await bill.populate('booking').execPopulate()
|
|
||||||
|
booking.bill = bill._id
|
||||||
|
await booking.save()
|
||||||
|
|
||||||
|
return bill.toJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function patchBill(bookingUUID: string, billData: BillDocument) {
|
||||||
|
await connect()
|
||||||
|
const booking = await getBookingByUUID(bookingUUID)
|
||||||
|
const bill =
|
||||||
|
(booking.bill && (await Bill.findById(booking.bill))) ||
|
||||||
|
(await Bill.create())
|
||||||
|
|
||||||
|
bill.set(billData)
|
||||||
|
await bill.save()
|
||||||
|
|
||||||
|
if (booking.bill !== bill._id) {
|
||||||
|
booking.bill = bill._id
|
||||||
|
await booking.save()
|
||||||
|
}
|
||||||
|
|
||||||
return bill.toJSON()
|
return bill.toJSON()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { BillDocument } from '../../../../db/bill'
|
import { BillDocument } from '../../../../db/bill'
|
||||||
import { createBill } from '../../../../db/index'
|
import { createBill, patchBill } from '../../../../db/index'
|
||||||
|
|
||||||
export default async function userHandler(
|
export default async function userHandler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
@@ -26,6 +26,16 @@ export default async function userHandler(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case 'PATCH':
|
||||||
|
try {
|
||||||
|
bill = await patchBill(bookingUUID, req.body)
|
||||||
|
res.status(200).json(bill)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
res.status(500).end(`Internal Server Error...Guru is meditating...`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
res.setHeader('Allow', ['POST'])
|
res.setHeader('Allow', ['POST'])
|
||||||
res.status(405).end(`Method ${method} Not Allowed`)
|
res.status(405).end(`Method ${method} Not Allowed`)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import Footer from '../../../components/footer'
|
import Footer from '../../../components/footer'
|
||||||
import Header from '../../../components/header'
|
import Header from '../../../components/header'
|
||||||
import Input from '../../../components/wizard/input'
|
import Input from '../../../components/wizard/input'
|
||||||
import Bill, { AdditionalCosts, BillDocument } from '../../../db/bill'
|
import { AdditionalCosts, BillDocument } from '../../../db/bill'
|
||||||
import { BookingDocument } from '../../../db/booking'
|
import { BookingDocument } from '../../../db/booking'
|
||||||
import {
|
import {
|
||||||
BILL_STATUS,
|
BILL_STATUS,
|
||||||
@@ -20,21 +20,17 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
|||||||
} = context
|
} = context
|
||||||
const uuid = Array.isArray(uuids) ? uuids[0] : uuids
|
const uuid = Array.isArray(uuids) ? uuids[0] : uuids
|
||||||
const booking = await getBookingByUUID(uuid)
|
const booking = await getBookingByUUID(uuid)
|
||||||
|
await booking.populate('booker').populate('bill').execPopulate()
|
||||||
|
|
||||||
if (!booking) {
|
if (!booking) {
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
res.end()
|
res.end()
|
||||||
return { props: {} }
|
return { props: {} }
|
||||||
}
|
}
|
||||||
const bill =
|
|
||||||
(await Bill.findOne({ booking: booking._id })) ||
|
|
||||||
new Bill({ booking: booking._id })
|
|
||||||
|
|
||||||
// TODO: hack, not sure why _id is not serilizable
|
// TODO: hack, not sure why _id is not serilizable
|
||||||
const bookingJSON = JSON.parse(JSON.stringify(booking.toJSON()))
|
const bookingJSON = JSON.parse(JSON.stringify(booking.toJSON()))
|
||||||
const billJSON = JSON.parse(JSON.stringify(bill?.toJSON()))
|
|
||||||
return {
|
return {
|
||||||
props: { booking: bookingJSON, bill: billJSON },
|
props: { booking: bookingJSON },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +78,11 @@ async function saveBill(
|
|||||||
milage?: number
|
milage?: number
|
||||||
rate: MILAGE_RATES
|
rate: MILAGE_RATES
|
||||||
additionalCosts?: AdditionalCosts[]
|
additionalCosts?: AdditionalCosts[]
|
||||||
|
status: BILL_STATUS
|
||||||
}
|
}
|
||||||
) {
|
): Promise<BillDocument> {
|
||||||
const response = await fetch(`/api/booking/${booking.uuid}/bill/`, {
|
const response = await fetch(`/api/booking/${booking.uuid}/bill/`, {
|
||||||
method: 'POST',
|
method: booking.bill?._id ? 'PATCH' : 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
@@ -100,17 +97,16 @@ async function saveBill(
|
|||||||
|
|
||||||
export default function BillPage({
|
export default function BillPage({
|
||||||
booking: bookingProp,
|
booking: bookingProp,
|
||||||
bill: billProp,
|
|
||||||
}: {
|
}: {
|
||||||
booking: BookingDocument
|
booking: BookingDocument
|
||||||
bill: BillDocument
|
|
||||||
}) {
|
}) {
|
||||||
const [booking, setBooking] = useState(bookingProp)
|
const [booking, setBooking] = useState(bookingProp)
|
||||||
const [bill, setBill] = useState(billProp)
|
const [milageStart, setMilageStart] = useState(booking.bill?.milageStart)
|
||||||
const [milageStart, setMilageStart] = useState(bill.milageStart)
|
const [milageEnd, setMilageEnd] = useState(booking.bill?.milageEnd)
|
||||||
const [milageEnd, setMilageEnd] = useState(bill.milageEnd)
|
const [rate, setRate] = useState(
|
||||||
const [rate, setRate] = useState(bill.rate)
|
booking.bill?.rate || MILAGE_RATES.EXTERN_LTE_200
|
||||||
const [status, setStatus] = useState(bill.status)
|
)
|
||||||
|
const [status, setStatus] = useState(booking.bill?.status)
|
||||||
const milage =
|
const milage =
|
||||||
(0 < milageStart && milageStart < milageEnd && milageEnd - milageStart) || 0
|
(0 < milageStart && milageStart < milageEnd && milageEnd - milageStart) || 0
|
||||||
const total =
|
const total =
|
||||||
@@ -119,25 +115,27 @@ export default function BillPage({
|
|||||||
|
|
||||||
// in case the props change, update the internal state
|
// in case the props change, update the internal state
|
||||||
useEffect(() => setBooking(bookingProp), [bookingProp])
|
useEffect(() => setBooking(bookingProp), [bookingProp])
|
||||||
useEffect(() => setBill(billProp), [billProp])
|
|
||||||
|
const onSubmit = async (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
const bill = await saveBill(booking, {
|
||||||
|
milageStart,
|
||||||
|
milageEnd,
|
||||||
|
milage,
|
||||||
|
rate,
|
||||||
|
status,
|
||||||
|
})
|
||||||
|
|
||||||
|
booking.bill = bill
|
||||||
|
setBooking(booking)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-3 flex flex-col min-h-screen">
|
<div className="mx-3 flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="flex-grow">
|
<main className="flex-grow">
|
||||||
<h2 className="text-3xl">Pfadi Bussle Buchung</h2>
|
<h2 className="text-3xl">Pfadi Bussle Buchung</h2>
|
||||||
<form
|
<form className="form" onSubmit={onSubmit}>
|
||||||
className="form"
|
|
||||||
onSubmit={(event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
saveBill(booking, {
|
|
||||||
milageStart,
|
|
||||||
milageEnd,
|
|
||||||
milage,
|
|
||||||
rate: MILAGE_RATES[rate],
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<strong>Buchungszeitraum:</strong>{' '}
|
<strong>Buchungszeitraum:</strong>{' '}
|
||||||
{dateFormatFrontend(new Date(booking.startDate))} -{' '}
|
{dateFormatFrontend(new Date(booking.startDate))} -{' '}
|
||||||
@@ -178,7 +176,9 @@ export default function BillPage({
|
|||||||
value={rate}
|
value={rate}
|
||||||
onChange={(
|
onChange={(
|
||||||
e: React.ChangeEvent<React.ElementRef<'select'>>
|
e: React.ChangeEvent<React.ElementRef<'select'>>
|
||||||
) => setRate(MILAGE_RATES[e.target.value])}
|
) => {
|
||||||
|
setRate(e.target.value as MILAGE_RATES)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{Object.values(MILAGE_RATES).map((rate) => {
|
{Object.values(MILAGE_RATES).map((rate) => {
|
||||||
return (
|
return (
|
||||||
@@ -210,7 +210,7 @@ export default function BillPage({
|
|||||||
value={status}
|
value={status}
|
||||||
onChange={(
|
onChange={(
|
||||||
e: React.ChangeEvent<React.ElementRef<'select'>>
|
e: React.ChangeEvent<React.ElementRef<'select'>>
|
||||||
) => setStatus(BILL_STATUS[e.target.value])}
|
) => setStatus(e.target.value as BILL_STATUS)}
|
||||||
>
|
>
|
||||||
{Object.values(BILL_STATUS).map((status) => {
|
{Object.values(BILL_STATUS).map((status) => {
|
||||||
return (
|
return (
|
||||||
@@ -236,7 +236,7 @@ export default function BillPage({
|
|||||||
<Input label="Summe" name="milage" readOnly value={total} />
|
<Input label="Summe" name="milage" readOnly value={total} />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="btn btn-blue">
|
<button type="submit" className="btn btn-blue">
|
||||||
Rechnung Erstellen
|
Rechnung {booking.bill?._id ? 'Updaten' : 'Erstellen'}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
|||||||
} = context
|
} = context
|
||||||
const uuid = Array.isArray(uuids) ? uuids[0] : uuids
|
const uuid = Array.isArray(uuids) ? uuids[0] : uuids
|
||||||
const booking = await getBookingByUUID(uuid)
|
const booking = await getBookingByUUID(uuid)
|
||||||
|
await booking.populate('booker').execPopulate()
|
||||||
|
|
||||||
if (!booking) {
|
if (!booking) {
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
|
|||||||
Reference in New Issue
Block a user