使用简单的 JavaScript 进行简单的财务计算(第 1 部分)- 库存估值方法
使用简单的 JavaScript 进行一系列财务计算的简单可视化的开端。
引言
本文及整个项目的目标是双重的:提供一个简单且交互式的图形界面,深入了解选定的财务计算,并通过阅读简单的代码来理解这些计算。这两个目标本身都很难实现,并且带有自己固有的复杂性,足以填充两个库。尽管如此,我将尝试直接应对这些问题,并为您——消费者——提供尽可能深刻的见解。
背景
对于本文,您只需要具备 Javascript、库存估值方法的基本知识,以及中等的街头智慧(可选)。
基本要求
本项目的主要目标是直观地展示三种主要库存估值方法(先进先出法 (FIFO)、后进先出法 (LIFO) 和加权平均成本法 (WAC))的特征。
因此,衍生出的要求是:
- 给定估值方法,正确计算库存价值
- 提供一个比较不同估值方法的平台
- 允许用户操作库存 - 不同的输入提供不同的结果
- 以最简单有效的方式呈现估值
既然我们的要求已经比较具体地设定好了,我们就可以开始。
库存计算
我将库存价值的计算分为三个部分:首先,我们将设置一个Warehouse
,然后我们将与这个Warehouse
进行交易,最后,显示这些交易的结果。
设置仓库
在我们开始计算库存价值之前,我们需要一个地方来存储它。这就是Warehouse
对象的作用。我已经这样设置了Warehouse
。本质上,Warehouse
对象包含三个TransactionTables
(每种估值方法一个),一个TransactionTable
将包含一个TransactionItems
列表以及特定于估值方法的其他值。
function Warehouse(initialItems) {
//... some other properties
this.LIFOTransactionTable = new TransactionTable();
this.FIFOTransactionTable = new TransactionTable();
this.WACTransactionTable = new TransactionTable();
//... some methods to be discussed later
}
function TransactionTable(){
this.ItemsList = [];
this.Total = 0;
this.BeginningTotal = 0;
this.EndingTotal = 0;
this.COGSTotal = 0;
this.TotalSell = 0;
this.GrossProfit = 0;
//ratios
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
中。让我们从导入TransactionItem
开始。
在导入TransactionItem
时,我们只需要回答两个问题:我们想要多少数量,价格是多少,以及我们是否真的要添加该商品(TransactionType
)。正如下面的截图所示,我们可以轻松地将这些信息归类到一个标题下。
此处输入的任何价格/数量都将创建一个TransactionItem
并将其添加到相应的TransactionTables
中。商品将按顺序添加,从 t0 开始,每次添加新商品时都会递增。当我们并排查看 LIFO、FIFO 和 WAC 表时,Timestamp
非常重要。加权平均成本表的 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;
}
以视觉友好的方式将所有内容整合在一起
现在我们有了一个Warehouse
,并且可以进出口商品以实现盈利/亏损。我们现在将通过在添加/移除商品时构建这些表格来展示这种交互。希望这能很好地描绘出Warehouse
在其三种形式下的当前状态。
获得比较视图的一个通用好方法是并排显示结果,因此决定将三个交易表放在一起。通过这种方式,可以获得良好的库存横截面视图。正如下面的图像所示,相同的 timestamps 在同一行上可见。
现在我们想看看销售商品时会发生什么。由于 FIFO/LIFO 的性质,这需要有效地表示出来,以便理解两种方法之间的差异。这是通过更改完全耗尽的商品(数量 = 0)的颜色来实现的。比喻地说,结果很清楚,表明一个事件正在仓库的不同末端发生。下面可以看到这一点。
此视图以以下方式在代码中构建:首先,当前库存将从视图中删除。然后,对于Warehouse
中的每个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(`<tr id="FIFOItemRow" class="danger">`);
else
var addRow = new String(`<tr id="FIFOItemRow">`);
addRow = addRow + tdText + time + endtd;
addRow = addRow + tdText + desc1 + endtd;
$("#FIFOTable").append(addRow);
addRow = "";
}
});
关于模拟的一点说明
“在我那个年代,我们不得不手动添加库存商品,”一位几个月前使用过这个应用程序的人说道。有一个漂亮的“模拟”部分,用户可以设置他是否想在通货膨胀/通货紧缩的环境中添加库存商品。只需按一下按钮,就会以恒定的价格变动率向库存中添加五件随机数量的商品。变动率也随机设置为 1% 到 10% 之间。下面可以看到这个部分。
结论
库存估值方法有时是一个难以完全理解的棘手概念,有时直观的表示可以帮助更容易地获得直觉。本项目旨在通过易于开发的技术提供这种表示,以便通过使用 Web 界面或阅读代码来衡量理解程度。我希望您在阅读本文的过程中有所收获,并感谢任何和所有反馈。
历史
2017 年 8 月 20 日:初始版本