Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Business modules

Module

Inventory module (inventory)

Module page for inventory, the warehouses, storage locations, stock levels per product breakdown, goods movements, reservations, batches, and serial numbers.

The inventory module in src/cluster_erp/modules/inventory/ keeps the stock of the company. It holds warehouses and storage locations, one stock level row per product, model, version, place, batch, owning project, and custodian, and the goods movements that change those rows. It also manages stock reservations, usage decisions on received goods, project stock loans, batches, and serial numbers. Purchasing, production, nesting, and sales post their stock changes to this module through the EventBus.

At a glance

Item Value Source
Package src/cluster_erp/modules/inventory/ folder
Module class Module src/cluster_erp/modules/inventory/module.py:16
name / version inventory / 0.1.0 src/cluster_erp/modules/inventory/module.py:17-23
API prefix /api/v1/inventory (ModuleBase.api_prefix) src/cluster_erp/core/module_base.py:37-38
Sub-modules 3: warehouses, stock, batches folder listing
Tables 9: inventory_warehouses, inventory_storage_locations, inventory_stock_levels, inventory_goods_movements, inventory_usage_decisions, inventory_stock_reservations, inventory_project_stock_loans, inventory_batches, inventory_serial_numbers __tablename__ in the three models.py files
Entities in metadata 14 keys in entities src/cluster_erp/modules/inventory/module.py:628-643
Routes 51 route decorators in the package grep for @...router.get/post/put/patch/delete
Actions 16 ActionMeta entries grep "ActionMeta(" in the package
Migrations 17 files files in migrations/versions/ whose text contains one of the nine table names
Web app presence Catalogue app inventory; the custom widget warehouse_dashboard from a gated bundle src/cluster_erp/modules/app_store/catalog.json (entry inventory), gated_apps/inventory/app.js:170
Desktop presence src/cluster_erp/desktop/modules/inventory/ (warehouse_dashboard.py) folder

What the module does

  • Keeps warehouses with a code that is unique among live rows, and their storage locations (src/cluster_erp/modules/inventory/warehouses/models.py:13-71).
  • Keeps stock per product, model, and version. The quantity is split into six categories: unrestricted, reserved, quality, blocked, blocked at goods receipt, and returns (src/cluster_erp/modules/inventory/stock/models.py:25-123).
  • Posts goods movements of 13 types and writes one movement row per change (src/cluster_erp/modules/inventory/stock/services.py:78-92).
  • Reserves unrestricted stock for a reference and later consumes or cancels the reservation (src/cluster_erp/modules/inventory/stock/services.py:1901-2080).
  • Records a usage decision on a received quantity and posts the follow-up movement (src/cluster_erp/modules/inventory/stock/services.py:1496-1666).
  • Moves stock between a project stripe and common stock, and lends stock between stripes with a payback (src/cluster_erp/modules/inventory/stock/services.py:542-800).
  • Tracks batches with origin, expiry, and status, and serial numbers with status (src/cluster_erp/modules/inventory/batches/models.py:14-125).
  • Serves a warehouse dashboard read model (src/cluster_erp/modules/inventory/warehouses/services.py:145).

Sub-modules

Sub-module Purpose Main entities Router Source
warehouses Warehouses, storage locations, dashboard Warehouse, StorageLocation /warehouses, /locations src/cluster_erp/modules/inventory/warehouses/routes.py:33-34
stock Stock levels, movements, reservations, usage decisions, loans StockLevel, GoodsMovement, StockReservation, UsageDecision, ProjectStockLoan /stock, /movements, /reservations, /usage-decisions src/cluster_erp/modules/inventory/stock/routes.py:47-53
batches Batches and serial numbers Batch, SerialNumber /batches, /serial-numbers src/cluster_erp/modules/inventory/batches/routes.py:33-34

All routers are mounted under the module prefix with the app entitlement dependency require_app("inventory") (src/cluster_erp/modules/inventory/module.py:52-62).

Data model

