You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/packages/domain/test/batch.test.ts

65 lines
2.4 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { isExpired, pickFefoBatch, sortFefo, type BatchStock } from '@sims/domain'
/**
* S5 FEFO — the single batch-pick rule shared by the POS picker and the expiry
* dashboard. Dates are ISO strings so ordering is lexicographic; "today" here is
* 2026-07-10 (the demo business date).
*/
const TODAY = '2026-07-10'
const b = (id: string, batchNo: string, expiryDate: string | undefined, onHand = 10): BatchStock => ({
id, itemId: 'i-107', batchNo, ...(expiryDate !== undefined ? { expiryDate } : {}), onHand,
})
describe('S5 FEFO — sortFefo', () => {
it('orders earliest expiry first, undated lots last', () => {
const sorted = sortFefo([
b('c', 'B-C', '2026-12-31'),
b('u', 'B-U', undefined),
b('a', 'B-A', '2026-07-15'),
b('x', 'B-X', '2026-07-05'), // already expired — still earliest
])
expect(sorted.map((x) => x.id)).toEqual(['x', 'a', 'c', 'u'])
})
it('breaks an exact expiry tie by batch_no and does not mutate the input', () => {
const input = [b('2', 'B-Z', '2026-07-20'), b('1', 'B-A', '2026-07-20')]
const sorted = sortFefo(input)
expect(sorted.map((x) => x.batchNo)).toEqual(['B-A', 'B-Z'])
expect(input.map((x) => x.id)).toEqual(['2', '1']) // original order intact
})
})
describe('S5 FEFO — isExpired', () => {
it('is true only for a dated lot strictly before today', () => {
expect(isExpired({ expiryDate: '2026-07-09' }, TODAY)).toBe(true)
expect(isExpired({ expiryDate: TODAY }, TODAY)).toBe(false) // expires today = still sellable
expect(isExpired({ expiryDate: '2026-07-11' }, TODAY)).toBe(false)
expect(isExpired({ expiryDate: undefined }, TODAY)).toBe(false) // undated never expires
})
})
describe('S5 FEFO — pickFefoBatch', () => {
it('preselects the earliest NON-expired lot, skipping expired ones', () => {
const pick = pickFefoBatch([
b('x', 'B-X', '2026-07-05'), // expired — skipped
b('a', 'B-A', '2026-07-15'), // earliest sellable
b('c', 'B-C', '2026-12-31'),
], TODAY)
expect(pick?.id).toBe('a')
})
it('falls back to the earliest-expiring lot when every lot is expired', () => {
const pick = pickFefoBatch([
b('y', 'B-Y', '2026-07-08'),
b('x', 'B-X', '2026-07-01'), // earliest of the expired
], TODAY)
expect(pick?.id).toBe('x')
})
it('returns undefined for an empty list', () => {
expect(pickFefoBatch([], TODAY)).toBeUndefined()
})
})