{"id":47984783,"url":"https://github.com/emisso-ai/emisso-inventory","last_synced_at":"2026-04-04T11:26:38.733Z","repository":{"id":348644347,"uuid":"1195707626","full_name":"emisso-ai/emisso-inventory","owner":"emisso-ai","description":"Pure TypeScript inventory engine — double-entry moves, SAP-compatible presets, FIFO/AVCO/standard valuation","archived":false,"fork":false,"pushed_at":"2026-04-02T04:56:16.000Z","size":150,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-02T17:46:38.336Z","etag":null,"topics":["avco","barcode","bom","erp","fifo","gs1","inventory","manufacturing","open-source","sap","typescript","warehouse"],"latest_commit_sha":null,"homepage":"https://github.com/emisso-ai/emisso-inventory#readme","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emisso-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["emisso-ai"]}},"created_at":"2026-03-30T01:33:20.000Z","updated_at":"2026-04-02T04:55:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/emisso-ai/emisso-inventory","commit_stats":null,"previous_names":["emisso-ai/emisso-inventory"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/emisso-ai/emisso-inventory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emisso-ai%2Femisso-inventory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emisso-ai%2Femisso-inventory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emisso-ai%2Femisso-inventory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emisso-ai%2Femisso-inventory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emisso-ai","download_url":"https://codeload.github.com/emisso-ai/emisso-inventory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emisso-ai%2Femisso-inventory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31397692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avco","barcode","bom","erp","fifo","gs1","inventory","manufacturing","open-source","sap","typescript","warehouse"],"created_at":"2026-04-04T11:26:38.593Z","updated_at":"2026-04-04T11:26:38.718Z","avatar_url":"https://github.com/emisso-ai.png","language":"TypeScript","funding_links":["https://github.com/sponsors/emisso-ai"],"categories":[],"sub_categories":[],"readme":"# @emisso/inventory\n\nPure TypeScript inventory engine with double-entry moves, SAP-compatible presets, and FIFO/AVCO/standard valuation.\n\n## When to Use This\n\n- You're building inventory management for a client and need stock tracking, valuation, and movement logic\n- You're replacing SAP inventory modules with a modern, lightweight alternative\n- You need barcode (GS1-128) encoding/decoding for warehouse operations\n- You're building a manufacturing system that needs BOM explosion, production orders, and WIP tracking\n- You want an SDK, not a monolith — pure functions, no framework, no database\n\n## Packages\n\n| Package | Description | Install |\n|---------|-------------|---------|\n| `@emisso/inventory` | Core inventory engine | `npm i @emisso/inventory` |\n\n## Quick Start\n\n```bash\nnpm install @emisso/inventory\n```\n\n### Goods Receipt (101)\n\n```typescript\nimport { fromPreset, transitionMove, applyMoveToQuants } from \"@emisso/inventory\";\n\n// Receive 100 units from supplier into warehouse\nconst move = fromPreset(\"101\", {\n  materialId: \"MAT-001\",\n  quantity: 100,\n  unitCost: 5000,\n  storageLocation: \"WH01/A1\",\n  reference: \"PO-2026-001\",\n});\n\n// Advance through state machine: draft → confirmed → assigned → done\nconst confirmed = transitionMove(move, \"confirmed\");\nconst assigned = transitionMove(confirmed, \"assigned\");\nconst done = transitionMove(assigned, \"done\");\n\n// Update stock on hand\nconst quants = applyMoveToQuants([], done);\n// → [{ locationId: \"WH01/A1\", materialId: \"MAT-001\", quantity: 100 }]\n```\n\n### Transfer Between Locations (311)\n\n```typescript\nimport { fromPreset } from \"@emisso/inventory\";\n\n// Transfer 20 units between storage locations\nconst transfer = fromPreset(\"311\", {\n  materialId: \"MAT-001\",\n  quantity: 20,\n  storageLocation: \"WH01/A1\",\n  toStorageLocation: \"WH01/B2\",\n});\n```\n\n### Sales Delivery (601)\n\n```typescript\nimport { fromPreset } from \"@emisso/inventory\";\n\n// Issue 5 units for sales delivery\nconst sale = fromPreset(\"601\", {\n  materialId: \"MAT-001\",\n  quantity: 5,\n  storageLocation: \"WH01/A1\",\n  reference: \"SO-2026-050\",\n});\n```\n\n## Architecture\n\nHybrid Odoo + SAP model: Odoo's double-entry location model for flexibility, SAP's movement type codes for familiarity.\n\n- **Double-entry location model** — every move is `(from, to)`. Receiving is `supplier → warehouse`, selling is `warehouse → customer`. Virtual locations (supplier, customer, production, scrap, inventory) represent external boundaries.\n- **SAP movement types as presets** — codes like 101 (goods receipt), 261 (production issue), 601 (sales delivery) resolve to the correct `from → to` locations automatically.\n- **Valuation layers** — immutable cost records. FIFO consumes oldest layers first, AVCO maintains running average, standard price uses fixed cost.\n- **Quants** — materialized stock on hand, derived by projecting all done moves.\n- **Move state machine** — `draft → confirmed → assigned → done`. Each transition is a pure function.\n- **Event-sourced** — moves are the source of truth, quants and valuations are projections.\n\n## Core Modules\n\n| Module | Description | Key Exports |\n|--------|-------------|-------------|\n| `locations` | Warehouse hierarchy + virtual locations | `createLocation`, `createWarehouseLocations`, `VIRTUAL_LOCATIONS` |\n| `moves` | Move creation, state machine, validation, reversal | `createMove`, `transitionMove`, `applyMove`, `validateMove`, `createReversalMove` |\n| `quants` | Stock on hand, projections, reservations | `applyMoveToQuants`, `projectQuants`, `reserveStock`, `autoReserve` |\n| `valuation` | FIFO, AVCO, standard price cost layers | `createValuationLayer`, `consumeFIFO`, `consumeAVCO`, `consumeStandard` |\n| `presets` | SAP movement type registry | `fromPreset`, `createDefaultRegistry`, `PresetRegistry` |\n| `routes` | Composable multi-step receipt/delivery | `defineRoute`, `applyRoute`, `oneStepReceipt`, `threeStepDelivery` |\n| `physical-inventory` | PI documents, cycle counting, ABC classification | `createPIDocument`, `enterCount`, `postDifferences`, `classifyABC` |\n| `barcode` | GS1-128 encode/decode + internal barcodes | `encodeGS1`, `decodeGS1`, `encodeInternal`, `decodeBarcode` |\n| `reports` | Stock, move history, and valuation reports | `generateStockReport`, `generateMoveHistory`, `generateValuationReport` |\n\n## Manufacturing Extension\n\n```typescript\nimport {\n  explodeBOM,\n  createProductionOrder,\n  releaseOrder,\n  confirmOperation,\n  calculateBackflush,\n  generateBackflushMoves,\n  calculateWIP,\n  calculateVariances,\n} from \"@emisso/inventory/manufacturing\";\n```\n\nThe manufacturing extension adds BOM management, production orders, and cost analysis:\n\n### BOM Explosion\n\n```typescript\nimport { explodeBOM, explodeBOMMultiLevel, calculateBOMCost } from \"@emisso/inventory/manufacturing\";\n\nconst bom = {\n  id: \"BOM-001\",\n  materialId: \"FINISHED-001\",\n  quantity: 1,\n  unit: \"EA\",\n  components: [\n    { materialId: \"COMP-A\", quantity: 2, unit: \"EA\" },\n    { materialId: \"COMP-B\", quantity: 5, unit: \"KG\" },\n  ],\n};\n\n// Single-level explosion\nconst requirements = explodeBOM(bom, 100);\n// → [{ materialId: \"COMP-A\", quantity: 200 }, { materialId: \"COMP-B\", quantity: 500 }]\n\n// Multi-level explosion (resolves sub-BOMs)\nconst allBOMs = [bom, subBOM1, subBOM2];\nconst flat = explodeBOMMultiLevel(bom, 100, allBOMs);\n\n// Cost rollup\nconst cost = calculateBOMCost(bom, { \"COMP-A\": 1000, \"COMP-B\": 500 });\n```\n\n### Production Orders\n\n```typescript\nimport { createProductionOrder, releaseOrder, confirmOperation } from \"@emisso/inventory/manufacturing\";\n\nconst order = createProductionOrder({\n  bom,\n  quantity: 100,\n  plannedStart: new Date(\"2026-04-01\"),\n  plannedEnd: new Date(\"2026-04-05\"),\n});\n\nconst released = releaseOrder(order);\n```\n\n### WIP and Variance Analysis\n\n```typescript\nimport { calculateWIP, calculateVariances } from \"@emisso/inventory/manufacturing\";\n\nconst wip = calculateWIP(order);\n// → { materialCost, laborCost, overheadCost, totalWIP }\n\nconst variances = calculateVariances(\n  { material: 50000, labor: 20000, overhead: 10000 }, // planned\n  { material: 52000, labor: 19000, overhead: 11000 }, // actual\n);\n// → { material, labor, overhead, volume, mix, total }\n```\n\n## SAP Movement Type Presets\n\n| Code | Name | Description |\n|------|------|-------------|\n| `101` | GR from Purchase Order | Goods receipt from supplier against PO |\n| `102` | Reversal of GR from PO | Reversal of 101 |\n| `103` | GR into Blocked Stock | Goods receipt into quality inspection |\n| `105` | Release from Blocked Stock | Release blocked stock to unrestricted |\n| `201` | GI to Cost Center | Goods issue for internal consumption |\n| `202` | Reversal of GI to Cost Center | Reversal of 201 |\n| `261` | GI for Production Order | Component issue to production (manufacturing) |\n| `262` | Reversal of GI for Production | Reversal of 261 (manufacturing) |\n| `301` | Plant-to-Plant Transfer | Transfer between plants/warehouses |\n| `302` | Reversal of Plant-to-Plant | Reversal of 301 |\n| `311` | Storage Location Transfer | Transfer within same plant |\n| `312` | Reversal of Storage Location Transfer | Reversal of 311 |\n| `551` | Scrapping | Write-off to scrap location |\n| `552` | Reversal of Scrapping | Reversal of 551 |\n| `601` | GI for Sales Delivery | Goods issue for customer delivery |\n| `602` | Reversal of GI for Sales | Reversal of 601 |\n| `701` | PI Increase | Physical inventory surplus adjustment |\n| `702` | PI Decrease | Physical inventory deficit adjustment |\n\n## Development\n\n```bash\npnpm install\npnpm build          # Build all packages\npnpm test           # Run all tests (watch mode)\npnpm test:run       # Run all tests (CI mode)\npnpm lint           # Typecheck all packages\n```\n\n## FAQ\n\n**What is the best TypeScript library for inventory management?**\n[@emisso/inventory](https://github.com/emisso-ai/emisso-inventory) is an MIT-licensed TypeScript engine that implements double-entry inventory moves, SAP-compatible movement types, FIFO/AVCO/standard valuation, BOM explosion, production orders, and WIP tracking. Pure functions, no database, no framework.\n\n**How do I track stock movements in TypeScript?**\nInstall `@emisso/inventory` and use `fromPreset(\"101\", params)` to create moves from SAP movement type codes. Advance moves through the state machine with `transitionMove()`, then project stock on hand with `applyMoveToQuants()`.\n\n**How do I calculate inventory valuation in TypeScript?**\nUse `createValuationLayer()` on receipt, then `consumeFIFO()`, `consumeAVCO()`, or `consumeStandard()` on issue. Generate a full report with `generateValuationReport()`.\n\n**Does this support manufacturing / BOM explosion?**\nYes. Import from `@emisso/inventory/manufacturing` for `explodeBOM()`, `createProductionOrder()`, `calculateWIP()`, and `calculateVariances()`. Supports single and multi-level BOM explosion, cost rollup, backflushing, and 5-category variance analysis.\n\n**Can I use this with SAP movement type codes?**\nYes. The preset registry maps SAP codes (101, 261, 601, etc.) to double-entry moves. Use `fromPreset(code, params)` or build a custom registry with `PresetRegistry`.\n\n## Alternatives\n\n| Library | Language | Double-Entry | Valuation | Manufacturing | Open Source |\n|---------|----------|:---:|:---:|:---:|:---:|\n| **@emisso/inventory** | TypeScript | Yes | FIFO/AVCO/Std | Yes | MIT |\n| SAP MM | ABAP | Yes | Yes | Yes | No |\n| Odoo Inventory | Python | Yes | FIFO/AVCO | Yes | LGPL |\n| ERPNext Stock | Python | Yes | FIFO/Moving Avg | Yes | GPL |\n| InvenTree | Python | No | No | Partial | MIT |\n\n## License\n\nMIT — [Emisso](https://emisso.ai)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femisso-ai%2Femisso-inventory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femisso-ai%2Femisso-inventory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femisso-ai%2Femisso-inventory/lists"}