mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 16:35:42 +00:00
Merge branch 'dev' into dj-franc
This commit is contained in:
@@ -165,52 +165,34 @@ describe('ContractBookingService — manual odd-20ft consolidation', () => {
|
||||
).rejects.toThrow(/cannot be consolidated with itself/i);
|
||||
});
|
||||
|
||||
it('offers only bookings whose own 20ft count is odd', async () => {
|
||||
// Two odd counts always sum to even, so an odd partner is exactly what fills
|
||||
// the wagon; an even one would leave the pair partial again.
|
||||
const rows = [
|
||||
{
|
||||
id: 'odd',
|
||||
reference: 'BK-ODD',
|
||||
bookingContainers: [
|
||||
{ quantity: 3, containerType: { sizeFt: 20 } },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'even',
|
||||
reference: 'BK-EVEN',
|
||||
bookingContainers: [
|
||||
{ quantity: 4, containerType: { sizeFt: 20 } },
|
||||
],
|
||||
},
|
||||
// Cargo not entered yet — its 20ft count is unknown, so it cannot be
|
||||
// shown to fill the wagon and is not offered.
|
||||
{ id: 'bare', reference: 'BK-BARE', bookingContainers: [] },
|
||||
];
|
||||
|
||||
// Which bookings qualify is the repository's decision (and its own spec's);
|
||||
// what matters here is that the odd 20ft count it resolved survives into the
|
||||
// response. A booking awaiting completion has no container lines of its own,
|
||||
// so re-deriving the count from bookingContainers would report 0 and the
|
||||
// picker would show every candidate as empty.
|
||||
it('reports the 20ft count the repository resolved, not the persisted lines', async () => {
|
||||
const { service } = makeService({
|
||||
bookingsRepository: {
|
||||
findByIdWithFiles: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ id: 'b-1', contractId: 'c-1' } as Booking),
|
||||
findManualConsolidationCandidates: jest.fn(async (booking: Booking) =>
|
||||
// Mirror the repository's in-memory odd filter.
|
||||
rows.filter((row) => {
|
||||
void booking;
|
||||
const lines = row.bookingContainers ?? [];
|
||||
if (lines.length === 0) return false;
|
||||
const ft20 = lines
|
||||
.filter((l) => Number(l.containerType?.sizeFt) === 20)
|
||||
.reduce((sum, l) => sum + Number(l.quantity || 0), 0);
|
||||
return ft20 % 2 === 1;
|
||||
}),
|
||||
),
|
||||
findManualConsolidationCandidates: jest.fn().mockResolvedValue([
|
||||
{
|
||||
// Cargo not persisted yet — the count came from its booking request.
|
||||
booking: {
|
||||
id: 'odd',
|
||||
reference: 'BK-2026-001116',
|
||||
bookingContainers: [],
|
||||
},
|
||||
ft20Quantity: 1,
|
||||
},
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
const candidates = await service.listConsolidationCandidates('c-1', 'b-1');
|
||||
expect(candidates.map((c) => c.reference)).toEqual(['BK-ODD']);
|
||||
expect(candidates[0].ft20Quantity).toBe(3);
|
||||
expect(candidates.map((c) => c.reference)).toEqual(['BK-2026-001116']);
|
||||
expect(candidates[0].ft20Quantity).toBe(1);
|
||||
expect(candidates[0].hasCargo).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -660,24 +660,25 @@ export class ContractBookingService {
|
||||
const rows = await this.bookingsRepository.findManualConsolidationCandidates(
|
||||
booking,
|
||||
);
|
||||
return rows.map((row) => {
|
||||
const lines = row.bookingContainers ?? [];
|
||||
return {
|
||||
id: row.id,
|
||||
reference: row.reference,
|
||||
contractId: row.contractId ?? null,
|
||||
companyName: row.company?.name ?? null,
|
||||
status: row.status,
|
||||
tradeDirection: row.tradeDirection ?? null,
|
||||
originYardId: row.originYardId ?? null,
|
||||
destinationYardId: row.destinationYardId ?? null,
|
||||
scheduledDate: row.scheduledDate ? row.scheduledDate.toISOString() : null,
|
||||
ft20Quantity: lines
|
||||
.filter((line) => Number(line.containerType?.sizeFt) === 20)
|
||||
.reduce((sum, line) => sum + Number(line.quantity || 0), 0),
|
||||
hasCargo: lines.length > 0,
|
||||
};
|
||||
});
|
||||
// ft20Quantity comes back from the repository already resolved — persisted
|
||||
// container lines when the booking has them, otherwise the quantities its
|
||||
// booking request was accepted with. Recomputing it here from
|
||||
// bookingContainers would report 0 for every not-yet-completed booking.
|
||||
return rows.map(({ booking: row, ft20Quantity }) => ({
|
||||
id: row.id,
|
||||
reference: row.reference,
|
||||
contractId: row.contractId ?? null,
|
||||
companyName: row.company?.name ?? null,
|
||||
status: row.status,
|
||||
tradeDirection: row.tradeDirection ?? null,
|
||||
originYardId: row.originYardId ?? null,
|
||||
destinationYardId: row.destinationYardId ?? null,
|
||||
scheduledDate: row.scheduledDate ? row.scheduledDate.toISOString() : null,
|
||||
ft20Quantity,
|
||||
// Cargo is known — from either source — since a candidate with an unknown
|
||||
// count is never offered.
|
||||
hasCargo: true,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user