From fde5e6de4b24f4754ee11acc941b62452e71cb19 Mon Sep 17 00:00:00 2001
From: Marshal
Date: Sat, 25 Jul 2026 17:14:58 +0000
Subject: [PATCH] add company stamp upload functionality for contract signing
- Introduced StampUpload component for uploading company stamp images.
- Integrated stamp upload in contract signing modal, supporting PNG and JPG formats.
- Implemented validation for file type and size (max 5 MB).
- Added visual feedback for drag-and-drop functionality.
- Updated contract-related pages to handle duplicate contract alerts and pricing notices.
- Enhanced contract expiry management with a nightly sweep service.
- Added unit tests for new features and updated existing tests for contract handling.
---
.../src/common/booking-guards.ts | 8 +
.../contract-document-view-model.builder.ts | 3 +
.../templates/_partials/signatures_block.hbs | 12 ++
.../contracts/templates/_partials/styles.hbs | 33 ++--
.../src/contracts/templates/edr-dynamic.hbs | 13 --
...2910000000000-AddContractSignatureStamp.ts | 27 +++
.../bookings/booking-pricing.service.spec.ts | 121 +++++++++++++
.../bookings/booking-pricing.service.ts | 31 +++-
.../contracts/contract-expiry.service.ts | 44 +++++
.../contracts/contract-pricing.service.ts | 16 +-
.../contract-signature-asset.spec.ts | 81 +++++++++
.../contracts/contract-transition.service.ts | 143 +++++++++++----
.../src/modules/contracts/contracts.module.ts | 2 +
.../modules/contracts/contracts.repository.ts | 45 ++++-
.../modules/contracts/contracts.service.ts | 41 +++++
.../contracts/dto/sign-contract.dto.ts | 11 ++
.../entities/contract-signature.entity.ts | 8 +
.../contracts/utils/contract-expiry.util.ts | 25 +++
.../locomotives/dto/create-locomotive.dto.ts | 21 ---
.../locomotives/entities/locomotive.entity.ts | 9 -
.../locomotives/locomotives.service.ts | 8 -
.../entities/rate-unit.util.spec.ts | 70 +++++++
.../rule-engine/entities/rate-unit.util.ts | 30 +++
.../rule-engine/entities/rate.entity.ts | 3 +
.../rule-engine/rule-engine.service.ts | 6 +-
.../rule-engine/services/rates.service.ts | 51 +++++-
.../train-scheduling/booking-batch.service.ts | 16 ++
.../booking-window.service.spec.ts | 85 +++++++++
.../booking-window.service.ts | 81 +++++++++
.../train-scheduling.controller.ts | 13 ++
.../train-scheduling.service.ts | 31 +++-
.../modules/trains/train-builder.service.ts | 31 ++++
.../modules/wagons/dto/reorder-wagons.dto.ts | 7 -
.../src/modules/wagons/wagons.controller.ts | 17 +-
.../src/modules/wagons/wagons.module.ts | 3 +-
.../src/modules/wagons/wagons.service.ts | 17 --
.../seed/data/contract-template-defaults.ts | 8 +-
.../src/seed/freight-permissions.registry.ts | 7 +
.../contracts/ContractDocumentEditorModal.tsx | 63 +++++--
.../src/components/contracts/StampUpload.tsx | 171 ++++++++++++++++++
.../contracts/gl-booking-form/total.ts | 4 +-
.../layout/FreightDashboardHeader.tsx | 7 +-
.../backoffice/src/constants/URLS.ts | 1 +
.../bookingWindows/DocReviewAlertButton.tsx | 125 +++++++++++++
.../src/hooks/rule-engine/useRuleEngine.ts | 8 +-
.../backoffice/src/lib/permissions.ts | 12 ++
.../pages/bookings/BookingRequestsPage.tsx | 28 ++-
.../src/pages/contracts/ContractViewPage.tsx | 25 ++-
.../src/pages/fleet/FleetCrudPages.tsx | 12 +-
.../src/pages/fleet/config/resources.ts | 12 +-
.../ruleEngine/RuleEngineResourcePage.tsx | 15 ++
.../src/pages/ruleEngine/config/resources.ts | 32 +++-
.../backoffice/src/services/api.ts | 17 +-
.../src/services/contracts.service.ts | 3 +
.../src/services/locomotives.service.ts | 3 -
.../src/services/trainScheduling.service.ts | 10 +
.../backoffice/src/services/wagon.service.ts | 2 -
.../backoffice/src/types/trainScheduling.ts | 19 ++
.../src/components/contracts/StampUpload.tsx | 171 ++++++++++++++++++
.../pages/contracts/ContractDetailPage.tsx | 2 +-
.../src/pages/contracts/ContractViewPage.tsx | 16 +-
.../src/pages/contracts/NewContractPage.tsx | 52 +++++-
.../contracts/new-contract-form/shared.tsx | 55 +++++-
.../new-contract-form/step3-cargo-scope.tsx | 9 +-
.../new-contract-form/step8-review.tsx | 77 ++++----
.../contracts/new-shipment-form/total.ts | 13 +-
.../portal/src/services/bookings.service.ts | 3 +
packages/types/src/freight/contracts.ts | 2 +
68 files changed, 1858 insertions(+), 289 deletions(-)
create mode 100644 apps/edr-freight-api/src/migrations/2910000000000-AddContractSignatureStamp.ts
create mode 100644 apps/edr-freight-api/src/modules/contracts/contract-expiry.service.ts
create mode 100644 apps/edr-freight-api/src/modules/contracts/contract-signature-asset.spec.ts
create mode 100644 apps/edr-freight-api/src/modules/contracts/utils/contract-expiry.util.ts
create mode 100644 apps/edr-freight-api/src/modules/rule-engine/entities/rate-unit.util.spec.ts
delete mode 100644 apps/edr-freight-api/src/modules/wagons/dto/reorder-wagons.dto.ts
create mode 100644 apps/edr-freight-web/backoffice/src/components/contracts/StampUpload.tsx
create mode 100644 apps/edr-freight-web/backoffice/src/features/bookingWindows/DocReviewAlertButton.tsx
create mode 100644 apps/edr-freight-web/portal/src/components/contracts/StampUpload.tsx
diff --git a/apps/edr-freight-api/src/common/booking-guards.ts b/apps/edr-freight-api/src/common/booking-guards.ts
index 92958b364..3e086101c 100644
--- a/apps/edr-freight-api/src/common/booking-guards.ts
+++ b/apps/edr-freight-api/src/common/booking-guards.ts
@@ -23,6 +23,14 @@ export const StaffReference = () => applyDecorators(UseGuards(JwtGuard));
export const BookingView = () => BookingStaff(FREIGHT_PERMS.bookings.view);
+/**
+ * The document-review countdown in the backoffice header. Its own permission so
+ * it can be granted to exactly the position types that decide operation
+ * requests, instead of every holder of bookings:view.
+ */
+export const BookingDocReviewAlert = () =>
+ BookingStaff(FREIGHT_PERMS.bookings.docReviewAlert);
+
export const TrainSchedulingView = () =>
BookingStaff(FREIGHT_PERMS.trainScheduling.view);
diff --git a/apps/edr-freight-api/src/contracts/contract-document-view-model.builder.ts b/apps/edr-freight-api/src/contracts/contract-document-view-model.builder.ts
index 363008660..5ceb73978 100644
--- a/apps/edr-freight-api/src/contracts/contract-document-view-model.builder.ts
+++ b/apps/edr-freight-api/src/contracts/contract-document-view-model.builder.ts
@@ -30,6 +30,8 @@ export interface ContractDocumentSignatureView {
signerDisplayName: string;
signedAt: string;
signatureImageUrl?: string | null;
+ /** Company stamp/seal; rendered next to the signature when present. */
+ stampImageUrl?: string | null;
}
/** A single unit-rate row on the contract PDF — price per unit, NO total. */
@@ -208,6 +210,7 @@ export class ContractDocumentViewModelBuilder {
signerDisplayName: row.signerDisplayName,
signedAt: this.formatDate(row.signedAt),
signatureImageUrl: row.signatureFile?.url ?? null,
+ stampImageUrl: row.stampFile?.url ?? null,
};
}
diff --git a/apps/edr-freight-api/src/contracts/templates/_partials/signatures_block.hbs b/apps/edr-freight-api/src/contracts/templates/_partials/signatures_block.hbs
index 05dd450e4..59cfceb0b 100644
--- a/apps/edr-freight-api/src/contracts/templates/_partials/signatures_block.hbs
+++ b/apps/edr-freight-api/src/contracts/templates/_partials/signatures_block.hbs
@@ -11,6 +11,12 @@
Name: {{signerDisplayName}}
Role: Authorized EDR representative
Date: {{signedAt}}
+ {{#if stampImageUrl}}
+
+ {{/if}}
{{/if}}
{{/each}}
{{else}}
@@ -32,6 +38,12 @@
Name: {{signerDisplayName}}
Role: Authorized client representative
Date: {{signedAt}}
+ {{#if stampImageUrl}}
+
+ {{/if}}
{{/if}}
{{/each}}
{{else}}
diff --git a/apps/edr-freight-api/src/contracts/templates/_partials/styles.hbs b/apps/edr-freight-api/src/contracts/templates/_partials/styles.hbs
index 118606065..d9bc9927f 100644
--- a/apps/edr-freight-api/src/contracts/templates/_partials/styles.hbs
+++ b/apps/edr-freight-api/src/contracts/templates/_partials/styles.hbs
@@ -372,25 +372,30 @@
font-size: 9pt;
margin: 4px 0;
}
-
- /* ── Witnesses ────────────────────────────────────────────────────────── */
- .witnesses { margin-top: 20px; }
- .witness-table {
- font-size: 9.5pt;
- margin-top: 6px;
+ .sig-stamp {
+ margin-top: 12px;
}
- .witness-table th,
- .witness-table td {
- border-bottom: 1px solid #c9e4d9;
- padding: 9px 8px;
- text-align: left;
- }
- .witness-table th {
+ .sig-stamp-label {
color: #0e5b45;
font-family: Arial, sans-serif;
- font-size: 8.5pt;
+ font-size: 7.5pt;
+ font-weight: 700;
+ letter-spacing: 0.4pt;
text-transform: uppercase;
}
+ .sig-stamp-box {
+ align-items: center;
+ display: flex;
+ height: 30mm;
+ justify-content: center;
+ margin-top: 5px;
+ }
+ .sig-stamp-box img {
+ display: block;
+ max-height: 30mm;
+ max-width: 45mm;
+ mix-blend-mode: multiply;
+ }
@media print {
body { background: #fff; }
diff --git a/apps/edr-freight-api/src/contracts/templates/edr-dynamic.hbs b/apps/edr-freight-api/src/contracts/templates/edr-dynamic.hbs
index 0e06d9f7b..9c4957b2b 100644
--- a/apps/edr-freight-api/src/contracts/templates/edr-dynamic.hbs
+++ b/apps/edr-freight-api/src/contracts/templates/edr-dynamic.hbs
@@ -147,19 +147,6 @@
authorized to sign and execute this Contract Agreement.
{{> signatures_block}}
-
-
-
Witnesses
-
-
- | Name | Signature | Date |
-
-
- | 1. | | | |
- | 2. | | | |
-
-
-