feat: PRD-003 ProductPrices históricos (ValidFrom/ValidTo) #45
Reference in New Issue
Block a user
Delete Branch "feature/PRD-003"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
PRD-003 introduces product price history with civil vigency (PriceValidFrom/PriceValidTo), SYSTEM_VERSIONING temporal audit (10 years retention), and forward-only price modifications. Enables future PRC-001 pricing resolution and historical price lookups via GetPriceAt(productId, date).
Commits (7)
59f30cdfeat(bd): V019 crea dbo.ProductPrices + SP + índices (PRD-003)54b0265feat(domain): ProductPrice entity + exceptions (PRD-003)4b0567dfeat(application): commands/queries + IProductPricingService (PRD-003)2d2e90ffeat(infrastructure): ProductPriceRepository Dapper + SP invocation (PRD-003)f6f24bcfeat(api): ProductPricesController + DI + ExceptionFilter integration (PRD-003)6a9818bfeat(frontend): productPrices feature — history + dialog (PRD-003)7cabb67test(integration): concurrency + SYSTEM_VERSIONING + e2e extra (PRD-003)Tests
Backend: 1098 Application.Tests + 306 Api.Tests = 1404 ✅ 0 rojos
Frontend: 453 Vitest ✅ 0 rojos
Total: 1857 tests (baseline PRD-002: 1708, delta +149) ✅
Spec Compliance
19/19 scenarios fully compliant:
Key Design Decisions
Colateral Bug Fix
AddProductPriceCommandHandler previously read repository result within
using(tx)scope aftertx.Complete()— illegal in distributed transactions. Restructured with explicit braces: DB read occurs within scope (before Complete), response construction occurs after scope exit. Caught in e2e, not by mocks — validates value of integration testing.Follow-ups Created (Non-Blocking)
Ready for Merge
All 52 tasks complete (7 batches, Strict TDD). 1857 tests, 0 rojos. Verify report: PASS. No CRITICAL or WARNING issues. Zero deuda técnica in PRD-003 code.
- GET /api/v1/products/{id}/prices [Authorize] → 200 IReadOnlyList<ProductPriceDto> - POST /api/v1/admin/products/{id}/prices [RequirePermission catalogo:productos:gestionar] → 201 AddProductPriceResponse + Location header - ExceptionFilter: 3 new cases (ProductPriceForwardOnlyException→409, ProductPriceInvalidException→400, ProductSinPrecioActivoException→404) - Fix AddProductPriceCommandHandler: move GetByProductIdAsync outside TransactionScope using block to avoid InvalidOperationException (scope already complete) - 16 e2e tests in ProductPricesControllerTests: 401/403, 200 history ordered DESC, 404 not found, 201 first/second price, 400 validation, 409 forward-only, audit event, DateOnly yyyy-MM-dd roundtrip - 305 Api.Tests + 1088 Application.Tests = 1393 total, 0 red- API layer: getProductPrices + addProductPrice (axiosClient) - Hooks: useProductPrices (useQuery, staleTime 30s, enabled productId>0) useAddProductPrice (useMutation, invalidates ['products', id, 'prices']) - Components: ProductPriceHistory (shadcn Table + Badge Vigente, formatCivilDate, formatCurrency) AddProductPriceDialog (shadcn Dialog + Form, Zod schema con priceValidFrom>=todayArgentina()) - Integration: ProductsPage gets "Ver precios" per row opening prices dialog - lib/numberFormat.ts: formatCurrency() con Intl.NumberFormat ARS - types.ts extended: ProductPrice, AddProductPriceRequest, AddProductPriceResponse - Tests (Vitest + RTL): 19 tests — RED→GREEN confirmed - ProductPriceHistory: loading/error/empty/data/Badge Vigente/dialog/permissions - AddProductPriceDialog: validation (fecha pasada, precio=0, precio negativo), happy path payload + close, server 409 inline error, vi.useFakeTimers ART - hooks: useProductPrices caching + disabled when productId=0, useAddProductPrice invalidateQueries + error 409 - 453 total tests, 0 rojos