VBA实现后入先出(LIFO)库存统计

先入先出(FIFO)比较容易理解,买入早的优先卖出。与之对应的是后人先出(LIFO),就是优先卖出最近买入的,例如:第8行卖出2K,当天还没有买入记录,只能找前一天的买入记录(即第7行),此示例中恰巧二者出入平衡,如果第7行买入数据不足,那么继续向前找(假设还有可用库存)。这只是一个最简单的粗略介绍,如果读者需要更深入了解LIFO的相关知识,请自行网络搜索。

接下来用几个辅助列粗略解释一下统计逻辑

  • I列为数量累加值,统计从期初到当前行的数量汇总值,如果数值为0,说明到当前行位置出入平衡
  • J列为当日的累加值,如果数值为0,说明该天出入平衡,例如:20250410

步骤1:确定起始日期,查找满足如下条件的最后一行日期(或者行号),结果为20250314(第5行),第6行将作为起始行。

  • 累加值(I列)为0 【满足此条件的有5行,标记为浅红色】
  • 某行是当日最后一条记录 【满足两个条件的有第3,5行,此处应取第5行】

步骤2:在累加列中从上到下查找首次出现的最终累加值(5800)作为终止日期,此示例中为20250409(行号20)。其中的逻辑是由于20行和第28行累加值相同,说明第21~28行的出入平衡,读者可以自行验证。

步骤3:对于起始日期(不包含)到截止日期之间的,剔除当日小计为零的日期,在每日最后一行提取均价作为最新价(填充H列),当日小计作为数量(填充G列)

结果如下图中G列和H列所示。

在这里插入图片描述

示例代码如下。

Sub Demo()
    Dim rngData As Range, i As Long, sKey As String, v
    Dim iSum As Long, sStart As String, sEnd As String
    Dim objDic As Object: Set objDic = CreateObject("scripting.dictionary")
    Dim objDicDay As Object: Set objDicDay = CreateObject("scripting.dictionary")
    Set rngData = Range("A1").CurrentRegion.Offset(1)
    Dim arr: arr = rngData.Value
    Dim iTotal As Long: iTotal = Application.Sum(rngData.Columns(4))
    For i = LBound(arr) To UBound(arr) - 1
        sKey = arr(i, 1)
        iSum = iSum + arr(i, 4)
        objDicDay(sKey) = objDicDay(sKey) + arr(i, 4)
        objDic(sKey) = Array(i, iSum, objDicDay(sKey))
        If arr(i, 1) <> arr(i + 1, 1) Then
            If iSum = 0 Then sStart = arr(i, 1)
            If iSum = iTotal And sEnd = "" Then sEnd = arr(i, 1)
        End If
    Next i
    Range("G:H").ClearContents
    Range("G1:H1").Value = Array("数量", "最新价")
    For Each v In objDic.keys
        If v <= sStart Or v > sEnd Or objDic(v)(1) = 0 Or objDic(v)(2) = 0 Then
            objDic.Remove v
        Else
            i = objDic(v)(0)
            Cells(i + 1, "G") = objDic(v)(2)
            Cells(i + 1, "H") = arr(i, 5)
        End If
    Next
End Sub

【代码解析】
第4~5行代码创建字典对象。
第6行代码获取当前数据区域向下偏移一行的单元格区域,其目的在于

  • 去除标题行
  • 单元格区域末尾包含一个空行,后续代码中可以更简洁的定位每日最后数据行

第7行代码将数据读取到数组中。
第8行代码调用工作表函数获取数量列汇总值,也就是最终累加值。
第9~18行代码循环遍历数据表。
第10行代码获取第一列日期作为字典的键。
第11行代码获取数量累加值。
第12行代码使用objDicDay统计当日的汇总值。
第13行代码将数组保存在objDic中,数组包含三个元素

  • 第1个元素:当前数据所在行号(注意此处比数据在工作表中的实际行号少一,第6行代码区域偏移导致的)
  • 第2个元素:数量累加值
  • 第3个元素:当日数量小计

对于每个日期存在多行数据的情况,由于我们只关心最后数据行,所以此处无需使用Exists判断sKey是否存在于字典中。

第14行代码判断下一个数据行的日期是否与当前行相同,如果不同,说明当前行为当日最后一行数据。
第15行代码判断数量累加值是否为0,如果满足条件,那么该数据行的日期作为起始日期(sStart)。
第16行代码判断数量累加值是否与累加终值,并且结束日期为空,如果满足条件,那么该数据行的日期为结束日期(sEnd)。
第19行代码清空G和H列,用于保存结果。
第20行代码填充标题行。
第21~29行代码循环遍历字典对象objDic,将结果写入工作表。
第22行代码判断字典中数组是否满足如下任意条件

  • 行号小于等于起始行
  • 行号大于结束行
  • 数量累加为0
  • 当日数量小计为0

如果满足任意一个条件,第23行代码将从字典中删除该记录(并非必需)。
如果不满足任意条件,那么将结果写入工作表。
第25行代码获取数据行的行号。
第26行代码将当日数量小计写入G列。
第27行代码将均价写入H列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值