feat: T4 wave 5 - colony end cleanup, withdrawal chain, next steps, inspection takeover UI
- Colony end releases the queen in the same transaction; open suggestions become obsolete and a CLEANUP suggestion lists open tasks and the queen; apply-cleanup validates evidence and blocks reactivated colonies - Withdrawal chain: plausibility warnings on harvest and bottling, conflict notifications also when a treatment is entered or corrected after the harvest (naming bottled lots), withdrawal spans in the calendar and M365 sync, Berlin-day end events - Next 3 steps per colony and the "Heute" endpoint with ranking and an explicit merge table by intent - Inspection-triggered rule suggestions follow the colony's latest inspection (resolve when the finding is gone, refresh when it stays) - Frontend: colony card takeover in the inspection form, feed status as condition, occupied frames 0-40; legacy offline entries keep syncing - Notifications and preferences are deleted with their user Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
02eb16924e
commit
ef33e5549b
122 files changed
+10707
-504
No files matched your search
@@ -644,6 +644,54 @@ describe('daily evaluation rules', () => {
|
||||
...unknown[],
|
||||
];
|
||||
expect(strings.join('?')).toContain('"withdrawalPeriodDays" IS NOT NULL');
|
||||
expect(values).toEqual(['2026-06-03', '2026-06-10']);
|
||||
// One day of margin on both sides: the SQL end day is a plain UTC date,
|
||||
// the event day is the Berlin day of the withdrawal-window helper.
|
||||
expect(values).toEqual(['2026-06-02', '2026-06-11']);
|
||||
});
|
||||
|
||||
it('bounds the withdrawal query by the Berlin day of the run, not by the instant it is given', async () => {
|
||||
const { db, queryRaw } = fakeDb({});
|
||||
const rule = DAILY_EVALUATION_RULES.find(
|
||||
(r) => r.type === 'WithdrawalEnded',
|
||||
)!;
|
||||
|
||||
// 2026-06-10T23:30Z is already 2026-06-11 in Berlin.
|
||||
await rule.occurrences(db, new Date('2026-06-10T23:30:00.000Z'), {
|
||||
seasons,
|
||||
day: '2026-06-11',
|
||||
});
|
||||
|
||||
const [, ...values] = queryRaw.mock.calls[0] as [
|
||||
readonly string[],
|
||||
...unknown[],
|
||||
];
|
||||
expect(values).toEqual(['2026-06-03', '2026-06-12']);
|
||||
});
|
||||
|
||||
it('takes the end day of a treatment stored with a time part from the Berlin day', async () => {
|
||||
// 2026-06-01T22:30Z is 00:30 on 2026-06-02 in Berlin: one withdrawal day
|
||||
// later the period ends on 2026-06-03, the first day of the window. The
|
||||
// plain UTC date of the SQL would say 2026-06-02.
|
||||
const { db } = fakeDb({
|
||||
treatments: [
|
||||
{
|
||||
id: 'treatment-1',
|
||||
colonyId: 'colony-1',
|
||||
date: new Date('2026-06-01T22:30:00.000Z'),
|
||||
endDate: null,
|
||||
withdrawalPeriodDays: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const events = await eventsOn(db, '2026-06-10');
|
||||
|
||||
expect(events).toEqual([
|
||||
expect.objectContaining({
|
||||
type: 'WithdrawalEnded',
|
||||
payload: { colonyId: 'colony-1', endedOn: '2026-06-03' },
|
||||
dedupeKey: 'WithdrawalEnded:treatment-1:2026-06-03',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user