Entity Table Key fields Notes Source
Warehouse inventory_warehouses code, name, address, is_active, extra code unique among live rows src/cluster_erp/modules/inventory/warehouses/models.py:13-46
StorageLocation inventory_storage_locations warehouse_id, code, name, zone, is_active, extra src/cluster_erp/modules/inventory/warehouses/models.py:48-71
StockLevel inventory_stock_levels product_id, product_model_id, product_version_id, warehouse_id, storage_location_id, batch_id, owner_project_id, custodian_party_id, six quantity columns one live row per composite key src/cluster_erp/modules/inventory/stock/models.py:25-123
GoodsMovement inventory_goods_movements movement_no, movement_type, product breakdown, warehouse_id, to_warehouse_id, qty, batch_id, serial_number_id, owner_project_id, reference_type, reference_id, waybill_no, posted_at, posted_by, is_reversed, original_movement_id movement_no unique src/cluster_erp/modules/inventory/stock/models.py:125-268
UsageDecision inventory_usage_decisions decision_no, movement_id, product breakdown, qty, source_stock_type, decision_code, target_stock_type, follow_up_movement_id src/cluster_erp/modules/inventory/stock/models.py:270-345
StockReservation inventory_stock_reservations product breakdown, warehouse_id, qty, reference_type, reference_id, status (default active), consumed_at, cancelled_at src/cluster_erp/modules/inventory/stock/models.py:347-413
ProjectStockLoan inventory_project_stock_loans loan_no, lender_project_id, borrower_project_id, product breakdown, qty, qty_paid, status (default open), due_date src/cluster_erp/modules/inventory/stock/models.py:415-474
Batch inventory_batches product breakdown, batch_number, origin_type, party_id, expiry_date, status batch_number unique per product among live rows src/cluster_erp/modules/inventory/batches/models.py:14-76
SerialNumber inventory_serial_numbers product breakdown, serial_number, batch_id, status serial_number unique among live rows src/cluster_erp/modules/inventory/batches/models.py:78-125

The stock key always carries the product breakdown. The partial unique index uq_stock_level_composite covers product, model, version, and warehouse, plus location, batch, owning project, and custodian with NULL coalesced, on live rows only (src/cluster_erp/modules/inventory/stock/models.py:27-47). Quantities are Numeric(14, 4) (src/cluster_erp/modules/inventory/stock/models.py:91-122). The module stores no money column; the analytics docstring states this (src/cluster_erp/modules/inventory/analytics_facts.py:17-18). The product breakdown is checked through the EventBus with ensure_product_breakdown, not by database foreign keys (src/cluster_erp/core/product_breakdown.py:86, src/cluster_erp/modules/inventory/stock/services.py:370-376).

Screens and metadata

get_metadata declares a sidebar group Inventory (Turkish Envanter) with eight sections: Warehouse Dashboard (custom widget warehouse_dashboard), Warehouses, Stock Levels, Goods Movements, Batches, Serial Numbers, Usage Decisions, and Stock Reservations (src/cluster_erp/modules/inventory/module.py:536-626). The 14 entity keys include drill-down views such as warehouse_stock_level, batch_movement, and product_stock_level (src/cluster_erp/modules/inventory/module.py:628-643). The web dashboard calls one read model, GET /inventory/warehouses/dashboard (gated_apps/inventory/app.js:7-11, src/cluster_erp/modules/inventory/warehouses/routes.py:99). The web app renders the rest generically; see generic list and gated app bundles. The desktop dashboard is described on desktop module screens.

Status and actions

Stock has no status field; movements change quantities. A reservation is active, consumed, or cancelled (src/cluster_erp/modules/inventory/stock/services.py:1950, src/cluster_erp/modules/inventory/stock/services.py:2015, src/cluster_erp/modules/inventory/stock/services.py:2068). A movement carries is_reversed (src/cluster_erp/modules/inventory/stock/models.py:253-257).

Action Label Condition Endpoint Permission Source
goods_receipt Goods Receipt none POST /stock/goods-receipt goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:675-770, src/cluster_erp/modules/inventory/stock/routes.py:156-183
goods_issue Goods Issue none POST /stock/goods-issue goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:771-866
subcontract_issue, subcontract_return, subcontract_consume Issue to Subcontractor, Return from Subcontractor, Consume at Subcontractor none POST /stock/subcontract-issue, /subcontract-return, /subcontract-consume goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:867-1004, src/cluster_erp/modules/inventory/stock/routes.py:598-633
transfer Transfer none POST /stock/transfer goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1005-1083
adjustment Adjustment none POST /stock/adjustment goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1084-1139
gr_blocked_receipt GR Blocked Receipt none POST /stock/gr-blocked-receipt goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1140-1220
stock_category_transfer Category Transfer none POST /stock/stock-category-transfer goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1221-1301
return_to_vendor Return to Vendor none POST /stock/return-to-vendor goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1302-1390
scrap Scrap none POST /stock/scrap goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1391-1456
reverse Reverse is_reversed is false POST /movements/{id}/reverse goods_movement.create src/cluster_erp/modules/inventory/stock/metadata.py:1457-1472
create_reservation Create Reservation none POST /reservations/ stock_reservation.create src/cluster_erp/modules/inventory/stock/metadata.py:1593-1639
consume, cancel Consume, Cancel status is active POST /reservations/{id}/consume, /cancel stock_reservation.create src/cluster_erp/modules/inventory/stock/metadata.py:1640-1661
create_usage_decision Record Usage Decision none POST /usage-decisions/ usage_decision.create src/cluster_erp/modules/inventory/stock/metadata.py:2633-2698, src/cluster_erp/modules/inventory/stock/routes.py:579-597

