feat: T6 wave 1 – compliance schema, permissions, lock order, backup
Backend Image / build-and-push (push) Successful in 6m39s
Frontend Image / build-and-push (push) Successful in 11m25s

- Migrations (additive): veterinary medicines with lots and movement
  ledger, treatment medicine/lot/dose links, label particulars, authority
  notices, apiary restrictions, compliance settings and acknowledgements;
  notification category COMPLIANCE, added to non-empty Telegram lists.
- Permissions veterinary-medicines:* and compliance:* with role grants.
- LOCKABLE_TABLES: Apiary locks before Colony; T6 tables appended.
- Backup exports and restores the new tables in FK order; unsealed files
  skip them and report dropped medicine links; an open TSK draft in the
  target wins.
- describeNullContracts shared helper; Telegram backlog e2e no longer
  depends on the time of day.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-15 13:04:13 +02:00
1 parent 7a1480138d
commit 8a31c42578
23 files changed
+2028 -84

No files matched your search

+17 -4
View File
@@ -998,13 +998,20 @@ describe('Telegram delivery (e2e)', () => {
it('an old backlog never reaches a chat linked later', async () => {
const user = await createUnlinkedUser('backlog');
const waiting = await queue(user.id, { message: 'alt' });
const backlogText = `Alter Rückstand ${suffix}`;
const waiting = await queue(user.id, { message: backlogText });
await prisma.telegramLink.create({
data: { userId: user.id, chatId: user.chatId, telegramFirstName: 'x' },
});
// Quiet hours all day keep the "Telegram verbunden" notice (SYSTEM)
// back whatever the time of day; the check below does not depend on it.
await prisma.notificationPreference.update({
where: { userId: user.id },
data: { telegramEnabled: true },
data: {
telegramEnabled: true,
quietFrom: '00:00',
quietUntil: '23:59',
},
});
const token = await newLinkToken(user.id);
const newChat = chatBase + 9_400n;
@@ -1014,8 +1021,14 @@ describe('Telegram delivery (e2e)', () => {
expect((await deliveryOf(waiting.deliveryId)).status).toBe('SKIPPED');
await drainFor(user.id);
expect(telegram.sentTo(newChat)).toHaveLength(0);
expect(telegram.sentTo(user.chatId)).toHaveLength(0);
// The backlog text reaches neither chat; the link notice may.
for (const chatId of [newChat, user.chatId]) {
expect(
telegram
.sentTo(chatId)
.filter((sent) => sent.text.includes(backlogText)),
).toEqual([]);
}
});
// Last on purpose: it uses up the 10-per-minute budget of the route.