diff --git a/backend/src/main/java/de/strichliste/dto/TallyEntryDto.java b/backend/src/main/java/de/strichliste/dto/TallyEntryDto.java new file mode 100644 index 0000000..a7886e2 --- /dev/null +++ b/backend/src/main/java/de/strichliste/dto/TallyEntryDto.java @@ -0,0 +1,13 @@ +package de.strichliste.dto; + +import java.time.LocalDateTime; + +public record TallyEntryDto( + Long id, + Long employeeId, + String employeeFirstName, + String employeeLastName, + String productName, + int priceCents, + LocalDateTime createdAt +) {} diff --git a/backend/src/main/java/de/strichliste/resource/CompanyAdminResource.java b/backend/src/main/java/de/strichliste/resource/CompanyAdminResource.java index 1244b1a..ee3184c 100644 --- a/backend/src/main/java/de/strichliste/resource/CompanyAdminResource.java +++ b/backend/src/main/java/de/strichliste/resource/CompanyAdminResource.java @@ -187,6 +187,53 @@ public class CompanyAdminResource { return Response.ok(line).build(); } + @GET + @Path("/entries") + public Response getTallyEntries(@QueryParam("token") String token, @QueryParam("month") String month) { + AccessLink link = AccessLink.findByToken(token).orElse(null); + if (link == null || link.company == null) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + + String monthKey = month != null ? month : LocalDateTime.now().format(MONTH_FORMAT); + + List entries = TallyEntry.find( + "SELECT t FROM TallyEntry t JOIN FETCH t.employee JOIN FETCH t.product " + + "WHERE t.employee.company.id = ?1 AND t.monthKey = ?2 ORDER BY t.createdAt DESC", + link.company.id, monthKey).list(); + + List dtos = entries.stream() + .map(t -> new TallyEntryDto( + t.id, + t.employee.id, + t.employee.firstName, + t.employee.lastName, + t.product.name, + t.priceCentsAtBooking, + t.createdAt)) + .toList(); + + return Response.ok(dtos).build(); + } + + @DELETE + @Path("/entries/{id}") + @Transactional + public Response deleteTallyEntry(@QueryParam("token") String token, @PathParam("id") Long id) { + AccessLink link = AccessLink.findByToken(token).orElse(null); + if (link == null || link.company == null) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + + TallyEntry entry = TallyEntry.findById(id); + if (entry == null || !entry.employee.company.id.equals(link.company.id)) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + entry.delete(); + return Response.noContent().build(); + } + static MonthlyReportDto buildCompanyReport(Long companyId, String monthKey) { Company company = Company.findById(companyId); List employees = Employee.findAllByCompany(companyId); diff --git a/frontend/src/lib/api/client.ts b/frontend/src/lib/api/client.ts index 49757a6..668a48d 100644 --- a/frontend/src/lib/api/client.ts +++ b/frontend/src/lib/api/client.ts @@ -44,6 +44,16 @@ export interface MonthlyTally { totalCents: number; } +export interface TallyEntry { + id: number; + employeeId: number; + employeeFirstName: string; + employeeLastName: string; + productName: string; + priceCents: number; + createdAt: string; +} + export interface EmployeeReportLine { employeeId: number; firstName: string; @@ -119,7 +129,11 @@ export const api = { getReport: (token: string, month?: string) => request(`/admin/company/report?token=${token}${month ? `&month=${month}` : ''}`), getEmployeeReport: (token: string, employeeId: number, month?: string) => - request(`/admin/company/report/employee/${employeeId}?token=${token}${month ? `&month=${month}` : ''}`) + request(`/admin/company/report/employee/${employeeId}?token=${token}${month ? `&month=${month}` : ''}`), + getTallyEntries: (token: string, month?: string) => + request(`/admin/company/entries?token=${token}${month ? `&month=${month}` : ''}`), + deleteTallyEntry: (token: string, id: number) => + request(`/admin/company/entries/${id}?token=${token}`, { method: 'DELETE' }) }, // --- Provider Admin --- diff --git a/frontend/src/routes/admin/company/+page.svelte b/frontend/src/routes/admin/company/+page.svelte index c2dc5f4..addf971 100644 --- a/frontend/src/routes/admin/company/+page.svelte +++ b/frontend/src/routes/admin/company/+page.svelte @@ -179,6 +179,17 @@ + +
+

Einträge verwalten

+

+ Einzelne Produktbuchungen einsehen und bei Bedarf löschen. +

+ + Einträge anzeigen & löschen + +
+
diff --git a/frontend/src/routes/admin/company/entries/+page.svelte b/frontend/src/routes/admin/company/entries/+page.svelte new file mode 100644 index 0000000..36aab2d --- /dev/null +++ b/frontend/src/routes/admin/company/entries/+page.svelte @@ -0,0 +1,145 @@ + + + + Einträge löschen – Qaffee + + +
+ +
+ {#if !token} +

Kein Zugangstoken angegeben.

+ {:else} +
+

Produkteinträge

+ +
+ + {#if loading} +

Laden...

+ {:else if entries.length === 0} +

+ Keine Einträge in diesem Monat. +

+ {:else} +

+ {entries.length} Einträge · {formatPrice(entries.reduce((s, e) => s + e.priceCents, 0))} gesamt +

+ + + + + + + + + + + + {#each entries as entry} + + + + + + + + {/each} + +
ZeitpunktMitarbeiterProduktPreis
+ {formatDateTime(entry.createdAt)} + {entry.employeeFirstName} {entry.employeeLastName}{entry.productName}{formatPrice(entry.priceCents)} + {#if deletingId === entry.id} + Löschen... + {:else} + + {/if} +
+ {/if} + {/if} +
+ +{#if confirmId !== null} + +{/if}