کاردکس کالا و روش FIFO در نرمافزار انبارداری: از تئوری تا کوئری
پیادهسازی بهای تمامشده کالا به روش FIFO در یک نرمافزار انبارداری: لایههای موجودی، مصرف لایهها هنگام فروش و ثبت سند حسابداری بهای کالای فروشرفته.
وقتی یک کالا را در زمانهای مختلف با قیمتهای متفاوت خریدهاید، سود واقعی فروش به این بستگی دارد که بهای تمامشده را چطور حساب کنید. در روش FIFO (اولین ورودی، اولین خروجی) فرض میشود قدیمیترین موجودی زودتر فروخته میشود.
لایههای موجودی
هر رسید خرید یک «لایه» با مقدار باقیمانده و بهای واحد میسازد. موجودی یک کالا جمع مقدار باقیمانده همه لایههای آن است. این مدل، کاردکس کالا را دقیق و قابل حسابرسی نگه میدارد.
CREATE TABLE stock_layer (
id BIGSERIAL PRIMARY KEY,
item_id BIGINT NOT NULL REFERENCES item(id),
warehouse_id BIGINT NOT NULL REFERENCES warehouse(id),
received_at TIMESTAMPTZ NOT NULL,
qty_in NUMERIC(18, 3) NOT NULL,
qty_left NUMERIC(18, 3) NOT NULL CHECK (qty_left >= 0),
unit_cost NUMERIC(20, 2) NOT NULL
);
CREATE INDEX idx_layer_fifo
ON stock_layer (item_id, warehouse_id, received_at)
WHERE qty_left > 0;مصرف لایهها هنگام فروش
هنگام فروش، از قدیمیترین لایه شروع کرده و مقدار را کم میکنیم تا مقدار فروش پوشش داده شود. جمع «مقدار × بهای واحد» لایههای مصرفشده، بهای کالای فروشرفته است. همه این کار باید داخل یک تراکنش و با قفل ردیفها انجام شود تا دو فروش همزمان یک لایه را دوبار مصرف نکنند.
async function consumeFifo(tx: Tx, itemId: bigint, whId: bigint, qty: Decimal) {
const layers = await tx.query(
`SELECT id, qty_left, unit_cost FROM stock_layer
WHERE item_id = $1 AND warehouse_id = $2 AND qty_left > 0
ORDER BY received_at, id
FOR UPDATE`, [itemId, whId]);
let remaining = qty;
let cost = new Decimal(0);
for (const l of layers.rows) {
if (remaining.isZero()) break;
const take = Decimal.min(remaining, l.qty_left);
await tx.query('UPDATE stock_layer SET qty_left = qty_left - $1 WHERE id = $2',
[take.toString(), l.id]);
cost = cost.plus(take.times(l.unit_cost));
remaining = remaining.minus(take);
}
if (!remaining.isZero()) throw new Error('موجودی کافی نیست');
return cost; // بهای کالای فروشرفته
}نکات کلیدی
- FOR UPDATE از مصرف همزمان یک لایه جلوگیری میکند.
- بهای تمامشده با Decimal حساب میشود، نه Float.
- سند حسابداری بهای کالای فروشرفته در همان تراکنش ثبت شود.
جمعبندی
FIFO وقتی درست پیاده شود، هم کاردکس کالا را قابل اعتماد میکند و هم سود واقعی هر فروش را نشان میدهد. کلید کار، لایههای صریح، تراکنش و قفل ردیف است.