mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
- Added parameter to and for server-side free-text search on contract reference, company name, and booking details. - Introduced new validation errors in for container clashes and space issues when creating bookings. - Implemented paginated dropdown settings retrieval in . - Updated to fetch active yards using a new method that handles pagination. - Enhanced with a method to fetch all records by walking through pages. - Refactored to support filtering and pagination in schedule listings. - Improved to return a paginated list of facilities. - Updated UI components in and to utilize debounced search inputs for better performance. - Added alerts in to inform users about booking constraints related to splits and capacity. - Enhanced to display notifications for split bookings and capacity usage.
117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
import { BookingSplitService } from './booking-split.service';
|
|
import { Booking } from '../bookings/entities/booking.entity';
|
|
import { BookingContainer } from '../bookings/entities/booking-container.entity';
|
|
import { BookingContainerUnit } from '../bookings/entities/booking-container-unit.entity';
|
|
import { Contract } from '../contracts/entities/contract.entity';
|
|
import { BookingBatchOffer } from './entities/booking-batch-offer.entity';
|
|
|
|
/**
|
|
* applySplit split-marking behaviour: the reduced booking is flagged is_split
|
|
* and keeps a pre_split_quantities snapshot (the remainder ledger for ONE_TIME
|
|
* contracts). The contract kind is NEVER changed — a ONE_TIME contract stays
|
|
* ONE_TIME through the split chain.
|
|
*/
|
|
describe('BookingSplitService — applySplit split marking', () => {
|
|
const bookingId = 'bk-1';
|
|
const contractId = 'ct-1';
|
|
const offerId = 'of-1';
|
|
|
|
const buildService = (bookingContractKind: 'ONE_TIME' | 'GENERAL') => {
|
|
const offer = {
|
|
id: offerId,
|
|
bookingId,
|
|
status: 'OFFERED',
|
|
offeredWagons: 3,
|
|
totalWagons: 5,
|
|
offeredWeightTons: 30,
|
|
offeredAmount: 300,
|
|
offeredPricingBreakdown: {},
|
|
offeredLines: null,
|
|
} as unknown as BookingBatchOffer;
|
|
|
|
const bookingRepo = {
|
|
update: jest.fn().mockResolvedValue(undefined),
|
|
findOne: jest.fn().mockResolvedValue({
|
|
id: bookingId,
|
|
contractId,
|
|
contractKind: bookingContractKind,
|
|
cargoTotalWeightVgm: 50,
|
|
}),
|
|
find: jest.fn().mockResolvedValue([]),
|
|
softDelete: jest.fn().mockResolvedValue(undefined),
|
|
};
|
|
const contractRepo = { update: jest.fn().mockResolvedValue(undefined) };
|
|
const offerRepo = {
|
|
findOne: jest.fn().mockResolvedValue(offer),
|
|
update: jest.fn().mockResolvedValue(undefined),
|
|
};
|
|
const containerRepo = {
|
|
find: jest.fn().mockResolvedValue([]),
|
|
update: jest.fn(),
|
|
softDelete: jest.fn(),
|
|
};
|
|
const unitRepo = { find: jest.fn().mockResolvedValue([]), softDelete: jest.fn() };
|
|
|
|
const repoFor = (entity: unknown) => {
|
|
if (entity === Booking) return bookingRepo;
|
|
if (entity === Contract) return contractRepo;
|
|
if (entity === BookingBatchOffer) return offerRepo;
|
|
if (entity === BookingContainer) return containerRepo;
|
|
if (entity === BookingContainerUnit) return unitRepo;
|
|
return { find: jest.fn().mockResolvedValue([]), update: jest.fn() };
|
|
};
|
|
|
|
const dataSource = {
|
|
getRepository: jest.fn(repoFor),
|
|
transaction: jest.fn(async (fn: (m: unknown) => Promise<void>) => {
|
|
await fn({ getRepository: repoFor });
|
|
}),
|
|
};
|
|
|
|
const service = new BookingSplitService(
|
|
dataSource as never,
|
|
{} as never,
|
|
{} as never,
|
|
{ expirePayable: jest.fn() } as never,
|
|
{ payNowPartial: jest.fn() } as never,
|
|
);
|
|
return { service, bookingRepo, contractRepo };
|
|
};
|
|
|
|
it('flags the reduced booking is_split with a pre-split bulk snapshot', async () => {
|
|
const { service, bookingRepo } = buildService('ONE_TIME');
|
|
|
|
await service.applySplit(bookingId);
|
|
|
|
expect(bookingRepo.update).toHaveBeenCalledWith(
|
|
bookingId,
|
|
expect.objectContaining({
|
|
isSplit: true,
|
|
preSplitQuantities: { bulkTons: 50 },
|
|
cargoTotalWeightVgm: 30,
|
|
wagonsRequired: 3,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('never changes the contract kind — ONE_TIME stays ONE_TIME', async () => {
|
|
const { service, bookingRepo, contractRepo } = buildService('ONE_TIME');
|
|
|
|
await service.applySplit(bookingId);
|
|
|
|
expect(contractRepo.update).not.toHaveBeenCalled();
|
|
expect(bookingRepo.update).not.toHaveBeenCalledWith(
|
|
bookingId,
|
|
expect.objectContaining({ contractKind: expect.anything() }),
|
|
);
|
|
});
|
|
|
|
it('leaves a GENERAL contract untouched too', async () => {
|
|
const { service, contractRepo } = buildService('GENERAL');
|
|
|
|
await service.applySplit(bookingId);
|
|
|
|
expect(contractRepo.update).not.toHaveBeenCalled();
|
|
});
|
|
});
|