feat(F-138): completed feature

This commit is contained in:
chattie
2026-08-22 10:18:37 +02:00
parent 5098723284
commit fb015932b2
23 changed files with 502 additions and 102 deletions

View File

@@ -245,11 +245,13 @@ export async function buildApp(deps: BuildAppDeps = {}): Promise<FastifyInstance
});
});
const pricing = createPricingService(deps.pool);
await app.register(async (instance) => {
await registerCatalogRoutes(instance, {
pool: deps.pool as pg.Pool,
authenticate: combinedAuth,
logger,
pricing,
});
});
@@ -261,7 +263,6 @@ export async function buildApp(deps: BuildAppDeps = {}): Promise<FastifyInstance
});
});
const pricing = createPricingService(deps.pool);
await app.register(async (instance) => {
await registerPricingRoutes(instance, {
pool: deps.pool as pg.Pool,

View File

@@ -209,6 +209,43 @@ describe.skipIf(!hasDb)('catalog product flows (real PostgreSQL)', () => {
expect(duplicateEan.statusCode).toBe(409);
});
it('seeds a default price row immediately after variant creation (F-138 AC1, AC2, AC4)', async () => {
const productRes = await app.inject({
method: 'POST',
url: '/products',
headers: { 'content-type': 'application/json' },
cookies: { [SESSION_COOKIE_NAME]: adminCookie },
payload: {
name: 'Producto Semilla F-138',
slug: 'semilla-f138-ac1',
categoryIds: [categoryId],
},
});
expect(productRes.statusCode).toBe(201);
const productId = (productRes.json() as { id: string }).id;
const variantRes = await app.inject({
method: 'POST',
url: `/products/${productId}/variants`,
headers: { 'content-type': 'application/json' },
cookies: { [SESSION_COOKIE_NAME]: adminCookie },
payload: { sku: 'SKU-SEMILLA-F138', ean: '8412345678902', attributes: { size: '500ml' } },
});
expect(variantRes.statusCode).toBe(201);
const variantId = (variantRes.json() as { id: string }).id;
// F-138 AC1/AC2: a default price row exists immediately after variant creation
// (no 404 window on GET /pricing/variants/:id) and re-seeding is a no-op.
const priceRow = await pool.query(
'SELECT net_unit_amount_cents, vat_rate, currency FROM pricing_variant_prices WHERE variant_id = $1',
[variantId],
);
expect(priceRow.rows[0]).toBeDefined();
expect(priceRow.rows[0]!.net_unit_amount_cents).toBe(0);
expect(priceRow.rows[0]!.vat_rate).toBe('general');
expect(priceRow.rows[0]!.currency).toBe('EUR');
});
it('stores nutrition provenance and protects manual nutrition from external overwrite (F-010 AC2, AC3)', async () => {
const manual = await app.inject({
method: 'PATCH',