StateSet One Guides
COGS Quickstart
Comprehensive guide to implementing COGS calculation with Stateset API
Cost of Goods Sold (COGS) Calculation Guide
This guide provides a comprehensive approach to calculating Cost of Goods Sold (COGS) using the Stateset API. It covers purchase order integration, work order processing, inventory management, and various costing methods.
Table of Contents
- Setup
- Purchase Order Integration
- Work Order and BOM Processing
- Inventory Management
- COGS Calculation Methods
- Periodic COGS Calculation
- Reporting
1. Setup
First, install the Stateset Node.js SDK:
npm install stateset-node
Then, initialize the Stateset client:
import { stateset } from 'stateset-node';
const client = stateset('YOUR_API_KEY');
2. Purchase Order Integration
2.1 Create Purchase Order
async function createPurchaseOrder(poData) {
try {
const created = await client.purchaseorder.create({
purchase_order: {
id: poData.id,
number: poData.number,
supplier: poData.supplier,
order_date: poData.orderDate,
expected_delivery_date: poData.expectedDeliveryDate,
status: poData.status,
total_amount: poData.totalAmount,
currency: poData.currency,
line_items: poData.lineItems
}
});
return created;
} catch (error) {
console.error("Error creating purchase order:", error);
throw error;
}
}
2.2 Retrieve and Update Purchase Orders
async function getPurchaseOrder(poNumber) {
try {
return await client.purchaseorder.retrieve(poNumber);
} catch (error) {
console.error("Error retrieving purchase order:", error);
throw error;
}
}
async function updatePurchaseOrder(poNumber, updateData) {
try {
return await client.purchaseorder.update(poNumber, updateData);
} catch (error) {
console.error("Error updating purchase order:", error);
throw error;
}
}
2.3 Receive Purchase Order
async function receivePurchaseOrder(poNumber) {
const po = await getPurchaseOrder(poNumber);
for (const lineItem of po.line_items) {
await updateInventory(lineItem.part_number, lineItem.quantity, lineItem.unit_cost);
}
await updatePurchaseOrder(poNumber, { status: 'RECEIVED', received_date: new Date() });
}
3. Work Order and BOM Processing
3.1 Retrieve Work Orders and BOMs
async function getWorkOrdersForPeriod(startDate, endDate) {
return await client.workOrder.list({
created_at: { gte: startDate, lte: endDate },
status: 'COMPLETED'
});
}
async function getBOMForWorkOrder(bomNumber) {
return await client.billOfMaterials.retrieve(bomNumber);
}
3.2 Calculate COGS for Work Order
async function calculateCOGSForWorkOrder(workOrder) {
const bom = await getBOMForWorkOrder(workOrder.bill_of_materials_number);
let totalCost = 0;
for (const lineItem of bom.line_items) {
const componentCost = await getComponentCost(lineItem.part_number);
totalCost += componentCost * lineItem.quantity;
}
return {
workOrderNumber: workOrder.number,
totalCost: totalCost,
quantityProduced: workOrder.line_items.reduce((sum, item) => sum + item.total_quantity, 0)
};
}
async function getComponentCost(partNumber) {
try {
// Retrieve the latest inventory record for the part
const latestInventory = await client.inventory.list({
part_number: partNumber,
limit: 1,
sort: '-updated_at'
});
if (latestInventory.length === 0) {
throw new Error(`No inventory found for part number: ${partNumber}`);
}
// Return the unit cost from the latest inventory record
return latestInventory[0].unit_cost;
} catch (error) {
console.error(`Error getting component cost for part ${partNumber}:`, error);
throw error;
}
}
4. Inventory Management
4.1 Update Inventory
async function updateInventory(partNumber, quantity, unitCost) {
try {
// Create a new inventory record
const newInventory = await client.inventory.create({
part_number: partNumber,
quantity: quantity,
unit_cost: unitCost
});
console.log(`Inventory updated for part ${partNumber}:`, newInventory);
return newInventory;
} catch (error) {
console.error(`Error updating inventory for part ${partNumber}:`, error);
throw error;
}
}
4.2 Get Inventory Movements
async function getInventoryMovements(productId, startDate, endDate) {
return await client.inventory.listMovements({
product: productId,
date: { gte: startDate, lte: endDate }
});
}
5. COGS Calculation Methods
5.1 Average Cost Method
async function calculateAverageCost(productId, dateRange) {
const purchases = await client.purchaseorder.list({ product: productId, date: dateRange });
const inventory = await client.inventory.list({ product: productId, date: dateRange });
const manufacturingCosts = await client.manufacturingorder.list({ product: productId, date: dateRange });
const totalCost = purchases.reduce((sum, po) => sum + po.total_cost, 0) +
manufacturingCosts.reduce((sum, cost) => sum + cost.amount, 0);
const totalQuantity = inventory.reduce((sum, inv) => sum + inv.quantity, 0);
return totalCost / totalQuantity;
}
5.2 Weighted Average Cost Method
import Big from 'big.js';
function calculateWeightedAverageCost(previousInventory, purchases) {
let totalCost = Big(previousInventory.cost).times(previousInventory.quantity);
let totalQuantity = Big(previousInventory.quantity);
for (const purchase of purchases) {
totalCost = totalCost.plus(Big(purchase.cost).times(purchase.quantity));
totalQuantity = totalQuantity.plus(purchase.quantity);
}
return totalQuantity.eq(0) ? Big(0) : totalCost.div(totalQuantity);
}
async function calculateWeightedAverageCOGS(productId, startDate, endDate) {
const purchaseOrders = await getPurchaseOrders(productId, startDate, endDate);
const inventoryMovements = await getInventoryMovements(productId, startDate, endDate);
const manufacturingCosts = await getManufacturingCosts(productId, startDate, endDate);
let inventory = { quantity: Big(0), cost: Big(0) };
let cogs = Big(0);
let totalPurchases = [];
const allMovements = [
...purchaseOrders.map(po => ({ date: po.date, type: 'purchase', ...po })),
...inventoryMovements.map(mov => ({ date: mov.date, type: 'movement', ...mov })),
...manufacturingCosts.map(mc => ({ date: mc.date, type: 'manufacturing', ...mc }))
].sort((a, b) => new Date(a.date) - new Date(b.date));
for (const movement of allMovements) {
switch (movement.type) {
case 'purchase':
case 'manufacturing':
totalPurchases.push({
quantity: Big(movement.quantity),
cost: Big(movement.type === 'purchase' ? movement.unitCost : movement.totalCost / movement.quantity)
});
break;
case 'movement':
if (movement.quantityChange > 0) {
const newAverageCost = calculateWeightedAverageCost(inventory, totalPurchases);
inventory.quantity = inventory.quantity.plus(movement.quantityChange);
inventory.cost = newAverageCost;
totalPurchases = [];
} else {
const quantitySold = Big(Math.abs(movement.quantityChange));
cogs = cogs.plus(inventory.cost.times(quantitySold));
inventory.quantity = inventory.quantity.minus(quantitySold);
}
break;
}
}
return {
cogs: cogs.toNumber(),
endingInventory: {
quantity: inventory.quantity.toNumber(),
averageCost: inventory.cost.toNumber()
}
};
}
6. Periodic COGS Calculation
async function calculateMonthlyCOGS() {
const date = new Date();
const startDate = new Date(date.getFullYear(), date.getMonth(), 1);
const endDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
const workOrders = await getWorkOrdersForPeriod(startDate, endDate);
let totalCOGS = 0;
let totalQuantityProduced = 0;
for (const workOrder of workOrders) {
const workOrderCOGS = await calculateCOGSForWorkOrder(workOrder);
totalCOGS += workOrderCOGS.totalCost;
totalQuantityProduced += workOrderCOGS.quantityProduced;
}
const averageCOGS = totalCOGS / totalQuantityProduced;
const previousPeriod = `${new Date(date.getFullYear(), date.getMonth() - 1, 1).getFullYear()}-${String(date.getMonth()).padStart(2, '0')}`;
const previousCOGSData = await getPreviousCOGSData(previousPeriod);
const cogsTrend = previousCOGSData
? ((totalCOGS - previousCOGSData.totalCOGS) / previousCOGSData.totalCOGS) * 100
: 0;
await storeCOGSData({
period: `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`,
totalCOGS,
averageCOGS,
quantityProduced: totalQuantityProduced,
cogsTrend
});
}
7. Reporting
async function generateCOGSReport(startDate, endDate) {
const workOrders = await getWorkOrdersForPeriod(startDate, endDate);
let totalCOGS = 0;
let totalQuantityProduced = 0;
const workOrderDetails = [];
for (const workOrder of workOrders) {
const workOrderCOGS = await calculateCOGSForWorkOrder(workOrder);
totalCOGS += workOrderCOGS.totalCost;
totalQuantityProduced += workOrderCOGS.quantityProduced;
workOrderDetails.push({
workOrderNumber: workOrder.number,
part: workOrder.part,
quantityProduced: workOrderCOGS.quantityProduced,
totalCost: workOrderCOGS.totalCost,
averageCost: workOrderCOGS.totalCost / workOrderCOGS.quantityProduced
});
}
return {
period: `${startDate.toISOString().split('T')[0]} to ${endDate.toISOString().split('T')[0]}`,
totalCOGS,
averageCOGS: totalCOGS / totalQuantityProduced,
quantityProduced: totalQuantityProduced,
workOrders: workOrderDetails
};
}
For any questions or assistance, please contact our support team at support@stateset.io.