尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从用例图到双账本:超市进销存系统库存一致性设计
简介这份超市进销存管理系统源码与用例图资料面向学习零售业务系统开发的学生、课程设计者及初级开发者围绕商品进货、销售、库存、订单等流程提供可运行参考。压缩包共103个文件、约2.03MB以aspx页面、cs后台代码、jpg/gif界面素材为主辅以css样式、数据库文件与配置文档便于观察页面交互、业务分层和数据表结构。已有4380人学习下载在课程实践与入门项目中具备一定参考面。读者可获得采购入库、销售出库、库存预警统计、订单追踪等模块的页面组织与实现线索并能结合用例图梳理员工、顾客、供应商等参与者与系统功能的关系用于快速搭建同类系统框架、对照完善需求分析或作为超市信息化项目的学习样板。1. 一张用例图定生死超市进销存系统为什么先画人再写码超市进销存管理系统这个题目网上一搜全是现成模板但真正卡住人的往往不是代码是那张用例图——参与者少画一个后面表结构就得返工库存对不平的后果还得自己承担。它的本质是把「货怎么来、放哪、怎么走、剩多少」这四件事用单据串起来采购单进来形成批次销售单出去扣批次盘点单修差异中间每一步都留流水。适合谁来动手想给自己店里搭一套的小老板、要交课程作业但更想做出能跑的东西的同学、以及接手了一套老系统发现账实不符想推倒重做的开发者。用例图是第一张必须画对的图它决定了后面写到哪一步会出问题。2. 用例图先立骨架参与者到底几个人、用例拆到多细才算合格2.1 五个参与者别漏掉那个「不是人」的刚接触用例图的人习惯把「顾客」画成参与者然后给顾客连一条「购买商品」的线图看起来挺完整。但真到写接口时就会发现「购买商品」这个用例既包含扫码、又包含称重、还包含付款和打小票一个用例对应了四五个接口验收时根本说不清做完没有。我一般按「谁在系统里留下一条操作记录」来筛参与者。按这个标准一家中小型超市跑不出这五类参与者典型动作是否需要登录对应的核心用例收银员扫码、称重、收款、退货是销售结算、销售退货、挂单取单库管员收货、上架、报损、移库是采购收货、报损出库、库存查询采购员下采购单、维护供应商是采购下单、供应商维护、到货跟踪店长看报表、改售价、审批盘点差异是销售报表、价格调整、盘点审批定时任务低库存预警、临期提醒、日结否系统触发库存预警、临期提醒、日结汇总第五行是最容易漏的。低库存预警、临期商品提醒、每日销售汇总这三件事没人会主动点按钮但业务上确实要求系统「自己动」。如果不在用例图里把它画成参与者后面写定时任务就没有出处需求评审时也容易被当成可有可无的附加功能砍掉。我的做法是在图右侧单独立一个火柴人标注「系统调度」只连三条线保持图面干净。至于「供应商」要不要画取决于系统边界。如果采购员手工录单、供应商不登录系统那它是外部实体画在图的右边用实线连到「采购下单」即可不要给它连「维护商品」——那会让边界模糊最后有人问你「供应商能不能自己改售价」就不好回答了。参与者确定之后还有一步常被跳过给每个参与者写一句职责边界。比如「收银员只能看到销售价看不到进价」这句话写进用例描述的前置条件里后面做权限表时直接照抄比事后补要省事得多。见过太多项目把权限留到最后一天加结果发现进价字段在收银端接口里已经返回了改起来涉及三四个模块。2.2 用例粒度一条用例必须能在一次操作里验收用例图最典型的失败方式是画出一堆「管理商品」「管理库存」「管理订单」这种包含式的大词。这种图看着对称好看但对落地没有任何帮助——「管理商品」底下到底包含几个接口、要不要审核、要不要批量导入一个字都没说。我用的判断标准很土一条用例必须能被一次操作完整验收。也就是一个人坐下来、点几下、填一张表单事情就结束了。按这个标准「管理商品」要拆成新增商品、修改商品资料、调整售价、停用商品、批量导入商品。「管理库存」要拆成库存查询、库存调整报损/报溢、移库、盘点录入、盘点审批。拆到这个粒度一个中型超市的用例总数大概落在 18 到 24 条之间。少于 15 条说明拆得不够多于 30 条说明把接口当用例了。下面是去掉界面细节之后的一份用例清单可以直接照着往图上摆编号用例名主参与者触发条件验收标准一句话UC-01商品建档采购员新品到货前条码、名称、单位、售价齐全重复条码被拒UC-02售价调整店长促销或成本变动生成调价记录旧价可追溯UC-03采购下单采购员库存低于补货点生成采购单状态为待收货UC-04采购收货库管员供应商送货到场生成批次库存增加应付挂账UC-05采购退货库管员质量或数量不符按原批次退回应付冲减UC-06销售结算收银员顾客到收银台扣减库存生成流水打印小票UC-07称重商品结算收银员散称商品过秤按重量计价金额四舍五入到分UC-08销售退货收银员顾客凭小票退货回写原批次退款金额一致UC-09挂单取单收银员顾客临时离开挂单期间库存不扣减UC-10报损出库库管员商品破损或过期库存减少生成损失记录UC-11盘点录入库管员月末或抽盘支持盲盘录入不影响现有库存UC-12盘点审批店长差异超过阈值审批后一次性回写库存UC-13库存查询全部角色随时按条码/名称/分类查询含批次明细UC-14销售报表店长每日/每周按品类、时段汇总与流水一致UC-15库存预警系统调度每日定时低于安全库存的商品进提醒列表这张表最大的价值不是画图是它同时充当了后面的验收单。每做完一个模块回来对着「验收标准」那一列打钩比口头说「做完了」靠谱得多。2.3 包含、扩展、泛化三种关系只在三处出现用例之间的连线新手容易画成蜘蛛网每条线都想加个include。实际项目里这三种关系各有各的固定用武之地用错了反而增加理解成本。包含include只用于「必然发生且被多处复用」的行为。典型例子是「销售结算」包含「计算应收金额」——不管现金还是扫码这一步一定发生。注意方向箭头从基础用例指向被包含用例。反过来把「会员折扣」做成 include 就错了因为不打折的顾客根本不走这一步。扩展extend用于「特定条件下才发生」的分支。会员折扣、临期特价、整单抹零都属于这一类。扩展用例要写清楚扩展点比如「当顾客出示会员卡时在计算应收金额之后插入折扣计算」。很多人把扩展和包含搞混记一句话就够包含是必须走的路扩展是岔路口。泛化generalization一般只用在参与者或者支付方式上。现金、扫码、挂账三种结算方式抽象出一个「支付」父用例各自泛化它图面立刻干净。参与者也可以泛化比如把「收银员」和「自助收银机」泛化成一个「结算操作者」但这个只在你确实要支持自助设备时才画否则纯属想象。2.4 没有图也能核对把用例图翻成三张检查表图画完之后我一般会让对方做三件事自查比盯着图看有效得多。第一张表是参与者-用例矩阵。行是五个参与者列是全部用例交叉处打勾。如果某一列一个勾都没有说明这条用例没有主人删掉如果某一行勾超过 12 个说明这个角色的权限过重考虑拆成两个角色——收银员能改售价这种事在这个矩阵里一眼就能看出来。第二张表是边界表。把每个用例涉及的字段列出来标注「读」和「写」。进价字段如果出现在收银员那一行的「读」列里权限设计就出问题了。这张表做半小时能省掉后期改接口的半天。第三张表是异常分支表。每条用例至少写两个失败场景扫码枪扫不出来、库存不足、金额为零、顾客中途取消。用例图本身不画异常但需求文档里必须有否则开发到一半才发现「负库存怎么处理」没人拍板就得停下来开会。提示用例图画完先别急着美化布局把上面三张表填完再回头调整线条。布局好不好看是给别人看的三张表填不填得满是给自己省事的。3. 双账本的表结构为什么库存不能只存一个数字3.1 只存一个数量的三种死法最常见的建表方式是item表里加一个stock字段进一批加卖一件减。教学 Demo 跑到这里就结束了但真到店里用第一周就会出问题而且每次出的都是同一类问题。第一种死法不知道钱花在哪了。同一瓶酱油上个月进货价 8.5 元这个月涨到 9.2 元。只有一个stock数字时你没法回答「现在仓库里这 30 瓶平均成本是多少」。老板问毛利只能拿售价减最后一次进价算出来的数虚高月底盘完发现利润对不上就开始怀疑系统。第二种死法临期商品无法定位。保质期是超市的生命线。只有一个总数你知道有 30 瓶酱油但不知道其中有 5 瓶是下个月到期的。等到顾客投诉只能整批下架损失自己承担。第三种死法退货和盘点没法追溯。顾客拿小票来退一瓶酱油你要冲回哪一批的成本盘点缺少两瓶是卖漏扫了还是被偷了没有流水这些问题只能靠猜而猜出来的结论通常不能写进报表。所以我一般用「批次 流水 汇总」的三层结构行内有人叫双账本批次表管实物和成本流水表管每一次变动的因果汇总表只是给查询用的缓存中小超市数据量不大汇总表甚至可以省掉用视图替代。这三层的关系是这样入库时先写一条批次记录再写一条流水出库时先按规则选中批次、扣减qty_left再写一条流水。流水表只增不改任何时刻的库存都等于「该商品所有批次qty_left之和」也等于「所有流水change_qty之和」。这两个等式就是后面所有对账逻辑的基础。3.2 五张核心表的建表 SQL 与字段取舍下面是我在 MySQL 8.x 上常用的一套骨架字段做过删减只保留真正影响业务的部分。-- 商品主档一个 SKU 一行。散称商品走店内码条码允许为空 CREATE TABLE item ( item_id BIGINT PRIMARY KEY AUTO_INCREMENT, barcode VARCHAR(32) NULL COMMENT 69码或店内码散称可为空, item_name VARCHAR(128) NOT NULL, category_id INT NOT NULL, unit VARCHAR(8) NOT NULL COMMENT 个/瓶/kg, is_weight TINYINT NOT NULL DEFAULT 0 COMMENT 1称重商品, sale_price DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 1在售 0停用, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_barcode (barcode), KEY idx_name (item_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 批次表库存的唯一真相来源qty_left 决定还能卖多少 CREATE TABLE stock_batch ( batch_id BIGINT PRIMARY KEY AUTO_INCREMENT, item_id BIGINT NOT NULL, batch_no VARCHAR(32) NOT NULL COMMENT 入库时生成关联采购单号, qty_in DECIMAL(12,3) NOT NULL COMMENT 入库数量, qty_left DECIMAL(12,3) NOT NULL COMMENT 剩余数量出库只扣这个, unit_cost DECIMAL(10,4) NOT NULL COMMENT 本批进价算毛利用, expire_date DATE NULL COMMENT 无保质期留空, in_at DATETIME NOT NULL, KEY idx_item_expire (item_id, expire_date, in_at), CONSTRAINT ck_qty CHECK (qty_left 0) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 流水表只增不改biz_type biz_no 组成幂等键 CREATE TABLE stock_flow ( flow_id BIGINT PRIMARY KEY AUTO_INCREMENT, item_id BIGINT NOT NULL, batch_id BIGINT NOT NULL, biz_type VARCHAR(16) NOT NULL COMMENT PURCHASE/SALE/CHECK/RETURN/LOSS, biz_no VARCHAR(32) NOT NULL COMMENT 关联单号重试时靠它去重, change_qty DECIMAL(12,3) NOT NULL COMMENT 入库为正出库为负, qty_after DECIMAL(12,3) NOT NULL COMMENT 变动后该批次余量排查用, operator VARCHAR(32) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_biz (biz_type, biz_no, batch_id, item_id), KEY idx_item_time (item_id, created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 销售单与明细主表存金额明细存每行扣了哪些批次 CREATE TABLE sale_order ( order_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, total_amt DECIMAL(12,2) NOT NULL, pay_type VARCHAR(16) NOT NULL COMMENT CASH/SCAN/CREDIT, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常 2已退, cashier VARCHAR(32) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE sale_order_item ( line_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, item_id BIGINT NOT NULL, batch_id BIGINT NOT NULL COMMENT 一行可能对应多条批次拆行存, qty DECIMAL(12,3) NOT NULL, price DECIMAL(10,2) NOT NULL, amount DECIMAL(12,2) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个字段的取舍值得单独说。barcode上建了唯一索引但允许 NULL。MySQL 的唯一索引允许多个 NULL 值正好解决散称商品比如散装瓜子按斤卖没有标准条码的问题。散称商品走收银台电子秤生成的店内码前缀固定入库时不需要销售时临时生成用完即弃。数量字段统一用DECIMAL(12,3)不用FLOAT也不用INT。称重商品的最小单位可以到 0.001 千克浮点数累加会产生0.30000000000000004这种结果对账时对到人怀疑人生这条是血泪经验。stock_flow上的uk_biz (biz_type, biz_no, batch_id, item_id)是幂等键。收银时网络抖动导致重试、消息队列重复投递同一个单号第二次插入会直接冲突报错程序捕获后当成成功处理即可库存不会被扣两次。这个唯一索引比任何应用层的「先查再插」都可靠。3.3 扣减库存的原子写法行锁、幂等键与重试扣库存是整个系统里唯一必须严格串行的地方。两个人同时买走最后一瓶酱油谁先谁后决定了会不会超卖。我一般用「按效期排序 行锁 影响行数校验」三件套from decimal import Decimal class InsufficientStock(Exception): pass def deduct_fifo(conn, item_id, qty, biz_no, biz_typeSALE, operatorsys): 按近效期优先扣减批次返回 [(batch_id, 扣减量, 成本价), ...] 任何一批扣减失败立即抛异常由调用方统一回滚事务 remain Decimal(str(qty)) detail [] with conn.cursor() as cur: # 关键 1有保质期的排前面无保质期垫底同效期按入库先后 cur.execute( SELECT batch_id, qty_left, unit_cost FROM stock_batch WHERE item_id %s AND qty_left 0 ORDER BY expire_date IS NULL, expire_date, in_at FOR UPDATE , (item_id,)) rows cur.fetchall() for batch_id, qty_left, unit_cost in rows: if remain 0: break take min(remain, Decimal(qty_left)) # 关键 2WHERE 里再判一次余量防止别的事务插队 cur.execute( UPDATE stock_batch SET qty_left qty_left - %s WHERE batch_id %s AND qty_left %s, (take, batch_id, take)) if cur.rowcount ! 1: raise InsufficientStock(f批次 {batch_id} 扣减失败余量已变化) cur.execute( INSERT INTO stock_flow (item_id, batch_id, biz_type, biz_no, change_qty, qty_after, operator) VALUES (%s, %s, %s, %s, %s, %s, %s) , (item_id, batch_id, biz_type, biz_no, -take, Decimal(qty_left) - take, operator)) remain - take detail.append((batch_id, take, unit_cost)) if remain 0: raise InsufficientStock(f商品 {item_id} 缺货 {remain}) return detailORDER BY expire_date IS NULL, expire_date, in_at这一行是核心。expire_date IS NULL在 MySQL 里返回 0 或 1所以有保质期的商品排在前面无保质期的沉底有保质期的内部再按日期升序实现先进先出兼临期优先。如果业务上就是纯 FIFO、不管效期把日期那一段换成ORDER BY in_at即可。FOR UPDATE锁住了这些批次行同一商品的并发扣减会在这里排队。代价是并发度下降但超市收银场景下同一 SKU 的并发冲突本来就低实测没必要上更复杂的方案。如果担心锁等待把innodb_lock_wait_timeout从默认 50 秒调到 5 秒让冲突快速失败、由上层重试比一直等着体验更好。cur.rowcount ! 1的校验看起来冗余实际能挡住一类隐蔽的错误如果隔离级别被改成读已提交以外的配置、或者有人手工把qty_left改成了负数这行会立刻暴露问题而不是等到月底对账才发现。注意整个扣减过程不要夹带任何外部调用。打印小票、调支付接口、发短信通知都必须放到事务提交之后。见过一个系统在事务里同步调打印服务网络卡了 8 秒那 8 秒里所有买同一瓶酱油的收银台全部排队现场画面相当难看。4. 三条主链路落地采购入库、收银出库、盘点损益的代码骨架4.1 采购入库单据、批次、应付一次写完收货是库存唯一的大额正向入口也是最容易做成「只加数量」的地方。按批次走入库函数的职责就很清楚建批次、写流水、更新采购单状态三件事在同一个事务里完成。def purchase_receive(conn, po_no, lines, operator): lines: [{item_id:1, qty:10, unit_cost:9.2, expire_date:2026-06-30}] with conn.cursor() as cur: # 幂等同一采购单不能重复收货 cur.execute(SELECT status FROM purchase_order WHERE po_no %s FOR UPDATE, (po_no,)) row cur.fetchone() if row is None: raise ValueError(f采购单 {po_no} 不存在) if row[0] ! 0: raise ValueError(f采购单 {po_no} 已收货状态 {row[0]}) for idx, ln in enumerate(lines): batch_no f{po_no}-{idx:02d} # 单号加行号天然唯一 cur.execute( INSERT INTO stock_batch (item_id, batch_no, qty_in, qty_left, unit_cost, expire_date, in_at) VALUES (%s, %s, %s, %s, %s, %s, NOW()) , (ln[item_id], batch_no, ln[qty], ln[qty], ln[unit_cost], ln.get(expire_date))) batch_id cur.lastrowid cur.execute( INSERT INTO stock_flow (item_id, batch_id, biz_type, biz_no, change_qty, qty_after, operator) VALUES (%s, %s, PURCHASE, %s, %s, %s, %s) , (ln[item_id], batch_id, po_no, ln[qty], ln[qty], operator)) cur.execute(UPDATE purchase_order SET status 1 WHERE po_no %s, (po_no,))batch_no用「采购单号 两位行号」生成好处是能反查、能手写核对不用再维护一个自增序列。qty_in和qty_left在入库时相等后面所有出库只动qty_leftqty_in永远保留原始值盘点时用来算「这条批次到底还剩多少比例」。入库要同时挂应付账款但应付不属于库存模型的核心很多小系统直接省略。我的建议是至少留一张payable表字段只要「供应商、采购单号、金额、已付金额、状态」五个字段足够支撑后面按账期结算真到要扩展时不用改库存表。采购退货走的是反向流程但不能简单地把qty_left减回去。正确做法是找到原批次、从qty_left里扣、写一条RETURN类型的负流水并且校验扣减量不超过qty_in - qty_left已经卖掉的部分不能退给供应商。这个校验漏掉退货退多了就会出现负库存而对账的时候负库存最难解释。4.2 收银出库称重商品和条码商品必须分流收银台是三条链路里唯一有实时性要求的。条码商品和称重商品在数据结构上的差异比想象中更大维度条码商品称重商品标识厂商 69 码全局唯一店内码电子秤当场生成数量整数件扫码即 1小数千克来自电子秤价格来源商品主档售价主档单价 × 重量常见问题一码多品、条码磨损秤未归零、单位是克还是千克称重商品的店内码一般有固定前缀常见做法是用22开头加 5 位自编码收银程序看到前缀就切到称重分支。这里最常出问题的是单位电子秤有的输出千克有的输出克如果系统里按千克存、秤上按克传一瓶 500 克的商品会被扣掉 500 千克第二天库存直接归零谁也说不清。WEIGHT_PREFIX 22 def checkout(conn, items, pay_type, order_no, cashier): items: [{barcode:6901234567890,qty:1}, {barcode:2200012,qty:0.486}] qty 单位统一为件或千克 total Decimal(0) with conn.cursor() as cur: for ln in items: cur.execute( SELECT item_id, sale_price, is_weight FROM item WHERE barcode %s AND status 1, (ln[barcode],)) it cur.fetchone() if it is None: raise ValueError(f条码 {ln[barcode]} 不存在或已停用) item_id, price, is_weight it qty Decimal(str(ln[qty])) # 称重商品必须两位以上小数整数件商品不允许小数 if is_weight and qty 0: raise ValueError(称重数量必须大于 0) if not is_weight and qty ! qty.to_integral_value(): raise ValueError(f非称重商品数量非法{qty}) amount (price * qty).quantize(Decimal(0.01)) detail deduct_fifo(conn, item_id, qty, order_no, biz_typeSALE, operatorcashier) for batch_id, take, cost in detail: cur.execute( INSERT INTO sale_order_item (order_no, item_id, batch_id, qty, price, amount) VALUES (%s, %s, %s, %s, %s, %s) , (order_no, item_id, batch_id, take, price, (price * take).quantize(Decimal(0.01)))) total amount cur.execute( INSERT INTO sale_order (order_no, total_amt, pay_type, status, cashier) VALUES (%s, %s, %s, 1, %s) , (order_no, total, pay_type, cashier)) return total金额用quantize(Decimal(0.01))强制四舍五入到分是为了避免收银小票上出现19.900000000000002。sale_order_item按批次拆行存储一条销售明细可能对应多条批次记录这样退货时才能精确回到原批次、原成本。这里有个业务判断要提前拍板库存不足时是拒绝交易还是允许负库存先卖。我的默认做法是拒绝但给店长留一个「强制出库」的开关走单独的LOSS流水并记录操作人。允许负库存的口子一旦放开不加限制三个月后你会得到一张到处是负数的库存表。4.3 盘点损益盲盘、阈值和差异回写盘点是唯一会「覆盖」库存的操作做错一次前后账全乱。第一步是生成盘点任务冻结范围按分类冻结或者按货架冻结。冻结的意思是盘点单生成之后该范围内的商品不允许再做采购收货和报损出库销售是否冻结看业务一般小店不冻结靠时间差和复核弥补。第二步是盲盘。录入界面不显示系统账面数量只让录入实盘数量。这一点在实操中非常重要——明盘时人会下意识地往系统数字上凑盘出来的差异永远偏小。盲盘录完之后再并排显示账面数、实盘数、差异。第三步是审批回写。差异不能一条条改库存要在审批时一次性处理def apply_check(conn, check_no, approver, thresholdDecimal(0.02)): 把盘点差异回写成盘盈/盘亏流水差异率超过阈值需二次确认 with conn.cursor() as cur: cur.execute( SELECT l.item_id, l.book_qty, l.real_qty FROM stock_check_line l JOIN stock_check h ON h.check_no l.check_no WHERE l.check_no %s AND h.status 1 , (check_no,)) lines cur.fetchall() for item_id, book_qty, real_qty in lines: diff Decimal(real_qty) - Decimal(book_qty) if diff 0: continue rate abs(diff) / max(Decimal(book_qty), Decimal(0.001)) if rate threshold: # 交给人工二次确认不直接回写避免录错一位数就改掉整批库存 cur.execute(UPDATE stock_check_line SET need_review 1 WHERE check_no %s AND item_id %s, (check_no, item_id)) continue if diff 0: # 盘盈新建一个盘点批次成本按最近一次进价 cur.execute( INSERT INTO stock_batch (item_id, batch_no, qty_in, qty_left, unit_cost, in_at) SELECT %s, CONCAT(%s, -P), %s, %s, COALESCE((SELECT unit_cost FROM stock_batch WHERE item_id %s ORDER BY in_at DESC LIMIT 1), 0), NOW() , (item_id, check_no, diff, diff, item_id)) else: # 盘亏直接从现有批次里扣走 LOSS 流水 deduct_fifo(conn, item_id, -diff, check_no, biz_typeLOSS, operatorapprover) cur.execute(UPDATE stock_check SET status 2 WHERE check_no %s, (check_no,))阈值0.02是我常用的默认值也就是差异率超过 2% 就转人工复核。这个值的来源是经验正常损耗水分流失、称重误差、零星破损很少超过 2%超过这个比例的差异更可能是录错数或者串码直接回写会把错误固化进库存。盘盈走「新建批次」而不是「加到原批次」是因为盘盈的商品往往不知道是哪一批进来的硬塞进老批次会让成本失真。新建批次的成本取最近一次进价是一个可解释的近似。提示盘点单据一旦审批就不要再允许修改。需要修正时生成一张新的调整单把两次调整都留在流水里。事后改历史单据是最容易把账做乱的操作。本文还有配套的精品资源点击获取
RELATED

相关推荐

重复文件清理技术:原理、工具与最佳实践

重复文件清理技术:原理、工具与最佳实践

1. 重复文件清理痛点与解决方案 作为一名长期与数字文件打交道的从业者,我深刻理解重复文件带来的困扰。在日常工作中,我们经常会遇到这样的情况:同一份文档被多次保存到不同文件夹,下载的图片在不同设备间同步时产生副本&#xf…

📅 2026/10/27 11:04:21
ModelSim老工程救星:GCC 4.2.1 MinGW32 VC9编译包详解与避坑指南

ModelSim老工程救星:GCC 4.2.1 MinGW32 VC9编译包详解与避坑指南

简介:这份资源是面向32位Windows环境下硬件仿真开发的GCC编译器集成包,专为ModelSim与QuestaSim用户准备,解决仿真环境中缺少可用C/C编译与链接工具的问题,适合从事Verilog/VHDL验证、嵌入式软件协同调试的工程师与学习者。压缩包…

📅 2026/10/27 11:04:21
Delphi老项目数据库层拆解:MyDAC v5.10源码精读与二次开发实践

Delphi老项目数据库层拆解:MyDAC v5.10源码精读与二次开发实践

简介:MySQL Data Access Components(MyDAC)v5.10 源文件面向使用 Delphi 7 至 2007 及 CBuilder 的数据库开发者,用于在不依赖第三方库或中间件的前提下直接连接 MySQL 服务器,完成数据读取、写入、更新与删除等操作。…

📅 2026/10/27 11:04:21
MORE NEWS

更多资讯

📰

n8n实现电商订单全自动处理工作流设计

1. 电商订单全自动处理工作流概述 电商订单处理是每个电商企业日常运营中最核心也最繁琐的环节之一。传统的人工处理方式不仅效率低下,还容易出错。n8n作为一款开源的工作流自动化工具,可以帮助我们构建一个完整的电商订单全自动处理系统。 我最近为一个…

📰

SAM一键抠图:零样本图像分割实战指南

1. 项目概述:为什么“SAM抠图”正在改变图像处理的工作流 最近在好几个设计协作群和开发组里,都看到有人甩出一张边缘毛茸茸的猫耳朵、一根根分明的蒲公英绒毛、甚至玻璃杯上反光水渍的局部截图,配文就一句:“SAM一划,…

📰

SQL Server数据库系统加固规范:11条安全基线落地与验收

简介:《Sql Server数据库系统加固规范》是一份面向数据库管理员、安全运维与合规检查人员的安全基线文档,主要用于指导 SQL Server 实例在账号、认证、日志、通信及主机层面的加固落地。压缩包内含 1 个 doc 文件,大小约 1.73MB,以…

📰

JMeter二次开发实战:自定义组件与性能优化

1. 为什么需要JMeter二次开发 JMeter作为一款开源的性能测试工具,其标准功能已经能够满足大多数基础测试需求。但在企业级应用场景中,我们经常会遇到以下痛点: 测试报告需要与企业内部系统集成 需要支持特殊的协议或数据格式 测试流程需要…

📰

SQL Server数据库加固:账号口令基线与权限最小化检查表

简介:面向数据库管理员、安全运维与等保测评人员的 Sql Server 安全加固规范文档,针对账号权限过大、认证策略薄弱、日志缺失和传输链路暴露等常见风险,给出可对照执行的安全基线。压缩包内为 1 个 doc 文件,约 1.73MB&#xff0c…

📰

2025 AI安全动荡期:70%攻击源自供应链,如何加固技术栈?

简介:这份PDF资料聚焦Gartner对2025年AI驱动网络安全新挑战的预测,面向安全与风险管理领导者、应用安全工程师及关注AI治理的从业者,帮助其理解AI动荡期的攻击面变化与应对思路。内容围绕软件供应链与技术栈中毒、早期GenAI落地受挫、AI代理漂…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