From 2a816912fcae7842b1dbcb011a18d7378eae1a42 Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Wed, 12 Aug 2026 13:18:29 +0300 Subject: [PATCH] fix(freight-permissions): split company stamp from per-officer teeter permission --- .../stamp-settings.controller.ts | 6 ++-- .../src/seed/freight-permissions.registry.ts | 31 ++++++++++++------- apps/edr-freight-web/backoffice/src/App.tsx | 2 +- .../components/layout/sidebar-sections.tsx | 2 +- .../backoffice/src/lib/permissions.ts | 13 +++++--- .../backoffice/src/user-management/route.tsx | 8 +++-- .../src/logging/request-log.middleware.ts | 6 +--- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/apps/edr-freight-api/src/modules/stamp-settings/stamp-settings.controller.ts b/apps/edr-freight-api/src/modules/stamp-settings/stamp-settings.controller.ts index f03d55058..1ba60139e 100644 --- a/apps/edr-freight-api/src/modules/stamp-settings/stamp-settings.controller.ts +++ b/apps/edr-freight-api/src/modules/stamp-settings/stamp-settings.controller.ts @@ -15,21 +15,21 @@ export class StampSettingsController { constructor(private readonly service: StampSettingsService) {} @Get() - @BookingStaff([FREIGHT_PERMS.settings.invoiceStamp.view, FREIGHT_PERMS.admin]) + @BookingStaff([FREIGHT_PERMS.settings.stamp.view, FREIGHT_PERMS.admin]) @ApiOperation({ summary: "Current company stamp used on invoice/receipt PDFs" }) get() { return this.service.getView(); } @Put() - @BookingStaff([FREIGHT_PERMS.settings.invoiceStamp.manage, FREIGHT_PERMS.admin]) + @BookingStaff([FREIGHT_PERMS.settings.stamp.manage, FREIGHT_PERMS.admin]) @ApiOperation({ summary: "Replace the company stamp" }) update(@Body() dto: UpdateStampSettingDto, @CurrentUser() user: TCurrentUser) { return this.service.setStamp(dto.stampImageBase64, user?.id ?? null); } @Delete() - @BookingStaff([FREIGHT_PERMS.settings.invoiceStamp.manage, FREIGHT_PERMS.admin]) + @BookingStaff([FREIGHT_PERMS.settings.stamp.manage, FREIGHT_PERMS.admin]) @ApiOperation({ summary: "Clear the company stamp (invoices fall back to the plain seal)", }) diff --git a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts index 67df922b5..f0b719d63 100644 --- a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts +++ b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts @@ -1190,22 +1190,28 @@ export const CONFIG_SETTINGS_PERMISSIONS: FreightPermissionSeed[] = [ perm( "b4b00002-0001-4000-8000-000000000001", "edr_freight_app:settings:stamp:view", - "View stamp settings", + "View the company stamp", ), perm( "b4b00002-0001-4000-8000-000000000002", "edr_freight_app:settings:stamp:manage", - "Manage stamp settings", + "Manage the company stamp", ), + // The per-officer approval teeter (ማህተም) — an individual's own stamp + + // signature, not the company seal. It used to ride on settings:stamp:*, which + // now gates the ONE company stamp; this key was split out when the two were + // untangled. `settings:invoice_stamp:*` retired at the same time: it gated the + // company stamp before the fold and is deliberately left orphaned in any DB + // that already seeded it (the seeder upserts by key and never deletes). perm( "b4b00003-0001-4000-8000-000000000001", - "edr_freight_app:settings:invoice_stamp:view", - "View invoice stamp settings", + "edr_freight_app:settings:teeter:view", + "View own approval teeter and signature", ), perm( "b4b00003-0001-4000-8000-000000000002", - "edr_freight_app:settings:invoice_stamp:manage", - "Manage invoice stamp settings", + "edr_freight_app:settings:teeter:manage", + "Manage own approval teeter and signature", ), perm( "b4c00001-0001-4000-8000-000000000001", @@ -1907,15 +1913,18 @@ export const FREIGHT_PERMS = { view: "edr_freight_app:settings:dropdown:view", manage: "edr_freight_app:settings:dropdown:manage", }, + // The ONE company stamp/seal, applied to every generated document + // (invoices, receipts, warehouse papers, the EDR side of contracts). stamp: { view: "edr_freight_app:settings:stamp:view", manage: "edr_freight_app:settings:stamp:manage", }, - // Company stamp/seal image stamped onto invoice/receipt PDFs — separate - // from `stamp` above, which is the per-employee approval-record teeter. - invoiceStamp: { - view: "edr_freight_app:settings:invoice_stamp:view", - manage: "edr_freight_app:settings:invoice_stamp:manage", + // The per-officer approval teeter (ማህተም) + signature — genuinely per-person, + // and NOT the company seal above. Retired: `invoiceStamp`, which used to + // gate the company stamp before the two were untangled. + teeter: { + view: "edr_freight_app:settings:teeter:view", + manage: "edr_freight_app:settings:teeter:manage", }, exchangeRate: { view: "edr_freight_app:settings:exchange_rate:view", diff --git a/apps/edr-freight-web/backoffice/src/App.tsx b/apps/edr-freight-web/backoffice/src/App.tsx index 83cf82909..64f91da72 100644 --- a/apps/edr-freight-web/backoffice/src/App.tsx +++ b/apps/edr-freight-web/backoffice/src/App.tsx @@ -796,7 +796,7 @@ const App = () => { path="stamp-settings" element={ diff --git a/apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx b/apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx index c06b5a7e1..95608b6db 100644 --- a/apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx +++ b/apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx @@ -493,7 +493,7 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] label: "Company stamp", href: "/dashboard/stamp-settings", icon: , - permission: FREIGHT_PERMS.settings.invoiceStamp.view, + permission: FREIGHT_PERMS.settings.stamp.view, }, { label: "Contract templates", diff --git a/apps/edr-freight-web/backoffice/src/lib/permissions.ts b/apps/edr-freight-web/backoffice/src/lib/permissions.ts index 26b46e37b..c69aa5d1b 100644 --- a/apps/edr-freight-web/backoffice/src/lib/permissions.ts +++ b/apps/edr-freight-web/backoffice/src/lib/permissions.ts @@ -328,15 +328,18 @@ export const FREIGHT_PERMS = { view: "edr_freight_app:settings:dropdown:view", manage: "edr_freight_app:settings:dropdown:manage", }, + // The ONE company stamp/seal, applied to every generated document + // (invoices, receipts, warehouse papers, the EDR side of contracts). stamp: { view: "edr_freight_app:settings:stamp:view", manage: "edr_freight_app:settings:stamp:manage", }, - // Company stamp/seal image stamped onto invoice/receipt PDFs — separate - // from `stamp` above, which is the per-employee approval-record teeter. - invoiceStamp: { - view: "edr_freight_app:settings:invoice_stamp:view", - manage: "edr_freight_app:settings:invoice_stamp:manage", + // The per-officer approval teeter (ማህተም) + signature — genuinely per-person, + // and NOT the company seal above. Retired: `invoiceStamp`, which used to + // gate the company stamp before the two were untangled. + teeter: { + view: "edr_freight_app:settings:teeter:view", + manage: "edr_freight_app:settings:teeter:manage", }, exchangeRate: { view: "edr_freight_app:settings:exchange_rate:view", diff --git a/apps/edr-freight-web/backoffice/src/user-management/route.tsx b/apps/edr-freight-web/backoffice/src/user-management/route.tsx index ed6b13413..a336b3f9f 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/route.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/route.tsx @@ -147,13 +147,17 @@ export function UserManagementRoutes(): ReactElement { (am/en) and genuinely per-person. It used to sit at /dashboard/stamp-settings under Settings, next to the single global company stamp, which read as duplication. - Permission gate unchanged from that route. + + Gated by settings:teeter:*, split out of settings:stamp:* + when the two were untangled — settings:stamp:* now means + the company stamp, so anyone who held it for the teeter + needs the new key granted. */} diff --git a/packages/api-common/src/logging/request-log.middleware.ts b/packages/api-common/src/logging/request-log.middleware.ts index b25463161..5d9ead8b0 100644 --- a/packages/api-common/src/logging/request-log.middleware.ts +++ b/packages/api-common/src/logging/request-log.middleware.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import { Injectable, Logger, NestMiddleware } from "@nestjs/common"; +import { Injectable, NestMiddleware } from "@nestjs/common"; import { RequestLogContext, @@ -58,8 +58,6 @@ const userId = (req: LoggedRequest): string | undefined => { */ @Injectable() export class RequestLogMiddleware implements NestMiddleware { - private readonly logger = new Logger("HTTP"); - use(req: LoggedRequest, res: LoggedResponse, next: () => void): void { const start = Date.now(); const requestId = header(req, "x-request-id") ?? randomUUID(); @@ -85,8 +83,6 @@ export class RequestLogMiddleware implements NestMiddleware { const status = res.statusCode; const durationMs = Date.now() - start; - this.logger.log(`${req.method} ${url} ${status} ${durationMs}ms`); - const line = { ...ctx, // Fields the Nest console prefix used to carry. They are IN the JSON