The ActionMeta class has no permission field; the permission comes from the route's require_permission (src/cluster_erp/core/metadata.py:418-434). The routes for ownership transfer and project loans have no ActionMeta (src/cluster_erp/modules/inventory/stock/routes.py:217-289).

Business rules

  • Every posting first asks GetProductById and refuses a product that is not stockable (src/cluster_erp/modules/inventory/stock/services.py:2119-2134).
  • A batch-managed product needs a batch, and a serial-tracked product needs a serial number. A blocked or expired batch is refused (src/cluster_erp/modules/inventory/stock/services.py:2136-2193).
  • A goods receipt adds to the unrestricted quantity of the matching stock row and creates the row when it is missing. The row is read with a row lock (src/cluster_erp/modules/inventory/stock/services.py:2195-2286).
  • A goods receipt that names a purchase order as reference is refused; purchase order receipts go through the procurement receive flow (src/cluster_erp/modules/inventory/stock/routes.py:166-178).
  • A decrease takes the requested quantity from the given project stripe. A goods issue may take the rest from common stock and then writes one movement per stripe; the other decreases refuse a shortfall (src/cluster_erp/modules/inventory/stock/services.py:2288-2363, src/cluster_erp/modules/inventory/stock/services.py:473-490).
  • A transfer between two project stripes is refused unless the setting allow_project_to_project_stock_transfer is on (src/cluster_erp/modules/inventory/stock/services.py:552-566).
  • A project loan needs two different sides, at least one of them a project (src/cluster_erp/modules/inventory/stock/services.py:615-640).
  • An adjustment sets the unrestricted quantity to the new value and records the difference as the movement quantity (src/cluster_erp/modules/inventory/stock/services.py:1051-1100).
  • Stock categories move only along nine allowed pairs, for example gr_blocked to unrestricted or quality to blocked (src/cluster_erp/modules/inventory/stock/services.py:112-122).
  • A usage decision applies only to a goods receipt or a blocked goods receipt, and not beyond the undecided quantity. accept and conditional_accept move to unrestricted, reject returns to the vendor, rework moves to blocked, and scrap posts a scrap movement (src/cluster_erp/modules/inventory/stock/services.py:124-135, src/cluster_erp/modules/inventory/stock/services.py:1496-1625).
  • A reservation moves quantity from unrestricted to reserved. Consuming removes it from reserved; cancelling returns it to unrestricted (src/cluster_erp/modules/inventory/stock/services.py:1901-2080).
  • A movement can be reversed once, and a reversal movement cannot be reversed (src/cluster_erp/modules/inventory/stock/services.py:1730-1760).
  • Batch origin is customer, supplier, or internal (src/cluster_erp/modules/inventory/batches/services.py:35).
  • Movement numbers come from the yearly sequence goods_movement, decisions from usage_decision, and loans from stock_loan (src/cluster_erp/modules/inventory/stock/services.py:383).

Events and queries

The module consumes 14 contracts and produces 20, counted as on the parties module page. The full list is on the contracts reference.

Direction Contract class Handler or emitter Source
emits GoodsReceived, GoodsIssued, StockTransferred, StockAdjusted, GrBlockedReceived, StockCategoryTransferred, ReturnedToVendor, ScrapPosted, UsageDecisionRecorded, GoodsMovementReversed the posting methods of StockService src/cluster_erp/modules/inventory/stock/services.py:431-447, src/cluster_erp/modules/inventory/stock/services.py:527-1879
emits StockReserved, StockReservationConsumed, StockReservationCancelled reservation methods src/cluster_erp/modules/inventory/stock/services.py:1957, src/cluster_erp/modules/inventory/stock/services.py:2020, src/cluster_erp/modules/inventory/stock/services.py:2073
emits BatchCreated BatchService.create src/cluster_erp/modules/inventory/batches/services.py:135
answers query GetStockLevel, GetWarehouseById, GetBulkStockLevels, GetConsumptionHistory, GetBatchExpirySchedule, GetBatchesByProduct, IsStockAvailable handlers in module.py src/cluster_erp/modules/inventory/module.py:86-110
answers query ReserveStock, ConsumeReservation, CancelReservation, PostGoodsReceipt, PostGoodsIssue handlers in module.py src/cluster_erp/modules/inventory/module.py:111-115, src/cluster_erp/modules/inventory/module.py:392-495
subscribes DescribeFieldConsumers _describe_field_consumers src/cluster_erp/modules/inventory/module.py:68, src/cluster_erp/modules/inventory/module.py:646-696
subscribes DescribeAnalyticsFacts describe_analytics_facts src/cluster_erp/modules/inventory/module.py:119-124

