feat(backend): Abfuellungen und Produkte (Modul 23)
ProductsModule (GET/POST /products, GET/PATCH /products/:id) und BottlingModule (GET/POST /bottling-batches) mit transaktionaler Reduzierung der HoneyBatch-Restmenge beim Abfuellen; Ablehnung bei Abfuellmenge > verfuegbarer Menge. Neue Prisma-Modelle Product und BottlingBatch (Migration bottling_products).
This commit is contained in:
1 parent
8b7fd61c1c
commit
e82cae257b
17 files changed
+916
-2
No files matched your search
@@ -0,0 +1,124 @@
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { BottlingService } from './bottling.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AuditLogService } from '../audit/audit-log.service';
|
||||
|
||||
describe('BottlingService', () => {
|
||||
let service: BottlingService;
|
||||
let prisma: {
|
||||
honeyBatch: { findUnique: jest.Mock; update: jest.Mock };
|
||||
product: { findUnique: jest.Mock };
|
||||
bottlingBatch: {
|
||||
findUnique: jest.Mock;
|
||||
findMany: jest.Mock;
|
||||
create: jest.Mock;
|
||||
};
|
||||
$transaction: jest.Mock;
|
||||
};
|
||||
let auditLog: { record: jest.Mock };
|
||||
let tx: {
|
||||
bottlingBatch: { create: jest.Mock };
|
||||
honeyBatch: { update: jest.Mock };
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
tx = {
|
||||
bottlingBatch: { create: jest.fn() },
|
||||
honeyBatch: { update: jest.fn() },
|
||||
};
|
||||
prisma = {
|
||||
honeyBatch: { findUnique: jest.fn(), update: jest.fn() },
|
||||
product: { findUnique: jest.fn() },
|
||||
bottlingBatch: {
|
||||
findUnique: jest.fn(),
|
||||
findMany: jest.fn(),
|
||||
create: jest.fn(),
|
||||
},
|
||||
$transaction: jest.fn((callback: (txArg: typeof tx) => unknown) =>
|
||||
callback(tx),
|
||||
),
|
||||
};
|
||||
auditLog = { record: jest.fn() };
|
||||
service = new BottlingService(
|
||||
prisma as unknown as PrismaService,
|
||||
auditLog as unknown as AuditLogService,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws NotFoundException when the honey batch does not exist', async () => {
|
||||
prisma.honeyBatch.findUnique.mockResolvedValueOnce(null);
|
||||
await expect(
|
||||
service.create(
|
||||
{
|
||||
honeyBatchId: 'missing',
|
||||
date: '2026-01-01',
|
||||
jarSize: 500,
|
||||
jarCount: 10,
|
||||
bestBeforeDate: '2027-01-01',
|
||||
lotNumber: 'L-1',
|
||||
},
|
||||
'user-1',
|
||||
),
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
|
||||
it('rejects when the bottled amount exceeds the available honey batch amount', async () => {
|
||||
prisma.honeyBatch.findUnique.mockResolvedValueOnce({
|
||||
id: 'h1',
|
||||
amount: 5,
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.create(
|
||||
{
|
||||
honeyBatchId: 'h1',
|
||||
date: '2026-01-01',
|
||||
jarSize: 500,
|
||||
jarCount: 20,
|
||||
bestBeforeDate: '2027-01-01',
|
||||
lotNumber: 'L-1',
|
||||
},
|
||||
'user-1',
|
||||
),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('creates a bottling batch and reduces the honey batch amount transactionally', async () => {
|
||||
prisma.honeyBatch.findUnique.mockResolvedValueOnce({
|
||||
id: 'h1',
|
||||
amount: 25,
|
||||
});
|
||||
tx.bottlingBatch.create.mockResolvedValueOnce({
|
||||
id: 'b1',
|
||||
totalAmount: 25,
|
||||
});
|
||||
prisma.bottlingBatch.findUnique.mockResolvedValueOnce({
|
||||
id: 'b1',
|
||||
totalAmount: 25,
|
||||
});
|
||||
|
||||
const result = await service.create(
|
||||
{
|
||||
honeyBatchId: 'h1',
|
||||
date: '2026-01-01',
|
||||
jarSize: 500,
|
||||
jarCount: 50,
|
||||
bestBeforeDate: '2027-01-01',
|
||||
lotNumber: 'L-1',
|
||||
},
|
||||
'user-1',
|
||||
);
|
||||
|
||||
expect(result).toEqual({ id: 'b1', totalAmount: 25 });
|
||||
expect(tx.honeyBatch.update).toHaveBeenCalledWith({
|
||||
where: { id: 'h1' },
|
||||
data: { amount: 0, status: 'BOTTLED' },
|
||||
});
|
||||
expect(auditLog.record).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
entityType: 'BottlingBatch',
|
||||
action: 'CREATE',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user