mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 06:27:11 +01:00
switch to prisma
This commit is contained in:
@@ -17,6 +17,6 @@ export function getNextBigger<T>(array: T[], pivot: T): T {
|
||||
return array.sort().find((day) => day > pivot)
|
||||
}
|
||||
|
||||
export function uniqueFilter(value, index, self) {
|
||||
export function uniqueFilter<T>(value: T, index: number, self: T[]) {
|
||||
return self.indexOf(value) === index
|
||||
}
|
||||
|
||||
@@ -1,79 +1,27 @@
|
||||
import { MILAGE_TARIFS } from '../db/enums'
|
||||
import { AdditionalCost, Bill } from '../db/bill'
|
||||
import { Bill, Prisma } from '@prisma/client'
|
||||
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
|
||||
tarif: Prisma.Decimal
|
||||
milage?: number
|
||||
additionalCosts: AdditionalCost[]
|
||||
}): number {
|
||||
const milageCosts = getMilageCosts({ tarif, km: milage })
|
||||
additionalCosts: Prisma.AdditionalCostsCreateInput[]
|
||||
}): Prisma.Decimal {
|
||||
const milageCosts = tarif.mul(milage)
|
||||
const additionalCostsSum = additionalCosts
|
||||
.map(({ value }) => value)
|
||||
.reduce((acc: number, value: number) => acc + value, 0)
|
||||
.reduce((acc, {value} ) => (value as Prisma.Decimal).plus(acc), new Prisma.Decimal(0))
|
||||
|
||||
return roundToCent(milageCosts + additionalCostsSum)
|
||||
|
||||
return additionalCostsSum.add(milageCosts).toDecimalPlaces(2);
|
||||
}
|
||||
|
||||
export async function createBill(
|
||||
bookingUuid: string,
|
||||
bill: Bill
|
||||
bill: Prisma.BillCreateInput
|
||||
): Promise<Bill> {
|
||||
return fetch(`/api/bookings/${bookingUuid}/bill`, {
|
||||
method: 'POST',
|
||||
@@ -83,7 +31,7 @@ export async function createBill(
|
||||
|
||||
export async function patchBill(
|
||||
bookingUuid: string,
|
||||
bill: Bill
|
||||
bill: Prisma.BillUpdateInput
|
||||
): Promise<Bill> {
|
||||
return fetch(`/api/bookings/${bookingUuid}/bill`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { BookFormData } from '../context/book'
|
||||
import { BOOKING_STATUS } from '../db/enums'
|
||||
import { Prisma, BookingStatus } from '@prisma/client'
|
||||
import fetch from './fetch'
|
||||
|
||||
export function getBookingStatus(status: BOOKING_STATUS) {
|
||||
export function getBookingStatus(status: BookingStatus) {
|
||||
switch (status) {
|
||||
case BOOKING_STATUS.REQUESTED:
|
||||
case BookingStatus.REQUESTED:
|
||||
return 'Angefragt'
|
||||
case BOOKING_STATUS.CONFIRMED:
|
||||
case BookingStatus.CONFIRMED:
|
||||
return 'Bestätigt'
|
||||
case BOOKING_STATUS.REJECTED:
|
||||
case BookingStatus.REJECTED:
|
||||
return 'Abgewiesen'
|
||||
case BOOKING_STATUS.CANCELED:
|
||||
case BookingStatus.CANCELED:
|
||||
return 'Storniert'
|
||||
default:
|
||||
return 'Unbekannt - bitte kontaktieren Sie uns!'
|
||||
}
|
||||
}
|
||||
|
||||
export async function createBooking(formData: BookFormData) {
|
||||
export async function createBooking(formData: Prisma.BookingCreateInput) {
|
||||
return fetch('/api/bookings', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
@@ -27,13 +26,13 @@ export async function createBooking(formData: BookFormData) {
|
||||
export async function cancelBooking(uuid: string) {
|
||||
return fetch(`/api/bookings/${uuid}`, {
|
||||
method: 'PATCH',
|
||||
body: { status: BOOKING_STATUS.CANCELED },
|
||||
body: { status: BookingStatus.CANCELED },
|
||||
})
|
||||
}
|
||||
|
||||
export async function patchBooking(
|
||||
uuid: string,
|
||||
bookingData: Partial<BookFormData>
|
||||
bookingData: Prisma.BookingUpdateInput
|
||||
) {
|
||||
return fetch(`/api/bookings/${uuid}`, {
|
||||
method: 'PATCH',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parse, format, addDays, subDays } from 'date-fns'
|
||||
import { parse, format, addDays, subDays, differenceInDays } from 'date-fns'
|
||||
import { utcToZonedTime } from 'date-fns-tz'
|
||||
|
||||
const FRONTEND_FORMAT = 'dd.MM.yyyy'
|
||||
@@ -23,18 +23,18 @@ export function getDays({
|
||||
endDate,
|
||||
endDateExclusive = false,
|
||||
}: {
|
||||
startDate: Date
|
||||
endDate: Date
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
endDateExclusive?: boolean
|
||||
}): string[] {
|
||||
let currentDay = new Date(startDate.getTime())
|
||||
let currentDay = new Date(startDate);
|
||||
const days = [dateFormatBackend(currentDay)]
|
||||
|
||||
if (!endDate) {
|
||||
return days
|
||||
}
|
||||
|
||||
const inclusiveEndDate = endDateExclusive ? subDays(endDate, 1) : endDate
|
||||
const inclusiveEndDate = endDateExclusive ? subDays(new Date(endDate), 1) : new Date(endDate)
|
||||
|
||||
while (currentDay < inclusiveEndDate) {
|
||||
currentDay = addDays(currentDay, 1)
|
||||
@@ -84,3 +84,8 @@ export function nowInTz(timezone = 'Europe/Berlin'): Date {
|
||||
export function getNextDay(date: Date) {
|
||||
return addDays(date, 1)
|
||||
}
|
||||
|
||||
export function getDayCount({ startDate, endDate }: { startDate: string, endDate: string }) {
|
||||
// TODO: check if this actually works as expected
|
||||
return differenceInDays(new Date(startDate), new Date(endDate)) + 1 // add one as it only counts full days;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createEvents, createEvent, EventStatus } from 'ics'
|
||||
import { Booking } from '../db/booking'
|
||||
import { BOOKING_STATUS } from '../db/enums'
|
||||
import { Booking, BookingStatus} from '@prisma/client';
|
||||
import { getBaseURL } from './url'
|
||||
import { daysFormatFrontend } from './date'
|
||||
import { dateFormatFrontend, getDayCount } from './date'
|
||||
|
||||
function convertDay(value: string): [number, number, number] {
|
||||
const parts = value.split('-')
|
||||
@@ -16,9 +15,9 @@ export function generateCalendarEntry(booking: Booking): string {
|
||||
const { error, value } = createEvent({
|
||||
productId: 'app.vercel.pfadi-bussle/ics',
|
||||
title: `Pfadi-Bussle Buchung`,
|
||||
start: convertDay(booking.days[0]),
|
||||
start: convertDay(booking.startDate),
|
||||
startOutputType: 'local',
|
||||
duration: { days: booking.days.length },
|
||||
duration: { days: getDayCount(booking) },
|
||||
location: 'Mömpelgardgasse 25, 72348 Rosenfeld, Deutschland',
|
||||
geo: { lat: 48.287044, lon: 8.726361 },
|
||||
description: `Gebucht auf ${booking.name}
|
||||
@@ -26,7 +25,7 @@ export function generateCalendarEntry(booking: Booking): string {
|
||||
Buchungs-Link: ${getBaseURL()}/bookings/${booking.uuid}
|
||||
`,
|
||||
status:
|
||||
booking.status === BOOKING_STATUS.CONFIRMED
|
||||
booking.status === BookingStatus.CONFIRMED
|
||||
? ('CONFIRMED' as EventStatus)
|
||||
: ('TENTATIVE' as EventStatus),
|
||||
})
|
||||
@@ -54,12 +53,12 @@ export function generateBookedCalendar(bookings: Booking[]): string {
|
||||
} => ({
|
||||
productId: 'app.vercel.pfadi-bussle/ics',
|
||||
calName: 'Pfadi-Bussle Buchungen',
|
||||
start: convertDay(booking.days[0]),
|
||||
start: convertDay(booking.startDate),
|
||||
startOutputType: 'local',
|
||||
duration: { days: booking.days.length },
|
||||
duration: { days: getDayCount(booking) },
|
||||
title: `Buchung ${booking.name}`,
|
||||
description: `Name: ${booking.name}
|
||||
Zeitraum: ${daysFormatFrontend(booking.days)}
|
||||
Zeitraum: ${dateFormatFrontend(new Date(booking.startDate))}-${dateFormatFrontend(new Date(booking.endDate))}
|
||||
|
||||
Email: ${booking.email}
|
||||
Telefon: ${booking.phone}
|
||||
@@ -67,7 +66,7 @@ Telefon: ${booking.phone}
|
||||
Link: ${getBaseURL()}/admin/bookings/${booking.uuid}
|
||||
`,
|
||||
status:
|
||||
booking.status === BOOKING_STATUS.CONFIRMED
|
||||
booking.status === BookingStatus.CONFIRMED
|
||||
? ('CONFIRMED' as EventStatus)
|
||||
: ('TENTATIVE' as EventStatus),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Booking } from '../db/booking'
|
||||
import { Booking, Prisma } from '@prisma/client';
|
||||
import { getBaseURL } from '../helpers/url'
|
||||
import { log } from '../helpers/log'
|
||||
import { daysFormatFrontend } from './date'
|
||||
import { dateFormatFrontend } from './date'
|
||||
import { generateCalendarEntry } from './ical'
|
||||
import sgMail from '@sendgrid/mail'
|
||||
|
||||
@@ -34,10 +34,11 @@ Tel. 0151/212 253 62
|
||||
${getBaseURL()}
|
||||
`
|
||||
|
||||
function getReceivedBookingBookerText(booking: Booking): string {
|
||||
function getReceivedBookingBookerText(booking: Prisma.BookingCreateInput): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
Vielen Dank für Deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)}!
|
||||
Vielen Dank für Deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
|
||||
Nach Prüfung bestätigen wir die Buchung bald per E-Mail!
|
||||
|
||||
@@ -54,9 +55,9 @@ ${footer}
|
||||
function getBookingConfirmedText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
deine Buchunganfrage zum ${daysFormatFrontend(
|
||||
booking.days
|
||||
)} bestätigen wir gerne!
|
||||
deine Buchunganfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
bestätigen wir gerne!
|
||||
|
||||
Bitte melde dich spätestens 7 Tage vor dem Buchungstermin per E-Mail oder Telefon
|
||||
um eine Schlüsselübergabe zu vereinbaren.
|
||||
@@ -71,9 +72,9 @@ ${footer}
|
||||
function getBookingRejectedText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
es tut uns leid, aber deine Buchungsanfrage zum ${daysFormatFrontend(
|
||||
booking.days
|
||||
)} konnten wir leider nicht bestätigen.
|
||||
es tut uns leid, aber deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
konnten wir leider nicht bestätigen.
|
||||
|
||||
Willst du das Bussle an einem anderen Termin buchen? Dann stelle bitte nochmal
|
||||
eine Buchungsanfrage auf ${getBaseURL()}.
|
||||
@@ -85,7 +86,9 @@ ${footer}
|
||||
function getBookingCanceledText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)} wurde storniert.
|
||||
deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
wurde storniert.
|
||||
|
||||
Willst du das Bussle an einem anderen Termin buchen? Dann stelle bitte nochmal
|
||||
eine Buchungsanfrage auf ${getBaseURL()}.
|
||||
@@ -109,7 +112,7 @@ export async function sendReceivedBookingAdminMail(
|
||||
await sendMail({
|
||||
to: [{ email: ADMIN_EMAIL }],
|
||||
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
||||
subject: `Buchung für ${booking.days} eingegangen!`,
|
||||
subject: `Buchung für ${booking.startDate}-${booking.endDate} eingegangen!`,
|
||||
textPlainContent: getReceivedBookingAdminText(booking),
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -118,7 +121,7 @@ export async function sendReceivedBookingAdminMail(
|
||||
}
|
||||
|
||||
export async function sendReceivedBookingBookerMail(
|
||||
booking: Booking
|
||||
booking: Prisma.BookingCreateInput
|
||||
): Promise<void> {
|
||||
try {
|
||||
await sendMail({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Booking } from '../db/booking'
|
||||
import { Booking } from '@prisma/client'
|
||||
import { log } from '../helpers/log'
|
||||
|
||||
const BOOKING_DATA_KEY = 'pfadiBussleBookingData'
|
||||
|
||||
Reference in New Issue
Block a user