From 862f86897979310b2be2d366ca38bc7aa13553a7 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 28 Oct 2026 15:38:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/inventory.py | 2 ++ routes/warehouse.py | 12 +++++++----- services/inventory_calc.py | 34 ++++++++++++++++++++++++++++++---- static/js/app.js | 15 +++++++++++++-- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/routes/inventory.py b/routes/inventory.py index 4878390..c423043 100644 --- a/routes/inventory.py +++ b/routes/inventory.py @@ -13,6 +13,7 @@ from flask import Blueprint, request, jsonify, session, redirect, url_for, rende from services.db import query, query_iter, get_db from routes.auth import login_required, role_required, operation_required from services.field_perms import filter_rows as _filter_field_rows +from services.inventory_calc import active_warehouse_condition inventory_bp = Blueprint('inventory', __name__) @@ -411,6 +412,7 @@ def inventory_list(): {join_type} {warehouse_join} WHERE {where_detail_clause} + AND {active_warehouse_condition('d')} """, params)[0] # 获取采购跟单、采购计划、供应商信息 prod_fields = ", p.procurement_follower AS 采购跟单, p.procurement_planner AS 采购计划" diff --git a/routes/warehouse.py b/routes/warehouse.py index 32575cd..ab15e0b 100644 --- a/routes/warehouse.py +++ b/routes/warehouse.py @@ -80,18 +80,19 @@ def warehouse_save(): if not code or not name: return jsonify({"code": 1, "msg": "仓库编号和名称不能为空"}) - exists = query("SELECT id, warehouse_property FROM warehouses WHERE warehouse_code=%s", (code,)) + exists = query("SELECT id, warehouse_property, disabled FROM warehouses WHERE warehouse_code=%s", (code,)) property_changed = not exists or (exists[0].get('warehouse_property') or '').strip() != warehouse_property + disabled_changed = not exists or (exists[0].get('disabled') or '否').strip() != disabled if exists: query("UPDATE warehouses SET warehouse_name=%s, category=%s, remark=%s, warehouse_property=%s, disabled=%s WHERE warehouse_code=%s", (name, category, remark, warehouse_property, disabled, code)) else: query("INSERT INTO warehouses (warehouse_code, warehouse_name, category, remark, warehouse_property, disabled) VALUES (%s,%s,%s,%s,%s,%s)", (code, name, category, remark, warehouse_property, disabled)) - if property_changed: + if property_changed or disabled_changed: from services.cache import refresh_all_caches refresh_all_caches() - return jsonify({"code": 0, "msg": "保存成功", "cache_refreshed": property_changed}) + return jsonify({"code": 0, "msg": "保存成功", "cache_refreshed": property_changed or disabled_changed}) @warehouse_bp.route('/api/warehouse/delete', methods=['POST']) @@ -125,16 +126,17 @@ def warehouse_update_field(): if field not in allowed: return jsonify({"code": 400, "msg": "不允许的字段"}) property_changed = False + disabled_changed = field == 'disabled' if field == 'warehouse_property': old_rows = query("SELECT warehouse_property FROM warehouses WHERE warehouse_code=%s", (code,)) old_value = (old_rows[0].get('warehouse_property') or '').strip() if old_rows else '' property_changed = old_value != str(value or '').strip() try: query(f"UPDATE warehouses SET {field}=%s WHERE warehouse_code=%s", (value, code)) - if property_changed: + if property_changed or disabled_changed: from services.cache import refresh_all_caches refresh_all_caches() - return jsonify({"code": 0, "msg": "更新成功", "cache_refreshed": property_changed}) + return jsonify({"code": 0, "msg": "更新成功", "cache_refreshed": property_changed or disabled_changed}) except Exception as e: return jsonify({"code": 500, "msg": str(e)}) diff --git a/services/inventory_calc.py b/services/inventory_calc.py index 81d053c..33f2f98 100644 --- a/services/inventory_calc.py +++ b/services/inventory_calc.py @@ -36,6 +36,28 @@ FORCE_FINISHED_PRODUCTS_SQL = ', '.join( ) +def active_warehouse_condition(inventory_alias='inventory_detail'): + """SQL condition that excludes warehouses marked disabled in warehouse management. + + Inventory rows whose warehouse is not maintained in ``warehouses`` remain eligible; + only an explicit ``disabled='是'`` removes a warehouse from calculations. Numeric + warehouse codes are compared by value so 29/029 and 35/035 behave consistently. + """ + alias = inventory_alias + return f"""NOT EXISTS ( + SELECT 1 FROM warehouses disabled_wh + WHERE COALESCE(disabled_wh.disabled, '否') = '是' + AND ( + disabled_wh.warehouse_code = {alias}.warehouse_code + OR ( + disabled_wh.warehouse_code REGEXP '^[0-9]+$' + AND {alias}.warehouse_code REGEXP '^[0-9]+$' + AND CAST(disabled_wh.warehouse_code AS UNSIGNED) = CAST({alias}.warehouse_code AS UNSIGNED) + ) + ) + )""" + + def resolve_cost_price(product_code, inventory_price): """解析产品成本价:命中特殊规则的用单独指定的正确成本价,否则用库存表成本价。""" if product_code in SPECIAL_COST_PRICES: @@ -54,10 +76,11 @@ def _get_latest_inv_date(): def _compute_product_inventory(): """计算每个产品的最新库存(排除15号仓库)""" latest_str = _get_latest_inv_date() - rows = query(""" + rows = query(f""" SELECT product_code, SUM(available_qty) AS total_qty FROM inventory_detail WHERE data_date = %s AND warehouse_code != '15' + AND {active_warehouse_condition('inventory_detail')} AND NOT (product_code IN ('POZ-03033', 'POZ-DXPW-1668-PDD', 'AH-YS-0584-TMGJ', 'POZ-JHY-7022', 'SEDATE-YMT-1682-PDD') AND LPAD(warehouse_code, 2, '0') = '20') GROUP BY product_code @@ -76,6 +99,7 @@ def _compute_raw_inventory_15(): "SELECT product_code, SUM(available_qty) AS total_qty " "FROM inventory_detail " "WHERE data_date = %s AND warehouse_code = '15' " + f"AND {active_warehouse_condition('inventory_detail')} " "GROUP BY product_code", (latest_str,)) inv15_map = {} @@ -97,7 +121,8 @@ def compute_product_inventory_15(): def compute_product_inventory_by_property(property_keyword): """根据仓库属性标签计算每个产品的库存(如云仓、外仓)""" wh_codes = query( - "SELECT warehouse_code FROM warehouses WHERE FIND_IN_SET(%s, warehouse_property) > 0", + "SELECT warehouse_code FROM warehouses WHERE FIND_IN_SET(%s, warehouse_property) > 0 " + "AND COALESCE(disabled, '否') != '是'", (property_keyword,) ) # 仓库管理和库存明细的编号格式可能不同(例如管理表为035、明细为35)。 @@ -171,7 +196,7 @@ def compute_product_in_transit(): "SELECT product_code, SUM(production_in_transit) AS prod_in_transit, " "SUM(purchase_in_transit) AS purch_in_transit " "FROM inventory_detail " - "WHERE data_date = %s " + f"WHERE data_date = %s AND {active_warehouse_condition('inventory_detail')} " "GROUP BY product_code", (latest_str,)) transit_map = {} @@ -272,7 +297,7 @@ def compute_inventory_cost_summary(latest_date=None): _cost_map = {r['product_code']: resolve_cost_price(r['product_code'], float(r['pr']) if r['pr'] else 0) for r in _latest_cost} # 成品/包材分类汇总 - _inventory_rows = query(""" + _inventory_rows = query(f""" SELECT id.product_code, SUM(CASE WHEN (p.product_type IN ('成品', '清库存', '现货', '赠品') AND p.deleted_at IS NULL) @@ -302,6 +327,7 @@ def compute_inventory_cost_summary(latest_date=None): FROM inventory_detail id LEFT JOIN products p ON id.product_code = p.product_code WHERE id.data_date = %s + AND {active_warehouse_condition('id')} AND id.product_code NOT LIKE %s AND id.product_name NOT LIKE %s AND NOT (id.product_code IN ('POZ-03033', 'POZ-DXPW-1668-PDD', 'AH-YS-0584-TMGJ', 'POZ-JHY-7022', 'SEDATE-YMT-1682-PDD') diff --git a/static/js/app.js b/static/js/app.js index 60261f1..a7f0bca 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -20786,7 +20786,10 @@ window.viewBackupDetail = function(bid, detailItem) { }); var html = '
'; - html += '

📋 下单详情 - ' + escHtml(code) + '

'; + html += '
'; + html += '

📋 下单详情 - ' + escHtml(code) + '

'; + html += ''; + html += '
'; // ─── 板块1:成品信息 ─── html += '

📦 成品信息

'; @@ -21499,7 +21502,6 @@ function showOrderDetail(encodedStr, title) { {label:'货品名称', field:'名称', editable:false}, {label:'到货仓库', field:'仓库到货', editable:true, type:'supplier'}, {label:'实际下单数量', field:'数量', editable:true}, - {label:'下单备注', field:'下单备注', editable:true}, {label:'计划下单日期', field:'计划下单时间', editable:true, type:'date'}, {label:'跟单下单日期', field:'日期', editable:true, type:'date'}, {label:'是否更换稿件', field:'有无新稿件', editable:true, type:'select', options:['是','否']}, @@ -21658,6 +21660,15 @@ function showOrderDetail(encodedStr, title) { html += '
'; html += ''; + // 三类订单的下单备注取自外层列表同一行,整行展示并放在进度上方。 + // 使用与进度相同的文本域样式,保存时仍走各订单现有更新接口。 + if (!isFinishedDetail) { + var orderRemarkVal = row['下单备注']; + if (orderRemarkVal === null || orderRemarkVal === undefined) orderRemarkVal = ''; + var remarkEditable = window._hasPagePerm(detailPermissionPage, 'edit'); + html += '
下单备注
'; + } + // 成品提货的进度字段整行显示,数据来自国内订单中最早的未完结订单,只读。 if (isFinishedDetail) { var finishedProgress = row['产品进度'] || row['进度'] || '';