mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 16:28:12 +00:00
Merge branch 'dev'
This commit is contained in:
@@ -28,13 +28,18 @@ import type { InterchangeDocument } from '../interchange-documents/entities/inte
|
||||
import { LastMileService } from '../last-mile/last-mile.service';
|
||||
import { UpdateLastMileDto } from '../last-mile/dto/update-last-mile.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { sendCompanyChannels } from '../notifications/notify-company.util';
|
||||
import {
|
||||
sendCompanyChannels,
|
||||
notifyCarriageAcceptanceReady as notifyCarriageAcceptanceReadyShared,
|
||||
} from '../notifications/notify-company.util';
|
||||
import {
|
||||
companyNotifyPhoneExpr,
|
||||
primaryContactUserJoin,
|
||||
} from '../notifications/resolve-company-phone.util';
|
||||
import { SignaturesService } from '../signatures/signatures.service';
|
||||
import { StampSettingsService } from '../stamp-settings/stamp-settings.service';
|
||||
import { LogoSettingsService } from '../logo-settings/logo-settings.service';
|
||||
import { logoImageCss, logoMarkup } from '../billing/documents/logo-markup.util';
|
||||
import { sealClass, sealImageCss, sealMarkup } from '../billing/documents/seal-markup.util';
|
||||
import { BulkInspectDto } from './dto/bulk-inspect.dto';
|
||||
import { BulkReceiveDto, TruckEntranceDto } from './dto/bulk-receive.dto';
|
||||
@@ -417,6 +422,7 @@ export class WarehouseInventoryService {
|
||||
private readonly inbox: NotificationInboxService,
|
||||
private readonly events: EventEmitter2,
|
||||
private readonly stampSettings: StampSettingsService,
|
||||
private readonly logoSettings: LogoSettingsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -3748,6 +3754,7 @@ export class WarehouseInventoryService {
|
||||
reference,
|
||||
issuedAt,
|
||||
stampImageUrl: await this.stampSettings.getStampImageUrl(),
|
||||
logoImageUrl: await this.logoSettings.getLogoImageUrl(),
|
||||
bookingReference,
|
||||
bookingStatus: row?.bookingStatus ?? null,
|
||||
customerName: row?.customerName ?? null,
|
||||
@@ -4219,6 +4226,7 @@ export class WarehouseInventoryService {
|
||||
|
||||
const html = this.buildGrnDocumentHtml({
|
||||
grnNumber: row.grnNumber,
|
||||
logoImageUrl: await this.logoSettings.getLogoImageUrl(),
|
||||
receivedAt: row.receivedAt ? new Date(row.receivedAt) : new Date(),
|
||||
bookingReference: row.bookingReference ?? row.bookingId ?? 'N/A',
|
||||
bookingStatus: row.bookingStatus ?? null,
|
||||
@@ -4803,6 +4811,7 @@ export class WarehouseInventoryService {
|
||||
reference,
|
||||
handedOverAt,
|
||||
stampImageUrl: await this.stampSettings.getStampImageUrl(),
|
||||
logoImageUrl: await this.logoSettings.getLogoImageUrl(),
|
||||
bookingReference,
|
||||
bookingStatus: row.bookingStatus ?? null,
|
||||
customerName: row.customerName ?? null,
|
||||
@@ -5152,6 +5161,7 @@ export class WarehouseInventoryService {
|
||||
activityType: 'INVENTORY_DISPATCHED',
|
||||
description: 'Inventory dispatched',
|
||||
performedBy,
|
||||
freeCapacity: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5436,6 +5446,8 @@ export class WarehouseInventoryService {
|
||||
description: string;
|
||||
performedBy?: string;
|
||||
preloaded?: WarehouseInventory;
|
||||
/** Cargo physically leaves the warehouse on this transition — free up capacity (mirrors deliver()). */
|
||||
freeCapacity?: boolean;
|
||||
},
|
||||
): Promise<WarehouseInventory> {
|
||||
const item = opts.preloaded ?? (await this.findById(id));
|
||||
@@ -5446,6 +5458,20 @@ export class WarehouseInventoryService {
|
||||
status: to,
|
||||
[opts.timestampField]: new Date(),
|
||||
});
|
||||
|
||||
if (opts.freeCapacity) {
|
||||
const weight = Number(item.weight) || 0;
|
||||
const volume = Number(item.volume) || 0;
|
||||
const containerCount = item.containerId ? Math.round(Number(item.quantity) || 0) : 0;
|
||||
await this.applyCapacityDelta(
|
||||
manager,
|
||||
{ warehouseId: item.warehouseId, yardId: item.yardId, zoneId: item.zoneId },
|
||||
-weight,
|
||||
-volume,
|
||||
-containerCount,
|
||||
);
|
||||
}
|
||||
|
||||
await this.activityLog.record(
|
||||
{
|
||||
activityType: opts.activityType,
|
||||
@@ -5484,6 +5510,8 @@ export class WarehouseInventoryService {
|
||||
zone: string | null;
|
||||
inventoryStatus: string | null;
|
||||
receiveSummary: string | null;
|
||||
/** The one global company logo; null renders the plain text brand. */
|
||||
logoImageUrl?: string | null;
|
||||
}): string {
|
||||
const esc = (value: unknown) =>
|
||||
String(value ?? '-')
|
||||
@@ -5540,6 +5568,7 @@ export class WarehouseInventoryService {
|
||||
.ref { text-align: right; font-size: 11px; color: #334155; padding-top: 8px; }
|
||||
.ref strong { display: block; color: #061323; font-size: 18px; margin: 5px 0 8px; letter-spacing: .02em; }
|
||||
.rule { height: 3px; background: #0f766e; margin: 16px 0 22px; }
|
||||
${logoImageCss()}
|
||||
.notice { width: 76%; margin: 0 0 18px; padding: 13px 18px; background: #f0fdfa; border: 1px solid #5eead4; border-left: 5px solid #0f766e; font-size: 13px; line-height: 1.45; }
|
||||
.section-title { margin: 18px 0 8px; font-size: 13px; font-weight: 800; color: #0f766e; text-transform: uppercase; letter-spacing: .12em; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
@@ -5553,6 +5582,7 @@ export class WarehouseInventoryService {
|
||||
<body>
|
||||
<div class="top">
|
||||
<div>
|
||||
${logoMarkup(data.logoImageUrl)}
|
||||
<div class="brand">Ethio-Djibouti Railway S.C.</div>
|
||||
<h1>Goods Received Note</h1>
|
||||
<div class="subtitle">Warehouse receiving confirmation</div>
|
||||
@@ -5610,6 +5640,8 @@ export class WarehouseInventoryService {
|
||||
truckWeightTons?: number | null;
|
||||
/** The one global company stamp; null falls back to the drawn text seal. */
|
||||
stampImageUrl?: string | null;
|
||||
/** The one global company logo; null renders the plain text brand. */
|
||||
logoImageUrl?: string | null;
|
||||
}): string {
|
||||
const esc = (value: unknown) =>
|
||||
String(value ?? '-')
|
||||
@@ -5697,12 +5729,14 @@ export class WarehouseInventoryService {
|
||||
.seal::before { content: ""; position: absolute; width: 78px; height: 78px; border: 1px solid #17633a; border-radius: 999px; }
|
||||
.seal span { position: relative; }
|
||||
${sealImageCss()}
|
||||
${logoImageCss()}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc">
|
||||
<div class="top">
|
||||
<div>
|
||||
${logoMarkup(data.logoImageUrl)}
|
||||
<div class="brand">Ethio-Djibouti Railway S.C.</div>
|
||||
<h1>Warehouse Release / Exit Paper</h1>
|
||||
<div class="subtitle">Official gate clearance and warehouse exit authorization</div>
|
||||
@@ -5772,6 +5806,8 @@ export class WarehouseInventoryService {
|
||||
} | null;
|
||||
/** The one global company stamp; null falls back to the drawn text seal. */
|
||||
stampImageUrl?: string | null;
|
||||
/** The one global company logo; null renders the plain text brand. */
|
||||
logoImageUrl?: string | null;
|
||||
}): string {
|
||||
const esc = (value: unknown) =>
|
||||
String(value ?? '-')
|
||||
@@ -5850,11 +5886,13 @@ export class WarehouseInventoryService {
|
||||
.seal::before { content: ""; position: absolute; width: 78px; height: 78px; border: 1px solid #17633a; border-radius: 999px; }
|
||||
.seal span { position: relative; }
|
||||
${sealImageCss()}
|
||||
${logoImageCss()}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="top">
|
||||
<div>
|
||||
${logoMarkup(data.logoImageUrl)}
|
||||
<div class="brand">Ethio-Djibouti Railway S.C.</div>
|
||||
<h1>Import Goods Handover Document</h1>
|
||||
<div class="subtitle">EDR to customer warehouse handover</div>
|
||||
@@ -6167,26 +6205,13 @@ export class WarehouseInventoryService {
|
||||
* fires right after receive, not at marshalling.
|
||||
*/
|
||||
private async notifyCarriageAcceptanceReady(bookingId: string): Promise<void> {
|
||||
try {
|
||||
const [b]: Array<{ companyId: string | null; reference: string }> = await this.dataSource.query(
|
||||
`SELECT company_id AS "companyId", reference FROM freight.bookings WHERE id = $1 AND deleted_at IS NULL`,
|
||||
[bookingId],
|
||||
);
|
||||
if (!b?.companyId) return;
|
||||
const body = `Your carriage acceptance sheet for booking ${b.reference} is ready to download from the portal.`;
|
||||
await this.inbox.notify({
|
||||
recipients: { companyId: b.companyId },
|
||||
audience: NotificationAudience.PORTAL,
|
||||
type: NotificationType.DOCUMENT_ACTION,
|
||||
title: 'Carriage acceptance sheet ready',
|
||||
body,
|
||||
link: `/bookings/${bookingId}`,
|
||||
data: { bookingId, reference: b.reference },
|
||||
});
|
||||
await sendCompanyChannels(this.dataSource, this.notifications, b.companyId, body);
|
||||
} catch (err) {
|
||||
this.logger.warn(`Carriage acceptance ready notify failed for ${bookingId}: ${(err as Error).message}`);
|
||||
}
|
||||
await notifyCarriageAcceptanceReadyShared(
|
||||
this.dataSource,
|
||||
this.notifications,
|
||||
this.inbox,
|
||||
bookingId,
|
||||
this.logger,
|
||||
);
|
||||
}
|
||||
|
||||
private async notifyOwnerInventoryReceived(params: {
|
||||
|
||||
Reference in New Issue
Block a user