Tambo360’s cost tracking system enables precise financial management by associating direct costs with specific production batches. This granular approach provides accurate per-batch profitability and helps identify cost optimization opportunities.
Overview
The cost tracking module provides:
Direct cost assignment to production batches
Predefined cost categories for consistency
Optional observations for each expense
Batch-level and aggregate cost analysis
Integration with dashboard financial metrics
Cost Categories
The system defines four primary cost categories:
// schema.prisma:38-43
enum ConceptoCosto {
insumos_basicos // Basic supplies
leche_cruda // Raw milk
cuajo_y_fermentos // Rennet and ferments
refrigeracion // Refrigeration/cooling
}
Insumos Básicos Basic Supplies - Essential production materials:
Packaging materials
Cleaning supplies
Labels and containers
General consumables
Leche Cruda Raw Milk - Primary input for dairy production:
Fresh milk purchases
Milk quality premiums
Transportation costs
Supplier payments
Cuajo y Fermentos Rennet & Ferments - Specialized ingredients:
Rennet enzymes
Bacterial cultures
Starter cultures
Specialty ingredients
Refrigeración Refrigeration - Cooling and preservation:
Electricity for cooling
Refrigeration maintenance
Cold storage costs
Temperature control
Data Model
// schema.prisma:99-107
model CostosDirecto {
idCostoDirecto String @id @default ( uuid ())
concepto ConceptoCosto
monto Decimal @db.Decimal ( 13 , 2 )
observaciones String ? @default ( "" )
fechaCreacion DateTime @default ( now ())
idLote String
lote LoteProduccion @relation ( fields : [ idLote ], references : [ idLote ] )
}
Key Fields
monto : Decimal with 2 decimal places for precise currency amounts
concepto : One of four predefined categories
observaciones : Optional notes about the expense
idLote : Links cost to specific production batch
All costs are stored as ARS (Argentine Pesos) by default. The system supports Decimal(13,2) which accommodates values up to 99,999,999,999.99.
Recording Costs
Identify Expense
During or after production, identify direct costs associated with the batch.
Create Cost Record
Submit cost information with the batch ID: // Create cost (costController.ts:7-27)
POST / api / costos
Authorization : Bearer < jwt - token >
{
"idLote" : "lote-uuid" ,
"concepto" : "leche_cruda" ,
"monto" : 45000.00 ,
"observaciones" : "Compra de 500L de leche cruda - Proveedor Tambo San José"
}
// Response
{
"success" : true ,
"statusCode" : 201 ,
"message" : "Costo registrado correctamente" ,
"data" : {
"idCostoDirecto" : "costo-uuid" ,
"concepto" : "leche_cruda" ,
"monto" : 45000.00 ,
"observaciones" : "Compra de 500L de leche cruda - Proveedor Tambo San José" ,
"fechaCreacion" : "2026-03-08T09:30:00.000Z" ,
"idLote" : "lote-uuid"
}
}
Validation
The system validates: // Validation checks (costController.ts:9-14)
- User is authenticated
- Schema validation passes
- Batch exists and belongs to user
- Amount is positive
- Concept is valid enum value
Cost Operations
Get Cost by ID
Retrieve a specific cost record:
// Get cost (costController.ts:29-51)
GET / api / costos / : id
Authorization : Bearer < jwt - token >
// Response
{
"success" : true ,
"data" : {
"idCostoDirecto" : "costo-uuid" ,
"concepto" : "refrigeracion" ,
"monto" : 8500.00 ,
"observaciones" : "Consumo eléctrico cámara de frío - Marzo" ,
"fechaCreacion" : "2026-03-08T10:00:00.000Z" ,
"idLote" : "lote-uuid"
}
}
Get Costs by Batch
Retrieve all costs for a specific production batch:
// Get batch costs (costController.ts:53-71)
GET / api / costos / lote / : loteId
Authorization : Bearer < jwt - token >
// Response
{
"success" : true ,
"data" : [
{
"idCostoDirecto" : "costo-uuid-1" ,
"concepto" : "leche_cruda" ,
"monto" : 45000.00 ,
"observaciones" : "Compra de leche"
},
{
"idCostoDirecto" : "costo-uuid-2" ,
"concepto" : "cuajo_y_fermentos" ,
"monto" : 3200.00 ,
"observaciones" : "Cultivos lácticos"
},
{
"idCostoDirecto" : "costo-uuid-3" ,
"concepto" : "refrigeracion" ,
"monto" : 8500.00 ,
"observaciones" : "Energía eléctrica"
}
]
}
This endpoint is useful for:
Calculating total batch costs
Cost breakdown analysis
Profitability calculations
Update Cost
Modify existing cost records:
// Update cost (costController.ts:73-102)
PUT / api / costos / : id
Authorization : Bearer < jwt - token >
{
"monto" : 46000.00 ,
"observaciones" : "Compra de 500L de leche cruda - Proveedor Tambo San José - Ajustado por calidad premium"
}
// Response
{
"success" : true ,
"message" : "Costo actualizado correctamente" ,
"data" : {
"idCostoDirecto" : "costo-uuid" ,
"concepto" : "leche_cruda" ,
"monto" : 46000.00 ,
"observaciones" : "Compra de 500L de leche cruda - Proveedor Tambo San José - Ajustado por calidad premium" ,
...
}
}
Only costs belonging to the authenticated user’s batches can be updated.
Delete Cost
// Delete cost (costController.ts:104-122)
DELETE / api / costos / : id
Authorization : Bearer < jwt - token >
// Response
{
"success" : true ,
"message" : "Costo eliminado correctamente" ,
"data" : null
}
Deleting a cost is permanent and affects batch profitability calculations. Consider updating instead.
Dashboard Integration
Costs are aggregated in the monthly dashboard:
// dashboardService.ts:44-52
const buildSummary = ( result : InfoMes ) => {
const summary = result . reduce < SummaryResult >(( acc , lote ) => {
const category = lote . producto . categoria ;
if ( ! acc [ category ]) {
acc [ category ] = { cantidad: 0 , costos: 0 , mermas: 0 };
}
acc [ category ]. costos += lote . costosDirectos . reduce (
( sum : number , costo : CostosDirecto ) => sum + Number ( costo . monto ),
0
);
return acc ;
}, {});
};
Dashboard Display
The dashboard shows total costs with month-over-month comparison:
// Dashboard.tsx:86-100
< StatCard
title = "Costos totales"
value = {data?.data.actual. costos }
unit = "$ "
trend = {
data?.data.variaciones.costos !== null
? {
value : data ?. data . variaciones . costos ,
isPositive : data ?. data . variaciones . costos >= 0 ,
}
: undefined
}
description = { 'vs ' + data?.data.mesPrevio}
isPending={isPending}
/>
Current Month Total
Previous Month
Percentage Change
Sum of all costs for batches produced in the current month.
Automatically calculated for the prior calendar month.
// dashboardService.ts:72-74
const variacion = ( actual : number , anterior : number ) : number | null => {
if ( anterior === 0 ) return null ;
return (( actual - anterior ) / anterior ) * 100 ;
};
Historical Cost Analysis
The system tracks costs over 6 months for trend analysis:
// dashboardService.ts:143-148
if ( metrica === "costos" ) {
valor = lote . costosDirectos . reduce (
( sum , c ) => sum + Number ( c . monto ),
0
);
}
This data powers the historical comparison chart in the dashboard.
AI Analysis Integration
Costs are included in AI analysis for alerts:
// tamboEngineService.ts:83-87
costosDirectos : l . costosDirectos . map ( c => ({
concepto: c . concepto ,
monto: Number ( c . monto ),
moneda: "ARS" ,
}))
The AI engine can identify:
Unusual cost patterns
Cost efficiency opportunities
Budget anomalies
Supplier comparison insights
Use Cases by Category
Scenario : Daily milk procurement// Morning delivery from supplier
POST / api / costos
{
"idLote" : "batch-morning-uuid" ,
"concepto" : "leche_cruda" ,
"monto" : 45000.00 ,
"observaciones" : "500L @ $90/L - Proveedor Tambo San José - Calidad A"
}
Track supplier pricing and quality over time.
Scenario : Cheese production supplies// Rennet and cultures for 100kg batch
POST / api / costos
{
"idLote" : "cheese-batch-uuid" ,
"concepto" : "cuajo_y_fermentos" ,
"monto" : 3200.00 ,
"observaciones" : "Cuajo líquido 250ml + Cultivo mesófilo 50g - Lote #1247"
}
Essential for per-kg production cost calculations.
Scenario : Refrigeration expenses// Allocated cooling costs
POST / api / costos
{
"idLote" : "batch-uuid" ,
"concepto" : "refrigeracion" ,
"monto" : 8500.00 ,
"observaciones" : "Consumo eléctrico cámara de frío - 15 días @ 4°C"
}
Allocate energy costs based on storage time and batch size.
Scenario : Basic production materials// Packaging for batch
POST / api / costos
{
"idLote" : "batch-uuid" ,
"concepto" : "insumos_basicos" ,
"monto" : 5600.00 ,
"observaciones" : "Envases plásticos 1kg x 150 unidades + etiquetas"
}
Track packaging costs for profitability analysis.
Validation & Security
User Authorization // costController.ts:16-17
const user = ( req as any ). user ;
if ( ! user ) throw new AppError ( "Usuario no autenticado" , 401 );
Ownership Verification Costs can only be created/modified for batches owned by the authenticated user
Schema Validation All inputs validated against Zod schema before processing
Decimal Precision Amounts stored as Decimal(13,2) for financial accuracy
Best Practices
Record Promptly Log costs as they occur to ensure accurate batch profitability
Detailed Notes Include supplier info, quantities, and unit prices in observations
Consistent Categorization Use the same category for similar expenses across batches
Regular Review Analyze cost trends monthly to identify optimization opportunities
Profitability Analysis
Calculate per-batch profitability:
// Pseudo-code for profitability calculation
// 1. Get batch details
const batch = await getBatch ( idLote );
// 2. Sum all costs for the batch
const costs = await getCostsByBatch ( idLote );
const totalCosts = costs . reduce (( sum , c ) => sum + c . monto , 0 );
// 3. Account for waste
const waste = batch . mermas . reduce (( sum , m ) => sum + m . cantidad , 0 );
const usableQuantity = batch . cantidad - waste ;
// 4. Calculate cost per unit
const costPerUnit = totalCosts / usableQuantity ;
// 5. Compare with selling price
const profit = sellingPrice - costPerUnit ;
const profitMargin = ( profit / sellingPrice ) * 100 ;
Common Issues
Error : “Costo no encontrado”Causes :
Incorrect cost ID
Cost belongs to another user
Cost was deleted
Solution : Verify the cost ID and ownership.
Error : Schema validation failureSolution : Use only the four valid concepts:
insumos_basicos
leche_cruda
cuajo_y_fermentos
refrigeracion
Error : Schema validation failureSolution : Amounts must be positive. For refunds or adjustments, delete the original cost and create a new one with the correct amount.
API Reference
Endpoint Method Purpose /api/costosPOST Create cost record /api/costos/:idGET Get specific cost /api/costos/lote/:loteIdGET Get all costs for a batch /api/costos/:idPUT Update cost record /api/costos/:idDELETE Delete cost record