feat(ADM-018): completed feature
This commit is contained in:
31
work/artifacts/F-019/architect.md
Normal file
31
work/artifacts/F-019/architect.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Architect — F-019 Promotions v1
|
||||
|
||||
## Feature
|
||||
F-019 adds server-side promo code validation and discount calculation. Discounts are recalculated from current cart totals and never trusted from the client.
|
||||
|
||||
## Design
|
||||
|
||||
### Module boundaries
|
||||
Create `project/src/modules/promotions/` with domain/application/infrastructure/api/tests. Promotions owns promo definitions and validation. Cart receives a public `PromotionServicePort` from the composition root and stores only the selected promo code, not discount amounts.
|
||||
|
||||
### Data model
|
||||
Add migration `014_promotions.js`:
|
||||
- `promotions_promotions`: code, type (`percent`, `fixed_amount`), value, validity window, usage_limit, usage_count, active, timestamps.
|
||||
- Add `promo_code text` to `cart_carts`.
|
||||
|
||||
### Rules
|
||||
- Percent discounts are basis points over current cart total.
|
||||
- Fixed amount discounts are cents and capped at current cart total.
|
||||
- Validity checks: active, starts_at <= now, ends_at >= now, usage_count < usage_limit when limit exists.
|
||||
- No stacking in v1: one promo code per cart.
|
||||
- Client discount fields are stripped/ignored by cart schemas.
|
||||
|
||||
### API
|
||||
- Admin `POST /promotions` creates promo codes.
|
||||
- Public/authenticated cart endpoint `POST /cart/promo-code` stores a code after server validation.
|
||||
- `GET /cart` recalculates discount through `PromotionService`.
|
||||
|
||||
## Acceptance trace
|
||||
- Valid promo: cart read applies recalculated server discount.
|
||||
- Expired/exhausted: applying code returns HTTP 422 reason.
|
||||
- Client discount ignored: schemas strip discount fields and cart stores no discount amount.
|
||||
25
work/artifacts/F-019/documenter.md
Normal file
25
work/artifacts/F-019/documenter.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Documenter — F-019 Promotions v1
|
||||
|
||||
## Summary
|
||||
Promotions v1 adds server-side promo code definitions and validation. Cart stores only a selected `promoCode`; discount amounts are recalculated from current cart totals.
|
||||
|
||||
## Public API notes
|
||||
|
||||
| Route | Access | Result |
|
||||
|---|---|---|
|
||||
| POST /promotions | admin only | Creates or updates a promo code |
|
||||
| POST /cart/promo-code | authenticated | Applies one promo code to the current cart |
|
||||
|
||||
## Rules
|
||||
- Promotion types: `percent` and `fixed_amount`.
|
||||
- Percent values are basis points; fixed values are cents and capped at cart total.
|
||||
- Validity checks: active flag, time window, usage limit.
|
||||
- No stacking in v1: one promo code per cart.
|
||||
- Client-supplied discount amounts are ignored.
|
||||
|
||||
## Evidence
|
||||
- `work/artifacts/F-019/architect.md`
|
||||
- `work/artifacts/F-019/implementer.md`
|
||||
- `work/artifacts/F-019/reviewer.json`
|
||||
- `work/artifacts/F-019/security.json`
|
||||
- `work/artifacts/F-019/qa.json`
|
||||
29
work/artifacts/F-019/implementer.md
Normal file
29
work/artifacts/F-019/implementer.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Implementer — F-019 Promotions v1
|
||||
|
||||
## Summary
|
||||
Implemented server-side promo codes with percent/fixed-amount rules, validity/usage validation, cart promo-code application, and recalculated discounts during cart reads.
|
||||
|
||||
## Files changed
|
||||
- `project/migrations/014_promotions.js`
|
||||
- `project/src/modules/promotions/**`
|
||||
- `project/src/modules/cart/**`
|
||||
- `project/src/app/build-app.ts`
|
||||
- `project/src/app/tests/promotions.itest.ts`
|
||||
|
||||
## Acceptance evidence
|
||||
- AC1 valid promo: `promotions.itest.ts` applies `SAVE10`; cart returns server-calculated discount and reduced total.
|
||||
- AC2 expired/exhausted promo: `promotions.itest.ts` verifies HTTP 422 for expired and usage-limit exhausted codes.
|
||||
- AC3 client-supplied discount ignored: promo and cart schemas strip unknown discount fields; cart stores only selected promo code, not discount amount.
|
||||
- `verify.sh`: passed.
|
||||
|
||||
## Commands run
|
||||
- `cd project && npm run lint` — passed
|
||||
- `cd project && npm run typecheck` — passed
|
||||
- `cd project && npm run build` — passed
|
||||
- `cd project && npm test` — passed
|
||||
- `cd project && TEST_DATABASE_URL='postgres://mdv:mdv_dev_only@localhost:5432/mdv_test' npm run test:integration -- promotions.itest migrations.itest` — passed; 11 files, 44 tests
|
||||
- `./scripts/verify.sh` — passed
|
||||
|
||||
## Notes
|
||||
- No stacking in v1: one `promo_code` per cart.
|
||||
- Usage count is validated for exhaustion; consumption/increment can be tied to checkout/order completion in a later feature.
|
||||
15
work/artifacts/F-019/leader-close.json
Normal file
15
work/artifacts/F-019/leader-close.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"feature_id": "F-019",
|
||||
"agent": "leader",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "F-019 closed with reviewer, security and QA gates approved. Final verify.sh passed.",
|
||||
"evidence": [
|
||||
"reviewer.json verdict APPROVED",
|
||||
"security.json verdict APPROVED",
|
||||
"qa.json verdict APPROVED",
|
||||
"./scripts/verify.sh passed during close",
|
||||
"backlog/features.json updated: F-019 status done and gates true",
|
||||
"work/current.md updated: no active feature, next suggested F-020"
|
||||
],
|
||||
"timestamp": "2026-08-15T17:44:59Z"
|
||||
}
|
||||
21
work/artifacts/F-019/qa.json
Normal file
21
work/artifacts/F-019/qa.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"feature_id": "F-019",
|
||||
"agent": "qa",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "QA approved. All F-019 acceptance criteria are covered by executable tests and green checks: valid promo discount recalculation, expired/exhausted 422 responses, and ignored client discount fields.",
|
||||
"evidence": [
|
||||
"promotions.itest.ts covers valid promo code with recalculated discount",
|
||||
"promotions.itest.ts covers expired and exhausted promo codes returning HTTP 422",
|
||||
"promotions.itest.ts sends client discountCents in cart payload and server ignores it",
|
||||
"cd project && npm run lint/typecheck/build/test passed",
|
||||
"DB integration promotions/migrations suite passed: 11 files, 44 tests",
|
||||
"./scripts/verify.sh passed"
|
||||
],
|
||||
"acceptance": [
|
||||
{ "criterion": "Given valid promo code When applied Then discount recalculated server-side", "status": "PASS", "evidence": "SAVE10 applied to current cart total returns server-calculated discount" },
|
||||
{ "criterion": "Given expired or exhausted code When applied Then HTTP 422 with reason", "status": "PASS", "evidence": "Expired OLD and exhausted USED both return 422" },
|
||||
{ "criterion": "Given client-supplied discount amount Then discount ignored", "status": "PASS", "evidence": "Client discountCents is stripped/ignored; cart discount comes from PromotionService" },
|
||||
{ "criterion": "verify.sh green", "status": "PASS", "evidence": "./scripts/verify.sh PASS" }
|
||||
],
|
||||
"timestamp": "2026-08-15T17:44:09Z"
|
||||
}
|
||||
17
work/artifacts/F-019/reviewer.json
Normal file
17
work/artifacts/F-019/reviewer.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"feature_id": "F-019",
|
||||
"agent": "reviewer",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "F-019 review approved. Promotions are server-owned, validated through a public PromotionService, integrated into cart recalculation by selected promo_code only, and covered by valid/expired/exhausted/client-discount tests.",
|
||||
"evidence": [
|
||||
"Read work/current.md, architect.md and implementer.md",
|
||||
"Inspected promotions domain/application/infrastructure/API and migration 014_promotions.js",
|
||||
"Verified cart stores promo_code only and recalculates discount through injected PromotionService",
|
||||
"Verified client discount fields are stripped/ignored by schemas and not persisted as amounts",
|
||||
"gentle-ai review mode status: receipt-driven development off globally, ordinary Orquestra gate used",
|
||||
"cd project && npm run lint/typecheck/build/test passed",
|
||||
"DB integration promotions/migrations suite passed with TEST_DATABASE_URL",
|
||||
"./scripts/verify.sh passed"
|
||||
],
|
||||
"timestamp": "2026-08-15T17:43:39Z"
|
||||
}
|
||||
16
work/artifacts/F-019/security.json
Normal file
16
work/artifacts/F-019/security.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"feature_id": "F-019",
|
||||
"agent": "security",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "Security approved. No new dependencies or secrets were introduced. Promo mutation is admin-only, cart promo application is authenticated, inputs are validated, and SQL uses parameterized queries.",
|
||||
"evidence": [
|
||||
"cd project && npm audit --audit-level=high --omit=dev: found 0 vulnerabilities",
|
||||
"Secret scan found only a test fixture password, no committed credentials",
|
||||
"Reviewed /promotions: admin-only creation through authenticated role check",
|
||||
"Reviewed /cart/promo-code: authenticated user scoped cart update",
|
||||
"Reviewed schemas: promo and cart payloads validate bounded fields and strip client discounts",
|
||||
"Reviewed repository SQL: parameterized queries only",
|
||||
"Reviewed migration constraints for promo type, positive value, usage limits and validity window"
|
||||
],
|
||||
"timestamp": "2026-08-15T17:43:57Z"
|
||||
}
|
||||
Reference in New Issue
Block a user