From f7c49f41a84d72b4c06f75365ee549548f0349e7 Mon Sep 17 00:00:00 2001 From: yueli Date: Thu, 17 Sep 2026 09:18:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(borrow):=20=E7=BB=9F=E4=B8=80=E5=BD=92?= =?UTF-8?q?=E8=BF=98/=E6=8A=A5=E5=BA=9F=E6=97=B6=E9=97=B4=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E4=B8=BA=E5=8C=97=E4=BA=AC=E6=97=B6=E9=97=B4=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=BF=AE=E6=AD=A3=E8=BF=87=E6=9C=9F=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 时间口径(与归还流水同时引入,故随本轮一并修) ---- process_return 与 scrap_sources.deduct 原用 datetime.now() 写 return_time, 而 datetime.now() 取的是**容器本地时间**(Docker 下为 UTC);同一行的 borrow_time 却由 beijing_time() 写入 —— 两个字段差 8 小时,台账时间线 自相矛盾,并会让新做的流转时间线出现「先借出、后归还,却显示归还更早」 的倒序假象。 统一改用 beijing_time(),并与新增的归还流水共用同一个时间戳,保证主表快照 与流水逐笔完全对齐。 (与 db_migrations/unify_approval_timezone.sql 处理的是同一类问题:aware/naive 与本地/北京时间的口径混用。) 注释修正 ---- borrow_service.submit_approval 的 docstring 写着「仅存储意向,不扣库存」, 但 Phase 1 起该函数已调用 reserve_for_items() 预占库存(扣 available_quantity)。 过期注释会误导后续开发者(尤其是做转交的人以为库存没动过),已改写为实际的 预占语义与三种释放路径(驳回 / 撤回 / 扫码执行)。 --- inventory-backend/app/services/borrow_service.py | 11 ++++++++++- inventory-backend/app/services/scrap_sources.py | 8 +++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/inventory-backend/app/services/borrow_service.py b/inventory-backend/app/services/borrow_service.py index 3dbc33e..1d94240 100644 --- a/inventory-backend/app/services/borrow_service.py +++ b/inventory-backend/app/services/borrow_service.py @@ -64,7 +64,16 @@ class BorrowApprovalService: def submit_approval(applicant_id, items, allowed_approvers, remark=None, approver_id=None, borrower_name=None, force_approval=False): """ - 提交借库申请(仅存储意向,不扣库存) + 提交借库申请。 + + ★ 库存语义:**提交即预占**(Phase 1 起)。 + 本函数会调用 reserve_for_items() 锁定具体批次并扣减 available_quantity, + 不动 stock_quantity —— 借出期间该批货不可再被他人领用。 + 预占的释放时机: + · 驳回 / 撤回 → release_reserved() 全额归还; + · 扫码执行 → restore_then_deduct() 先全量释放、再按实扫批次扣减。 + (原注释写「仅存储意向,不扣库存」,是 Phase 1 改造前的旧描述, + 与下方实现不符,已修正。) Args: applicant_id: 申请人ID diff --git a/inventory-backend/app/services/scrap_sources.py b/inventory-backend/app/services/scrap_sources.py index c9abcc1..76151df 100644 --- a/inventory-backend/app/services/scrap_sources.py +++ b/inventory-backend/app/services/scrap_sources.py @@ -29,7 +29,7 @@ """ import logging -from app.extensions import db +from app.extensions import db, beijing_time logger = logging.getLogger(__name__) @@ -368,7 +368,6 @@ class BorrowScrapAdapter(ScrapSourceAdapter): (base.spec_model if base else '') or '') def deduct(self, row_id, qty, req, operator_name): - from datetime import datetime from app.models.transaction import TransScrap, TransBorrow record = TransBorrow.query.with_for_update().get(row_id) @@ -390,7 +389,10 @@ class BorrowScrapAdapter(ScrapSourceAdapter): # 1) 标记借用记录:不再追讨归还 record.is_returned = True record.status = 'scrapped' - record.return_time = datetime.now() + # ★ 时间口径:与 borrow_time / 归还流水统一取北京时间。 + # 原用 datetime.now()(容器 UTC),会让报废时间在台账上比实际早 8 小时, + # 与同一行的 borrow_time 自相矛盾,也让流转时间线出现倒序假象。 + record.return_time = beijing_time() record.return_operator = operator_name # 2) 扣总库存(该物品确认损失);可用量已在借出时冻结,不重复扣