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