在本篇文章中,将探讨如何通过一个交互式的图形界面来理解三种主要的库存估值方法:先进先出(FIFO)、后进先出(LIFO)和加权平均成本(WAC)。这些方法在库存管理中扮演着重要的角色,但理解它们的差异和应用场景可能需要一定的财务知识。本文的目标是简化这个过程,让读者通过直观的界面和简单的代码阅读,来理解这些概念。
为了更好地理解本文,需要具备一些基本的JavaScript知识、了解库存估值方法,并具备一定的实际应用能力。
本项目的主要目标是图形化地展示三种主要库存估值方法的特征。具体要求包括:
将库存价值的计算分为三个部分:首先设置一个仓库,然后与这个仓库进行交易,最后展示这些交易的结果。
在开始计算库存价值之前,需要一个地方来存储它。这就是Warehouse
对象的作用。Warehouse
对象包含三个TransactionTable
(每个估值方法一个),每个TransactionTable
将包含一个TransactionItems
列表和其他特定于估值方法的值。
function Warehouse(initialItems) {
// ... 其他属性
this.LIFOTransactionTable = new TransactionTable();
this.FIFOTransactionTable = new TransactionTable();
this.WACTransactionTable = new TransactionTable();
// ... 稍后讨论的方法
}
function TransactionTable() {
this.ItemsList = [];
this.Total = 0;
this.BeginningTotal = 0;
this.EndingTotal = 0;
this.COGSTotal = 0;
this.TotalSell = 0;
this.GrossProfit = 0;
// ... 比率
this.GrossMargin = 0;
this.InventoryTurnover = 0;
this.DaysOnHand = 0;
}
function TransactionItem(txType, quantity, price) {
this.Timestamp = ticker;
this.TxType = txType;
if (txType == TransactionType.IMPORT) {
this.Price = price;
}
this.Quantity = quantity;
ticker++;
}
现在已经设置了仓库,可以开始导入和导出物品了。目标是一次只向仓库添加一个TransactionItem
。然后,这个项目将在三个TransactionTables
中复制。让从导入TransactionItems
开始。
导入TransactionItem
时,需要回答的两个问题是:想要多少这种物品(数量),以什么价格,以及否真的在添加这个物品(TransactionType
)。可以很容易地将这些归为一个标题,如下截图所示。
在这里输入的任何价格/数量都将创建一个TransactionItem
,并将其添加到相应的TransactionTables
中。物品将按顺序添加,从t0
开始,每次添加新物品时递增。Timestamp
在并排查看LIFO、FIFO和WAC表格时很重要。加权平均成本表的Timestamp
将始终是t_avg
。
由于库存物品的导入总是在同一时间添加,无论库存估值方法如何,因此处理起来相对容易。差异出现在销售物品时。在这个项目中,这被归类为出口。当出口物品时,在FIFO的情况下,将移除最先添加的物品(从t0
开始)。在LIFO的情况下,将移除最后添加的物品。这导致三个交易表中的销售成本(COGS)不同。下面是一个FIFO计算COGS的代码片段示例。需要检查当前正在耗尽的TransactionItem
中还剩下多少数量(CurrentTx
)。FIFO计算中的CurrentTx
将始终是列表中的第一项(currentIndex = 0
),而在LIFO的情况下,第一个要销售的物品是列表中的最后一项(currentIndex = Warehouse.LIFOTransactionTable.ItemsList.length - 1;
)。
function CalculateCOGSFIFO_Perpetual(sellQuantity) {
var neededItems = sellQuantity;
var itemsCalculated = 0;
var currentIndex = 0;
var COGS_total = 0;
var alterList = [];
while (neededItems != itemsCalculated) {
var currentTx = new TransactionItem(TransactionType.IMPORT, Warehouse.FIFOTransactionTable.ItemsList[currentIndex].Quantity, Warehouse.FIFOTransactionTable.ItemsList[currentIndex].Price);
currentTx.Timestamp = Warehouse.FIFOTransactionTable.ItemsList[currentIndex].Timestamp;
if (currentTx.Quantity == 0) {
} else if (currentTx.Quantity > (neededItems - itemsCalculated)) {
var used = (neededItems - itemsCalculated);
COGS_total = COGS_total + used * currentTx.Price;
currentTx.Quantity = currentTx.Quantity - used;
itemsCalculated = itemsCalculated + used;
} else if (currentTx.Quantity == (neededItems - itemsCalculated)) {
COGS_total = COGS_total + currentTx.Quantity * currentTx.Price;
itemsCalculated += currentTx.Quantity;
currentTx.Quantity = 0;
} else if (currentTx.Quantity < (neededItems - itemsCalculated)) {
COGS_total = COGS_total + currentTx.Quantity * currentTx.Price;
itemsCalculated += currentTx.Quantity;
currentTx.Quantity = 0;
}
alterList.push(currentTx);
currentIndex++;
}
Warehouse.FIFOTransactionTable.COGSTotal += COGS_total;
AlterTransactionTable(CostMethod.FIFO, alterList);
}
为了得到WAC的COGS,计算相对简单,因为WAC表将始终只包含一个TransactionItem
。这在下面的代码片段中可以看到。
function CalculateCOGSWeightedAverage_Perpetual(sellQuantity) {
var WACItem = Warehouse.WACTransactionTable.ItemsList[0];
Warehouse.WACTransactionTable.COGSTotal += (WACItem.Price * sellQuantity);
WACItem.Quantity -= sellQuantity;
}
现在有了一个仓库,可以为利润/亏损添加和移除物品。现在将通过构建表格来展示这种交互,每当添加/移除物品时都会构建这些表格。希望这能很好地描述仓库的当前状态及其三种形式。
获得比较视图的一个好方法是并排显示结果,因此决定将三个交易表放在一起。这样,可以获得库存的不错横截面视图。相同的时间戳在一行上可视化,如下图像所示。
现在想看看当销售物品时会发生什么。由于FIFO/LIFO的性质,这需要有效地表示,以便理解两种方法之间的差异。这是通过改变完全耗尽的物品(数量=0)的颜色来完成的。打个比方,结果清楚地表明在仓库的不同端发生了事件。这在下面可以看到。
这种视图在代码中构建如下:首先,从视图中移除任何当前库存。然后对于仓库中的每个TransactionTables
,使用下面的代码片段添加物品。如果一个TransactionItem
已经被耗尽,那么它就会被赋予不同的颜色(选择了bootstrap的"danger"类)。之后,显示总值。
Warehouse.FIFOTransactionTable.ItemsList.forEach(function(addItem){
var time = "t" + tick++;
if (addItem.TxType == TransactionType.IMPORT) {
var desc1 = addItem.Quantity + " items @ R " + addItem.Price;
if (addItem.Quantity == 0)
var addRow = new String(`
`);
else
var addRow = new String(`
`);
addRow = addRow + tdText + time + endtd;
addRow = addRow + tdText + desc1 + endtd;
$("#FIFOTable").append(addRow);
addRow = "";
}
});