feat: add website social link, fix herkunft origin display, GUI backup schedule, and format-based batch numbering
- SiteSettings gains websiteUrl (e.g. online shop link) alongside the
existing social links, editable in Verwaltung > Automatisierung.
- /herkunft/[lot] now shows the apiary's Standort instead of its internal
Name (so multiple apiaries at one location read as one origin), adds
small icons per social link, and surfaces previously-fetched-but-unused
data (Honigsorte, Abfülldatum, stat cards) to make the page a richer
customer-facing info page.
- Backup schedule (path/cron/retention) is now also configurable via the
GUI (Verwaltung > Backup), stored in a new BackupSettings singleton;
an env var always wins over the GUI value per field, and the scheduler
reloads its cron job live when GUI settings change.
- New Automatisierung rules let an admin define a format (e.g.
"WH-{YYYY}-{SEQ:4}") that auto-generates HoneyBatch.batchNumber and
BottlingBatch.lotNumber on creation, with an atomic per-rule sequence
counter and a manual reset. Manual entry still works when the rule is
inactive.
This commit is contained in:
1 parent
7b23862a41
commit
412d033d8d
42 files changed
+1045
-119
No files matched your search
@@ -79,7 +79,7 @@ describe('AutomationService', () => {
|
||||
|
||||
const result = await service.findAll();
|
||||
|
||||
expect(prisma.automationRule.upsert).toHaveBeenCalledTimes(6);
|
||||
expect(prisma.automationRule.upsert).toHaveBeenCalledTimes(8);
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
|
||||
@@ -307,4 +307,86 @@ describe('AutomationService', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateNumber', () => {
|
||||
it('returns null when the rule is inactive', async () => {
|
||||
prisma.automationRule.findUnique.mockResolvedValue({
|
||||
id: 'rule-4',
|
||||
triggerType: 'HONEY_BATCH_NUMBER_FORMAT',
|
||||
active: false,
|
||||
config: { format: 'WH-{YYYY}-{SEQ:4}' },
|
||||
sequenceValue: 0,
|
||||
});
|
||||
|
||||
const result = await service.generateNumber('HONEY_BATCH_NUMBER_FORMAT');
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(prisma.automationRule.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns null when no format is configured', async () => {
|
||||
prisma.automationRule.findUnique.mockResolvedValue({
|
||||
id: 'rule-4',
|
||||
triggerType: 'HONEY_BATCH_NUMBER_FORMAT',
|
||||
active: true,
|
||||
config: {},
|
||||
sequenceValue: 0,
|
||||
});
|
||||
|
||||
const result = await service.generateNumber('HONEY_BATCH_NUMBER_FORMAT');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('atomically increments sequenceValue and formats the number', async () => {
|
||||
prisma.automationRule.findUnique.mockResolvedValue({
|
||||
id: 'rule-4',
|
||||
triggerType: 'HONEY_BATCH_NUMBER_FORMAT',
|
||||
active: true,
|
||||
config: { format: 'WH-{YYYY}-{SEQ:4}' },
|
||||
sequenceValue: 0,
|
||||
});
|
||||
prisma.automationRule.update.mockResolvedValue({ sequenceValue: 7 });
|
||||
|
||||
const result = await service.generateNumber('HONEY_BATCH_NUMBER_FORMAT');
|
||||
|
||||
expect(prisma.automationRule.update).toHaveBeenCalledWith({
|
||||
where: { triggerType: 'HONEY_BATCH_NUMBER_FORMAT' },
|
||||
data: { sequenceValue: { increment: 1 } },
|
||||
select: { sequenceValue: true },
|
||||
});
|
||||
expect(result).toBe(`WH-${new Date().getFullYear()}-0007`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetSequence', () => {
|
||||
it('sets sequenceValue back to 0 and records an audit log entry', async () => {
|
||||
prisma.automationRule.findUnique.mockResolvedValue({
|
||||
id: 'rule-4',
|
||||
triggerType: 'HONEY_BATCH_NUMBER_FORMAT',
|
||||
active: true,
|
||||
config: { format: 'WH-{YYYY}-{SEQ:4}' },
|
||||
sequenceValue: 12,
|
||||
});
|
||||
prisma.automationRule.update.mockResolvedValue({
|
||||
id: 'rule-4',
|
||||
sequenceValue: 0,
|
||||
});
|
||||
|
||||
await service.resetSequence('rule-4', 'user-1');
|
||||
|
||||
expect(prisma.automationRule.update).toHaveBeenCalledWith({
|
||||
where: { id: 'rule-4' },
|
||||
data: { sequenceValue: 0 },
|
||||
});
|
||||
expect(auditLog.record).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
entityType: 'AutomationRule',
|
||||
entityId: 'rule-4',
|
||||
action: 'UPDATE',
|
||||
userId: 'user-1',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user