Saltar al contenido principal

Production Formulas

Overview

Production formulas define reusable bill-of-materials (BOM) recipes for manufacturing or assembly processes. Each formula specifies:

  • Output product — what is being produced
  • Batch quantity — how many units the formula produces per batch
  • Input lines — the raw materials or components consumed
  • Production run type — the nature of the process (assembly, mixing, baking, etc.)

Formulas can be "expanded" to scale all quantities for a target output, making it easy to create production runs without manually recalculating input requirements.

Architecture

The module follows hexagonal architecture:

production-formulas/
├── production-formulas.module.ts # NestJS module with DI wiring
├── domain/
│ └── production-formulas-repository.domain.ts # Port interface + domain types
├── application/
│ └── production-formulas.service.ts # Business logic / use cases
├── infrastructure/
│ └── production-formulas.repository.ts # Kysely database adapter
└── interfaces/
├── production-formulas.controller.ts # HTTP endpoints
├── dtos/
│ ├── create-production-formula.dto.ts
│ └── update-production-formula.dto.ts
└── query/
├── paginate-production-formulas.query.ts
├── get-production-formula.query.ts
└── expand-formula.query.ts

Dependency flow: Controller → Service → Repository (via injection token)

The service depends on IProductionFormulasRepository (port interface), not the concrete Kysely implementation. The module binds the concrete class via PRODUCTION_FORMULAS_REPOSITORY token.

Domain Concepts

Production Formula

FieldTypeDescription
idUUIDAuto-generated primary key
businessIdUUIDMulti-tenant scope
namestringHuman-readable formula name
descriptionstring?Optional description
outputProductIdUUIDProduct being produced
outputQuantityPerBatchnumericUnits produced per batch
outputUomIdUUID?Unit of measure for output
productionRunTypeenumProcess type (see below)
isActivebooleanSoft-delete flag
createdBy / updatedByUUIDAudit trail

Production Formula Input

FieldTypeDescription
idUUIDAuto-generated primary key
productionFormulaIdUUIDParent formula
productIdUUIDInput material product
quantitynumericQuantity per batch
uomIdUUID?Unit of measure
sortOrderintegerDisplay ordering
detailJSON?Extensible metadata

Production Run Types

collection | processing | mixing | baking | packing | assembly | harvest | transfer

API Endpoints

All endpoints require Bearer authentication (Firebase ID token).

POST /production-formulas

Creates a new formula with optional input lines.

Body:

{
"businessId": "uuid",
"createdBy": "uuid",
"name": "Bicycle Assembly",
"description": "Assemble 1 bicycle from frame and wheels",
"outputProductId": "uuid",
"outputQuantityPerBatch": 1,
"productionRunType": "assembly",
"inputs": [
{ "productId": "uuid", "quantity": 1, "uomId": null, "sortOrder": 0 }
]
}

Response: 201 — Created formula with inputs.

GET /production-formulas

Lists active formulas for a business with pagination and search.

Query params: businessId (required), search, page (default 1), size (default 10)

Response: 200{ formulas: [...], total: number }

GET /production-formulas/:id

Returns a single formula with inputs. Validates business ownership.

Query params: businessId (required)

Response: 200 — Formula with inputs | 404 — Not found | 403 — Wrong business

PATCH /production-formulas/:id

Partially updates a formula. If inputs array is provided, it replaces all existing inputs.

Query params: businessId (required)

Body: All fields optional except updatedBy. Same shape as create.

Response: 200 — Updated formula with inputs.

GET /production-formulas/:id/expand

Scales formula quantities for a target output amount. Returns inputs and outputs ready for production run creation.

Query params: businessId (required), plannedOutputQuantity (required, > 0), locationId (required)

Response: 200

{
"inputs": [
{ "productId": "uuid", "locationId": "uuid", "quantity": 10, "uomId": null, "unitCost": 0, "sortOrder": 0 }
],
"outputs": [
{ "productId": "uuid", "quantity": 10, "uomId": null, "unitCost": 0, "sortOrder": 0 }
]
}

Scale formula: scaledQuantity = inputQuantity × (plannedOutputQuantity / outputQuantityPerBatch)

Integration with Production Runs

The ProductionFormulasModule is exported and consumed by ProductionRunsModule. When creating a production run with a productionFormulaId and plannedOutputQuantity, the runs service calls:

  1. getProductionFormulaById() — fetch and validate the formula
  2. expandFormulaToRunData() — scale inputs/outputs

This allows users to create runs either manually (with explicit inputs/outputs) or from a formula (auto-expanded).

Design Decisions

  1. Delete-and-replace for inputs on update — When updating formula inputs, all existing inputs are deleted and re-inserted. This simplifies diffing and maintains sortOrder consistency.
  2. Soft-delete via isActive — Formulas are never hard-deleted. The list endpoint filters isActive = true.
  3. No cascading to runs — Changing a formula does not affect existing production runs. Runs snapshot their inputs/outputs at creation time.