Traceability
Learn about the traceability system in Carbon
Carbon's traceability system tracks the complete lineage of materials and products through the manufacturing process. The system is built around two core concepts:
- Tracked Entities - Physical items, batches, or lots that need to be tracked (nodes in the graph)
- Tracked Activities - Transformations or processes that consume inputs and produce outputs (nodes in the graph)
- Tracked Activity Inputs - Links activities to their input entities (edges in the graph)
- Tracked Activity Outputs - Links activities to their output entities (edges in the graph)
Together, these form a directed acyclic graph (DAG) that represents material flow from raw materials through transformations to finished products.
Core Tables
trackedEntity
Represents a physical item, batch, or lot that needs to be tracked through the system.
Schema:
CREATE TABLE "trackedEntity" (
"id" TEXT NOT NULL DEFAULT id(),
"quantity" NUMERIC NOT NULL,
"status" "trackedEntityStatus" NOT NULL DEFAULT 'Available',
"sourceDocument" TEXT NOT NULL,
"sourceDocumentId" TEXT NOT NULL,
"sourceDocumentReadableId" TEXT,
"attributes" JSONB NOT NULL DEFAULT '{}',
"companyId" TEXT NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
"createdBy" TEXT NOT NULL
)Key Fields:
id- Unique identifier (xid)quantity- Current quantity of this entitystatus- Enum: 'Available', 'Reserved', 'On Hold', 'Consumed'sourceDocument- Type of document that created this entity (e.g., "Receipt", "Manufacturing")sourceDocumentId- UUID of the specific revision that created this entitysourceDocumentReadableId- Human-readable ID of the source documentattributes- JSONB containing:- Serial numbers
- Batch numbers
- Manufacturing dates
- Expiration dates
- Receipt/supplier info
- Custom properties
Relationship to Item Ledger:
itemLedger.trackedEntityId→trackedEntity.id- Each inventory transaction can be linked to a specific tracked entity
- On deletion: SET NULL, On update: CASCADE
trackedActivity
Represents a transformation or process that converts input entities into output entities.
Schema:
CREATE TABLE "trackedActivity" (
"id" TEXT NOT NULL DEFAULT id(),
"type" TEXT NOT NULL,
"sourceDocument" TEXT,
"sourceDocumentId" TEXT,
"sourceDocumentReadableId" TEXT,
"attributes" JSONB NOT NULL DEFAULT '{}',
"companyId" TEXT NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
"createdBy" TEXT NOT NULL
)Key Fields:
id- Unique identifiertype- Describes the transformation (e.g., "Manufacturing", "Assembly", "Conversion")sourceDocument- Document type that triggered this activitysourceDocumentId- Specific document instanceattributes- Additional metadata about the transformation
Bridge Tables: trackedActivityInput & trackedActivityOutput
These tables link activities to entities and define the graph structure.
trackedActivityInput:
CREATE TABLE "trackedActivityInput" (
"trackedActivityId" TEXT NOT NULL,
"trackedEntityId" TEXT NOT NULL,
"quantity" NUMERIC NOT NULL,
"entityType" TEXT,
"companyId" TEXT NOT NULL,
"createdBy" TEXT NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
)trackedActivityOutput:
CREATE TABLE "trackedActivityOutput" (
"trackedActivityId" TEXT NOT NULL,
"trackedEntityId" TEXT NOT NULL,
"quantity" NUMERIC NOT NULL,
"companyId" TEXT NOT NULL,
"createdBy" TEXT NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
)Graph Construction
Graph Structure
The traceability system forms a directed acyclic graph (DAG) where:
Nodes: TrackedEntity records
- Represent physical items, batches, or lots
- Have a status ('Available', 'Reserved', 'On Hold', 'Consumed')
- Contain attributes specific to the entity
Edges: TrackedActivity records with Input/Output relationships
- Represent transformations or processes
- Link consumed entities (inputs) to produced entities (outputs)
- Create parent-child relationships in the lineage
Graph Traversal
SQL Functions
Two key functions enable bidirectional graph traversal:
Get Descendants (Forward Tracing):
get_direct_descendants_of_tracked_entity_strict(entity_id TEXT)- Follows: Entity → Activity Input → Activity → Activity Output → Descendant Entity
- Returns all entities produced from the given entity
- Used for forward tracing (where did this material go?)
Get Ancestors (Backward Tracing):
get_direct_ancestors_of_tracked_entity_strict(entity_id TEXT)- Follows: Entity → Activity Output → Activity → Activity Input → Ancestor Entity
- Returns all entities consumed to produce the given entity
- Used for backward tracing (where did this come from?)
Graph Visualization
The traceability graph is visualized in apps/erp/app/routes/x+/traceability+/graph.tsx:
- Constructs nodes and links from entities and activities
- Supports ancestor/descendant traversal
- Provides interactive visualization of material lineage
Common Workflows
Creating Tracked Entities
Tracked entities are created during various operations:
-
Receipt (
post-receipt/index.ts)- Creates trackedEntity for received materials
- Links to receipt document
- Stores supplier info, batch numbers, etc. in attributes
-
Manufacturing (
post-production/index.ts, MES operations)- Creates trackedActivity
- Links input materials via trackedActivityInput
- Creates output entities via trackedActivityOutput
- Updates entity statuses (inputs → 'Consumed', outputs → 'Available')
-
Shipment (
post-shipment/index.ts)- May create activities for shipped items
- Tracks what entities left the facility
-
Stock Transfer (
post-stock-transfer/index.ts)- Transfers entities between locations
- Maintains traceability across locations
Conversion Pattern
Step-by-step process for converting tracked entities (e.g., material → part):
- Create a
trackedActivityrecord describing the transformation - Create
trackedActivityInputrecords for consumed entities - Create
trackedActivityOutputrecords for produced items - Update item ledgers with deductions (inputs) and additions (outputs)
- Update entity statuses (consumed entities → 'Consumed')
Example from revision-system.md:
-- 1. Create the activity
INSERT INTO "trackedActivity" (id, type, sourceDocument, sourceDocumentId, ...)
VALUES ('activity_123', 'Manufacturing', 'ProductionOrder', 'po_456', ...);
-- 2. Link consumed entities
INSERT INTO "trackedActivityInput" (trackedActivityId, trackedEntityId, quantity, ...)
VALUES ('activity_123', 'material_entity_1', 10, ...);
-- 3. Link produced entities
INSERT INTO "trackedActivityOutput" (trackedActivityId, trackedEntityId, quantity, ...)
VALUES ('activity_123', 'part_entity_1', 5, ...);
-- 4. Update ledgers and statuses
UPDATE "trackedEntity" SET status = 'Consumed' WHERE id = 'material_entity_1';Key Service Functions
ERP Inventory Service
apps/erp/app/modules/inventory/inventory.service.ts:
getTrackedEntities()- Get entities for an itemgetTrackedEntitiesByMakeMethodId()- Get entities produced by a make methodgetTrackedEntitiesByOperationId()- Get entities produced by an operation
MES Operations Service
apps/mes/app/services/operations.service.ts:
getTrackedEntitiesByMakeMethodId()- Get available entities for productiongetTrackedEntity()- Get specific entity detailsgetTrackedInputs()- Get consumed inputs for an activitystartProductionEvent()- Creates activities and links during production
QR Code Integration
Tracked entities can have QR codes generated for labeling:
ProductLabelPDFusesgenerateQRCode(item.trackedEntityId, size)- Enables scanning for instant traceability lookup
- Reference:
llm/cache/pdf-generation-patterns.md:43
Common Queries
Get Tracked Entities for an Item (All Revisions)
SELECT te.*
FROM "trackedEntity" te
JOIN "item" i ON i.id = te."sourceDocumentId"
WHERE i."readableId" = 'ITEM-001'
AND te."sourceDocument" = 'Item';Get Tracked Entities for Specific Revision
SELECT *
FROM "trackedEntity"
WHERE "sourceDocumentId" = '<item-revision-uuid>'
AND "sourceDocument" = 'Item';