Commit Graph
67 Commits
Author SHA1 Message Date
toanhoangandClaude Sonnet 5 065c8cb59e Merge branch 'feat/asset-category'
AssetCategory entity: Prisma model+migration (valueGroup enum
high_value/low_value), Zod+CRUD, API routes, tests. Verified green by
test-runner (164/164 tests, tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 20:36:20 -04:00
toanhoang aaab8fd9b7 test: add AssetCategory API tests 2026-08-02 20:32:38 -04:00
toanhoang 63eb3df33d feat: add AssetCategory API routes 2026-08-02 20:31:27 -04:00
toanhoang 20ec7a11df feat: add AssetCategory validation + CRUD (src/lib/asset-categories.ts) 2026-08-02 20:31:22 -04:00
toanhoang 69fa00012b feat: add AssetCategory Prisma model + migration 2026-08-02 20:30:38 -04:00
toanhoangandClaude Sonnet 5 b8b1709a31 Merge branch 'feat/request-po-id'
Add nullable Request.purchaseOrderId FK (demand-side PO
traceability) — closes out Lane A (Vendor -> License.vendorId ->
PurchaseOrder -> purchaseOrderId x3). Verified green by test-runner
(143/143 tests, tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 20:28:50 -04:00
toanhoang 65232fc62c feat: add Request.purchaseOrderId (nullable FK) 2026-08-02 20:24:59 -04:00
toanhoangandClaude Sonnet 5 3dd1b22828 Merge branch 'feat/license-po-id'
Add nullable License.purchaseOrderId FK (supply-side PO
traceability). Verified green by test-runner (139/139 tests,
tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 20:20:58 -04:00
toanhoang abec58d239 feat: add License.purchaseOrderId (nullable FK)
Nullable FK from License to PurchaseOrder for supply-side traceability
(which PO a license came in through), mirroring AssetItem.purchaseOrderId's
just-merged pattern exactly. Separate commit from the earlier
License.vendorId retrofit per the project's 1-commit-per-business-change
convention.
2026-08-02 20:17:09 -04:00
toanhoangandClaude Sonnet 5 fb52f473a5 Merge branch 'feat/asset-item-po-id'
Add nullable AssetItem.purchaseOrderId FK (supply-side PO
traceability). Verified green by test-runner (131/131 tests,
tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 20:12:50 -04:00
toanhoang a6d7c7b302 feat: add AssetItem.purchaseOrderId (nullable FK) 2026-08-02 20:09:06 -04:00
toanhoangandClaude Sonnet 5 4d12885500 Merge branch 'feat/purchase-order'
PurchaseOrder entity: Prisma model+migration, Zod+CRUD (P2003 on both
vendorId/receivedById, receivedAt/receivedById co-nullability
refine), API routes, tests. Verified green by test-runner (127/127
tests, tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 20:04:58 -04:00
toanhoang 2b3c39a291 test: add PurchaseOrder API tests 2026-08-02 20:00:48 -04:00
toanhoang ad4b5d7c2f feat: add PurchaseOrder API routes 2026-08-02 19:59:18 -04:00
toanhoang cc98ca4d51 feat: add PurchaseOrder validation + CRUD (src/lib/purchase-orders.ts) 2026-08-02 19:59:05 -04:00
toanhoang 58235112f0 feat: add PurchaseOrder Prisma model + migration 2026-08-02 19:57:58 -04:00
toanhoangandClaude Sonnet 5 7b5ab70596 Merge branch 'feat/license-vendor-id'
Retrofit License.vendor (string) -> vendorId (FK to Vendor),
createLicense() now returns {ok,error} pattern. Verified green by
test-runner (97/97 tests, tsc/lint/build clean) before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 19:55:35 -04:00
toanhoang 9d99ebb425 fix: retrofit License.vendor to vendorId FK + createLicense error handling
License.vendor (free-text string) is now vendorId, a required FK to
Vendor, per docs/asset-license-management.md's "## Vendor (chốt
2026-08-02)" spec. Mirrors the Request.employeeId string->FK precedent:
Zod validates UUID format only, Prisma's own FK constraint (P2003) is
caught at insert time and translated into a clean 400 instead of a raw
500. No License data existed yet, so this is a pure column-type
migration, no backfill needed.

Also converts createLicense() to the {ok,value}|{ok,error} result
pattern used elsewhere (createRequest, createAssetItem) instead of
returning a bare LicenseRecord promise - closes the gap flagged in
CLAUDE.md now that License has its first FK-violation error case to
handle.
2026-08-02 19:41:33 -04:00
toanhoangandClaude Sonnet 5 9a4a3fede3 Merge branch 'feat/vendor'
Vendor entity: Prisma model+migration, Zod+CRUD, API routes, tests.
Verified green by test-runner (94/94 tests, tsc/lint/build clean)
before merge.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 19:36:49 -04:00
toanhoang f613763ba4 test: add Vendor API tests 2026-08-02 19:29:48 -04:00
toanhoang 283d94f07a feat: add Vendor API routes 2026-08-02 19:28:36 -04:00
toanhoang 19b652142a feat: add Vendor validation + CRUD (src/lib/vendors.ts) 2026-08-02 19:28:25 -04:00
toanhoang 07ef1582d3 feat: add Vendor Prisma model + migration 2026-08-02 19:28:09 -04:00
toanhoangandClaude Sonnet 5 f325abf22c docs: resolve Assignment open questions, unblock Lane C
Stakeholder decided: createAssignment() blocks if AssetItem.status
isn't in_stock, and update routes use PATCH /api/<entity>/[id]
(applies to both Assignment and PurchaseOrder).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 19:22:43 -04:00
toanhoang e3a3780253 Merge branch 'worktree-vendor-po-assignment-spec'
# Conflicts:
#	docs/asset-license-management.md
2026-08-02 19:20:34 -04:00
toanhoangandClaude Sonnet 5 4a0d00afd8 docs: spec Vendor/PurchaseOrder/Assignment + attribute defs, sequence tasks
BA formalizes the Vendor, PurchaseOrder (absorbs Receipt), Assignment,
and AssetCategoryAttributeDef designs decided in stakeholder brainstorm
into docs/asset-license-management.md. PM sequences the resulting work
into dependency-ordered, commit-mapped tasks across 3 independent lanes
in CLAUDE.md, surfacing that AssetCategory/AssetModel/ConsumableItem
were spec'd earlier but never actually built, and flagging two open
questions (Assignment precondition on AssetItem.status; REST shape for
update operations) that need stakeholder input before Lane C starts.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 13:12:27 -04:00
toanhoangandClaude Sonnet 5 29febf8205 docs: add asset high-value/low-value classification spec
AssetCategory/AssetModel hierarchy, ConsumableItem for low-value/
consumable items, data constraints, and open questions for the
consumable request flow — decisions already confirmed with
stakeholder, spec was written but not yet committed.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 13:01:02 -04:00
toanhoangandClaude Sonnet 5 513e70ff52 docs: add asset high-value/low-value classification spec
AssetCategory/AssetModel hierarchy, ConsumableItem for low-value/
consumable items, data constraints, and open questions for the
consumable request flow — decisions already confirmed with
stakeholder, spec was written but not yet committed.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 13:00:51 -04:00
toanhoang 3a18414d03 docs: add ba<->pm feedback loop + commit-mapping to workflow
pm now asks ba back when a spec gap could change business behavior,
instead of guessing; task lists must also map cleanly onto the
existing per-commit convention (model+migration/lib/API/tests).
2026-08-02 11:24:35 -04:00
toanhoangandClaude Sonnet 5 296a8c5d35 docs: update CLAUDE.md + docs for License entity
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 08:55:51 -04:00
toanhoang 5af1465a91 test: add License API tests 2026-08-02 08:55:08 -04:00
toanhoang 7b47385509 feat: add License API routes 2026-08-02 08:55:03 -04:00
toanhoang e232658b3d feat: add License validation + CRUD (src/lib/licenses.ts) 2026-08-02 08:54:57 -04:00
toanhoang 96088421ba feat: add License Prisma model + migration 2026-08-02 08:54:53 -04:00
toanhoangandClaude Sonnet 5 9e08c3f994 feat: add ba + pm agents to multi-agent dev workflow
Stakeholder should only need to state a business goal/priority, not
pick between implementation options or break work into tasks
themselves. `ba` turns a business ask into a concrete spec grounded in
docs/asset-license-management.md, `pm` breaks that spec into an
ordered, dependency-aware sequence of feature-implementer-sized tasks.
Neither writes code or resolves business-rule ambiguity — that stays
with the stakeholder.

Also fixes code-reviewer.md/feature-implementer.md pointing at the old
../processes/asset-license-management.md path — the doc moved into
docs/ inside this repo a while ago.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 08:36:21 -04:00
toanhoangandClaude Sonnet 5 87e2cca019 feat: add Dockerfile + app service for production deploy
Adds a production Docker image (node:20-alpine) and an `app` service in
docker-compose.yml so the Next.js app can run as a long-lived container
instead of just build/test in ephemeral containers. Requires `apk add
openssl` before `npm ci`, otherwise Prisma silently picks the wrong query
engine binary on Alpine and only fails at runtime. Also wires
AUTH_TRUST_HOST=true, required by Auth.js when running behind the NPM
reverse proxy.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 08:25:25 -04:00
toanhoang 539e511022 docs: update CLAUDE.md + docs for AssetItem entity 2026-07-31 16:10:57 +07:00
toanhoang 087e613a69 test: add AssetItem API tests 2026-07-31 16:04:58 +07:00
toanhoang 84f8177995 feat: add AssetItem API routes 2026-07-31 16:04:35 +07:00
toanhoang e7e359cbf7 feat: add AssetItem validation + CRUD (src/lib/assets.ts) 2026-07-31 16:04:22 +07:00
toanhoang 09b54f3c6b feat: add AssetItem Prisma model + migration 2026-07-31 16:04:01 +07:00
toanhoang 3f6497e2df docs: fix stale Employee entry, note fileParallelism scalability tradeoff 2026-07-31 15:25:34 +07:00
toanhoang 41381147f8 docs: update CLAUDE.md for Employee entity + Request FK retrofit
- Trang thai hien tai: Employee entity done (model, lib, API route,
  tests), Request.employee_id now a real FK to Employee - documents
  the intentional behavior change (employee_id must now be a valid
  UUID referencing a real Employee, not free text; non-existent
  references get a 400, not a 500). Also documents the vitest
  fileParallelism fix and the resulting 31/31 test count.
- Viec con do: remove Employee from the remaining-entities list.
2026-07-31 15:16:12 +07:00
toanhoang 2d838857d5 fix: serialize test file execution to avoid cross-file DB races
Each test file gets its own throwaway Postgres database by mutating
process.env.DATABASE_URL in tests/setup.ts's beforeAll. That per-file
DB switch is not safe under Vitest's default parallel file execution:
timing between two files' beforeAll/query calls can interleave enough
that both end up issuing real queries against the same throwaway DB.

This was invisible before because no two test files shared tables
with a real FK between them. It became visible once Request gained a
real FK to Employee: tests/employees.test.ts and tests/requests.test.ts
started intermittently deleting/reading each other's rows (FK
violations on deleteMany, wrong row counts), reproducible on every
run. Setting fileParallelism: false makes each file run to completion
before the next starts, so the per-file throwaway DB is genuinely
private. Confirmed: 31/31 tests pass reliably under plain npm test
(previously reproducibly 4 failing).
2026-07-31 15:15:11 +07:00
toanhoang 7e69efa4a8 fix: update Request validation/tests for Employee FK relation
- createRequestSchema now requires employee_id to be a UUID-format
  string (was a free-text non-empty check), matching the new FK to
  Employee in schema.prisma.
- createRequest() now returns a CreateRequestResult discriminated
  union and catches Prisma P2003 (FK violation), returning a friendly
  ok:false error instead of a raw 500 when employee_id does not
  reference an existing Employee. POST /api/requests branches on
  created.ok and returns 400 in that case.
- tests/requests.test.ts: every test that used to POST a fake string
  employee_id (emp-1, emp-2) now creates a real Employee first via
  createTestEmployee() and uses its id, since employee_id is now a
  real FK. Added coverage for a syntactically invalid UUID and for a
  well-formed but non-existent UUID (expects the friendly 400 from
  the P2003 catch, not a raw 500) - the exact case this retrofit was
  meant to protect against.
2026-07-31 15:14:55 +07:00
toanhoang 49d96b77e8 feat: relate Request.employee_id to Employee via FK
Retrofits Request.employeeId from a free-text String with no
referential integrity to a real relation: employeeId String @db.Uuid
+ employee Employee @relation(...), plus the inverse Employee.requests
back-relation field. Migration alters requests.employee_id from TEXT
to UUID with an FK constraint (drop+recreate, confirmed empty table -
no production/dev data existed to migrate).
2026-07-31 14:58:47 +07:00
toanhoang 10a8b0411d test: add Employee API tests
Mirrors tests/requests.test.ts's structure and @/auth mocking
pattern. Covers: successful create (with and without managerId),
empty name/department validation failures, malformed managerId UUID,
nonexistent managerId (FK violation -> friendly 400, not a raw 500),
a self-referential manager sanity check via direct Prisma update,
list ordering, empty list, and 401-when-unauthenticated for both
routes.
2026-07-31 14:57:37 +07:00
toanhoang a8964a091a feat: add Employee API routes
POST/GET /api/employees, same thin-route pattern as
src/app/api/requests/route.ts including the auth() 401 check
(middleware.ts excludes /api from page-route gating). POST also
surfaces createEmployee()'s FK-violation translation (nonexistent
managerId) as a 400, not a raw 500.
2026-07-31 14:55:50 +07:00
toanhoang eeaf946712 feat: add Employee validation + CRUD (src/lib/employees.ts)
Mirrors src/lib/requests.ts's shape: Zod schema, ValidationResult
discriminated union, CRUD via Prisma, toRecord() camelCase->snake_case
mapping. managerId is only format-checked as a UUID here; a
nonexistent managerId is caught via Prisma's P2003 FK-violation error
in createEmployee() and translated into a friendly {ok:false,error}
instead of leaking a raw Prisma error.
2026-07-31 14:54:48 +07:00
toanhoang 4c69bcf2c1 feat: add Employee Prisma model + migration
Employee directory entity per docs/asset-license-management.md's data
model table (id, name, department, manager). department stays a plain
String for now (not a separate Department entity). managerId is a
nullable self-relation (EmployeeManager) - top-level employees have
managerId = null.
2026-07-31 14:53:57 +07:00