feat(backend): Lagerbehälter-Umlagerung für Honigchargen (Modul 32)
Neues HoneyBatchRelocation-Modell dokumentiert jede Umlagerung einer Honigcharge zwischen Lagerbehältern (von/nach, Zeitpunkt, Benutzer, Notiz). HoneyBatchesService.relocate() aktualisiert HoneyBatch.storageContainerId transaktional zusammen mit dem Historieneintrag; wirft NotFoundException bei unbekanntem Zielbehälter. Neue Endpunkte: POST /honey-batches/:id/relocate, GET /honey-batches/:id/relocations. honeyBatchInclude liefert jetzt zusätzlich den aktuell zugeordneten storageContainer mit. Unit- und E2E-Tests ergänzt (Mehrfachumlagerung, Historienreihenfolge, 404 bei unbekanntem Behälter). Migration auf Dev- und Test-Datenbank angewendet.
This commit is contained in:
1 parent
fa13543578
commit
59a320ae36
7 files changed
+328
-44
No files matched your search
@@ -11,8 +11,12 @@ describe('HoneyBatchesService', () => {
|
||||
findMany: jest.Mock;
|
||||
findUnique: jest.Mock;
|
||||
create: jest.Mock;
|
||||
update: jest.Mock;
|
||||
};
|
||||
honeyMeasurement: { create: jest.Mock };
|
||||
storageContainer: { findUnique: jest.Mock };
|
||||
honeyBatchRelocation: { create: jest.Mock; findMany: jest.Mock };
|
||||
$transaction: jest.Mock;
|
||||
};
|
||||
let auditLog: { record: jest.Mock };
|
||||
|
||||
@@ -47,8 +51,12 @@ describe('HoneyBatchesService', () => {
|
||||
findMany: jest.fn(),
|
||||
findUnique: jest.fn(),
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
honeyMeasurement: { create: jest.fn() },
|
||||
storageContainer: { findUnique: jest.fn() },
|
||||
honeyBatchRelocation: { create: jest.fn(), findMany: jest.fn() },
|
||||
$transaction: jest.fn(),
|
||||
};
|
||||
auditLog = { record: jest.fn() };
|
||||
const webhooks = { dispatch: jest.fn().mockResolvedValue(undefined) };
|
||||
@@ -98,4 +106,94 @@ describe('HoneyBatchesService', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
describe('relocate', () => {
|
||||
beforeEach(() => {
|
||||
prisma.$transaction.mockImplementation(
|
||||
async (
|
||||
callback: (tx: typeof prisma) => Promise<unknown>,
|
||||
): Promise<unknown> => callback(prisma),
|
||||
);
|
||||
});
|
||||
|
||||
it('throws NotFoundException when the target container does not exist', async () => {
|
||||
prisma.honeyBatch.findUnique.mockResolvedValue({
|
||||
...honeyBatch,
|
||||
storageContainerId: null,
|
||||
});
|
||||
prisma.storageContainer.findUnique.mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
service.relocate(
|
||||
'batch-1',
|
||||
{ storageContainerId: 'missing' },
|
||||
'user-1',
|
||||
),
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
|
||||
it('records the relocation and updates the batch container', async () => {
|
||||
prisma.honeyBatch.findUnique
|
||||
.mockResolvedValueOnce({
|
||||
...honeyBatch,
|
||||
storageContainerId: 'container-old',
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
...honeyBatch,
|
||||
storageContainerId: 'container-new',
|
||||
});
|
||||
prisma.storageContainer.findUnique.mockResolvedValue({
|
||||
id: 'container-new',
|
||||
});
|
||||
prisma.honeyBatchRelocation.create.mockResolvedValue({
|
||||
id: 'relocation-1',
|
||||
honeyBatchId: 'batch-1',
|
||||
fromStorageContainerId: 'container-old',
|
||||
toStorageContainerId: 'container-new',
|
||||
});
|
||||
|
||||
await service.relocate(
|
||||
'batch-1',
|
||||
{ storageContainerId: 'container-new', notes: 'Umzug ins Lager 2' },
|
||||
'user-1',
|
||||
);
|
||||
|
||||
const [[createArg]] = prisma.honeyBatchRelocation.create.mock.calls as [
|
||||
[
|
||||
{
|
||||
data: {
|
||||
fromStorageContainerId: string | null;
|
||||
toStorageContainerId: string;
|
||||
};
|
||||
},
|
||||
],
|
||||
];
|
||||
expect(createArg.data.fromStorageContainerId).toBe('container-old');
|
||||
expect(createArg.data.toStorageContainerId).toBe('container-new');
|
||||
const [[updateArg]] = prisma.honeyBatch.update.mock.calls as [
|
||||
[{ data: { storageContainerId: string } }],
|
||||
];
|
||||
expect(updateArg.data.storageContainerId).toBe('container-new');
|
||||
expect(auditLog.record).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ entityType: 'HoneyBatchRelocation' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findRelocations', () => {
|
||||
it('returns the relocation history ordered by date', async () => {
|
||||
prisma.honeyBatch.findUnique.mockResolvedValue(honeyBatch);
|
||||
prisma.honeyBatchRelocation.findMany.mockResolvedValue([
|
||||
{ id: 'relocation-1', honeyBatchId: 'batch-1' },
|
||||
]);
|
||||
|
||||
const result = await service.findRelocations('batch-1');
|
||||
|
||||
expect(result).toEqual([{ id: 'relocation-1', honeyBatchId: 'batch-1' }]);
|
||||
const [[findArg]] = prisma.honeyBatchRelocation.findMany.mock.calls as [
|
||||
[{ where: { honeyBatchId: string } }],
|
||||
];
|
||||
expect(findArg.where.honeyBatchId).toBe('batch-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user