mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 04:15:43 +00:00
implement contract cancellation feature and update contract statuses
- Added functionality to cancel contracts, allowing users to provide a reason for cancellation. - Updated contract statuses to include SUSPENDED and changed CLOSED to COMPLETED. - Enhanced the UI to reflect the new cancellation option and updated messaging for contract statuses. - Refactored contract booking actions to accommodate changes in booking logic for ONE_TIME and GENERAL contracts. - Removed clearance document management from the contract detail page, as it is now handled per booking. - Introduced a SQL script to reset bookings and train schedules for development purposes.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { ConflictException } from '@nestjs/common';
|
||||
|
||||
import { ContractsService } from './contracts.service';
|
||||
import type { CreateContractDto } from './dto/create-contract.dto';
|
||||
|
||||
/**
|
||||
* The duplicate guard blocks a new request only when EVERY commercial
|
||||
* dimension matches a live contract — service type, operation type, contract
|
||||
* kind, cargo scope and route. Any one differing must let the request through.
|
||||
*/
|
||||
describe('ContractsService duplicate guard', () => {
|
||||
const LANE = { originYardId: 'yard-dj', destinationYardId: 'yard-mj' };
|
||||
|
||||
const existing = {
|
||||
id: 'c-1',
|
||||
reference: 'CTR-2026-00001',
|
||||
status: 'PENDING_APPROVAL',
|
||||
contractValidUntil: null,
|
||||
tradeDirection: 'IMPORT',
|
||||
contractKind: 'ONE_TIME',
|
||||
freightType: 'CONTAINER',
|
||||
routes: [LANE],
|
||||
cargoScope: [{ containerSize: '20ft' }, { containerSize: '40ft' }],
|
||||
};
|
||||
|
||||
const dto = (overrides: Partial<CreateContractDto> = {}) =>
|
||||
({
|
||||
serviceTypeId: 'svc-1',
|
||||
tradeDirection: 'IMPORT',
|
||||
contractKind: 'ONE_TIME',
|
||||
freightType: 'CONTAINER',
|
||||
routes: [LANE],
|
||||
cargoScope: [{ containerSize: '20ft' }, { containerSize: '40ft' }],
|
||||
...overrides,
|
||||
}) as CreateContractDto;
|
||||
|
||||
const guard = (input: CreateContractDto) => {
|
||||
const service = new ContractsService(
|
||||
{} as never,
|
||||
{ findDuplicateCandidates: async () => [existing] } as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
);
|
||||
return (
|
||||
service as unknown as {
|
||||
assertNoDuplicateContract(companyId: string, dto: CreateContractDto): Promise<void>;
|
||||
}
|
||||
).assertNoDuplicateContract('company-1', input);
|
||||
};
|
||||
|
||||
it('blocks an identical request', async () => {
|
||||
await expect(guard(dto())).rejects.toBeInstanceOf(ConflictException);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['operation type', { tradeDirection: 'EXPORT' }],
|
||||
['contract kind', { contractKind: 'GENERAL' }],
|
||||
['freight type', { freightType: 'BULK' }],
|
||||
['cargo scope', { cargoScope: [{ containerSize: '20ft' }] }],
|
||||
['route', { routes: [{ originYardId: 'yard-dj', destinationYardId: 'yard-aa' }] }],
|
||||
])('allows a request with a different %s', async (_label, overrides) => {
|
||||
await expect(guard(dto(overrides as Partial<CreateContractDto>))).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('ignores quantity caps when comparing cargo scope', async () => {
|
||||
await expect(
|
||||
guard(
|
||||
dto({
|
||||
cargoScope: [
|
||||
{ containerSize: '20ft', quantityCap: 10 },
|
||||
{ containerSize: '40ft', quantityCap: 5 },
|
||||
],
|
||||
}),
|
||||
),
|
||||
).rejects.toBeInstanceOf(ConflictException);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user