From 2437e9c00655c959698b03fd26c38dee6b0096b4 Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 09:26:04 +0100 Subject: [PATCH 01/11] wip --- src/lib/server/db/getCompanyCosts.test.ts | 188 +++++++++--------- src/lib/server/db/getCompanyCosts.ts | 98 ++++----- src/lib/ui/AccountingView.svelte | 102 +++++----- src/lib/ui/SortableTable.svelte | 14 +- src/lib/ui/tableData.ts | 139 +++++++------ src/routes/admin/accounting/+page.server.ts | 7 +- src/routes/admin/accounting/+page.svelte | 3 +- src/routes/taxi/accounting/+page.server.ts | 5 +- src/routes/taxi/accounting/+page.svelte | 3 +- .../taxi/availability/api/tour/+server.ts | 11 +- 10 files changed, 287 insertions(+), 283 deletions(-) diff --git a/src/lib/server/db/getCompanyCosts.test.ts b/src/lib/server/db/getCompanyCosts.test.ts index ddd0197e..ad23c563 100644 --- a/src/lib/server/db/getCompanyCosts.test.ts +++ b/src/lib/server/db/getCompanyCosts.test.ts @@ -22,20 +22,20 @@ const testDays = [midnight - 30 * DAY]; describe('test accounting', () => { it('empty db', async () => { - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(0); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(0); }); it('no customer', async () => { const c1 = await addCompany(1, dummyCoordinates); const v1 = await addTaxi(c1, dummyCapacities); await setAvailability(v1, testDays[0] + HOUR * 5, testDays[0] + HOUR * 6); await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 1000); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(1000); - expect(companyCostsPerDay[0].capped).toBe(1000); - expect(companyCostsPerDay[0].uncapped).toBe(1000); - expect(companyCostsPerDay[0].customerCount).toBe(0); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(1000); + expect(costPerDayAndVehicle[0].capped).toBe(1000); + expect(costPerDayAndVehicle[0].uncapped).toBe(1000); + expect(costPerDayAndVehicle[0].customerCount).toBe(0); }); it('one customer', async () => { @@ -45,13 +45,13 @@ describe('test accounting', () => { await setAvailability(v1, testDays[0] + HOUR * 5, testDays[0] + HOUR * 6); const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 1000))!.id; await setRequest(t1, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(1000); - expect(companyCostsPerDay[0].uncapped).toBe(700); - expect(companyCostsPerDay[0].capped).toBe(700); - expect(companyCostsPerDay[0].customerCount).toBe(1); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(1000); + expect(costPerDayAndVehicle[0].uncapped).toBe(700); + expect(costPerDayAndVehicle[0].capped).toBe(700); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); }); it('two customers', async () => { @@ -62,13 +62,13 @@ describe('test accounting', () => { const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 400))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t1, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(400); - expect(companyCostsPerDay[0].uncapped).toBe(-200); - expect(companyCostsPerDay[0].capped).toBe(-200); - expect(companyCostsPerDay[0].customerCount).toBe(2); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(400); + expect(costPerDayAndVehicle[0].uncapped).toBe(-200); + expect(costPerDayAndVehicle[0].capped).toBe(-200); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); }); it('cap reached', async () => { @@ -78,13 +78,13 @@ describe('test accounting', () => { await setAvailability(v1, testDays[0] + HOUR * 5, testDays[0] + HOUR * 6); const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 5000))!.id; await setRequest(t1, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(5000); - expect(companyCostsPerDay[0].uncapped).toBe(4700); - expect(companyCostsPerDay[0].capped).toBe(3800); - expect(companyCostsPerDay[0].customerCount).toBe(1); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(5000); + expect(costPerDayAndVehicle[0].uncapped).toBe(4700); + expect(costPerDayAndVehicle[0].capped).toBe(3800); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); }); it('two tours on same day - cap reached', async () => { @@ -96,13 +96,13 @@ describe('test accounting', () => { const t2 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 3400))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(5600); - expect(companyCostsPerDay[0].uncapped).toBe(5000); - expect(companyCostsPerDay[0].capped).toBe(3875); - expect(companyCostsPerDay[0].customerCount).toBe(2); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(5600); + expect(costPerDayAndVehicle[0].uncapped).toBe(5000); + expect(costPerDayAndVehicle[0].capped).toBe(3875); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); }); it('two tours and two availabilities on same day', async () => { @@ -118,13 +118,13 @@ describe('test accounting', () => { const t2 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 3400))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(7650); - expect(companyCostsPerDay[0].uncapped).toBe(7050); - expect(companyCostsPerDay[0].capped).toBe(5700); - expect(companyCostsPerDay[0].customerCount).toBe(2); - expect(companyCostsPerDay[0].availabilityDuration).toBe(90 * MINUTE); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(7650); + expect(costPerDayAndVehicle[0].uncapped).toBe(7050); + expect(costPerDayAndVehicle[0].capped).toBe(5700); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(90 * MINUTE); }); it('availabilitiy on insignificant day', async () => { @@ -134,13 +134,13 @@ describe('test accounting', () => { await setAvailability(v1, midnight + 30 * MINUTE, midnight + 90 * MINUTE); const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 2600))!.id; await setRequest(t1, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(2600); - expect(companyCostsPerDay[0].uncapped).toBe(2300); - expect(companyCostsPerDay[0].capped).toBe(575); - expect(companyCostsPerDay[0].availabilityDuration).toBe(0); - expect(companyCostsPerDay[0].customerCount).toBe(1); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(2600); + expect(costPerDayAndVehicle[0].uncapped).toBe(2300); + expect(costPerDayAndVehicle[0].capped).toBe(575); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(0); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); }); it('two tours same vehicle', async () => { @@ -151,13 +151,13 @@ describe('test accounting', () => { const t2 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 3000))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(5600); - expect(companyCostsPerDay[0].uncapped).toBe(5000); - expect(companyCostsPerDay[0].capped).toBe(1250); - expect(companyCostsPerDay[0].availabilityDuration).toBe(0); - expect(companyCostsPerDay[0].customerCount).toBe(2); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(5600); + expect(costPerDayAndVehicle[0].uncapped).toBe(5000); + expect(costPerDayAndVehicle[0].capped).toBe(1250); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(0); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); }); it('two tours different vehicles', async () => { @@ -171,13 +171,13 @@ describe('test accounting', () => { const t2 = (await setTour(v2, testDays[0] + HOUR, testDays[0] + HOUR * 2, 5100))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(10200); - expect(companyCostsPerDay[0].uncapped).toBe(9600); - expect(companyCostsPerDay[0].capped).toBe(7650); - expect(companyCostsPerDay[0].availabilityDuration).toBe(2 * HOUR); - expect(companyCostsPerDay[0].customerCount).toBe(2); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(10200); + expect(costPerDayAndVehicle[0].uncapped).toBe(9600); + expect(costPerDayAndVehicle[0].capped).toBe(7650); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(2 * HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); }); it('tours with 2 customers', async () => { @@ -187,13 +187,13 @@ describe('test accounting', () => { await setAvailability(v1, testDays[0] + 3 * HOUR, testDays[0] + 4 * HOUR); const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 5700))!.id; await setRequest(t1, u.id, '', 2, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(5700); - expect(companyCostsPerDay[0].uncapped).toBe(5100); - expect(companyCostsPerDay[0].capped).toBe(3900); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); - expect(companyCostsPerDay[0].customerCount).toBe(2); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(5700); + expect(costPerDayAndVehicle[0].uncapped).toBe(5100); + expect(costPerDayAndVehicle[0].capped).toBe(3900); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); }); it('overlapping availabilities', async () => { @@ -208,13 +208,13 @@ describe('test accounting', () => { const t2 = (await setTour(v2, testDays[0] + HOUR, testDays[0] + HOUR * 2, 35300))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(1); - expect(companyCostsPerDay[0].taxameter).toBe(40400); - expect(companyCostsPerDay[0].uncapped).toBe(39800); - expect(companyCostsPerDay[0].capped).toBe(24050); - expect(companyCostsPerDay[0].availabilityDuration).toBe(7 * HOUR); - expect(companyCostsPerDay[0].customerCount).toBe(2); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(1); + expect(costPerDayAndVehicle[0].taxameter).toBe(40400); + expect(costPerDayAndVehicle[0].uncapped).toBe(39800); + expect(costPerDayAndVehicle[0].capped).toBe(24050); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(7 * HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(2); }); it('2 companies', async () => { @@ -229,21 +229,21 @@ describe('test accounting', () => { const t2 = (await setTour(v2, testDays[0] + HOUR, testDays[0] + HOUR * 2, 9200))!.id; await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 2, true); - const { companyCostsPerDay } = await getCompanyCosts(); - expect(companyCostsPerDay).toHaveLength(2); - expect(companyCostsPerDay[0].timestamp).toBe(companyCostsPerDay[1].timestamp); - companyCostsPerDay.sort((c1, c2) => c1.companyId - c2.companyId); - - expect(companyCostsPerDay[0].taxameter).toBe(5100); - expect(companyCostsPerDay[0].uncapped).toBe(4800); - expect(companyCostsPerDay[0].capped).toBe(3825); - expect(companyCostsPerDay[0].availabilityDuration).toBe(HOUR); - expect(companyCostsPerDay[0].customerCount).toBe(1); - - expect(companyCostsPerDay[1].taxameter).toBe(9200); - expect(companyCostsPerDay[1].uncapped).toBe(8600); - expect(companyCostsPerDay[1].capped).toBe(7400); - expect(companyCostsPerDay[1].availabilityDuration).toBe(2 * HOUR); - expect(companyCostsPerDay[1].customerCount).toBe(2); + const { costPerDayAndVehicle } = await getCompanyCosts(); + expect(costPerDayAndVehicle).toHaveLength(2); + expect(costPerDayAndVehicle[0].timestamp).toBe(costPerDayAndVehicle[1].timestamp); + costPerDayAndVehicle.sort((c1, c2) => c1.companyId - c2.companyId); + + expect(costPerDayAndVehicle[0].taxameter).toBe(5100); + expect(costPerDayAndVehicle[0].uncapped).toBe(4800); + expect(costPerDayAndVehicle[0].capped).toBe(3825); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); + + expect(costPerDayAndVehicle[1].taxameter).toBe(9200); + expect(costPerDayAndVehicle[1].uncapped).toBe(8600); + expect(costPerDayAndVehicle[1].capped).toBe(7400); + expect(costPerDayAndVehicle[1].availabilityDuration).toBe(2 * HOUR); + expect(costPerDayAndVehicle[1].customerCount).toBe(2); }); }); diff --git a/src/lib/server/db/getCompanyCosts.ts b/src/lib/server/db/getCompanyCosts.ts index 917be0e7..c8d22783 100644 --- a/src/lib/server/db/getCompanyCosts.ts +++ b/src/lib/server/db/getCompanyCosts.ts @@ -22,7 +22,7 @@ export async function getCompanyCosts(companyId?: number) { tours: [], earliestTime: Date.now(), latestTime: Date.now(), - companyCostsPerDay: [] + costPerDayAndVehicle: [] }; } const earliestTime = @@ -90,6 +90,18 @@ export async function getCompanyCosts(companyId?: number) { ); }); + const companyByVehicle = new Map< + number, + { name: string | null; id: number; licensePlate: string } + >(); + tours.forEach((t) => + companyByVehicle.set(t.vehicleId, { + name: t.companyName, + id: t.companyId, + licensePlate: t.licensePlate + }) + ); + // cumulate the total taxameter readings on every relevant day for each vehicle const taxameterPerDayAndVehicle = new Array< Map< @@ -142,6 +154,10 @@ export async function getCompanyCosts(companyId?: number) { customerCount: number; timestamp: UnixtimeMs; verifiedCustomerCount: number; + availabilityDuration: UnixtimeMs; + companyName: string | null; + vehicleId: number; + licensePlate: string; } > >(); @@ -155,6 +171,10 @@ export async function getCompanyCosts(companyId?: number) { customerCount: number; timestamp: UnixtimeMs; verifiedCustomerCount: number; + availabilityDuration: UnixtimeMs; + companyName: string | null; + vehicleId: number; + licensePlate: string; } >(); if (taxameterPerDayAndVehicle[d] == undefined) { @@ -171,65 +191,11 @@ export async function getCompanyCosts(companyId?: number) { capped, customerCount: taxameter.customerCount, timestamp: taxameter.timestamp, - verifiedCustomerCount: taxameter.verifiedCustomerCount - }); - }); - } - const companyByVehicle = new Map(); - tours.forEach((t) => companyByVehicle.set(t.vehicleId, { name: t.companyName, id: t.companyId })); - - // Gather the daily (soft-capped and uncapped) costs per day and company - const companyCostsPerDay = new Array< - Map< - number, - { - taxameter: number; - capped: number; - uncapped: number; - companyName: string | null; - availabilityDuration: number; - customerCount: number; - timestamp: UnixtimeMs; - verifiedCustomerCount: number; - } - > - >(); - for (let d = 0; d != days.length; ++d) { - companyCostsPerDay[d] = new Map< - number, - { - taxameter: number; - capped: number; - uncapped: number; - companyName: string | null; - availabilityDuration: number; - customerCount: number; - timestamp: UnixtimeMs; - verifiedCustomerCount: number; - } - >(); - if (costPerDayAndVehicle[d] === undefined) { - continue; - } - costPerDayAndVehicle[d].forEach((cost, vehicle) => { - const company = companyByVehicle.get(vehicle)!; - companyCostsPerDay[d].set(company.id, { - capped: (companyCostsPerDay[d].get(company.id)?.capped ?? 0) + cost.capped, - uncapped: - (companyCostsPerDay[d].get(company.id)?.uncapped ?? 0) + - (costPerDayAndVehicle[d].get(vehicle)?.uncapped ?? 0), - companyName: company.name, - availabilityDuration: - (companyCostsPerDay[d].get(company.id)?.availabilityDuration ?? 0) + - (availabilitiesPerDayAndVehicle[d]?.get(vehicle) ?? 0), - customerCount: - (companyCostsPerDay[d].get(company.id)?.customerCount ?? 0) + - (costPerDayAndVehicle[d].get(vehicle)?.customerCount ?? 0), - verifiedCustomerCount: - (companyCostsPerDay[d].get(company.id)?.verifiedCustomerCount ?? 0) + - (costPerDayAndVehicle[d].get(vehicle)?.verifiedCustomerCount ?? 0), - taxameter: (companyCostsPerDay[d].get(company.id)?.taxameter ?? 0) + cost.taxameter, - timestamp: cost.timestamp + verifiedCustomerCount: taxameter.verifiedCustomerCount, + availabilityDuration: availabilitiesPerDayAndVehicle[d].get(vehicle) ?? 0, + companyName: companyByVehicle.get(vehicle)!.name, + vehicleId: vehicle, + licensePlate: companyByVehicle.get(vehicle)!.licensePlate }); }); } @@ -237,8 +203,8 @@ export async function getCompanyCosts(companyId?: number) { tours, earliestTime, latestTime, - companyCostsPerDay: companyCostsPerDay.flatMap((companyCosts) => - Array.from(companyCosts).map( + costPerDayAndVehicle: costPerDayAndVehicle.flatMap((vehicleCosts) => + Array.from(vehicleCosts).map( ([ companyId, { @@ -249,7 +215,9 @@ export async function getCompanyCosts(companyId?: number) { customerCount, taxameter, timestamp, - verifiedCustomerCount + verifiedCustomerCount, + vehicleId, + licensePlate } ]) => { return { @@ -261,7 +229,9 @@ export async function getCompanyCosts(companyId?: number) { customerCount, taxameter, timestamp, - verifiedCustomerCount + verifiedCustomerCount, + vehicleId, + licensePlate }; } ) diff --git a/src/lib/ui/AccountingView.svelte b/src/lib/ui/AccountingView.svelte index 72e6473c..291b5c2e 100644 --- a/src/lib/ui/AccountingView.svelte +++ b/src/lib/ui/AccountingView.svelte @@ -39,17 +39,19 @@ const { isAdmin, tours, - companyCostsPerDay, - earliestTime + costPerDayAndVehicle, + earliestTime, + latestTime }: { isAdmin: boolean; tours: ToursWithRequests; - companyCostsPerDay: Subtractions[]; + costPerDayAndVehicle: Subtractions[]; earliestTime: UnixtimeMs; + latestTime: UnixtimeMs; } = $props(); const updateCompanySums = (subtractionRows: Subtractions[]) => { - const accumulatedCompanyRowEntries = (arr: (Subtractions | CompanyRow)[]) => { + const accumulateCompanyRowEntries = (arr: (Subtractions | CompanyRow)[]) => { return arr.reduce( (acc, current) => { acc.capped += current.capped; @@ -70,7 +72,6 @@ } ); }; - const costsPerCompany = groupBy( subtractionRows, (c) => c.companyId, @@ -81,19 +82,16 @@ if (arr.length === 0) { return; } - const accumulated = accumulatedCompanyRowEntries(arr); + const accumulated = accumulateCompanyRowEntries(arr); if (isAdmin) { newCompanyRows.push({ ...accumulated, companyName: arr[0].companyName, companyId }); } else { newCompanyRows.push({ ...accumulated, companyId }); } }); - if (newCompanyRows.length === 0) { - return []; - } - if (isAdmin) { + if (isAdmin && newCompanyRows.length > 1) { newCompanyRows.push({ - ...accumulatedCompanyRowEntries(newCompanyRows), + ...accumulateCompanyRowEntries(newCompanyRows), companyId: -1, companyName: 'Summiert' }); @@ -102,8 +100,8 @@ }; let currentRowsToursTable: TourWithRequests[] = $state(tours); - let currentRowsSubtractionsTable: Subtractions[] = $state(companyCostsPerDay); - let currentCompanyRows: CompanyRow[] = $state(updateCompanySums(companyCostsPerDay)); + let currentRowsSubtractionsTable: Subtractions[] = $state(costPerDayAndVehicle); + let currentCompanyRows: CompanyRow[] = $state(updateCompanySums(costPerDayAndVehicle)); const getNewSum = (rows: Subtractions[]) => { let newSum = 0; @@ -112,11 +110,11 @@ } return newSum; }; - let sum = $state(getNewSum(companyCostsPerDay)); + let sum = $state(getNewSum(costPerDayAndVehicle)); const years: number[] = []; for ( - let i = new Date(Date.now()).getFullYear(); + let i = new Date(latestTime).getFullYear(); i != new Date(earliestTime).getFullYear() - 1; --i ) { @@ -147,7 +145,7 @@ (selectedCancelledToursIdx == -1 || cancelledFilters[selectedCancelledToursIdx](t)) && (selectedCompletedToursIdx === -1 || completedFilters[selectedCompletedToursIdx](t)) ); - const subtractionRows = getNewRows(subtractionFilters, companyCostsPerDay); + const subtractionRows = getNewRows(subtractionFilters, costPerDayAndVehicle); currentCompanyRows = updateCompanySums(subtractionRows); currentRowsSubtractionsTable = subtractionRows; }); @@ -203,37 +201,41 @@ (row: Subtractions) => new Date(row.timestamp).getFullYear() === selectedYear ]; - const csvExportBothTables = ( - tourRows: TourWithRequests[], - subtractionRows: Subtractions[], - filename: string + const filename = 'Abrechnung'; + const csvExport = ( + rows: T[], + cols: Column[], + filename: string, + addSumRow: boolean ) => { + let data = []; + data.push(cols.map((col) => col.text.trim())); + for (let row of rows) { + data.push(cols.map((col) => col.toTableEntry(row))); + } + if (addSumRow) { + const lastRow = ['Summe insgesamt']; + for (let i = 0; i != cols.length - 3; ++i) { + lastRow.push(''); + } + lastRow.push(getEuroString(sum)); + data.push(lastRow); + } + const csvContent = Papa.unparse(data, { header: true }); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + saveAs(blob, filename); + }; + + const csvExportToursTable = (tourRows: TourWithRequests[]) => { tourRows.sort((a, b) => a.startTime - b.startTime); + csvExport(tourRows, isAdmin ? tourColsAdmin : tourColsCompany, filename + '_tour.csv', false); + }; + + const csvExportDayTable = (subtractionRows: Subtractions[]) => { subtractionRows.sort((a, b) => { const companyDifference = a.companyId - b.companyId; return companyDifference == 0 ? a.timestamp - b.timestamp : companyDifference; }); - - const csvExport = (rows: T[], cols: Column[], filename: string, addSumRow: boolean) => { - let data = []; - data.push(cols.map((col) => col.text.trim())); - for (let row of rows) { - data.push(cols.map((col) => col.toTableEntry(row))); - } - if (addSumRow) { - const lastRow = ['Summe insgesamt']; - for (let i = 0; i != cols.length - 4; ++i) { - lastRow.push(''); - } - lastRow.push(getEuroString(sum)); - data.push(lastRow); - } - const csvContent = Papa.unparse(data, { header: true }); - const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); - saveAs(blob, filename); - }; - - csvExport(tourRows, isAdmin ? tourColsAdmin : tourColsCompany, filename + '_tour.csv', false); csvExport( subtractionRows, isAdmin ? subtractionColsAdmin : subtractionColsCompany, @@ -280,13 +282,14 @@ {#snippet tourTable()} 'cursor-pointer ' + (row.cancelled ? (row.message === null ? 'bg-orange-500' : 'bg-destructive') : 'bg-white-0')} bind:selectedRow={selectedToursTableRow} + bindSelectedRow={true} /> @@ -294,7 +297,7 @@ {#snippet subtractionTable()} @@ -302,7 +305,7 @@ {#snippet companyTable()} @@ -361,12 +364,11 @@ disabled={false} /> - + {/snippet} diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index dcb847e0..69f1593e 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -2,13 +2,13 @@ import * as Table from '$lib/shadcn/table/index'; import { ChevronsUpDown } from 'lucide-svelte'; import { Button } from '$lib/shadcn/button'; - import type { TourWithRequests } from '$lib/server/db/getTours'; let { - rows, + rows = $bindable(), cols, getRowStyle, - selectedRow = $bindable() + selectedRow = $bindable(), + bindSelectedRow }: { rows: T[]; cols: { @@ -19,6 +19,7 @@ isAdmin: boolean; getRowStyle?: (row: T) => string; selectedRow?: undefined | T[]; + bindSelectedRow?: boolean; } = $props(); const descending = Array.from({ length: cols.length }, () => true); @@ -33,11 +34,6 @@ } descending[idx] = !descending[idx]; }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function isTourWithRequests(data: any): data is TourWithRequests { - return data && typeof data.tourId === 'number' && Array.isArray(data.requests); - }
@@ -67,7 +63,7 @@ { - if (isTourWithRequests(row)) { + if (bindSelectedRow) { selectedRow = [row]; } }} diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index 4370b1b8..163d01ef 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -1,4 +1,4 @@ -import { FIXED_PRICE } from '$lib/constants'; +import { CAP, FIXED_PRICE } from '$lib/constants'; import type { TourWithRequests } from '$lib/server/db/getTours'; import { HOUR, MINUTE, SECOND } from '$lib/util/time'; import type { UnixtimeMs } from '$lib/util/UnixtimeMs'; @@ -14,7 +14,11 @@ export type CompanyRow = { verifiedCustomerCount: number; }; -export type Subtractions = CompanyRow & { timestamp: UnixtimeMs }; +export type Subtractions = CompanyRow & { + timestamp: UnixtimeMs; + vehicleId: number; + licensePlate: string; +}; export type Column = { text: string; @@ -56,7 +60,7 @@ const displayDuration = (duration: number) => { }; const hours = Math.floor(duration / HOUR); const minutes = ((duration / HOUR - hours) * MINUTE) / SECOND; - return addLeadingZero(hours) + ':' + addLeadingZero(minutes); + return addLeadingZero(hours) + ':' + addLeadingZero(minutes) + 'h'; }; const firstTourColAdmin: Column = { @@ -65,63 +69,75 @@ const firstTourColAdmin: Column = { toTableEntry: (r: TourWithRequests) => r.companyName ?? '' }; -const firstTourColCompany: Column = { - text: 'Fahrzeug', - sort: (a: TourWithRequests, b: TourWithRequests) => a.vehicleId - b.vehicleId, - toTableEntry: (r: TourWithRequests) => r.licensePlate ?? '' +const restTourCols = (isAdmin: boolean): Column[] => { + return [ + { + text: 'Fahrzeug', + sort: (a: TourWithRequests, b: TourWithRequests) => a.vehicleId - b.vehicleId, + toTableEntry: (r: TourWithRequests) => r.licensePlate ?? '' + }, + { + text: 'Abfahrt ', + sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.startTime - t2.startTime, + toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.startTime, true) + }, + { + text: 'Ankunft', + sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.endTime - t2.endTime, + toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.endTime, true) + }, + { + text: 'Kunden', + sort: (t1: TourWithRequests, t2: TourWithRequests) => + getCustomerCount(t1, false) - getCustomerCount(t2, false), + toTableEntry: (r: TourWithRequests) => getCustomerCount(r, false) + }, + { + text: 'erschienene Kunden ', + sort: (t1: TourWithRequests, t2: TourWithRequests) => + getCustomerCount(t1, true) - getCustomerCount(t2, true), + toTableEntry: (r: TourWithRequests) => getCustomerCount(r, true) + }, + { + text: 'Taxameterstand ', + sort: (t1: TourWithRequests, t2: TourWithRequests) => (t1.fare ?? 0) - (t2.fare ?? 0), + toTableEntry: (r: TourWithRequests) => getEuroString(r.fare) + }, + { + text: isAdmin ? 'Kosten ' : 'Einnahmen ', + sort: (t1: TourWithRequests, t2: TourWithRequests) => getTourCost(t1) - getTourCost(t2), + toTableEntry: (r: TourWithRequests) => getEuroString(getTourCost(r)) + } + ]; }; -const restTourCols: Column[] = [ - { - text: 'Abfahrt ', - sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.startTime - t2.startTime, - toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.startTime, true) - }, - { - text: 'Ankunft', - sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.endTime - t2.endTime, - toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.endTime, true) - }, - { - text: 'Kunden', - sort: undefined, - toTableEntry: (r: TourWithRequests) => getCustomerCount(r, false) - }, - { - text: 'erschienene Kunden ', - sort: undefined, - toTableEntry: (r: TourWithRequests) => getCustomerCount(r, true) - }, - { - text: 'Taxameterstand ', - sort: (t1: TourWithRequests, t2: TourWithRequests) => (t1.fare ?? 0) - (t2.fare ?? 0), - toTableEntry: (r: TourWithRequests) => getEuroString(r.fare) - }, - { - text: 'Kosten ', - sort: (t1: TourWithRequests, t2: TourWithRequests) => getTourCost(t1) - getTourCost(t2), - toTableEntry: (r: TourWithRequests) => getEuroString(getTourCost(r)) - } -]; - -export const tourColsAdmin = [firstTourColAdmin].concat(restTourCols); -export const tourColsCompany = [firstTourColCompany].concat(restTourCols); +export const tourColsAdmin = [firstTourColAdmin].concat(restTourCols(true)); +export const tourColsCompany = restTourCols(false); export const subtractionColsAdmin: Column[] = [ { text: 'Unternehmen', - sort: (a: CompanyRow, b: CompanyRow) => a.companyId - b.companyId, + sort: (a: Subtractions, b: Subtractions) => a.companyId - b.companyId, toTableEntry: (r: Subtractions) => r.companyName ?? '' }, + { + text: 'Fahrzeug', + sort: (a: Subtractions, b: Subtractions) => a.vehicleId - b.vehicleId, + toTableEntry: (r: Subtractions) => r.licensePlate ?? '' + }, { text: 'Tag ', sort: (a: Subtractions, b: Subtractions) => a.timestamp - b.timestamp, toTableEntry: (r: Subtractions) => displayUnixtimeMs(r.timestamp) }, - { text: 'Kunden', sort: undefined, toTableEntry: (r: Subtractions) => r.customerCount }, + { + text: 'Kunden', + sort: (a: Subtractions, b: Subtractions) => a.customerCount - b.customerCount, + toTableEntry: (r: Subtractions) => r.customerCount + }, { text: 'erschienene Kunden ', - sort: undefined, + sort: (a: Subtractions, b: Subtractions) => a.verifiedCustomerCount - b.verifiedCustomerCount, toTableEntry: (r: Subtractions) => r.verifiedCustomerCount }, { @@ -130,12 +146,31 @@ export const subtractionColsAdmin: Column[] = [ toTableEntry: (r: Subtractions) => getEuroString(r.taxameter) }, { - text: 'Kosten ohne Obergrenze ', + text: 'Einnahmen ohne Obergrenze ', sort: (a: Subtractions, b: Subtractions) => a.uncapped - b.uncapped, toTableEntry: (r: Subtractions) => getEuroString(r.uncapped) }, { - text: 'Kosten mit Obergrenze ', + text: 'gesetzte Verfügbarkeit', + sort: (a: Subtractions, b: Subtractions) => a.availabilityDuration - b.availabilityDuration, + toTableEntry: (r: Subtractions) => displayDuration(r.availabilityDuration) + }, + { + text: 'Obergrenze', + sort: (a: Subtractions, b: Subtractions) => a.availabilityDuration - b.availabilityDuration, + toTableEntry: (r: Subtractions) => getEuroString((r.availabilityDuration / HOUR) * CAP) + }, + { + text: 'über Obergrenze', + sort: (a: Subtractions, b: Subtractions) => + a.uncapped - + (a.availabilityDuration / HOUR) * CAP - + (b.uncapped - (b.availabilityDuration / HOUR) * CAP), + toTableEntry: (r: Subtractions) => + getEuroString(r.uncapped - (r.availabilityDuration / HOUR) * CAP) + }, + { + text: 'Einnahmen mit Obergrenze ', sort: (a: Subtractions, b: Subtractions) => a.capped - b.capped, toTableEntry: (r: Subtractions) => getEuroString(r.capped) }, @@ -143,11 +178,6 @@ export const subtractionColsAdmin: Column[] = [ text: 'Abzüge ', sort: (a: Subtractions, b: Subtractions) => a.uncapped - a.capped - b.uncapped + b.capped, toTableEntry: (r: Subtractions) => getEuroString(r.uncapped - r.capped) - }, - { - text: 'gesetzte Verfügbarkeit', - sort: (a: Subtractions, b: Subtractions) => a.availabilityDuration - b.availabilityDuration, - toTableEntry: (r: Subtractions) => displayDuration(r.availabilityDuration) } ]; @@ -184,11 +214,6 @@ export const companyColsAdmin: Column[] = [ text: 'Abzüge ', sort: (a: CompanyRow, b: CompanyRow) => a.uncapped - a.capped - b.uncapped + b.capped, toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped - r.capped) - }, - { - text: 'gesetzte Verfügbarkeit', - sort: (a: CompanyRow, b: CompanyRow) => a.availabilityDuration - b.availabilityDuration, - toTableEntry: (r: CompanyRow) => displayDuration(r.availabilityDuration) } ]; diff --git a/src/routes/admin/accounting/+page.server.ts b/src/routes/admin/accounting/+page.server.ts index 7ac42021..bc468de6 100644 --- a/src/routes/admin/accounting/+page.server.ts +++ b/src/routes/admin/accounting/+page.server.ts @@ -1,11 +1,12 @@ import { getCompanyCosts } from '$lib/server/db/getCompanyCosts'; +import type { PageServerLoad } from './$types.js'; -export const load = async () => { - const { tours, earliestTime, latestTime, companyCostsPerDay } = await getCompanyCosts(); +export const load: PageServerLoad = async () => { + const { tours, earliestTime, latestTime, costPerDayAndVehicle } = await getCompanyCosts(); return { tours: tours.map(({ interval: _, ...rest }) => rest), earliestTime, latestTime, - companyCostsPerDay + costPerDayAndVehicle }; }; diff --git a/src/routes/admin/accounting/+page.svelte b/src/routes/admin/accounting/+page.svelte index ab116112..b0fe86e7 100644 --- a/src/routes/admin/accounting/+page.svelte +++ b/src/routes/admin/accounting/+page.svelte @@ -7,6 +7,7 @@ diff --git a/src/routes/taxi/accounting/+page.server.ts b/src/routes/taxi/accounting/+page.server.ts index c1d4933e..be943f5d 100644 --- a/src/routes/taxi/accounting/+page.server.ts +++ b/src/routes/taxi/accounting/+page.server.ts @@ -3,11 +3,12 @@ import type { PageServerLoad } from './$types.js'; export const load: PageServerLoad = async ({ locals }) => { const companyId = locals.session!.companyId!; - const { tours, earliestTime, latestTime, companyCostsPerDay } = await getCompanyCosts(companyId); + const { tours, earliestTime, latestTime, costPerDayAndVehicle } = + await getCompanyCosts(companyId); return { tours: tours.map(({ interval: _, ...rest }) => rest), earliestTime, latestTime, - companyCostsPerDay + costPerDayAndVehicle }; }; diff --git a/src/routes/taxi/accounting/+page.svelte b/src/routes/taxi/accounting/+page.svelte index 3c6ad4c0..e81f8233 100644 --- a/src/routes/taxi/accounting/+page.svelte +++ b/src/routes/taxi/accounting/+page.svelte @@ -7,6 +7,7 @@ diff --git a/src/routes/taxi/availability/api/tour/+server.ts b/src/routes/taxi/availability/api/tour/+server.ts index a7373ca5..0ecdaf13 100644 --- a/src/routes/taxi/availability/api/tour/+server.ts +++ b/src/routes/taxi/availability/api/tour/+server.ts @@ -43,6 +43,7 @@ export const POST = async (event) => { .select((eb) => [ 'tour.departure', 'tour.arrival', + 'tour.id', 'vehicle.passengers', 'vehicle.bikes', 'vehicle.wheelchairs', @@ -56,6 +57,7 @@ export const POST = async (event) => { 'request.wheelchairs', 'request.luggage', 'request.passengers', + 'request.id', jsonArrayFrom( eb .selectFrom('event') @@ -74,10 +76,15 @@ export const POST = async (event) => { if (!movedTour) { return; } - console.assert(movedTour.requests.length != 0, 'Found a tour which contains no requests.'); + console.assert( + movedTour.requests.length != 0, + 'Found a tour which contains no requests. tourId: ', + movedTour.id + ); console.assert( !movedTour.requests.some((r) => r.events.length == 0), - 'Found a request which contains no events.' + 'Found a request which contains no events. requestId: ' + + movedTour.requests.find((r) => r.events.length === 0)?.id ); const events = movedTour.requests.flatMap((r) => r.events.map((e) => { From 2e37c92372cc4b6e6a722b1b738e766bcae3bf47 Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 09:48:57 +0100 Subject: [PATCH 02/11] wip --- src/lib/server/db/getCompanyCosts.ts | 2 +- src/lib/ui/SortableTable.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/server/db/getCompanyCosts.ts b/src/lib/server/db/getCompanyCosts.ts index c8d22783..e4591b71 100644 --- a/src/lib/server/db/getCompanyCosts.ts +++ b/src/lib/server/db/getCompanyCosts.ts @@ -83,7 +83,7 @@ export async function getCompanyCosts(companyId?: number) { availabilitiesPerDayAndVehicle[dayIdx].set( vehicle, (availabilitiesPerDayAndVehicle[dayIdx].get(vehicle) ?? 0) + - availability.getDurationMs() + (day.intersect(availability)?.getDurationMs() ?? 0) ); } }) diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index 69f1593e..4532eb9e 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -49,7 +49,7 @@ onclick={() => sortAndToggle(i)} > {col.text} - + {:else} From 1ebda84dc6cfa2176614f45f26a8c8476fc6c4aa Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 10:10:03 +0100 Subject: [PATCH 03/11] wip --- src/lib/server/db/getCompanyCosts.test.ts | 34 +++++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/lib/server/db/getCompanyCosts.test.ts b/src/lib/server/db/getCompanyCosts.test.ts index ad23c563..1eba1daf 100644 --- a/src/lib/server/db/getCompanyCosts.test.ts +++ b/src/lib/server/db/getCompanyCosts.test.ts @@ -172,12 +172,17 @@ describe('test accounting', () => { await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); const { costPerDayAndVehicle } = await getCompanyCosts(); - expect(costPerDayAndVehicle).toHaveLength(1); - expect(costPerDayAndVehicle[0].taxameter).toBe(10200); - expect(costPerDayAndVehicle[0].uncapped).toBe(9600); - expect(costPerDayAndVehicle[0].capped).toBe(7650); - expect(costPerDayAndVehicle[0].availabilityDuration).toBe(2 * HOUR); - expect(costPerDayAndVehicle[0].customerCount).toBe(2); + expect(costPerDayAndVehicle).toHaveLength(2); + expect(costPerDayAndVehicle[0].taxameter).toBe(5100); + expect(costPerDayAndVehicle[0].uncapped).toBe(4800); + expect(costPerDayAndVehicle[0].capped).toBe(3900); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); + expect(costPerDayAndVehicle[1].taxameter).toBe(5100); + expect(costPerDayAndVehicle[1].uncapped).toBe(4800); + expect(costPerDayAndVehicle[1].capped).toBe(3900); + expect(costPerDayAndVehicle[1].availabilityDuration).toBe(HOUR); + expect(costPerDayAndVehicle[1].customerCount).toBe(1); }); it('tours with 2 customers', async () => { @@ -209,12 +214,17 @@ describe('test accounting', () => { await setRequest(t1, u.id, '', 1, true); await setRequest(t2, u.id, '', 1, true); const { costPerDayAndVehicle } = await getCompanyCosts(); - expect(costPerDayAndVehicle).toHaveLength(1); - expect(costPerDayAndVehicle[0].taxameter).toBe(40400); - expect(costPerDayAndVehicle[0].uncapped).toBe(39800); - expect(costPerDayAndVehicle[0].capped).toBe(24050); - expect(costPerDayAndVehicle[0].availabilityDuration).toBe(7 * HOUR); - expect(costPerDayAndVehicle[0].customerCount).toBe(2); + expect(costPerDayAndVehicle).toHaveLength(2); + expect(costPerDayAndVehicle[0].taxameter).toBe(5100); + expect(costPerDayAndVehicle[0].uncapped).toBe(4800); + expect(costPerDayAndVehicle[0].capped).toBe(4800); + expect(costPerDayAndVehicle[0].availabilityDuration).toBe(3 * HOUR); + expect(costPerDayAndVehicle[0].customerCount).toBe(1); + expect(costPerDayAndVehicle[1].taxameter).toBe(35300); + expect(costPerDayAndVehicle[1].uncapped).toBe(35000);14000 + 21000/4 + expect(costPerDayAndVehicle[1].capped).toBe(19250); + expect(costPerDayAndVehicle[1].availabilityDuration).toBe(4 * HOUR); + expect(costPerDayAndVehicle[1].customerCount).toBe(1); }); it('2 companies', async () => { From 53841945195299d8d36db40e689c53dea23b05be Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 10:11:58 +0100 Subject: [PATCH 04/11] wip --- src/lib/server/db/getCompanyCosts.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/db/getCompanyCosts.test.ts b/src/lib/server/db/getCompanyCosts.test.ts index 1eba1daf..8692423f 100644 --- a/src/lib/server/db/getCompanyCosts.test.ts +++ b/src/lib/server/db/getCompanyCosts.test.ts @@ -221,7 +221,7 @@ describe('test accounting', () => { expect(costPerDayAndVehicle[0].availabilityDuration).toBe(3 * HOUR); expect(costPerDayAndVehicle[0].customerCount).toBe(1); expect(costPerDayAndVehicle[1].taxameter).toBe(35300); - expect(costPerDayAndVehicle[1].uncapped).toBe(35000);14000 + 21000/4 + expect(costPerDayAndVehicle[1].uncapped).toBe(35000); expect(costPerDayAndVehicle[1].capped).toBe(19250); expect(costPerDayAndVehicle[1].availabilityDuration).toBe(4 * HOUR); expect(costPerDayAndVehicle[1].customerCount).toBe(1); From 4a769d89afe3090ed969f525b347d7a72406b53d Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 10:24:24 +0100 Subject: [PATCH 05/11] wip --- src/lib/server/db/getCompanyCosts.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/server/db/getCompanyCosts.test.ts b/src/lib/server/db/getCompanyCosts.test.ts index 8692423f..9bdcd9d8 100644 --- a/src/lib/server/db/getCompanyCosts.test.ts +++ b/src/lib/server/db/getCompanyCosts.test.ts @@ -166,7 +166,7 @@ describe('test accounting', () => { const v1 = await addTaxi(c1, dummyCapacities); const v2 = await addTaxi(c1, dummyCapacities); await setAvailability(v1, testDays[0] + 3 * HOUR, testDays[0] + 4 * HOUR); - await setAvailability(v2, testDays[0] + 3 * HOUR, testDays[0] + 4 * HOUR); + await setAvailability(v2, testDays[0] + 2 * HOUR, testDays[0] + 4 * HOUR); const t1 = (await setTour(v1, testDays[0] + HOUR, testDays[0] + HOUR * 2, 5100))!.id; const t2 = (await setTour(v2, testDays[0] + HOUR, testDays[0] + HOUR * 2, 5100))!.id; await setRequest(t1, u.id, '', 1, true); @@ -175,13 +175,13 @@ describe('test accounting', () => { expect(costPerDayAndVehicle).toHaveLength(2); expect(costPerDayAndVehicle[0].taxameter).toBe(5100); expect(costPerDayAndVehicle[0].uncapped).toBe(4800); - expect(costPerDayAndVehicle[0].capped).toBe(3900); + expect(costPerDayAndVehicle[0].capped).toBe(3825); expect(costPerDayAndVehicle[0].availabilityDuration).toBe(HOUR); expect(costPerDayAndVehicle[0].customerCount).toBe(1); expect(costPerDayAndVehicle[1].taxameter).toBe(5100); expect(costPerDayAndVehicle[1].uncapped).toBe(4800); - expect(costPerDayAndVehicle[1].capped).toBe(3900); - expect(costPerDayAndVehicle[1].availabilityDuration).toBe(HOUR); + expect(costPerDayAndVehicle[1].capped).toBe(4800); + expect(costPerDayAndVehicle[1].availabilityDuration).toBe(2 * HOUR); expect(costPerDayAndVehicle[1].customerCount).toBe(1); }); From a359e2b7ef7f333243a154dcb8919f1d01effe76 Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 14:22:42 +0100 Subject: [PATCH 06/11] wip --- src/lib/ui/AccountingView.svelte | 2 +- src/lib/ui/SortableTable.svelte | 36 +++++++++++-------- src/lib/ui/tableData.ts | 62 +++++++++++++++++--------------- 3 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/lib/ui/AccountingView.svelte b/src/lib/ui/AccountingView.svelte index 291b5c2e..07ad2e77 100644 --- a/src/lib/ui/AccountingView.svelte +++ b/src/lib/ui/AccountingView.svelte @@ -209,7 +209,7 @@ addSumRow: boolean ) => { let data = []; - data.push(cols.map((col) => col.text.trim())); + data.push(cols.map((col) => col.text.join(' '))); for (let row of rows) { data.push(cols.map((col) => col.toTableEntry(row))); } diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index 4532eb9e..b4ab7c01 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -12,7 +12,7 @@ }: { rows: T[]; cols: { - text: string; + text: string[]; sort: undefined | ((r1: T, r2: T) => number); toTableEntry: (r: T) => string | number; }[]; @@ -36,25 +36,31 @@ }; +{#snippet tableHead(text: string[], i: number, sort: boolean)} + {#if sort} + + + + {:else} + + {#each text as line} + {line}
+ {/each}
+ {/if} +{/snippet} +
{#each cols as col, i} - {#if col.sort != undefined} - - - - {:else} - {col.text} - {/if} + {@render tableHead(col.text, i, col.sort != undefined)} {/each} diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index 163d01ef..773fa031 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -21,7 +21,7 @@ export type Subtractions = CompanyRow & { }; export type Column = { - text: string; + text: string[]; sort: undefined | ((r1: T, r2: T) => number); toTableEntry: (r: T) => string | number; }; @@ -64,7 +64,7 @@ const displayDuration = (duration: number) => { }; const firstTourColAdmin: Column = { - text: 'Unternehmen', + text: ['Unternehmen'], sort: (a: TourWithRequests, b: TourWithRequests) => a.companyId - b.companyId, toTableEntry: (r: TourWithRequests) => r.companyName ?? '' }; @@ -72,39 +72,39 @@ const firstTourColAdmin: Column = { const restTourCols = (isAdmin: boolean): Column[] => { return [ { - text: 'Fahrzeug', + text: ['Fahrzeug'], sort: (a: TourWithRequests, b: TourWithRequests) => a.vehicleId - b.vehicleId, toTableEntry: (r: TourWithRequests) => r.licensePlate ?? '' }, { - text: 'Abfahrt ', + text: ['Abfahrt'], sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.startTime - t2.startTime, toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.startTime, true) }, { - text: 'Ankunft', + text: ['Ankunft'], sort: (t1: TourWithRequests, t2: TourWithRequests) => t1.endTime - t2.endTime, toTableEntry: (r: TourWithRequests) => displayUnixtimeMs(r.endTime, true) }, { - text: 'Kunden', + text: ['Kunden'], sort: (t1: TourWithRequests, t2: TourWithRequests) => getCustomerCount(t1, false) - getCustomerCount(t2, false), toTableEntry: (r: TourWithRequests) => getCustomerCount(r, false) }, { - text: 'erschienene Kunden ', + text: ['erschienene', 'Kunden'], sort: (t1: TourWithRequests, t2: TourWithRequests) => getCustomerCount(t1, true) - getCustomerCount(t2, true), toTableEntry: (r: TourWithRequests) => getCustomerCount(r, true) }, { - text: 'Taxameterstand ', + text: ['Taxameterstand'], sort: (t1: TourWithRequests, t2: TourWithRequests) => (t1.fare ?? 0) - (t2.fare ?? 0), toTableEntry: (r: TourWithRequests) => getEuroString(r.fare) }, { - text: isAdmin ? 'Kosten ' : 'Einnahmen ', + text: isAdmin ? ['Kosten'] : ['Einnahmen'], sort: (t1: TourWithRequests, t2: TourWithRequests) => getTourCost(t1) - getTourCost(t2), toTableEntry: (r: TourWithRequests) => getEuroString(getTourCost(r)) } @@ -116,52 +116,52 @@ export const tourColsCompany = restTourCols(false); export const subtractionColsAdmin: Column[] = [ { - text: 'Unternehmen', + text: ['Unternehmen'], sort: (a: Subtractions, b: Subtractions) => a.companyId - b.companyId, toTableEntry: (r: Subtractions) => r.companyName ?? '' }, { - text: 'Fahrzeug', + text: ['Fahrzeug'], sort: (a: Subtractions, b: Subtractions) => a.vehicleId - b.vehicleId, toTableEntry: (r: Subtractions) => r.licensePlate ?? '' }, { - text: 'Tag ', + text: ['Tag'], sort: (a: Subtractions, b: Subtractions) => a.timestamp - b.timestamp, toTableEntry: (r: Subtractions) => displayUnixtimeMs(r.timestamp) }, { - text: 'Kunden', + text: ['Kunden'], sort: (a: Subtractions, b: Subtractions) => a.customerCount - b.customerCount, toTableEntry: (r: Subtractions) => r.customerCount }, { - text: 'erschienene Kunden ', + text: ['erschienene', 'Kunden'], sort: (a: Subtractions, b: Subtractions) => a.verifiedCustomerCount - b.verifiedCustomerCount, toTableEntry: (r: Subtractions) => r.verifiedCustomerCount }, { - text: 'Taxameterstand kumuliert ', + text: ['Taxameterstand', 'kumuliert'], sort: (a: Subtractions, b: Subtractions) => a.taxameter - b.taxameter, toTableEntry: (r: Subtractions) => getEuroString(r.taxameter) }, { - text: 'Einnahmen ohne Obergrenze ', + text: ['Einnahmen', 'ohne Obergrenze'], sort: (a: Subtractions, b: Subtractions) => a.uncapped - b.uncapped, toTableEntry: (r: Subtractions) => getEuroString(r.uncapped) }, { - text: 'gesetzte Verfügbarkeit', + text: ['gesetzte', 'Verfügbarkeit'], sort: (a: Subtractions, b: Subtractions) => a.availabilityDuration - b.availabilityDuration, toTableEntry: (r: Subtractions) => displayDuration(r.availabilityDuration) }, { - text: 'Obergrenze', + text: ['Obergrenze'], sort: (a: Subtractions, b: Subtractions) => a.availabilityDuration - b.availabilityDuration, toTableEntry: (r: Subtractions) => getEuroString((r.availabilityDuration / HOUR) * CAP) }, { - text: 'über Obergrenze', + text: ['über', 'Obergrenze'], sort: (a: Subtractions, b: Subtractions) => a.uncapped - (a.availabilityDuration / HOUR) * CAP - @@ -170,12 +170,12 @@ export const subtractionColsAdmin: Column[] = [ getEuroString(r.uncapped - (r.availabilityDuration / HOUR) * CAP) }, { - text: 'Einnahmen mit Obergrenze ', + text: ['Einnahmen', 'mit Obergrenze'], sort: (a: Subtractions, b: Subtractions) => a.capped - b.capped, toTableEntry: (r: Subtractions) => getEuroString(r.capped) }, { - text: 'Abzüge ', + text: ['Abzüge'], sort: (a: Subtractions, b: Subtractions) => a.uncapped - a.capped - b.uncapped + b.capped, toTableEntry: (r: Subtractions) => getEuroString(r.uncapped - r.capped) } @@ -185,33 +185,37 @@ export const subtractionColsCompany = subtractionColsAdmin.slice(1); export const companyColsAdmin: Column[] = [ { - text: 'Unternehmen', + text: ['Unternehmen'], sort: (a: CompanyRow, b: CompanyRow) => a.companyId - b.companyId, toTableEntry: (r: CompanyRow) => r.companyName ?? '' }, - { text: 'Kunden', sort: undefined, toTableEntry: (r: CompanyRow) => r.customerCount }, { - text: 'erschienene Kunden ', - sort: undefined, + text: ['Kunden'], + sort: (a: CompanyRow, b: CompanyRow) => a.customerCount - b.customerCount, + toTableEntry: (r: CompanyRow) => r.customerCount + }, + { + text: ['erschienene', 'Kunden'], + sort: (a: CompanyRow, b: CompanyRow) => a.verifiedCustomerCount - b.verifiedCustomerCount, toTableEntry: (r: CompanyRow) => r.verifiedCustomerCount }, { - text: 'Taxameterstand kumuliert ', + text: ['Taxameterstand', 'kumuliert'], sort: (a: CompanyRow, b: CompanyRow) => a.taxameter - b.taxameter, toTableEntry: (r: CompanyRow) => getEuroString(r.taxameter) }, { - text: 'Kosten ohne Obergrenze ', + text: ['Kosten ohne', 'Obergrenze'], sort: (a: CompanyRow, b: CompanyRow) => a.uncapped - b.uncapped, toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped) }, { - text: 'Kosten mit Obergrenze ', + text: ['Kosten mit Obergrenze'], sort: (a: CompanyRow, b: CompanyRow) => a.capped - b.capped, toTableEntry: (r: CompanyRow) => getEuroString(r.capped) }, { - text: 'Abzüge ', + text: ['Abzüge'], sort: (a: CompanyRow, b: CompanyRow) => a.uncapped - a.capped - b.uncapped + b.capped, toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped - r.capped) } From ab981ab888086bfa80bdf838e1956aafffa2d36f Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 15:13:51 +0100 Subject: [PATCH 07/11] wip --- src/lib/server/db/getTours.ts | 1 - src/lib/ui/SortableTable.svelte | 4 ++-- src/lib/ui/tableData.ts | 25 ++++++++++++++++--------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/lib/server/db/getTours.ts b/src/lib/server/db/getTours.ts index 205bca7c..97deb05b 100644 --- a/src/lib/server/db/getTours.ts +++ b/src/lib/server/db/getTours.ts @@ -76,7 +76,6 @@ export const getToursWithRequests = async ( companyId?: number, timeRange?: [UnixtimeMs, UnixtimeMs] ) => { - console.log({ companyId }); return await db .selectFrom('tour') .innerJoin('vehicle', 'vehicle.id', 'tour.vehicle') diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index b4ab7c01..4f50c106 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -38,7 +38,7 @@ {#snippet tableHead(text: string[], i: number, sort: boolean)} {#if sort} - + {:else} - + {#each text as line} {line}
{/each}
[] = [ (a.availabilityDuration / HOUR) * CAP - (b.uncapped - (b.availabilityDuration / HOUR) * CAP), toTableEntry: (r: Subtractions) => - getEuroString(r.uncapped - (r.availabilityDuration / HOUR) * CAP) + getEuroString(Math.max(r.uncapped - (r.availabilityDuration / HOUR) * CAP, 0)) }, { text: ['Einnahmen', 'mit Obergrenze'], @@ -182,41 +182,48 @@ export const subtractionColsAdmin: Column[] = [ ]; export const subtractionColsCompany = subtractionColsAdmin.slice(1); - +const summationLast = (tiebreak: (a: CompanyRow, b: CompanyRow) => number) => { + return (a: CompanyRow, b: CompanyRow) => + a.companyName === 'Summiert' ? -1 : b.companyName === ' Summiert' ? 1 : tiebreak(a, b); +}; export const companyColsAdmin: Column[] = [ { text: ['Unternehmen'], - sort: (a: CompanyRow, b: CompanyRow) => a.companyId - b.companyId, + sort: summationLast((a: CompanyRow, b: CompanyRow) => a.companyId - b.companyId), toTableEntry: (r: CompanyRow) => r.companyName ?? '' }, { text: ['Kunden'], - sort: (a: CompanyRow, b: CompanyRow) => a.customerCount - b.customerCount, + sort: summationLast((a: CompanyRow, b: CompanyRow) => a.customerCount - b.customerCount), toTableEntry: (r: CompanyRow) => r.customerCount }, { text: ['erschienene', 'Kunden'], - sort: (a: CompanyRow, b: CompanyRow) => a.verifiedCustomerCount - b.verifiedCustomerCount, + sort: summationLast( + (a: CompanyRow, b: CompanyRow) => a.verifiedCustomerCount - b.verifiedCustomerCount + ), toTableEntry: (r: CompanyRow) => r.verifiedCustomerCount }, { text: ['Taxameterstand', 'kumuliert'], - sort: (a: CompanyRow, b: CompanyRow) => a.taxameter - b.taxameter, + sort: summationLast((a: CompanyRow, b: CompanyRow) => a.taxameter - b.taxameter), toTableEntry: (r: CompanyRow) => getEuroString(r.taxameter) }, { text: ['Kosten ohne', 'Obergrenze'], - sort: (a: CompanyRow, b: CompanyRow) => a.uncapped - b.uncapped, + sort: summationLast((a: CompanyRow, b: CompanyRow) => a.uncapped - b.uncapped), toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped) }, { text: ['Kosten mit Obergrenze'], - sort: (a: CompanyRow, b: CompanyRow) => a.capped - b.capped, + sort: summationLast((a: CompanyRow, b: CompanyRow) => a.capped - b.capped), toTableEntry: (r: CompanyRow) => getEuroString(r.capped) }, { text: ['Abzüge'], - sort: (a: CompanyRow, b: CompanyRow) => a.uncapped - a.capped - b.uncapped + b.capped, + sort: summationLast( + (a: CompanyRow, b: CompanyRow) => a.uncapped - a.capped - b.uncapped + b.capped + ), toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped - r.capped) } ]; From 92816d9936df556fa270352d9ccfad8ab4f37195 Mon Sep 17 00:00:00 2001 From: nils Date: Tue, 11 Mar 2025 15:50:30 +0100 Subject: [PATCH 08/11] wip --- src/lib/server/db/getCompanyCosts.ts | 12 ++++++------ src/lib/ui/AccountingView.svelte | 1 + src/lib/ui/SortableTable.svelte | 7 ++++++- src/lib/ui/tableData.ts | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/lib/server/db/getCompanyCosts.ts b/src/lib/server/db/getCompanyCosts.ts index e4591b71..6c590793 100644 --- a/src/lib/server/db/getCompanyCosts.ts +++ b/src/lib/server/db/getCompanyCosts.ts @@ -156,8 +156,8 @@ export async function getCompanyCosts(companyId?: number) { verifiedCustomerCount: number; availabilityDuration: UnixtimeMs; companyName: string | null; - vehicleId: number; licensePlate: string; + companyId: number; } > >(); @@ -173,8 +173,8 @@ export async function getCompanyCosts(companyId?: number) { verifiedCustomerCount: number; availabilityDuration: UnixtimeMs; companyName: string | null; - vehicleId: number; licensePlate: string; + companyId: number; } >(); if (taxameterPerDayAndVehicle[d] == undefined) { @@ -194,8 +194,8 @@ export async function getCompanyCosts(companyId?: number) { verifiedCustomerCount: taxameter.verifiedCustomerCount, availabilityDuration: availabilitiesPerDayAndVehicle[d].get(vehicle) ?? 0, companyName: companyByVehicle.get(vehicle)!.name, - vehicleId: vehicle, - licensePlate: companyByVehicle.get(vehicle)!.licensePlate + licensePlate: companyByVehicle.get(vehicle)!.licensePlate, + companyId: companyByVehicle.get(vehicle)!.id }); }); } @@ -206,8 +206,9 @@ export async function getCompanyCosts(companyId?: number) { costPerDayAndVehicle: costPerDayAndVehicle.flatMap((vehicleCosts) => Array.from(vehicleCosts).map( ([ - companyId, + vehicleId, { + companyId, capped, uncapped, companyName, @@ -216,7 +217,6 @@ export async function getCompanyCosts(companyId?: number) { taxameter, timestamp, verifiedCustomerCount, - vehicleId, licensePlate } ]) => { diff --git a/src/lib/ui/AccountingView.svelte b/src/lib/ui/AccountingView.svelte index 07ad2e77..f78a6d65 100644 --- a/src/lib/ui/AccountingView.svelte +++ b/src/lib/ui/AccountingView.svelte @@ -308,6 +308,7 @@ bind:rows={currentCompanyRows} cols={isAdmin ? companyColsAdmin : companyColsCompany} {isAdmin} + fixLastRow={isAdmin} /> {/snippet} diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index 4f50c106..dacf97fb 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -8,7 +8,8 @@ cols, getRowStyle, selectedRow = $bindable(), - bindSelectedRow + bindSelectedRow, + fixLastRow }: { rows: T[]; cols: { @@ -20,6 +21,7 @@ getRowStyle?: (row: T) => string; selectedRow?: undefined | T[]; bindSelectedRow?: boolean; + fixLastRow?: boolean; } = $props(); const descending = Array.from({ length: cols.length }, () => true); @@ -27,6 +29,9 @@ rows.sort(cols[idx].sort); if (!descending[idx]) { rows.reverse(); + if (fixLastRow) { + rows = rows.splice(1).concat([rows[0]]); + } } else { for (let i = 0; i < descending.length; i++) { if (i != idx) descending[i] = true; diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index adabfc98..76fa374b 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -167,7 +167,7 @@ export const subtractionColsAdmin: Column[] = [ (a.availabilityDuration / HOUR) * CAP - (b.uncapped - (b.availabilityDuration / HOUR) * CAP), toTableEntry: (r: Subtractions) => - getEuroString(Math.max(r.uncapped - (r.availabilityDuration / HOUR) * CAP, 0)) + getEuroString(r.uncapped - (r.availabilityDuration / HOUR) * CAP) }, { text: ['Einnahmen', 'mit Obergrenze'], @@ -184,7 +184,7 @@ export const subtractionColsAdmin: Column[] = [ export const subtractionColsCompany = subtractionColsAdmin.slice(1); const summationLast = (tiebreak: (a: CompanyRow, b: CompanyRow) => number) => { return (a: CompanyRow, b: CompanyRow) => - a.companyName === 'Summiert' ? -1 : b.companyName === ' Summiert' ? 1 : tiebreak(a, b); + a.companyId === -1 ? 1 : b.companyId === -1 ? -1 : tiebreak(a, b); }; export const companyColsAdmin: Column[] = [ { From e11ee69f97b8c1b950bc251927f9cd4bc25befd6 Mon Sep 17 00:00:00 2001 From: nils Date: Wed, 12 Mar 2025 10:02:31 +0100 Subject: [PATCH 09/11] wip --- src/lib/ui/AccountingView.svelte | 6 +++--- src/lib/ui/SortableTable.svelte | 8 ++++---- src/lib/ui/tableData.ts | 9 +++++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/lib/ui/AccountingView.svelte b/src/lib/ui/AccountingView.svelte index f78a6d65..2365bd65 100644 --- a/src/lib/ui/AccountingView.svelte +++ b/src/lib/ui/AccountingView.svelte @@ -343,7 +343,7 @@ {range.start === undefined ? 'Zeitspanne' : range.start + ' - ' + range.end} Abrechnung - -
+ +
{@render filterOptions()}
diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index dacf97fb..0efbdfbe 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -46,7 +46,7 @@ @@ -54,9 +54,9 @@ {:else} {#each text as line} - {line}
- {/each}
+ {line}
+ {/each} +
{/if} {/snippet} diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index 76fa374b..c63a0a54 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -107,6 +107,11 @@ const restTourCols = (isAdmin: boolean): Column[] => { text: isAdmin ? ['Kosten'] : ['Einnahmen'], sort: (t1: TourWithRequests, t2: TourWithRequests) => getTourCost(t1) - getTourCost(t2), toTableEntry: (r: TourWithRequests) => getEuroString(getTourCost(r)) + }, + { + text: ['Status'], + sort: undefined, + toTableEntry: (r: TourWithRequests) => r.cancelled ? (r.message === null ? 'von Kunden storniert' : 'storniert') : (r.fare === null ? (r.endTime < Date.now() ? 'Taxameterstand nicht eingetragen' : 'geplant') : 'beendet') } ]; }; @@ -167,7 +172,7 @@ export const subtractionColsAdmin: Column[] = [ (a.availabilityDuration / HOUR) * CAP - (b.uncapped - (b.availabilityDuration / HOUR) * CAP), toTableEntry: (r: Subtractions) => - getEuroString(r.uncapped - (r.availabilityDuration / HOUR) * CAP) + getEuroString(Math.max(r.uncapped - (r.availabilityDuration / HOUR) * CAP, 0)) }, { text: ['Einnahmen', 'mit Obergrenze'], @@ -215,7 +220,7 @@ export const companyColsAdmin: Column[] = [ toTableEntry: (r: CompanyRow) => getEuroString(r.uncapped) }, { - text: ['Kosten mit Obergrenze'], + text: ['Kosten mit', 'Obergrenze'], sort: summationLast((a: CompanyRow, b: CompanyRow) => a.capped - b.capped), toTableEntry: (r: CompanyRow) => getEuroString(r.capped) }, From 3a5f30d41e9e80706d568f428ff7a7e03b30e23f Mon Sep 17 00:00:00 2001 From: nils Date: Wed, 12 Mar 2025 10:04:52 +0100 Subject: [PATCH 10/11] wip --- src/lib/ui/SortableTable.svelte | 4 ++-- src/lib/ui/tableData.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/lib/ui/SortableTable.svelte b/src/lib/ui/SortableTable.svelte index 0efbdfbe..4d623dd9 100644 --- a/src/lib/ui/SortableTable.svelte +++ b/src/lib/ui/SortableTable.svelte @@ -46,7 +46,7 @@ @@ -54,7 +54,7 @@ {:else} {#each text as line} - {line}
+ {line}
{/each}
{/if} diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index c63a0a54..d2a01977 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -111,7 +111,16 @@ const restTourCols = (isAdmin: boolean): Column[] => { { text: ['Status'], sort: undefined, - toTableEntry: (r: TourWithRequests) => r.cancelled ? (r.message === null ? 'von Kunden storniert' : 'storniert') : (r.fare === null ? (r.endTime < Date.now() ? 'Taxameterstand nicht eingetragen' : 'geplant') : 'beendet') + toTableEntry: (r: TourWithRequests) => + r.cancelled + ? r.message === null + ? 'von Kunden storniert' + : 'storniert' + : r.fare === null + ? r.endTime < Date.now() + ? 'Taxameterstand nicht eingetragen' + : 'geplant' + : 'beendet' } ]; }; From 6e1f3189ca180ad75577d706d7beb4c247977d08 Mon Sep 17 00:00:00 2001 From: nils Date: Wed, 12 Mar 2025 10:12:39 +0100 Subject: [PATCH 11/11] wip --- src/lib/ui/tableData.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/lib/ui/tableData.ts b/src/lib/ui/tableData.ts index d2a01977..1c5c719f 100644 --- a/src/lib/ui/tableData.ts +++ b/src/lib/ui/tableData.ts @@ -63,6 +63,18 @@ const displayDuration = (duration: number) => { return addLeadingZero(hours) + ':' + addLeadingZero(minutes) + 'h'; }; +const getStatus = (r: TourWithRequests) => { + return r.cancelled + ? r.message === null + ? 'von Kunden storniert' + : 'storniert' + : r.fare === null + ? r.endTime < Date.now() + ? 'Taxameterstand nicht eingetragen' + : 'geplant' + : 'beendet'; +}; + const firstTourColAdmin: Column = { text: ['Unternehmen'], sort: (a: TourWithRequests, b: TourWithRequests) => a.companyId - b.companyId, @@ -110,17 +122,9 @@ const restTourCols = (isAdmin: boolean): Column[] => { }, { text: ['Status'], - sort: undefined, - toTableEntry: (r: TourWithRequests) => - r.cancelled - ? r.message === null - ? 'von Kunden storniert' - : 'storniert' - : r.fare === null - ? r.endTime < Date.now() - ? 'Taxameterstand nicht eingetragen' - : 'geplant' - : 'beendet' + sort: (t1: TourWithRequests, t2: TourWithRequests) => + getStatus(t1) < getStatus(t2) ? -1 : 1, + toTableEntry: (r: TourWithRequests) => getStatus(r) } ]; };