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) => { 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(); }); });