Happy House - Ecommerce Docs
Developer ResourcesInventory

Inventory Features and Flows

Complete feature list, actor journeys, state flows, business rules, edge cases, and diagrams for the Inventory module.

Inventory Features and Flows

Use this page for the inventory domain: what it does for users, admins, workers and systems, and how each flow behaves from start to finish.

1. Documentation Evidence

Source TypeFiles or DocsWhat Was Extracted
Technical designinventory-write.service.ts, inventory-reservation.service.ts, inventory-ledger.service.ts, inventory-math.util.ts, inventory.constants.tsGuarded UPDATEs, reservation lifecycle, generated columns, caps
APIadmin/stock/*.controller.ts, admin/job/*.controller.tsRoute surface, permissions, rate limits
Backendinventory-availability.service.ts, inventory-projection.service.ts, workers/*, import-export/*Side effects, projections, job lifecycle
Schemapackages/db/src/schema/inventory/*.tsFour tables, CHECKs, generated columns
Jobspackages/jobs/src/index.tsInventoryJob names, payloads

2. Feature Summary

FieldValue
Moduleinventory
SubmoduleN/A (single domain: stock, reservations, ledger, jobs)
Primary user valueTrustworthy stock: availability computed by the database, reservations with a full lifecycle, an auditable movement ledger, and admin adjustment/import/export
ActorsAdmin, worker/system. No guest surface — stock reaches the storefront via the product response's inventory group
Main entry points/api/admin/inventory, /api/admin/inventory/jobs, INVENTORY queue, outbox
Main outputsInventory responses, movement ledger, reservations, CSV imports/exports, MongoDB activity projections
Related docsAPI, Backend

3. Actor Matrix

ActorCan DoCannot DoAuth RequirementNotes
AdminList inventory, view one product's row, update configuration, adjust stock, view movements, bulk adjust, submit/cancel import/export jobsWrite available_quantity/stock_status (GENERATED), disable oversell while in debt, re-enable tracking on a row in debtAdmin JWT + Inventory_READ / Inventory_UPDATECursor-paginated reads; ADMIN_WRITE 10/min, ADMIN_BULK_WRITE 5/min, ADMIN_ASYNC_JOB_SUBMIT 10/hour
Worker/systemRun import/export jobs, sweep expired reservations, reconcile drift, project activityBullMQ workerLease-based claim; cooperative cancellation; outbox enqueued
Product domain (system)Create inventory rows on product create/import; read availability for responsesUpdate counters outside InventoryWriteServiceInternal serviceInventoryWriteService is the only permitted writer

4. Capability Matrix

CapabilitySurfaceActorRoute/TriggerState ReadState WrittenLinked API Section
List inventoryAdminAdminGET /api/admin/inventoryRowsAPI §2
One product's inventoryAdminAdminGET /api/admin/inventory/products/:productPublicIdRowAPI §2
Update configurationAdminAdminPATCH /products/:productPublicId/configurationRow + versiontrack/oversell/thresholdAPI §2.3
Adjust stockAdminAdminPOST /products/:productPublicId/adjustRowCounters + movement + projectionAPI §2.2
View movementsAdminAdminGET /products/:productPublicId/movementsLedgerAPI §2
Bulk adjustAdminAdminPOST /bulk/adjustRowsCounters + movementsAPI §2.4
Import/export jobsAdmin/workerAdmin → system/api/admin/inventory/jobsJob rowsJob rows, CSVAPI §2.1
Reserve / release / finalizeSystem (future cart/checkout)Internalservice callsRow + reservationsCounters + reservation + movementReservations
Sweep expired reservationsWorkerSystemcron → SWEEP_EXPIRED_RESERVATIONSExpired rowsSettled + movementsbackend §9
Reconcile driftWorkerSystemcron → RECONCILECounters vs ledgercorrection movementsbackend §9
Project activityWorkerSystemoutbox → PROJECT_ACTIVITYMovementsMongoDBbackend §9

5. User-Facing Flows

There is no guest-facing inventory flow. Stock reaches the storefront through the product response's inventory group (batched, one query per page) — see the products docs. All flows below are admin or system flows.

5.1 Adjust stock (admin)

Summary

An admin corrects a stock level — a damage write-off, a physical count fix, a restock. The adjustment is a guarded UPDATE; a refused change (would go negative without oversell) returns a 409 and changes nothing.

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Negative resulttotal_quantity would go negative without oversellRefused409 INVENTORY_ADJUSTMENT_WOULD_GO_NEGATIVE
Untracked rowNo row / track_inventory = falseFirst adjustment auto-enables trackingAccepted
Oversell allowedallow_oversell = trueNegative allowed (units owed)Accepted
Duplicate requestSame adjustment twiceTwo distinct movements (no idempotency on adjust)Two ledger rows

5.2 Reservation lifecycle (system)

  • Reserve — idempotent via the globally unique reservation_key (duplicate insert is a no-op).
  • Release — idempotent via a status = 'active' guard; release twice is success (the caller's intent is "make sure this hold is gone").
  • Finalize — the same guard, but finalize twice is a 409 INVENTORY_RESERVATION_ALREADY_SETTLED (the caller's intent is a one-time event).
  • Expire — default TTL 15 minutes; a per-minute cron sweep settles expired rows and returns the units.

6. Admin Flows

6.1 Update configuration

PATCH /products/:productPublicId/configurationtrackInventory, allowOversell, lowStockThreshold, version. Guards: re-enabling tracking on a row in debt is refused; disabling oversell while units are owed is refused (409 INVENTORY_OVERSELL_DISABLE_BLOCKED); stale version → 409 INVENTORY_VERSION_CONFLICT.

6.2 Bulk adjust

POST /bulk/adjust — up to 100 items (409 INVENTORY_BULK_LIMIT_EXCEEDED); per-item result { succeeded, failures }, each row naming the product AND the variant the item resolved to; an item may name a variantPublicId and means the default when it does not; writes ordered by variant_id ascending to avoid deadlock (reproduced as SQLSTATE 40P01 without the ordering). An unresolvable product or variant is a per-item failure, never a rollback of the items beside it.

6.3 Import / export

CSV import of adjustments (only .csv accepted; required productPublicId + quantityDelta); export of the ledger; job lifecycle with lease claim, cooperative cancellation, zero-row export = success with header-only file.

7. Lifecycle and State Transitions

7.1 Reservation states

EntityFromEvent/ActionToGuard ConditionSide Effects
inventory_reservationreserveactivereservation_key unused globallymovement reservation_created
inventory_reservationactivereleasereleasedstatus = 'active' matchmovement reservation_released
inventory_reservationactivefinalizefinalizedstatus = 'active' matchmovement reservation_finalized; 409 if already settled
inventory_reservationactiveexpireexpiredTTL passed, sweep claimmovement reservation_expired

7.2 Job states

9. Data and Side Effects by Flow

FlowDB WritesCache EffectsJobsRealtimeAnalyticsNotifications
AdjustCounters + movement (same tx)inventory domainoutbox → projectionMongoDB activity (async)
Reserve/release/finalize/expireCounters + reservation + movementinventory domainoutbox → projectionMongoDB
ConfigurationConfig fields (+ movement if counters change)inventory domain
Reconcilecorrection movementsinventory domain
Import/exportJob row + rows/CSVinventory domainoutbox

10. Error and Recovery Flows

ScenarioTriggerUser/System ExperienceRecoverySource
Guard refusedAdjustment would go negative409, nothing changedAdjust within limits or enable oversellinventory-write.service.ts
Oversell disable blockedDebt exists409Resolve debt firstmath util
DeadlockMulti-product write unorderedSQLSTATE 40P01Product_id ordering preventsprobe
Job cancelled mid-importAdmin cancelCooperative abort, rollbackResubmitinventory-job-lifecycle.service.ts
Drift detectedCounters ≠ ledgercorrection movementHourly reconcileinventory-reconcile.processor.ts
Export too large> 50,000 rowsJob failsNarrow filtersexport service

11. Diagrams Required Per Module

  • Actor capability diagram — §3/§4.
  • Sequence diagram per major flow — §5.1, §5.2.
  • State machine diagram — §5.2/§7.
  • Data side-effect diagram — §9.
  • Error branch diagram — §6.1/§10.

12. Mandatory Feature and Flow Deep-Dive Pack

12.1 Feature Inventory With Minor Behaviors

FeatureMinor BehaviorActorTriggerUser/System ResultBackend Side EffectSource
AdjustAuto-enable trackingAdminFirst adjustment on untracked rowRow becomes trackedConfig flip in same txinventory-write.service.ts
AdjustReason enumAdminreason fieldMovement kindEnum validationdto
ReserveTTL overrideSystemttlSeconds paramCustom expiryBusiness timestampreservation service
ReserveKey collision after expirySystemSame key reused409 — key global, not partialINVENTORY_RESERVATION_KEY_CONFLICT
ListCursor paginationAdmincursorNext pageKeyset
ReconcileHeals missing rowsWorkerHourlyProducts without rows get rowsCompare-and-set
SweepBatch boundsWorkerPer-minuteSettles expired in batchesINVENTORY_RESERVATION_SWEEP_BATCH_SIZEconstants
Projectionpre_order freezeSystemTracked product in pre-orderNever overwrittenProjection rulemath util

12.2 Business Process Diagram Pack

12.3 Business Rules and Policy Traceability

RuleBusiness ReasonActor ImpactEnforced InAPI ImpactBackend ImpactTests
available_quantity/stock_status GENERATEDAvailability cannot go staleAdmin sees computed truthDB GENERATED ALWAYS ASRead-only fields428C9 on write attemptprobe
Negative legal under oversellBackorders = units owedAdmin can over-adjustConditional CHECKs409 otherwiseallow_oversell OR NOT track_inventoryprobe
One guarded UPDATE per counter changeNo read-then-write racesRefused ops return 409Service409 codesrowCount = 0 = refusedspec
Multi-row ordering by variant_idDeadlock preventionBulk adjust worksService sortOrdering by product_id leaves two variants of one product unorderedReproduced 40P01probe + int spec
Reservation key global + permanentNo double-hold by replaySystem callersUnique index409 on reuseNot partialspec
Write monopolyInventoryWriteService onlyConsistent rulesService architectureReviewedspec
pre_order never overwritten by projectionPre-order status is admin truthProduct stock staysProjection ruleprojectToProductStockStatusspec

12.4 Tradeoffs and Product Rationale

Product DecisionUser BenefitEngineering BenefitAlternativeTradeoffRisk
Generated columnsTrustworthy availabilityNo stale-write pathService-computedDB-version couplingDocumented
Ledger in PostgreSQL same-txAuditable, atomicNo dual-write gapMongo ledgerPostgres growthRetention via maintenance
Analytics async via outboxFast writesDecoupledSync Mongo writeEventualAccepted
Oversell opt-in per variantBackorders supportedExplicit configGlobal flagPer-variant debtGuards
Untracked defaultExisting products unchangedSafe backfillTrack-all migrationManual enableAuto-enable on first adjust

12.5 Flow Edge-Case Matrix

FlowEdge CaseTriggerExpected BehaviorUser/System FeedbackSource
AdjustEmpty deltaquantityDelta missingValidation error400dto
AdjustZero delta0No-op movement? (validated)Accepted/rejected per dto
ReserveInsufficient stockNo oversell, not enoughRefused409 INVENTORY_INSUFFICIENT_STOCK
ReserveDuplicate keySame key twiceSecond is no-opSame reservationunique index
FinalizeTwiceSecond finalize409INVENTORY_RESERVATION_ALREADY_SETTLED
SweepRace with releaseSweep claims a row being releasedIdempotent per rowOne settlesguarded UPDATE
ReconcileCounters vs ledger disagreeDriftcorrection movementLedger records
ImportRow invalidBad rowAll-or-nothingJob failed, nothing written
ExportZero rowsEmpty filterHeader-only fileSuccess
Tracking re-enableRow in debtConfig changeRefused409canEnableTracking

12.6 Flow-to-Data Trace

FlowReadsWritesCacheJobs/EventsResponse Fields
Adjustinventory rowcounters + inventory_movementinvalidateoutbox → projectioninventory response
Reserverow + reservationsreservation + counters + movementinvalidateoutbox → projectionreservation
Listrowsinventory domaincursor list
Movementsledgercursor list
Importrows by productPublicIdcounters + movements + inventory_jobinvalidateoutbox → importjob

12.7 Experience Quality Checklist

  • The doc explains what the actor is trying to accomplish.
  • The doc explains what the backend does that the actor does not see (GENERATED columns, guarded UPDATEs, ledger).
  • The doc covers every minor flow and branch (12.1, 12.5).
  • The doc includes user, admin, worker, and system flows.
  • The doc explains business logic, tradeoffs, and rationale.
  • The doc maps every flow to API routes and backend side effects.
  • The doc includes diagrams appropriate to each flow type.
  • The doc covers edge cases and failure recovery.

13. Completion Checklist

  • Every feature, minor action, and submodule capability is listed.
  • Every actor has allowed and forbidden behavior.
  • Every major and minor flow includes steps, branches, and diagrams.
  • Every lifecycle has a transition table and state diagram.
  • Every flow links to the API and backend docs.
  • TDD dependencies are called out where they shape behavior (no TDD pages published yet).

See Also