The query handlers for reservations and postings commit their own session (src/cluster_erp/modules/inventory/module.py:410, src/cluster_erp/modules/inventory/module.py:465, src/cluster_erp/modules/inventory/module.py:494). The field-consumer answer marks the product flag is_stockable as a hard dependency and batch_managed and serial_tracked as soft ones (src/cluster_erp/modules/inventory/module.py:646-696); see tenant fields.

Permissions

Permission key Used by Source
warehouse.read, .create, .update, .delete warehouse routes src/cluster_erp/modules/inventory/warehouses/routes.py:73-149
storage_location.read, .create, .update, .delete location routes and the warehouse location list src/cluster_erp/modules/inventory/warehouses/routes.py:168-274
stock_level.read stock level, availability, and loan reads src/cluster_erp/modules/inventory/stock/routes.py:69-136, src/cluster_erp/modules/inventory/stock/routes.py:273
goods_movement.read, goods_movement.create movement reads and all postings src/cluster_erp/modules/inventory/stock/routes.py:163-437, src/cluster_erp/modules/inventory/stock/routes.py:601-627
stock_reservation.read, stock_reservation.create reservation routes src/cluster_erp/modules/inventory/stock/routes.py:460-520
usage_decision.read, usage_decision.create usage decision routes src/cluster_erp/modules/inventory/stock/routes.py:540-586
batch.*, serial_number.* batch and serial routes src/cluster_erp/modules/inventory/batches/routes.py:58-217

Privacy, retention, and history

The module has no privacy_handlers.py. Warehouse, location, batch, and serial deletes set deleted_at (src/cluster_erp/modules/inventory/warehouses/services.py:137-143, src/cluster_erp/modules/inventory/batches/services.py:178-184). Movements have no delete route; a wrong movement is reversed by a counter movement (src/cluster_erp/modules/inventory/stock/routes.py:376-450, src/cluster_erp/modules/inventory/stock/services.py:1730). Changes are captured by change history.

Analytics facts

analytics_facts.py contributes one fact set, goods_movement, over inventory_goods_movements. Its dimensions are movement type, product, warehouse, party, reference type, reversal flag, and posting date. Its measures are movement_count and total_qty, read with goods_movement.read (src/cluster_erp/modules/inventory/analytics_facts.py:29-104). Stock levels are not contributed; the docstring gives the reason (src/cluster_erp/modules/inventory/analytics_facts.py:1-8). See analytics facts.

Settings and reference data

The module has no settings section. The project-to-project transfer switch is the server setting allow_project_to_project_stock_transfer, default False, read through Settings (src/cluster_erp/config/settings.py:184, src/cluster_erp/modules/inventory/stock/services.py:561); see settings reference.

Tests

Found by a grep for modules.inventory in tests/ (no hit) and for the path /inventory/ in web-client/_e2e_*.mjs.

Kind File Covers
e2e web-client/_e2e_inventory_app.mjs the inventory app
e2e web-client/_e2e_procurement_gr.mjs goods receipt from purchase orders
e2e web-client/_e2e_project_stock_phase2a.mjs, web-client/_e2e_project_pegging_phase2b.mjs project stock stripes and pegging
e2e web-client/_e2e_make_or_buy_stock.mjs cut-from-stock issues
e2e web-client/_e2e_mrp_hygiene_faz0.mjs and the other _e2e_mrp_* suites stock read by the MRP run
e2e web-client/_e2e_unique_soft_delete.mjs live-row uniqueness

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
stock level StockLevel The stock quantities of one product breakdown at one place, batch, owning project, and custodian. Stok seviyesi src/cluster_erp/modules/inventory/stock/models.py:25
goods movement GoodsMovement One posted change of stock with a type, a quantity, and a reference. Mal hareketi src/cluster_erp/modules/inventory/stock/models.py:125
stock category _VALID_STOCK_CATEGORIES One of the six quantity buckets of a stock level. Stok kategorisi src/cluster_erp/modules/inventory/stock/services.py:94-101
usage decision UsageDecision The decision on a received quantity that moves it to a target category or out of stock. Kullanım kararı src/cluster_erp/modules/inventory/stock/models.py:270
stock reservation StockReservation Unrestricted stock set aside for a reference. Stok rezervasyonu src/cluster_erp/modules/inventory/stock/models.py:347
project stripe owner_project_id The part of a stock level owned by one project; NULL is common stock. Proje şeridi src/cluster_erp/modules/inventory/stock/models.py:79
project stock loan ProjectStockLoan Stock lent from one stripe to another with a payback obligation. Proje stok ödüncü src/cluster_erp/modules/inventory/stock/models.py:415

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13