mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
feat: scope notification to permission actions
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { ContractExpiryService } from './contract-expiry.service';
|
||||
import type { Contract } from './entities/contract.entity';
|
||||
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
||||
|
||||
/**
|
||||
* The reminder must warn each customer once, ten days out, and must never let a
|
||||
@@ -60,4 +61,18 @@ describe('ContractExpiryService — expiry reminder', () => {
|
||||
inbox.notify.mockRejectedValue(new Error('inbox down'));
|
||||
await expect(service.remindExpiringContracts()).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// The sweep-failure alert is staff-facing. It used to go to every employee;
|
||||
// it belongs to the people who would notice expired contracts still listed
|
||||
// as active, i.e. the contract desk.
|
||||
it('alerts the contract desk when the sweep itself fails', async () => {
|
||||
repo.expireLapsedContracts.mockRejectedValue(new Error('deadlock'));
|
||||
|
||||
await service.expireLapsedContracts();
|
||||
|
||||
expect(inbox.notify).toHaveBeenCalledTimes(1);
|
||||
expect(inbox.notify.mock.calls[0][0].recipients).toEqual({
|
||||
permissionKeys: [FREIGHT_PERMS.contracts.getNotification],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { NotificationAudience, NotificationType } from '@edr/types';
|
||||
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import { ContractsRepository } from './contracts.repository';
|
||||
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
||||
|
||||
/**
|
||||
* How many days before a contract lapses the customer is reminded. Mirrored by
|
||||
@@ -79,7 +80,11 @@ export class ContractExpiryService {
|
||||
);
|
||||
try {
|
||||
await this.inbox.notify({
|
||||
recipients: { allBackoffice: true },
|
||||
// The people who would notice expired contracts still listed as
|
||||
// active are the ones working the contract desk.
|
||||
recipients: {
|
||||
permissionKeys: [FREIGHT_PERMS.contracts.getNotification],
|
||||
},
|
||||
audience: NotificationAudience.BACKOFFICE,
|
||||
type: NotificationType.GENERIC,
|
||||
title: 'Contract expiry sweep failed',
|
||||
|
||||
@@ -11,6 +11,16 @@ import { Contract } from './entities/contract.entity';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import { resolveCompanyNotifyPhone } from '../notifications/resolve-company-phone.util';
|
||||
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
||||
|
||||
/**
|
||||
* Clearance items are worked by the GL desks, which hold no intake keys — so
|
||||
* they take their own selector rather than the contract desk's. Every override
|
||||
* using this deep-links to a clearance or shipment-request page.
|
||||
*/
|
||||
const CLEARANCE_DESK = {
|
||||
permissionKeys: [FREIGHT_PERMS.contracts.clearanceGetNotification],
|
||||
};
|
||||
|
||||
/**
|
||||
* Customer + staff notifications for the contract lifecycle. Every customer
|
||||
@@ -86,7 +96,11 @@ export class ContractNotifierService {
|
||||
});
|
||||
}
|
||||
|
||||
/** Persist + push an in-app item to every backoffice staff user. */
|
||||
/**
|
||||
* Persist + push an in-app item to the contract desk — staff holding
|
||||
* `contracts:get_notification`. Callers whose item belongs to a different
|
||||
* desk override `recipients` (see {@link CLEARANCE_DESK}).
|
||||
*/
|
||||
private inAppStaff(
|
||||
c: Contract,
|
||||
title: string,
|
||||
@@ -94,7 +108,7 @@ export class ContractNotifierService {
|
||||
overrides: Partial<NotifyInput> = {},
|
||||
): void {
|
||||
void this.inbox.notify({
|
||||
recipients: { allBackoffice: true },
|
||||
recipients: { permissionKeys: [FREIGHT_PERMS.contracts.getNotification] },
|
||||
audience: NotificationAudience.BACKOFFICE,
|
||||
type: NotificationType.REQUEST_SUBMITTED,
|
||||
title,
|
||||
@@ -230,6 +244,7 @@ export class ContractNotifierService {
|
||||
`the customs declaration can be filed.${note ? ` Note: "${note}"` : ''}`;
|
||||
this.logger.log(`TRANSIT ASSIGNEE REQUESTED — ${c.reference}`);
|
||||
this.inAppStaff(c, `Transit assignee needed — ${c.reference}`, msg, {
|
||||
recipients: CLEARANCE_DESK,
|
||||
type: NotificationType.CLEARANCE_REVIEW,
|
||||
link: `/dashboard/gl-djibouti/clearance/${c.id}`,
|
||||
});
|
||||
@@ -248,6 +263,7 @@ export class ContractNotifierService {
|
||||
`The customs declaration can now be filed.`;
|
||||
this.logger.log(`TRANSIT ASSIGNEE ASSIGNED — ${c.reference}`);
|
||||
this.inAppStaff(c, `Transit assignee set — ${c.reference}`, msg, {
|
||||
recipients: CLEARANCE_DESK,
|
||||
type: NotificationType.CLEARANCE_REVIEW,
|
||||
link: `/dashboard/contracts/clearance/${c.id}`,
|
||||
});
|
||||
@@ -264,6 +280,7 @@ export class ContractNotifierService {
|
||||
`"${note}". Review and re-advise the amount on the clearance page.`;
|
||||
this.logger.log(`DUTY DISPUTED — ${c.reference}`);
|
||||
this.inAppStaff(c, `Duty disputed on ${c.reference}`, msg, {
|
||||
recipients: CLEARANCE_DESK,
|
||||
type: NotificationType.CLEARANCE_REVIEW,
|
||||
link: `/dashboard/contracts/clearance/${c.id}`,
|
||||
});
|
||||
@@ -321,6 +338,7 @@ export class ContractNotifierService {
|
||||
'Clearance documents uploaded',
|
||||
`Customer uploaded clearance documents for contract ${this.ref(c)} — review them in the clearance queue.`,
|
||||
{
|
||||
recipients: CLEARANCE_DESK,
|
||||
type: NotificationType.CLEARANCE_REVIEW,
|
||||
link: `/dashboard/contracts/clearance/${c.id}`,
|
||||
},
|
||||
@@ -334,6 +352,7 @@ export class ContractNotifierService {
|
||||
'Duty slip uploaded',
|
||||
`Customer uploaded the duty & tax payment slip for contract ${this.ref(c)}.`,
|
||||
{
|
||||
recipients: CLEARANCE_DESK,
|
||||
type: NotificationType.PAYMENT_RECEIVED,
|
||||
link: `/dashboard/contracts/clearance/${c.id}`,
|
||||
},
|
||||
@@ -347,6 +366,9 @@ export class ContractNotifierService {
|
||||
'New shipment request',
|
||||
`Shipment request ${requestRef} was filed under contract ${this.ref(c)} and awaits GL review.`,
|
||||
{
|
||||
// GL reviews these, and the shipment-requests page is gated on
|
||||
// contracts:create_booking — a key only the GL Ethiopia preset holds.
|
||||
recipients: CLEARANCE_DESK,
|
||||
link: `/dashboard/shipment-requests/${requestId}`,
|
||||
data: { contractId: c.id, requestId, reference: requestRef },
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user