mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
- Introduced HazardDeclarationPanel component to display dangerous goods declaration details. - Updated URL constants to include CLEARANCE_PROCEED endpoint for re-requesting operations. - Enhanced permissions to include hazardous approval roles for contract approvals. - Integrated HazardDeclarationPanel into ContractRequestDetailPage and ContractClearanceDetailPage. - Added proceedToOperation method in bookings service for handling operation re-requests. - Updated contract forms and schemas to include hazard class and UN number fields. - Implemented validation for hazardous contracts in the contract creation flow. - Added expiry notice functionality for contracts nearing validity end. - Created tests for expiry notice calculations and labels. - Updated UI components to reflect hazardous cargo information and validation errors.
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import { ContractExpiryService } from './contract-expiry.service';
|
|
import type { Contract } from './entities/contract.entity';
|
|
|
|
/**
|
|
* The reminder must warn each customer once, ten days out, and must never let a
|
|
* notification failure escape into the scheduler (that would also take out the
|
|
* expiry sweep sharing this service).
|
|
*/
|
|
describe('ContractExpiryService — expiry reminder', () => {
|
|
const contract = (over: Partial<Contract> = {}): Contract =>
|
|
({
|
|
id: 'c-1',
|
|
reference: 'CTR-2026-00042',
|
|
companyId: 'co-1',
|
|
contractValidUntil: new Date('2026-08-10T00:00:00.000Z'),
|
|
status: 'CONTRACT_ACTIVE',
|
|
...over,
|
|
}) as Contract;
|
|
|
|
let repo: { expireLapsedContracts: jest.Mock; findExpiringInDays: jest.Mock };
|
|
let inbox: { notify: jest.Mock };
|
|
let service: ContractExpiryService;
|
|
|
|
beforeEach(() => {
|
|
repo = {
|
|
expireLapsedContracts: jest.fn().mockResolvedValue(0),
|
|
findExpiringInDays: jest.fn().mockResolvedValue([]),
|
|
};
|
|
inbox = { notify: jest.fn().mockResolvedValue(undefined) };
|
|
service = new ContractExpiryService(repo as never, inbox as never);
|
|
});
|
|
|
|
it('asks for the contracts lapsing ten days out', async () => {
|
|
await service.remindExpiringContracts();
|
|
expect(repo.findExpiringInDays).toHaveBeenCalledWith(10);
|
|
});
|
|
|
|
it('notifies the owning company once, deep-linking the contract list', async () => {
|
|
repo.findExpiringInDays.mockResolvedValue([contract()]);
|
|
|
|
await service.remindExpiringContracts();
|
|
|
|
expect(inbox.notify).toHaveBeenCalledTimes(1);
|
|
const sent = inbox.notify.mock.calls[0][0];
|
|
expect(sent.recipients).toEqual({ companyId: 'co-1' });
|
|
expect(sent.title).toContain('CTR-2026-00042');
|
|
expect(sent.title).toContain('10 days');
|
|
expect(sent.link).toBe('/contracts');
|
|
expect(sent.data).toMatchObject({ contractId: 'c-1', action: 'CONTRACT_EXPIRING' });
|
|
});
|
|
|
|
it('skips a contract with no owning company (nobody to notify)', async () => {
|
|
repo.findExpiringInDays.mockResolvedValue([contract({ companyId: null })]);
|
|
await service.remindExpiringContracts();
|
|
expect(inbox.notify).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('swallows a notification failure instead of throwing into the scheduler', async () => {
|
|
repo.findExpiringInDays.mockResolvedValue([contract()]);
|
|
inbox.notify.mockRejectedValue(new Error('inbox down'));
|
|
await expect(service.remindExpiringContracts()).resolves.toBeUndefined();
|
|
});
|
|
});
|