fy36
15 小时以前 f1a64ab92120ffc0f1b1cf2c4848528a0d6b5d18
一系列脚本更新
1个文件已修改
8个文件已添加
1 文件已重命名
3204 ■■■■ 已修改文件
lua_code/Lua/GK-API-01-SKU_Sync.lua 790 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-02-Storer_Sync.lua 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-03-SKU-Batch_Sync.lua 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-04-Receipt_Sync.lua 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-05-InboundSync.lua 525 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-06-Outbound_Sync.lua 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-07-Order_Cancel.lua 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-08-Outbound_Priority_Chg.lua 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-09-Count_Pan_Sync.lua 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-11-StockQuery.lua 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lua_code/Lua/GK-API-01-SKU_Sync.lua
@@ -1,7 +1,7 @@
--[[ 
    编码: GK-API-001
    名称: 盘点计划同步
    作者: HAN
    作者: Yuanfeng
    日期: 2025-1-29
    入口函数: SKU_Sync
@@ -19,326 +19,170 @@
       V2.3 移除error字段,合并到message中
       V2.4 新增SKU_GRID_PARM字段
       V2.5 2025-07-01 SKU_GRID料箱格参数优化为根据cell_type 自动同步
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
</soap:Header>
    <soapenv:Body>
        <v1:inCommodityReq xmlns:v1="http://www.gkht.com/Information/INV/Ebs/Schemas/InCommodity/V1.0">
            <v1:COMMODITY_Input>
                <v1:RESTHeader>
                    <v1:Responsibility/>
                    <v1:RespApplication/>
                    <v1:SecurityGroup/>
                    <v1:NLSLanguage>SIMPLIFIED CHINESE</v1:NLSLanguage>
                    <v1:Org_Id>0</v1:Org_Id>
                </v1:RESTHeader>
                <v1:InputParameters>
                    <v1:COMMODITY_TB>
                        <!--1 or more repetitions:-->
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM125262</v1:skuId>
                            <v1:storerId>TGKHMB</v1:storerId>
                            <v1:skuName>Exceed ABT微孔涂层髋臼杯62mm</v1:skuName>
                            <v1:skuDec>Exceed ABT微孔涂层髋臼杯62mm</v1:skuDec>
                            <v1:spec>Acetabular Shell PC 52 x 62mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>501927908583</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM141234</v1:skuId>
                            <v1:storerId>CGKHTY</v1:storerId>
                            <v1:skuName>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuName>
                            <v1:skuDec>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuDec>
                            <v1:spec>75mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>.5</v1:length>
                            <v1:width>.5</v1:width>
                            <v1:height>.5</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>1</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM141234</v1:skuId>
                            <v1:storerId>GGKHN1</v1:storerId>
                            <v1:skuName>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuName>
                            <v1:skuDec>-</v1:skuDec>
                            <v1:spec>75mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType>C</v1:abcType>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions/>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>088030400832</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM141234</v1:skuId>
                            <v1:storerId>TGKHMB</v1:storerId>
                            <v1:skuName>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuName>
                            <v1:skuDec>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuDec>
                            <v1:spec>75mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>088030400832</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM141234</v1:skuId>
                            <v1:storerId>TGKRTH</v1:storerId>
                            <v1:skuName>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuName>
                            <v1:skuDec>-</v1:skuDec>
                            <v1:spec>75mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>088030400832</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM141234</v1:skuId>
                            <v1:storerId>TNMGGK</v1:storerId>
                            <v1:skuName>Biomet 固定十字型柄胫骨平台假体 75mm</v1:skuName>
                            <v1:skuDec>-</v1:skuDec>
                            <v1:spec>75mm</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>088030400832</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM154720</v1:skuId>
                            <v1:storerId>CGKHTY</v1:storerId>
                            <v1:skuName>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuName>
                            <v1:skuDec>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuDec>
                            <v1:spec>左膝内侧 Size B</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>501927938886</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM154720</v1:skuId>
                            <v1:storerId>GGKHN1</v1:storerId>
                            <v1:skuName>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuName>
                            <v1:skuDec>-</v1:skuDec>
                            <v1:spec>左膝内侧 Size B</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType>C</v1:abcType>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions/>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>501927938886</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM154720</v1:skuId>
                            <v1:storerId>TGKHMB</v1:storerId>
                            <v1:skuName>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuName>
                            <v1:skuDec>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuDec>
                            <v1:spec>左膝内侧 Size B</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>501927938886</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                        <v1:COMMODITY_TB_ITEM>
                            <v1:skuId>BM154720</v1:skuId>
                            <v1:storerId>TNMGGK</v1:storerId>
                            <v1:skuName>Oxford第三代单髁膝关节骨水泥型解剖型标准胫骨底板 左膝内侧 Size B</v1:skuName>
                            <v1:skuDec>-</v1:skuDec>
                            <v1:spec>左膝内侧 Size B</v1:spec>
                            <v1:packageCode>件</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>件</v1:goodsUnit>
                            <v1:length>10</v1:length>
                            <v1:width>10</v1:width>
                            <v1:height>10</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>A</v1:cidtype>
                            <v1:productLine>BM-关节</v1:productLine>
                            <v1:storageConditions>是</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount>120</v1:maxCount>
                            <v1:bMaxCount/>
                            <v1:cMaxCount/>
                            <v1:dMaxCount/>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>501927938886</v1:upc>
                        </v1:COMMODITY_TB_ITEM>
                    </v1:COMMODITY_TB>
                </v1:InputParameters>
            </v1:COMMODITY_Input>
        </v1:inCommodityReq>
    </soapenv:Body>
</soapenv:Envelope>
--]] wms_base = require("wms_base")
       V2.6 本次更新主要针对SKU料箱格参数(SKU_GridBox_Parm)同步逻辑进行优化,
       解决了在不同cid_type变更场景下的料格处理问题,确保数据同步的准确性和完整性。
       ]] wms_base = require("wms_base")
xml = require("oi_base_xml")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
-- 创建或更新SKU_UPC条码记录(重构版)
-- @param strLuaDEID: Lua数据交换ID
-- @param storer: 货主编码
-- @param item_code: SKU编码
-- @param upc_code: UPC条码
-- @return: 返回两个值 (错误码, 错误信息/成功信息)
-- 创建或更新SKU_UPC条码记录(优化版)
-- @param strLuaDEID: Lua数据交换ID
-- @param storer: 货主编码
-- @param item_code: SKU编码
-- @param upc_code: UPC条码
-- @return: 返回两个值 (错误码, 错误信息/成功信息)
local function create_or_update_sku_upc(strLuaDEID, storer, item_code, upc_code)
    local nRet, strRetInfo
    -- 1. 检查UPC是否有效
    if not upc_code or upc_code == '' then
        -- lua.debugex(strLuaDEID, "UPC为空", "货主:"..storer..", SKU:"..item_code)
        return 0, "UPC为空,跳过处理" -- 修改为返回0表示跳过
    end
    -- 2. 检查该SKU的UPC记录是否已存在(三字段主键检查)
    local strCondition = string.format("S_STORER = '%s' AND S_ITEM_CODE = '%s'", storer, item_code)
    nRet, strRetInfo = mobox.existThisData(strLuaDEID, "SKU_UPC", strCondition)
    if nRet ~= 0 then
        local errMsg = "检查SKU_UPC记录失败: " .. tostring(strRetInfo)
        return 1, errMsg
    end
    -- 3. 处理检查结果
    if strRetInfo == 'yes' then
        -- 记录已存在且完全相同,直接跳过
        local updateAttr = string.format("S_UPC_CODE='%s'", upc_code);
        local nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "SKU_UPC", strCondition, updateAttr)
        if (nRet ~= 0) then
            return 1, "数据更新失败!" .. strRetInfo
        else
            lua.DebugEx(strLuaDEID, "数据更新成功....", strRetInfo)
            return 0, "UPC 已更新" .. strRetInfo
        end
        -- 只作为更新操作
        local sku_upc = m3.AllocObject(strLuaDEID, "SKU_UPC")
        sku_upc.storer = storer
        sku_upc.item_code = item_code
        sku_upc.upc_code = upc_code
        nRet, strRetInfo = m3.UpdateDataObj(strLuaDEID, sku_upc)
        if nRet ~= 0 then
            local errMsg = "更新SKU_UPC失败: " .. tostring(strRetInfo)
            return 1, errMsg
        end
        return 0, "SKU_UPC已存在且相同,跳过处理"
    end
    -- 5. 创建新记录
    local sku_upc = m3.AllocObject(strLuaDEID, "SKU_UPC")
    sku_upc.storer = storer
    sku_upc.item_code = item_code
    sku_upc.upc_code = upc_code
    nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, sku_upc)
    if nRet ~= 0 then
        local errMsg = "创建SKU_UPC失败: " .. tostring(strRetInfo)
        return 1, errMsg
    end
    return 0, "创建SKU_UPC成功"
end
-- 创建/更新SKU对应的货品料箱格参数
-- 不再依赖cell_type作为截止范围,而是检查所有maxCount~fMaxCount字段
-- @param strLuaDEID: Lua数据交换ID
-- @param skuModel: SKU模型对象
-- @param sku_input_data: 输入数据
-- @return: 返回错误码和错误信息
local function create_or_update_sku_gridbox(strLuaDEID, skuModel, sku_input_data)
    local strRetInfo
    -- 1. 获取默认容器类型编码
    local nConstRet, CONST_CTD_CODE = wms_base.Get_sConst2("WMS_Default_CNTR_Type")
    if nConstRet ~= 0 then
        return 1, "获取默认容器类型定义编码失败: " .. CONST_CTD_CODE
    end
    -- 2. 定义料箱格类型与字段映射关系
    local gridbox_types = {
        A = {
            field = "maxCount",
            value = sku_input_data.maxCount
        },
        B = {
            field = "bMaxCount",
            value = sku_input_data.bMaxCount
        },
        C = {
            field = "cMaxCount",
            value = sku_input_data.cMaxCount
        },
        D = {
            field = "dMaxCount",
            value = sku_input_data.dMaxCount
        },
        E = {
            field = "eMaxCount",
            value = sku_input_data.eMaxCount
        },
        F = {
            field = "fMaxCount",
            value = sku_input_data.fMaxCount
        }
    }
    -- 3. 处理所有料箱格类型
    for cell_type, type_info in pairs(gridbox_types) do
        local loading_limit = lua.Get_NumAttrValue(type_info.value) or 0
        -- 只有当限制值大于0时才处理
        if loading_limit > 0 then
            -- 精确查询该类型的料箱格记录
            local strCondition = string.format(
                "S_ITEM_CODE = '%s' AND S_STORER = '%s' AND S_CTD_CODE='%s' AND S_CELL_TYPE='%s'", skuModel.item_code,
                skuModel.storer, CONST_CTD_CODE, cell_type)
            local nRet, existing_records = m3.GetDataObjByCondition(strLuaDEID, "SKU_GridBox_Parm", strCondition)
            if nRet == 0 then
                -- 记录存在,更新现有记录
                local update_gridbox_obj = {{
                    id = existing_records.id,
                    attrs = {{
                        attr = "N_LOADING_LIMIT",
                        value = loading_limit
                    }}
                }}
                nRet, strRetInfo =
                    mobox.updateDataObj(strLuaDEID, "SKU_GridBox_Parm", lua.table2str(update_gridbox_obj))
                if nRet ~= 0 then
                    return 1, string.format("更新%s类型料箱格属性失败: %s", cell_type, strRetInfo)
                end
            elseif nRet == 1 then
                -- 记录不存在,创建新记录
                local gridbox = m3.AllocObject(strLuaDEID, "SKU_GridBox_Parm")
                gridbox.item_code = skuModel.item_code
                gridbox.storer = skuModel.storer
                gridbox.ctd_code = CONST_CTD_CODE
                gridbox.loading_limit = loading_limit
                gridbox.cell_type = cell_type
                nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, gridbox)
                if nRet ~= 0 then
                    return 1, string.format("创建%s类型料箱格失败: %s", cell_type, strRetInfo)
                end
            else
                -- 查询出错
                return 1, string.format("查询%s类型料箱格记录失败: %s", cell_type,
                    existing_records or "未知错误")
            end
        end
    end
    return 0, "料箱格参数处理完成"
end
-- 创建统一返回结果(修改后版本,移除error字段)
function Create_result(flag, code, msg)
    return {
@@ -350,13 +194,10 @@
-- 创建或更新GK_PROLINE数据(生产线数据)
local function create_or_update_proline(strLuaDEID, proline_no, proline_name)
    local nRet, strRetInfo
    -- 分配GK_PROLINE对象
    local proline = m3.AllocObject(strLuaDEID, "GK_PROLINE")
    proline.proline_no = proline_no
    proline.proline_name = proline_name
    -- 检查是否已存在
    local strCondition = string.format("S_PROLINE_NO = '%s'", proline_no)
    local nRet, id, strRetInfo = mobox.getDataObjAttrByKeyAttr(strLuaDEID, "GK_PROLINE", strCondition)
@@ -380,7 +221,6 @@
                value = proline_name
            }}
        }}
        nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "GK_PROLINE", lua.table2str(update_proline_obj))
        if nRet ~= 0 then
            return 1, "更新GK_PROLINE失败: " .. strRetInfo
@@ -389,204 +229,99 @@
    return 0, strRetInfo
end
-- 创建SKU_UPC
local function create_sku_upc(strLuaDEID, storer, item_code, upc_code)
    local nRet, strRetInfo
    if (upc_code == '' or upc_code == nil) then
        return 0
-- 重构后的build_sku_grid_json函数
-- 现在完全基于数据库中的实际料格记录生成JSON
-- @param strLuaDEID: Lua数据交换ID
-- @param item_code: SKU编码
-- @param storer: 货主编码
-- @return: 返回两个值 (JSON字符串, 错误信息)
local function build_sku_grid_json(strLuaDEID, item_code, storer)
    -- 1. 获取默认容器类型编码
    local nConstRet, CONST_CTD_CODE = wms_base.Get_sConst2("WMS_Default_CNTR_Type")
    if nConstRet ~= 0 then
        return "", "获取默认容器类型定义编码失败: " .. CONST_CTD_CODE
    end
    local sku_upc = m3.AllocObject(strLuaDEID, "SKU_UPC")
    sku_upc.storer = storer
    sku_upc.item_code = item_code
    sku_upc.upc_code = upc_code
    nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, sku_upc)
    -- 2. 查询该SKU所有现有的料箱格记录
    local strCondition = string.format("S_ITEM_CODE = '%s' AND S_STORER = '%s' AND S_CTD_CODE='%s'", item_code, storer,
        CONST_CTD_CODE)
    local nRet, grid_records = m3.QueryDataObject(strLuaDEID, "SKU_GridBox_Parm", strCondition)
    return nRet, strRetInfo
end
-- 新增sku_grid_parm JSON字符串,在SKU 创建的时候插入json数据
local function build_sku_grid_parm(ctd_code, sku_input_data, cell_type)
    -- 如果cell_type为空则跳过
    if not cell_type or cell_type == "" then
        return ""
    -- 3. 处理查询结果
    if nRet ~= 0 then
        return "", "查询SKU_GridBox_Parm记录失败: " .. (grid_records or "未知错误")
    end
    -- 定义基础json框架结构(放在数组中)
    -- 4. 构建基础JSON结构
    local grid_def = {{
        ctd_code = ctd_code,
        ctd_code = CONST_CTD_CODE,
        cell_def = {}
    }}
    -- 根据cell_type确定处理范围
    local types_to_process = {}
    if cell_type == "A" then
        types_to_process = {"A"}
    elseif cell_type == "B" then
        types_to_process = {"A", "B"}
    elseif cell_type == "C" then
        types_to_process = {"A", "B", "C"}
    elseif cell_type == "D" then
        types_to_process = {"A", "B", "C", "D"}
    elseif cell_type == "E" then
        types_to_process = {"A", "B", "C", "D", "E"}
    elseif cell_type == "F" then
        types_to_process = {"A", "B", "C", "D", "E", "F"}
    else
        -- 未知类型,默认只处理A类型
        types_to_process = {"A"}
    end
    -- 5. 处理查询到的记录
    if grid_records and #grid_records > 0 then
        for _, record in ipairs(grid_records) do
            -- 将属性数组转换为对象
            local record_obj = m3.KeyValueAttrsToObjAttr(record.attrs)
    -- 定义料箱类型与对应字段的映射
    local grid_mapping = {
        A = {loading_limit = sku_input_data.maxCount},
        B = {loading_limit = sku_input_data.bMaxCount},
        C = {loading_limit = sku_input_data.cMaxCount},
        D = {loading_limit = sku_input_data.dMaxCount},
        E = {loading_limit = sku_input_data.eMaxCount},
        F = {loading_limit = sku_input_data.fMaxCount}
    }
            -- 提取需要的字段
            local cell_type = record_obj.S_CELL_TYPE or ""
            local loading_limit = lua.Get_NumAttrValue(record_obj.N_LOADING_LIMIT) or 0
            local load_capacity = lua.Get_NumAttrValue(record_obj.F_LOAD_CAPACITY) or loading_limit -- 默认等于loading_limit
    -- 构建cell_def数组
    for _, type_key in ipairs(types_to_process) do
        local grid = grid_mapping[type_key]
        local limit = lua.Get_NumAttrValue(grid.loading_limit) or 0
        if limit > 0 then  -- 只有当限制值大于0时才添加
            table.insert(grid_def[1].cell_def, {
                cell_type = type_key,
                loading_limit = limit,
                load_capacity = limit
            })
        end
    end
    lua.DebugEx(strLuaDEID, "grid", grid_def)
    return lua.table2str(grid_def)
end
-- 创建/更新SKU对应的货品料箱格参数
-- skuModel 是数据库定义的数据模型sku,也就是Alloc所创建的SKU对象。
local function create_or_update_sku_gridbox(strLuaDEID, skuModel, sku_input_data)
    local strRetInfo
    -- 获取默认容器类型编码
    local nConstRet, CONST_CTD_CODE = wms_base.Get_sConst2("WMS_Default_CNTR_Type")
    if nConstRet ~= 0 then
        return 1, "获取默认容器类型定义编码失败: " .. CONST_CTD_CODE
    end
    -- 如果cell_type为空则跳过
    if not skuModel.cell_type or skuModel.cell_type == "" then
        return 0, "cell_type为空,跳过料箱格参数处理"
    end
    -- 根据cell_type确定处理范围
    local types_to_process = {}
    if skuModel.cell_type == "A" then
        types_to_process = {"A"}
    elseif skuModel.cell_type == "B" then
        types_to_process = {"A", "B"}
    elseif skuModel.cell_type == "C" then
        types_to_process = {"A", "B", "C"}
    elseif skuModel.cell_type == "D" then
        types_to_process = {"A", "B", "C", "D"}
    elseif skuModel.cell_type == "E" then
        types_to_process = {"A", "B", "C", "D", "E"}
    elseif skuModel.cell_type == "F" then
        types_to_process = {"A", "B", "C", "D", "E", "F"}
    else
        -- 未知类型,默认只处理A类型
        types_to_process = {"A"}
    end
    -- 定义料箱格类型与对应字段的映射
    local gridbox_types = {
        A = sku_input_data.maxCount,
        B = sku_input_data.bMaxCount,
        C = sku_input_data.cMaxCount,
        D = sku_input_data.dMaxCount,
        E = sku_input_data.eMaxCount,
        F = sku_input_data.fMaxCount
    }
    -- 处理每种料箱格类型
    for _, type_key in ipairs(types_to_process) do
        local loading_limit = lua.Get_NumAttrValue(gridbox_types[type_key]) or 0
        if loading_limit <= 0 then  -- 只有当限制值大于0时才处理
            goto continue
        end
        -- 精确查询该类型的料箱格记录
        local strCondition = string.format(
            "S_ITEM_CODE = '%s' AND S_STORER = '%s' AND S_CTD_CODE='%s' AND S_CELL_TYPE='%s'",
            skuModel.item_code, skuModel.storer, CONST_CTD_CODE, type_key)
        local nRet, existing_records = m3.GetDataObjByCondition(strLuaDEID, "SKU_GridBox_Parm", strCondition)
        if nRet == 0 then
            -- 更新现有记录
            local update_gridbox_obj = {{
                id = existing_records.id,
                attrs = {{
                    attr = "N_LOADING_LIMIT",
                    value = loading_limit
                }}
            }}
            nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "SKU_GridBox_Parm", lua.table2str(update_gridbox_obj))
            if nRet ~= 0 then
                return 1, string.format("更新%s类型料箱格属性失败: %s", type_key, strRetInfo)
            -- 只有当值有效时才添加到JSON
            if cell_type ~= "" and loading_limit > 0 then
                table.insert(grid_def[1].cell_def, {
                    cell_type = cell_type,
                    loading_limit = loading_limit,
                    load_capacity = load_capacity
                })
            end
        elseif nRet == 1 then
            -- 创建新记录
            local gridbox = m3.AllocObject(strLuaDEID, "SKU_GridBox_Parm")
            gridbox.item_code = skuModel.item_code
            gridbox.storer = skuModel.storer
            gridbox.ctd_code = CONST_CTD_CODE
            gridbox.loading_limit = loading_limit
            gridbox.cell_type = type_key
            nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, gridbox)
            if nRet ~= 0 then
                return 1, string.format("创建%s类型料箱格失败: %s", type_key, strRetInfo)
            end
        else
            -- 查询出错
            return 1, string.format("查询%s类型料箱格记录失败: %s", type_key, existing_records or "未知错误")
        end
        ::continue::
    end
    return 0, "料箱格参数处理完成"
    -- 6. 转换为JSON字符串
    local json_str = lua.table2str(grid_def)
    -- -- lua.debugex(strLuaDEID, "生成的grid_json", json_str)
    return json_str
end
-- 创建或更新SKU主数据
-- @param  sku_input_data: 实际上就是报文中的:sku_data.COMMODITY_TB_ITEM 下面的字段内容
local function create_sku(strLuaDEID, sku_input_data)
    local nRet, strRetInfo
    local err_msg = ''
    -- 首先校验cidtype是否为空
    if not sku_input_data.cidtype or sku_input_data.cidtype == '' then
        return 1, "skuId为:" .. sku_input_data.skuId .. "的适配料格cidtype不允许为空!"
    end
    -- 获取系统常量
    -- 获取默认容器类型编码
    local nConstRet, CONST_CTD_CODE = wms_base.Get_sConst2("WMS_Default_CNTR_Type")
    if nConstRet ~= 0 then
        return 1, "获取默认容器类型定义编码失败: " .. CONST_CTD_CODE
    end
    -- 新增SKU_grid_param
    local sku_grid_parm = build_sku_grid_parm(CONST_CTD_CODE, sku_input_data, sku_input_data.cidtype)
    lua.DebugEx(strLuaDEID, "sku_grid_parm", sku_grid_parm)
    if (sku_input_data.cidtype == nil or sku_input_data.cidtype == '') then
        return 1, "skuId为:" .. sku_input_data.skuId .. "的适配料格cidtype不允许为空!"
    end
    -- 首先检查SKU是否已存在
    local strCondition = string.format("S_ITEM_CODE = '%s' AND S_STORER = '%s'", sku_input_data.skuId,
        sku_input_data.storerId)
    local nRet, existing_sku = m3.GetDataObjByCondition(strLuaDEID, "SKU", strCondition)
    -- lua.DebugEx(strLuaDEID, "获取条件查询:", existing_sku)
    if nRet > 1 then
        return 1, "检查SKU是否存在时失败: " .. (existing_sku or "未知错误")
    local existSKUnRet, existingSKU = mobox.existThisData(strLuaDEID, "SKU", strCondition)
    lua.DebugEx(strLuaDEID, "检查SKU是否存在返回结果", existingSKU);
    if existSKUnRet ~= 0 then
        return 1, "检查SKU是否存在时失败: " .. (existingSKU or "未知错误")
    end
    -- 分配SKU对象并设置属性
    local sku = m3.AllocObject(strLuaDEID, "SKU")
    sku.item_code = sku_input_data.skuId
    sku.storer = sku_input_data.storerId
    sku.short_name = sku_input_data.skuName
    sku.item_name = sku_input_data.skuDec
    sku.item_name = sku_input_data.skuName
    sku.note = sku_input_data.skuDec
    sku.spec = sku_input_data.spec
    sku.unit = sku_input_data.goodsUnit
    sku.long = lua.Get_NumAttrValue(sku_input_data.length) or 0
@@ -600,24 +335,46 @@
    sku.cell_type = sku_input_data.cidtype
    sku.item_type = sku_input_data.skuType
    sku.loading_limit = lua.Get_NumAttrValue(sku_input_data.maxCount) or 0
    sku.sku_grid_parm = '[{"ctd_code":"CTD-001","cell_def":{}}]'; -- 先给个默认值
    -- 2025-07-21 新增主表字段
    sku.udf01 = sku_input_data.productLine or ""
    sku.udf03 = sku_input_data.isSnStorageMgr or ""
    sku.udf02 = sku_input_data.storageConditions or ""
    sku.udf04 = sku_input_data.packageCode or ""
    sku.udf05 = lua.Get_NumAttrValue(sku_input_data.packageQty) or 0
    sku.sku_grid_parm = sku_grid_parm;
    -- lua.DebugEx(strLuaDEID, "更新/创建数据结果:", sku)
    if nRet == 1 then
        -- SKU不存在,创建新记录
        nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, sku)
        if nRet ~= 0 then
            return 1, "创建SKU失败: " .. strRetInfo .. " skuId = " .. sku_input_data.skuId
    sku.udf02 = sku_input_data.registerNo or "" -- 注册证号
    sku.udf03 = sku_input_data.companyName or "" -- 生产企业
    sku.udf04 = sku_input_data.certCompanyName or "" -- 证件生产厂家
    sku.udf05 = sku_input_data.consignCompanyWTName or "" -- 委托生产厂家
    sku.udf06 = sku_input_data.sterilizationDate or "" -- 灭菌日期
    sku.udf06 = sku_input_data.ex1 or "" -- 扩展字段1
    sku.udf07 = sku_input_data.ex2 or "" -- 扩展字段2
    sku.udf08 = sku_input_data.ex3 or "" -- 扩展字段3
    -- 1. 先处理料箱格参数(确保数据库记录最新)
    local nRet, strRetInfo = create_or_update_sku_gridbox(strLuaDEID, sku, sku_input_data)
    if nRet ~= 0 then
        return 1, "处理SKU_GridBox_Parm失败: " .. strRetInfo
    end
    -- 2. 基于数据库实际记录生成JSON(不再依赖传入的cidtype)
    local sku_grid_parm, err = build_sku_grid_json(strLuaDEID, sku.item_code, sku.storer)
    if err then
        return 1, "生成SKU_GRID_PARM失败: " .. err
    end
    sku.sku_grid_parm = sku_grid_parm -- 直接更新SKU对象的属性
    -- SKU不存在,创建新记录
    if existingSKU == 'no' then
        lua.DebugEx(strLuaDEID, "SKU不存在,创建新记录");
        local createnRet, strRetInfo = m3.CreateDataObj(strLuaDEID, sku)
        if createnRet ~= 0 then
            return 1, "创建SKU失败: 信息:" .. strRetInfo
        end
    else
        -- SKU已存在,更新记录
        local update_sku_obj = {{
            id = existing_sku.id,
            id = sku.id,
            attrs = {{
                attr = "S_CTD_CODE",
                value = CONST_CTD_CODE
            }, {
                attr = "S_ITEM_NAME",
                value = sku.item_name
            }, {
@@ -632,6 +389,9 @@
            }, {
                attr = "S_SHORT_NAME",
                value = sku.short_name
            }, {
                attr = "S_NOTE",
                value = sku.note
            }, {
                attr = "S_ABCTYPE",
                value = sku.abc_type
@@ -675,12 +435,21 @@
                attr = "S_UDF05",
                value = sku.udf05
            }, {
                attr = "S_UDF06",
                value = sku.udf06
            }, {
                attr = "S_UDF07",
                value = sku.udf07
            }, {
                attr = "S_UDF08",
                value = sku.udf08
            }, {
                attr = "S_SKU_GRID_PARM",
                value = sku.sku_grid_parm
            }}
        }}
        nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "SKU", lua.table2str(update_sku_obj))
        -- lua.DebugEx(strLuaDEID, "更新返回值", nRet)
        lua.DebugEx(strLuaDEID, "更新SKU对象结果", nRet .. "  " .. strRetInfo);
        if nRet ~= 0 then
            return 1, "更新SKU属性失败: " .. strRetInfo
        end
@@ -697,29 +466,28 @@
    end
    -- 处理SKU_UPC条码
    local upc_codes = {sku_input_data.sptm, sku_input_data.barcode1, sku_input_data.barcode2, sku_input_data.barcode3,
                       sku_input_data.barcode_pk, sku_input_data.upc}
    for _, upc_code in ipairs(upc_codes) do
        if upc_code and upc_code ~= '' then
            nRet, strRetInfo = create_sku_upc(strLuaDEID, sku.storer, sku.item_code, upc_code)
            if nRet ~= 0 then
                return 1, "创建SKU_UPC失败: " .. strRetInfo .. " UPC: " .. upc_code
            end
    -- 处理SKU_UPC条码(修改后版本)
    -- 在create_sku函数中处理SKU_UPC条码的部分修改为:
    local upc_code = sku_input_data.upc
    if upc_code and upc_code ~= '' then
        nRet, strRetInfo = create_or_update_sku_upc(strLuaDEID, sku.storer, sku.item_code, upc_code)
        if nRet ~= 0 then
            -- lua.debugex(strLuaDEID, "处理SKU_UPC失败", strRetInfo)
            return 1, "处理SKU_UPC失败: " .. strRetInfo
        end
        -- lua.debugex(strLuaDEID, "处理SKU_UPC成功", upc_code)
    end
    -- 处理SKU_GridBox_Parm料箱格参数
    nRet, strRetInfo = create_or_update_sku_gridbox(strLuaDEID, sku, sku_input_data)
    local nRet, strRetInfo = create_or_update_sku_gridbox(strLuaDEID, sku, sku_input_data)
    if nRet ~= 0 then
        return 1, "处理SKU_GridBox_Parm失败: " .. strRetInfo
    end
    return 0
end
-- Main函数
function SKU_Sync(strLuaDEID)
    m3.PrintLuaDEInfo(strLuaDEID)
    -- 初始化最终结果
    local FinalRes = Create_result()
lua_code/Lua/GK-API-02-Storer_Sync.lua
New file
@@ -0,0 +1,247 @@
--[[
    编码: GK-API-01
    名称: 货主信息同步接口
    作者: PMN
    日期: 2025-5-20
    入口函数:Storer_Sync
    功能说明:
        1. 接收货主信息同步请求
        2. 判断货主是否存在
        3. 执行新增或修改
        4. 返回处理结果
    输入XML示例:
    <soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    </soap:Header>
    <soapenv:Body>
        <v1:InCompanyReq
            xmlns:v1="http://www.gkht.com/Information/INV/Ebs/Schemas/InCompany/V1.0">
            <v1:Company_Input>
                <v1:RESTHeader>
                    <v1:Responsibility/>
                    <v1:RespApplication/>
                    <v1:SecurityGroup/>
                    <v1:NLSLanguage>SIMPLIFIED CHINESE</v1:NLSLanguage>
                    <v1:Org_Id>0</v1:Org_Id>
                </v1:RESTHeader>
                <v1:InputParameters>
                    <v1:COMPANY_TB>
                        <!--1 or more repetitions:-->
                        <v1:COMPANY_TB_ITEM>
                            <v1:companyCode>CBJJT1</v1:companyCode>
                            <v1:companyName>北京京泰日晟科技有限公司</v1:companyName>
                            <v1:contactName></v1:contactName>
                            <v1:contactPhone></v1:contactPhone>
                            <v1:contactAddress></v1:contactAddress>
                            <v1:memo>个</v1:memo>
                        </v1:COMPANY_TB_ITEM>
                             <v1:COMPANY_TB_ITEM>
                            <v1:companyCode>CBJJXK</v1:companyCode>
                            <v1:companyName>北京吉鑫康商贸有限公司</v1:companyName>
                            <v1:contactName></v1:contactName>
                            <v1:contactPhone></v1:contactPhone>
                            <v1:contactAddress></v1:contactAddress>
                            <v1:memo>个</v1:memo>
                        </v1:COMPANY_TB_ITEM>
                    </v1:COMPANY_TB>
                </v1:InputParameters>
            </v1:Company_Input>
        </v1:InCompanyReq>
    </soapenv:Body>
</soapenv:Envelope>
--]]
wms_base = require("wms_base")
xml = require("oi_base_xml")
-- 检查货主是否存在
local function check_storer_exists(strLuaDEID, storer_item)
    local storerId = storer_item.storerId
    local strCondition = string.format("S_STORER = '%s' ", storerId)
    local nRet, id, strRetInfo = mobox.getDataObjAttrByKeyAttr(strLuaDEID, "STORER", strCondition)
    if nRet > 1 then
        return nRet, "检查货主是否存在时出错: " .. strRetInfo
    elseif nRet == 1 then
        return 1, "货主不存在"  -- 不存在
    else
        return 0, id  -- 存在
    end
end
-- 新增货主
local function create_storer(strLuaDEID, storer_data)
    local storer = m3.AllocObject(strLuaDEID, "STORER")
    local nRet1, CONST_WH = wms_base.Get_sConst2(strLuaDEID, "WMS_Default_Warehouse")
    local storerCode = storer_data.storerId
    -- 字段映射
    storer.storer = storerCode
    storer.storer_name = storerCode
    storer.company_code = storer_data.companyCode
    storer.company_name = storer_data.companyName
    storer.contact_name = storer_data.contactName or ""
    storer.contact_phone = storer_data.contactPhone or ""
    storer.contact_address = storer_data.contactAddress or ""
    storer.note = storer_data.memo or ""
    storer.default_wh_code = CONST_WH
    local nRet, result = m3.CreateDataObj(strLuaDEID, storer)
    return nRet, result
end
-- 更新货主信息
local function update_storer(strLuaDEID, storer_id, storer_data)
    local update_storer_obj = {
            {
                id = storer_id,
                attrs = {
                    { attr = "S_COMPANY_CODE", value = storer_data.companyCode },
                    { attr = "S_COMPANY_NAME", value = storer_data.companyName },
                    { attr = "S_STORER_NAME", value = storer_data.companyName },
                    { attr = "S_CONTACT_NAME", value = storer_data.contactName },
                    { attr = "S_CONTACT_PHONE", value = storer_data.contactPhone },
                    { attr = "S_CONTACT_ADDRESS", value = storer_data.contactAddress },
                    { attr = "S_NOTE", value = storer_data.memo },
                }
            }
        }
    local nRet, result = mobox.updateDataObj( strLuaDEID, "STORER", lua.table2str(update_storer_obj) )
    return nRet, result
end
-- 处理单个货主记录
local function process_storer_item(strLuaDEID, storer_item)
    -- 1. 检查货主是否存在
    local nRet, result = check_storer_exists(strLuaDEID, storer_item)
    if nRet > 1 then
        return nRet, result
    end
    -- 2. 存在则更新,不存在则新增
    if nRet == 0 then
        local storer_id = result
        -- 货主存在,更新信息
        nRet, result = update_storer(strLuaDEID, storer_id, storer_item)
        if nRet ~= 0 then
            return nRet, "更新货主信息失败: " .. tostring(result)
        end
        return 0, {action = "update", id = storer_id}
    else
        -- 货主不存在,新增
        nRet, result = create_storer(strLuaDEID, storer_item)
        if nRet ~= 0 then
            return nRet, "新增货主失败: " .. tostring(result)
        end
        return 0, {action = "create", id = result}
    end
end
function Storer_Sync(strLuaDEID)
    local nRet, strRetInfo, nErr
    local err_msg
    nErr = 0
    local isStop = 0
    local err = {}
    -- m3.PrintLuaDEInfo(strLuaDEID)
    -- 1. 获取接口输入数据
    local soap_xml
    nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
    if nRet ~= 0 then
        lua.Stop(strLuaDEID, "无法获取数据包: " .. soap_xml)
        isStop = 3
        return
    end
    -- 2. 解析XML
    local parsed_data
    nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
        lua.Stop(strLuaDEID, "接口输入的XML格式非法!")
        isStop = 3
        return
    end
    -- 3. 提取货主数据
    local company_data = parsed_data.Envelope.Body.InCompanyReq.Company_Input
    local input_params = company_data.InputParameters
    local company_tb = input_params.COMPANY_TB
    -- 处理单条或多条货主数据
    local storer_items = {}
    if company_tb.COMPANY_TB_ITEM[1] ~= nil then
        storer_items = company_tb.COMPANY_TB_ITEM
    else
        storer_items = {company_tb.COMPANY_TB_ITEM}
    end
    -- 4. 处理每条货主记录
    for i, item in ipairs(storer_items) do
        local storer_data = {
            storerId = item.storerId,
            companyCode = item.companyCode,
            companyName = item.companyName,
            contactName = item.contactName or "",
            contactPhone = item.contactPhone or "",
            contactAddress = item.contactAddress or "",
            memo = item.memo or ""
        }
        -- 必填字段校验
        if not storer_data.companyCode or storer_data.companyCode == "" then
            err_msg = string.format("第%d条记录: companyCode不能为空", i)
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步货主信息")
            nErr = nErr + 1
            err[nErr] = err_msg
            lua.Stop(strLuaDEID, err_msg)
            isStop = 5
        end
        if not storer_data.companyName or storer_data.companyName == "" then
            err_msg = string.format("第%d条记录: companyName不能为空", i)
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步货主信息")
            nErr = nErr + 1
            err[nErr] = err_msg
            lua.Stop(strLuaDEID, err_msg)
            isStop = 5
        end
        if not storer_data.storerId or storer_data.storerId == "" then
            err_msg = string.format("第%d条记录: storerId不能为空", i)
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步货主信息")
            nErr = nErr + 1
            err[nErr] = err_msg
            lua.Stop(strLuaDEID, err_msg)
            isStop = 5
        end
        -- 处理货主记录
        nRet, result = process_storer_item(strLuaDEID, storer_data)
        if nRet ~= 0 then
            err_msg = result
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步货主信息")
            nErr = nErr + 1
            err[nErr] = err_msg
            lua.Stop(strLuaDEID, err_msg)
            isStop = 5
        end
    end
    -- 5. 返回响应
    local response = {
        flag = nErr > 0 and "failure" or "success",
        code = nErr,
        message = nErr > 0 and table.concat(err, "; ") or "处理成功",
    }
    local xml_result = xml.json_to_xml(response, "response")
    mobox.returnValue(strLuaDEID, 0, xml_result, isStop)
end
lua_code/Lua/GK-API-03-SKU-Batch_Sync.lua
New file
@@ -0,0 +1,271 @@
--[[
编码: GK-API-03
名称: 商品批次同步接口
作者: yuanfeng
日期: 2025/5/13
入口函数:Batch_Sync
来源项目: 国科项目
功能说明:
    1. 创建、覆盖GK_BATCH实体
    2. 更新INV_Detail相关自定义字段
变更历史:
v0.0.2 - 格式验证不通过的返回报文
v0.0.3 - CG_Detail弃用,变更为库存表INV_Detail
v2.0 - 2025-07-16 优化代码结构,保持原有业务逻辑,统一返回格式
v2.1 - 2025-07-16 修复SQL注入和特殊字符处理问题
--]] local mobox = require("OILua_JavelinExt")
local m3 = require("oi_base_mobox")
local xml = require("oi_base_xml")
local wms_base = require("wms_base")
-- 实体标识
local CLSID_GKbatch = "GK_BATCH"
local CLSID_INVdetail = "INV_Detail"
-- 创建统一返回结果
function Create_result(flag, code, msg)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or ""
    }
end
-- SQL转义处理
local function sql_escape(value)
    if value == nil then
        return ""
    end
    value = tostring(value)
    -- 转义单引号、反斜杠、百分号、下划线等特殊字符
    value = string.gsub(value, "(['\\%%_])", "\\%1")
    return value
end
-- 构建批次数据对象(修改consign_company_name处理)
local function buildBatchData(dataSet)
    local consign_company_name = lua.Get_StrAttrValue(dataSet.consignCompanyWTName)
    -- 特殊处理反斜杠
    if consign_company_name == "\\" then
        consign_company_name = "\\\\" -- 将单个反斜杠转义为双反斜杠
    end
    return {
        storer_no = lua.Get_StrAttrValue(dataSet.storerId),
        item_code = lua.Get_StrAttrValue(dataSet.skuId),
        owner_no = lua.Get_StrAttrValue(dataSet.ownerId),
        wms_bn = lua.Get_StrAttrValue(dataSet.batchNo),
        batch_no = lua.Get_StrAttrValue(dataSet.produceCode),
        product_date = lua.Get_StrAttrValue(dataSet.productDate),
        expiry_date = lua.Get_StrAttrValue(dataSet.expiryDate),
        register_no = lua.Get_StrAttrValue(dataSet.registerNo),
        company_name = lua.Get_StrAttrValue(dataSet.companyName),
        cert_company_name = lua.Get_StrAttrValue(dataSet.certCompanyName),
        consign_company_name = consign_company_name, -- 使用处理后的值
        sterilization_date = lua.Get_StrAttrValue(dataSet.sterilizationData),
        note = lua.Get_StrAttrValue(dataSet.memo),
        ex1 = lua.Get_StrAttrValue(dataSet.ex1),
        ex2 = lua.Get_StrAttrValue(dataSet.ex2),
        ex3 = lua.Get_StrAttrValue(dataSet.ex3)
    }
end
-- 创建或更新批次数据
local function create_or_update_batch(strLuaDEID, dataSet)
    -- 构建查询条件
    local strCondition = string.format("S_STORER = '%s' AND S_ITEM_CODE = '%s' AND S_OWNER = '%s' AND S_WMS_BN = '%s'",
        sql_escape(lua.Get_StrAttrValue(dataSet.storerId)), sql_escape(lua.Get_StrAttrValue(dataSet.skuId)),
        sql_escape(lua.Get_StrAttrValue(dataSet.ownerId)), sql_escape(lua.Get_StrAttrValue(dataSet.batchNo)))
    -- 检查批次是否存在
    local nRet, id, strRetInfo = mobox.getDataObjAttrByKeyAttr(strLuaDEID, CLSID_GKbatch, strCondition)
    -- 构建批次数据(会调用修改后的buildBatchData)
    local batch_data = buildBatchData(dataSet)
    if nRet == 0 then
        -- 批次存在,更新记录
        local update_batch_obj = {{
            id = id,
            attrs = {{
                attr = "S_BATCH_NO",
                value = batch_data.batch_no
            }, {
                attr = "D_PRODUCT_DATE",
                value = batch_data.product_date
            }, {
                attr = "D_EXPIRY_DATE",
                value = batch_data.expiry_date
            }, {
                attr = "S_REGISTER_NO",
                value = batch_data.register_no
            }, {
                attr = "S_COMPANY_NAME",
                value = batch_data.company_name
            }, {
                attr = "S_CERT_COMPANY_NAME",
                value = batch_data.cert_company_name
            }, {
                attr = "S_CONSIGN_COMPANY_NAME",
                value = batch_data.consign_company_name -- 这里已经是处理过的值
            }, {
                attr = "D_STERILIZATION_DATE",
                value = batch_data.sterilization_date
            }, {
                attr = "S_NOTE",
                value = batch_data.note
            }, {
                attr = "S_EX1",
                value = batch_data.ex1
            }, {
                attr = "S_EX2",
                value = batch_data.ex2
            }, {
                attr = "S_EX3",
                value = batch_data.ex3
            }}
        }}
        nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, CLSID_GKbatch, lua.table2str(update_batch_obj))
        if nRet ~= 0 then
            return 1, "更新批次数据失败: " .. strRetInfo
        end
    elseif nRet == 1 then
        -- 批次不存在,创建新记录
        local batch = m3.AllocObject(strLuaDEID, CLSID_GKbatch)
        batch.storer_no = batch_data.storer_no
        batch.item_code = batch_data.item_code
        batch.owner_no = batch_data.owner_no
        batch.wms_bn = batch_data.wms_bn
        batch.batch_no = batch_data.batch_no
        batch.product_date = batch_data.product_date
        batch.expiry_date = batch_data.expiry_date
        batch.register_no = batch_data.register_no
        batch.company_name = batch_data.company_name
        batch.cert_company_name = batch_data.cert_company_name
        batch.consign_company_name = batch_data.consign_company_name
        batch.sterilization_date = batch_data.sterilization_date
        batch.note = batch_data.note
        batch.ex1 = batch_data.ex1
        batch.ex2 = batch_data.ex2
        batch.ex3 = batch_data.ex3
        nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, batch)
        if nRet ~= 0 then
            return 1, "创建批次数据失败: " .. strRetInfo
        end
    else
        return 1, "查询批次数据失败: " .. strRetInfo
    end
    return 0
end
-- 更新库存明细数据
local function update_inv_detail(strLuaDEID, dataSet)
    -- 构建查询条件
    local strCondition = string.format("S_STORER = '%s' AND S_OWNER = '%s' AND S_ITEM_CODE = '%s' AND S_WMS_BN = '%s'",
        sql_escape(lua.Get_StrAttrValue(dataSet.storerId)), sql_escape(lua.Get_StrAttrValue(dataSet.ownerId)),
        sql_escape(lua.Get_StrAttrValue(dataSet.skuId)), sql_escape(lua.Get_StrAttrValue(dataSet.batchNo)))
    -- 构建更新字段
    local strUpdateSql = string.format("S_BATCH_NO = '%s', D_PRD_DATE = '%s', D_EXP_DATE = '%s', " ..
                                           "S_UDF01 = '%s', S_UDF02 = '%s', S_UDF03 = '%s', " ..
                                           "S_UDF04 = '%s', S_UDF05 = '%s', " ..
                                           "S_UDF06 = '%s', S_UDF07 = '%s', S_UDF08 = '%s'",
        sql_escape(lua.Get_StrAttrValue(dataSet.produceCode)), sql_escape(lua.Get_StrAttrValue(dataSet.productDate)),
        sql_escape(lua.Get_StrAttrValue(dataSet.expiryDate)), sql_escape(lua.Get_StrAttrValue(dataSet.registerNo)),
        sql_escape(lua.Get_StrAttrValue(dataSet.companyName)),
        sql_escape(lua.Get_StrAttrValue(dataSet.certCompanyName)),
        sql_escape(lua.Get_StrAttrValue(dataSet.consignCompanyWTName)),
        sql_escape(lua.Get_StrAttrValue(dataSet.sterilizationDate)), sql_escape(lua.Get_StrAttrValue(dataSet.ex1)),
        sql_escape(lua.Get_StrAttrValue(dataSet.ex2)), sql_escape(lua.Get_StrAttrValue(dataSet.ex3)))
    -- 执行更新
    local nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, CLSID_INVdetail, strCondition, strUpdateSql)
    if nRet ~= 0 then
        return 1, "更新库存明细失败: " .. strRetInfo
    end
    return 0
end
-- 处理单条批次数据
local function process_batch_item(strLuaDEID, dataSet)
    -- 处理批次数据
    local nRet, err_msg = create_or_update_batch(strLuaDEID, dataSet)
    if nRet ~= 0 then
        return 1, err_msg
    end
    -- 更新库存明细
    lua.DebugEx(strLuaDEID, "准备更新的库存明细数据: ", lua.table2str(dataSet))
    nRet, err_msg = update_inv_detail(strLuaDEID, dataSet)
    if nRet ~= 0 then
        return 1, err_msg
    end
    return 0
end
-- 主函数
function Batch_Sync(strLuaDEID)
    -- 初始化最终结果
    local FinalRes = Create_result()
    -- 1. 获取xml数据包
    local nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "201", "无法获取数据包: " .. soap_xml)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取数据包失败", FinalRes)
        return
    end
    -- 2. 解析xml
    local nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "202", "xml格式非法")
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml格式非法", FinalRes)
        return
    end
    -- 3. 提取批次数据
    local batch_data = parsed_data.Envelope.Body.InBatchGoodsReq.BatchGoods_Input.InputParameters.BATCHGOODS_TB
    if not batch_data or not batch_data.BATCHGOODS_TB_ITEM then
        FinalRes = Create_result("failure", "203", "xml数据格式错误,缺少BATCHGOODS_TB_ITEM")
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml数据格式错误", FinalRes)
        return
    end
    -- 4. 统一处理:确保batch_items是数组
    local batch_items = batch_data.BATCHGOODS_TB_ITEM
    if batch_items[1] == nil then
        batch_items = {batch_items}
    end
    -- 5. 遍历所有批次数据
    for i = 1, #batch_items do
        local batch_item = batch_items[i]
        local nRet, err_msg = process_batch_item(strLuaDEID, batch_item)
        if nRet ~= 0 then
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步批次信息")
            FinalRes = Create_result("failure", "204", "批次同步失败: " .. err_msg)
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "批次同步失败: " .. batch_item.batchNo, FinalRes)
            return
        end
    end
    -- 6. 返回成功
    FinalRes = Create_result("success", "0", "批次同步成功")
    local xml_result = xml.json_to_xml(FinalRes, "response")
    mobox.returnValue(strLuaDEID, 0, xml_result, 0)
end
lua_code/Lua/GK-API-04-Receipt_Sync.lua
New file
@@ -0,0 +1,292 @@
--[[
 编码: GK-API-04
 名称: GK-WMS-Receipt_Sync
 作者: 袁峰
 入口函数:Receipt_Sync
 功能说明: 接收上游系统的XML数据,创建收货单主表(Receipt_Order)和明细记录(Receipt_Detail)。
 输入参数: xmlData(XML格式的字符串)
 返回参数: xml 格式的响应报文:
 形如:
 <response>
    <message>收货单已存在</message>
    <code>204</code>
    <flag>failure</flag>
    <error>收货单[SO2025050701]已存在</error>
 </response>
 调用示例: Receipt_Sync(xmlData)
 注意事项: 1. 输入参数xmlData为XML格式的字符串,包含了收货单主表和明细记录的数据。
          2. 函数会解析XML数据,并将其插入到数据库中。
          3. 允许xml 传入1条或者多条收货单主表数据,每条收货单主表数据可以包含多条收货单明细数据。
 变更历史: 2025-05-14 优化返回结果处理
 XML 报文示例如下;
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    </soap:Header>
    <soapenv:Body>
        <v1:InSmallPieceReq
            xmlns:v1="http://www.gkht.com/InReceive/INV/Ebs/Schemas/InSmallPiece/V1.0">
            <v1:SmallPiece_Input>
                <v1:RESTHeader>
                    <v1:Responsibility/>
                    <v1:RespApplication/>
                    <v1:SecurityGroup/>
                    <v1:NLSLanguage>SIMPLIFIED CHINESE</v1:NLSLanguage>
                    <v1:Org_Id>0</v1:Org_Id>
                </v1:RESTHeader>
                <v1:InputParameters>
                        <!--1 or more repetitions:-->
                       <v1:SmallPiece_TB>
                           <v1:orderNo>SO2025050601</v1:orderNo>
                           <v1:asnNo>KPD00001</v1:asnNo>
                           <v1:storerId>CGKHTY</v1:storerId>
                           <v1:ownerId>CGKHTY</v1:ownerId>
                           <v1:orderDate>2025-05-06</v1:orderDate>
                                    <v1:priority>0</v1:priority>
                                 <v1:memo></v1:memo>
                                <v1:SmallPiece_TB_ITEM>
                            <v1:orderItemId>1</v1:orderItemId>
                            <v1:skuId>XR33201-2L080B</v1:skuId>
                            <v1:skuStatus>AVL</v1:skuStatus>
                            <v1:qty>2</v1:qty>
                                    <v1:batchNo>PHI00000000000001309</v1:batchNo>
                            <v1:produceCode>YL201125</v1:produceCode>
                                    <v1:productDate>2016-04-23</v1:productDate>
                                    <v1:expiryDate>2099-12-31</v1:expiryDate>
                                    <v1:registerNo>国食药监械(准)字2012</v1:registerNo>
                        </v1:SmallPiece_TB_ITEM>
                             <v1:SmallPiece_TB_ITEM>
                            <v1:orderItemId>2</v1:orderItemId>
                            <v1:skuId>KH32803017</v1:skuId>
                            <v1:skuStatus>AVL</v1:skuStatus>
                            <v1:qty>5</v1:qty>
                                  <v1:batchNo>PHI00000000000001308</v1:batchNo>
                            <v1:produceCode>YL2011256</v1:produceCode>
                                 <v1:productDate>2023-04-23</v1:productDate>
                                 <v1:expiryDate>2099-12-31</v1:expiryDate>
                                  <v1:registerNo></v1:registerNo>
                        </v1:SmallPiece_TB_ITEM>
                    </v1:SmallPiece_TB>
                </v1:InputParameters>
            </v1:SmallPiece_Input>
        </v1:InSmallPieceReq>
    </soapenv:Body>
</soapenv:Envelope>
--]] wms_base = require("wms_base")
xml = require("oi_base_xml")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
function Create_result(flag, code, msg)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or ""
    }
end
function Receipt_Sync(strLuaDEID)
    -- 初始化最终结果
    local FinalRes = Create_result()
    -- 1. 获取 xml 数据包
    local nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "201", "无法获取数据包: " .. soap_xml)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取数据包失败", soap_xml)
        return
    end
    -- lua.DEbugEx(strLuaDEID, "获取到的数据包", soap_xml)
    -- 2. 解析 xml
    local nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "202", "xml 格式非法" .. parsed_data)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml格式非法", parsed_data)
        return
    end
    -- 3. 提取主表数据
    local receipt_data = parsed_data.Envelope.Body.InSmallPieceReq.SmallPiece_Input
    local input_params = receipt_data.InputParameters
    -- 检查是否存在 SmallPiece_TB
    if not input_params or not input_params.SmallPiece_TB then
        FinalRes = Create_result("failure", "203", "xml 数据格式错误,缺少 SmallPiece_TB")
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml 数据格式错误", xml_result)
        return
    end
    -- 4. 统一处理:确保 receipt_headers 是数组(即使只有一个主表)
    local receipt_headers = input_params.SmallPiece_TB
    if receipt_headers[1] == nil then
        receipt_headers = {receipt_headers}
    end
    -- 5. 获取系统常量
    local RetWH_COE, CONST_WH = wms_base.Get_sConst2("WMS_Default_Warehouse")
    local RetFAC_COE, CONST_FACTORY = wms_base.Get_sConst2("WMS_Default_Factory")
    -- 仓库常量异常捕获
    if (RetWH_COE ~= 0) then
        FinalRes = Create_result("failure", "204", "获取仓库常量失败" .. CONST_WH)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取系统常量:WMS_Default_Warehouse 失败", xml_result)
        return
    end
    -- 工厂常量异常捕获
    if (RetFAC_COE ~= 0) then
        FinalRes = Create_result("failure", "204", "获取工厂常量失败" .. CONST_FACTORY)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取系统常量:WMS_Default_Factory", xml_result)
        return
    end
    -- 6. 遍历所有收货单
    for i = 1, #receipt_headers do
        local header = receipt_headers[i]
        -- 检查收货单是否已存在
        local strCondition = string.format("S_NO = '%s'", header.orderNo)
        -- lua.DEbugEx(strLuaDEID, "查询收货单条件", strCondition)
        local nRet, retReceipt = m3.GetDataObjByCondition(strLuaDEID, "Receipt_Order", strCondition)
        if nRet == 0 then
            -- 查询成功且找到记录,说明收货单已存在
            -- lua.DEbugEx(strLuaDEID, "查询成功,收货单已存在", retReceipt)
            FinalRes = Create_result("failure", "1", "收货单[" .. header.orderNo .. "]已存在")
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "收货单已存在", retReceipt)
            return
        elseif nRet ~= 1 then
            -- 查询出错
            -- lua.DEbugEx(strLuaDEID, "查询出错", retReceipt)
            FinalRes = Create_result("failure", "206",
                "检查收货单[" .. header.orderNo .. "]时出错: " .. retReceipt)
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "检查收货单是否存在时出错", xml_result)
            return
        end
        -- 创建主表数据
        local receipt = m3.AllocObject(strLuaDEID, "Receipt_Order")
        receipt.no = header.orderNo
        receipt.asn_no = header.asnNo
        receipt.op_date = header.orderDate
        receipt.priority = header.priority
        receipt.note = header.memo or ""
        receipt.bs_type = "SMALL_PIECE"
        receipt.factory = CONST_FACTORY
        receipt.wh_code = CONST_WH
        -- 检查是否已存在相同关键字的记录
        local nRet, ret_info = m3.CreateDataObj(strLuaDEID, receipt)
        if nRet ~= 0 then
            -- 再次检查是否真的存在
            local nRetCheck, retReceiptCheck = m3.GetDataObjByCondition(strLuaDEID, "Receipt_Order", strCondition)
            if nRetCheck == 0 then
                FinalRes = Create_result("failure", "205", "收货单[" .. header.orderNo .. "]已存在")
            else
                FinalRes = Create_result("failure", "207",
                    "收货单[" .. header.orderNo .. "]创建失败: " .. ret_info)
            end
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "创建收货单主表失败", ret_info)
            return
        end
        -- 检查明细数据是否存在
        if header.SmallPiece_TB_ITEM == nil then
            -- lua.DEbugEx(strLuaDEID, "警告:收货单 " .. header.orderNo .. " 无明细数据")
            FinalRes = Create_result("failure", "208", "收货单[" .. header.orderNo .. "]无明细数据")
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "收货单无明细数据", xml_result)
            return
        end
        -- 7. 遍历当前收货单的所有明细数据
        local details = header.SmallPiece_TB_ITEM
        -- 确保 details 是数组(即使只有一个明细)
        if details[1] == nil then
            details = {details}
        end
        for j = 1, #details do
            local item = details[j]
            -- 先检验skuId是否存在
            local strCondition = string.format("S_ITEM_CODE= '%s' AND S_STORER = '%s'", item.skuId, header.storerId)
            -- 执行查询
            local nRet, receiptInfo = m3.GetDataObjByCondition(strLuaDEID, "SKU", strCondition)
            if (nRet ~= 0) then
                -- lua.DEbugEx(strLuaDEID, "SKU查询出错",
                    -- "ITEM_CODE: " .. item.skuId .. ", 错误信息: " .. receiptInfo)
                FinalRes = Create_result("failure", "207",
                    "检查 SKU 是否存在时出错:SKU ID为 " .. item.skuId .. ", 错误: " .. receiptInfo)
                local xml_result = xml.json_to_xml(FinalRes, "response")
                mobox.returnValue(strLuaDEID, 0, xml_result, 0)
                lua.Stop(strLuaDEID, "查询失败", xml_result)
                return
            elseif (receiptInfo == "") then
                -- lua.DEbugEx(strLuaDEID, "查询为空", "ITEM_CODE:" .. item.skuId)
                FinalRes = Create_result("failure", "208", "查询数据为空:ITEM_CODE : " .. item.skuId)
                local xml_result = xml.json_to_xml(FinalRes, "response")
                mobox.returnValue(strLuaDEID, 0, xml_result, 0)
                lua.Stop(strLuaDEID, "物料数据不存在", xml_result)
                return
            end
            -- 创建明细数据
            local receipt_detail = m3.AllocObject(strLuaDEID, "Receipt_Detail")
            receipt_detail.row_no = item.orderItemId
            receipt_detail.receipt_no = header.orderNo
            receipt_detail.qty = lua.Get_NumAttrValue(item.qty) or 0
            receipt_detail.acc_put_qty = lua.Get_NumAttrValue("0")
            receipt_detail.acc_unq_qty = lua.Get_NumAttrValue("0")
            receipt_detail.acc_c_qty = lua.Get_NumAttrValue("0")
            receipt_detail.item_code = item.skuId
            receipt_detail.item_state = item.skuStatus
            receipt_detail.item_name = receiptInfo.item_name or ""
            receipt_detail.wu = "kg"
            receipt_detail.batch_no = item.produceCode
            receipt_detail.wms_bn = item.batchNo
            receipt_detail.prd_date = item.productDate
            receipt_detail.exp_date = item.expiryDate
            receipt_detail.udf01 = item.registerNo
            receipt_detail.storer = header.storerId
            receipt_detail.owner = header.ownerId
            receipt_detail.item_name = receiptInfo.item_name or ""
            receipt_detail.net_weight = receiptInfo.weight or 0
            receipt_detail.gross_weight = receiptInfo.weight or 0
            receipt_detail.uom = receiptInfo.uom or ""
            -- lua.DEbugEx(strLuaDEID, "创建明细数据:", receipt_detail)
            local nRet, ret_info = m3.CreateDataObj(strLuaDEID, receipt_detail)
            if nRet ~= 0 then
                FinalRes = Create_result("failure", "208", "创建收货明细失败: " .. ret_info)
                local xml_result = xml.json_to_xml(FinalRes, "response")
                mobox.returnValue(strLuaDEID, 0, xml_result, 0)
                -- lua.DEbugEx(strLuaDEID, "创建收货明细失败", xml_result)
                lua.Stop(strLuaDEID, "创建收货明细失败", ret_info)
                return
            end
        end
    end
    -- 8. 返回成功
    FinalRes = Create_result("success", "0", "收货单创建成功")
    local xml_result = xml.json_to_xml(FinalRes, "response")
    mobox.returnValue(strLuaDEID, 0, xml_result, 0)
end
lua_code/Lua/GK-API-05-InboundSync.lua
New file
@@ -0,0 +1,525 @@
--[[
    编码:   GK-API-05
    名称:   小件任务创建接口
    作者:   Peter fang
    日期:   2025-05-13
    入口函数: Inbound_Sync
    来源项目: 国科项目
    功能说明:
        1. 接收来自上游系统的XML格式数据,并解析该数据,创建Inbound Order及Inbound Detail
        2. 创建Inbound Palletization及Inbound Pallet Detail
]]
wms_base = require( "wms_base" )
xml=require( "oi_base_xml" )
wms_pallet = require( "wms_palletizing" )
-- 创建统一返回结果
local function create_result(flag, code, msg)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or ""
    }
end
local function return_value(strLuaDEID, success,err_msg)
    if success==false then
        lua.Stop( strLuaDEID, err_msg )
    end
    local flag = "success"
    local code="0"
    if success==true then
        flag = "success"
        code="0"
    else
        flag = "failure"
        code="1"
    end
    local result= create_result( flag, code, err_msg )
    local xml_result = xml.json_to_xml(result, "response")
    local value=0
    if flag=="success" then
        value=0
    else
        value=3
    end
    mobox.returnValue(strLuaDEID,1,xml_result,value)
end
local function count_cell_list(t)
    local count = 0
    for cell,group in pairs(t) do
        if(cell~=nil and cell~="") then
            count = count + 1
        end
    end
    return count
end
local function group_by(t, key)
    local result = {}
    local getKey = type(key) == "function" and key or function(item)
        return item[key]
    end
    for _, item in ipairs(t) do
        local groupKey = getKey(item)
        if groupKey ~= nil then
            if not result[groupKey] then
                result[groupKey] = {}
            end
            table.insert(result[groupKey], item)
        end
    end
    return result
end
local function goods_count(items)
    local group_table = group_by(items, "skuId")
    local count = 0
    for _ in pairs(group_table) do
        count = count + 1
    end
    return count
end
local function sku_count(items)
    local qty=0
    for _,v in ipairs(items) do
        if v.qty~=nil then
            qty=qty+lua.Get_NumAttrValue(v.qty)
        end
    end
    return qty
end
local function get_celltype(group,cid)
    local spec="A"
    local cellItems={}
    for _,cell in ipairs(group) do
        table.insert(cellItems,cell)
    end
    local by_cell=group_by(cellItems,"boxCode")
    local cellCount=count_cell_list(by_cell)
    if (cellCount==1) then
        spec="A"
    elseif (cellCount==2) then
        spec="B"
    elseif (cellCount==3) then
        spec="C"
    elseif (cellCount==4) then
        spec="D"
    elseif (cellCount==6) then
        spec="E"
    elseif (cellCount==8) then
        spec="F"
    else
        return 1, "料格数量"..cellCount.. "料箱规格超出系统定义!cntr_code= "..cid
    end
    return 0, spec
end
local function create_inbound_pallet(strLuaDEID,inboundData,items)
    local nRet,strRetInfo
    local by_pallet=group_by(items,"cid")
    for cid, group in pairs(by_pallet) do
        if cid~=nil and cid~="" then
            if(group[1].boxCode~=nil and group[1].boxCode~="") then
                local spec
                nRet, spec = get_celltype(group,cid)
                if nRet ~= 0 then
                    return 1, spec
                end
                local detail_item_data = {}
                for _,cell in ipairs(group) do
                    local strTableName = "TN_SKU"
                    local strFieldList = '["S_UDF01"]'
                    local strCondition =string.format("S_STORER='%s' AND S_ITEM_CODE='%s'",inboundData.storerId,cell.skuId)
                    nRet, strRetInfo=mobox.queryTable(strLuaDEID,strTableName, strFieldList,1, strCondition, "")
                    if (nRet ~= 0) then
                        return 1,"查询SKU信息失败!"..strRetInfo
                    end
                    local sku_info = json.decode(strRetInfo)
                    if not sku_info or #sku_info == 0 then
                        return 1,"SKU信息不存在"..cell.skuId
                    end
                    local sudf01 = sku_info[1].S_UDF01
                    local item_detail
                    item_detail = m3.AllocObject2( strLuaDEID, "INB_Pallet_Detail")
                    item_detail.S_CNTR_CODE = cid
                    local seg = lua.split(cell.boxCode, '-' )
                    if ( #seg ~= 2 ) then
                        return 1,"料格号格式错误"..cell.boxCode
                    end
                    item_detail.S_CELL_NO =spec.."-".. lua.StrToNumber(seg[2])
                    item_detail.S_CELL_CODE=cell.boxCode
                    item_detail.S_ITEM_CODE = cell.skuId
                    item_detail.S_ITEM_NAME = cell.skuId
                    item_detail.S_ITEM_STATE=cell.skuStatus
                    item_detail.S_STORER=inboundData.storerId
                    item_detail.S_OWNER=inboundData.ownerId
                    item_detail.F_QTY = cell.qty
                    item_detail.S_UDF01=sudf01
                    item_detail.S_UDF02=cell.registerNo
                    item_detail.S_WMS_BN = cell.batchNo
                    item_detail.S_BATCH_NO = cell.produceCode
                    item_detail.D_PRD_DATE = cell.productDate
                    item_detail.D_EXP_DATE = cell.expiryDate
                    item_detail.S_BS_TYPE="GK_IN_TASK"
                    item_detail.S_BS_NO=inboundData.taskId
                    item_detail.N_BS_ROW_NO=cell.row_no
                    table.insert(detail_item_data, item_detail)
                end
                nRet, strRetInfo = wms_pallet.Add_INB_Pallet_Detail( strLuaDEID, cid, detail_item_data,"GK_IN_TASK",inboundData.taskId)
                if nRet ~= 0 then
                    return 1,"生成码盘信息失败".. strRetInfo
                end
            end
        end
    end
    return 0, ""
end
local function create_container(strLuaDEID,inboundData,items)
    local nRet,strRetInfo
    local by_pallet=group_by(items,"cid")
    for cid, group in pairs(by_pallet) do
        if cid~=nil and cid~="" then
            if(group[1].boxCode~=nil and group[1].boxCode~="") then
                local spec
                nRet, spec = get_celltype(group,cid)
                if nRet ~= 0 then
                    lua.DebugEx(strLuaDEID, "create_container", group[1].boxCode.. "cid="..cid)
                    return 1, spec
                end
            nRet, strRetInfo = wms.wms_GetConst("WMS_Default_CNTR_Type")
                if (nRet ~= 0) then
                    return 1, "获取默认容器类型失败!"..strRetInfo
                end
                local container_type_info = strRetInfo
                local strObjID
                local strCondition =string.format("S_CODE='%s'",cid)
                nRet,strObjID,strRetInfo=mobox.getDataObjAttrByKeyAttr(strLuaDEID,"Container",strCondition)
                if nRet ~= 0 then
                    return 1,cid.."容器不存在!"..strRetInfo
                end
                local update_container_obj={
                {
                    id = strObjID,
                    attrs = {
                        { attr = "S_CTD_CODE", value =container_type_info }, -- 料箱类型
                        { attr = "S_SPEC", value = spec }, -- 料箱规格
                    }
                }}
                nRet, strRetInfo = mobox.updateDataObj( strLuaDEID, "Container", lua.table2str(update_container_obj) )
                if (nRet ~= 0 ) then
                    return 1,"更新【Container】属性失败!"..strRetInfo
                end
            end
        end
    end
    return 0, ""
end
local function create_inbound_detail(strLuaDEID,detail)
    local nRet, strRetInfo
    local inbound_detail = m3.AllocObject( strLuaDEID,"Inbound_Detail")
    inbound_detail.row_no=detail.row_no
    inbound_detail.io_no=detail.io_no
    inbound_detail.storer=detail.storer
    inbound_detail.owner=detail.owner
    inbound_detail.bs_row_no=detail.bs_row_no
    inbound_detail.item_code=detail.item_code
    inbound_detail.item_name=detail.item_name
    inbound_detail.item_state=detail.item_state
    inbound_detail.qty=detail.qty
    inbound_detail.wms_bn=detail.wms_bn
    inbound_detail.batch_no=detail.batch_no
    inbound_detail.prd_date=detail.prd_date
    inbound_detail.exp_date=detail.exp_date
    inbound_detail.ext_attr1=detail.registerNo
    inbound_detail.note=detail.note
    local strTableName = "TN_SKU"
    local strFieldList = '["S_UDF01"]'
    local strCondition =string.format("S_STORER='%s' AND S_ITEM_CODE='%s'",detail.storer,detail.item_code)
    nRet, strRetInfo=mobox.queryTable(strLuaDEID,strTableName, strFieldList,1, strCondition, "")
    if (nRet ~= 0) then
        return 1,"查询SKU信息失败!"..strRetInfo
    end
    local sku_info = json.decode(strRetInfo)
    if not sku_info or #sku_info == 0 then
        return 1,"SKU信息不存在"..detail.item_code
    end
    inbound_detail.udf01 = sku_info[1].S_UDF01
    inbound_detail.udf19=detail.sn
    inbound_detail.udf20=detail.cntr_code
    nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, inbound_detail)
    if (nRet==nil or nRet ~= 0 ) then
        return 1, "创建系统入库单明细失败!"..strRetInfo.."no= "..detail.io_no
    end
    return 0, ""
end
local function gk_create_inbound_detail(strLuaDEID,inboundData, inboundItem,index)
    local nRet, strRetInfo
    local inbound_detail = m3.AllocObject( strLuaDEID,"GK_IN_TASK_Detail")
    inbound_detail.row_no=index
    inbound_detail.io_no=inboundData.taskId
    inbound_detail.storer=inboundData.storerId
    inbound_detail.owner=inboundData.ownerId
    inbound_detail.bs_row_no=inboundItem.orderItemId
    inbound_detail.item_code=inboundItem.skuId
    inbound_detail.item_name=inboundItem.skuId
    inbound_detail.item_state=inboundItem.skuStatus
    inbound_detail.qty=lua.Get_NumAttrValue(inboundItem.qty)
    inbound_detail.wms_bn=inboundItem.batchNo
    inbound_detail.batch_no=inboundItem.produceCode
    inbound_detail.prd_date=inboundItem.productDate
    inbound_detail.exp_date=inboundItem.expiryDate
    inbound_detail.register_no=inboundItem.registerNo
    inbound_detail.cntr_code=inboundItem.cid
    inbound_detail.cell_no=inboundItem.boxCode
    inbound_detail.sn=inboundItem.sn
    nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, inbound_detail)
    if (nRet==nil or nRet ~= 0 ) then
        return 1, "创建入库单明细失败!"..strRetInfo.."no= "..inboundData.taskId
    end
    if inboundItem.boxCode==nil or inboundItem.boxCode=="" then
         nRet, strRetInfo=create_inbound_detail(strLuaDEID,inbound_detail)
        if (nRet==nil or nRet ~= 0 ) then
            return 1, "创建系统入库单明细失败!"..strRetInfo.."no= "..inboundData.taskId
        end
    end
    return 0, ""
end
local function create_inbound(strLuaDEID, gk_inbound)
   local nRet, strRetInfo
   local inbound = m3.AllocObject( strLuaDEID,"Inbound_Order")
   inbound.no=gk_inbound.no
   inbound.b_state=0
   inbound.factory=gk_inbound.factory
   inbound.wh_code=gk_inbound.wh_code
   inbound.bs_type=gk_inbound.bs_type
   inbound.bs_no=gk_inbound.bs_no
   inbound.asn_no=gk_inbound.asn_no
   inbound.op_date=gk_inbound.op_date
   inbound.priority=gk_inbound.priority
   inbound.note=gk_inbound.note
    nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, inbound)
   if (nRet==nil or nRet ~= 0 ) then
       return 1,  "创建系统入库单失败!"..strRetInfo.."no= "..gk_inbound.no
   end
    return 0,""
end
local function gk_create_inbound(strLuaDEID, inboundData,emptyCellCount)
   local nRet, strRetInfo
   local inbound = m3.AllocObject( strLuaDEID,"GK_IN_TASK")
   inbound.no=inboundData.taskId
   inbound.b_state=0
   nRet, strRetInfo = wms.wms_GetConst("WMS_Default_Factory")
    if (nRet ~= 0) then
         return 1, "获取默认工厂失败!"..strRetInfo
    end
   inbound.factory=strRetInfo
   nRet, strRetInfo = wms.wms_GetConst("WMS_Default_Warehouse")
   if (nRet ~= 0) then
        return 1, "获取默认仓库失败!"..strRetInfo
   end
   inbound.wh_code=strRetInfo
   inbound.bs_type=inboundData.taskType
   inbound.bs_no=inboundData.orderNo
   inbound.asn_no=inboundData.asnNo
   inbound.op_date=inboundData.orderDate
   inbound.priority=inboundData.priority
   inbound.source_no=inboundData.sourceBillNo
   inbound.note=inboundData.memo
   local detailItems = inboundData.TaskCreation_TB_ITEM
   local items={}
   if detailItems==nil then
       return 1, "入库单明细为空"
   end
   if detailItems[1]==nil then
        items={detailItems}
   else
        items=detailItems
   end
   if items==nil or items[1]==nil then
       return 1, "入库单明细为空"
    else
        local index=1
        for index, item in ipairs(items) do
            --创建入库单明细
            item.row_no=index
             nRet, strRetInfo = gk_create_inbound_detail(strLuaDEID,inboundData,item,index)
             if (nRet==nil or nRet ~= 0 ) then
                return 1, "创建入库单明细失败!"..strRetInfo.."no= "..inboundData.taskId
             end
        end
    end
    inbound.good_type_num=goods_count(items)
    inbound.total_qty=sku_count(items)
    nRet, inbound = m3.CreateDataObj(strLuaDEID, inbound)
   if (nRet==nil or nRet ~= 0 ) then
       return 1,  "创建入库单失败!"..inbound.."no= "..inboundData.taskId
   end
    --创建系统入库单
    if emptyCellCount>0 then
         nRet, strRetInfo =create_inbound(strLuaDEID,inbound)
        if (nRet==nil or nRet ~= 0 ) then
            return 1, "创建系统入库单失败!"..strRetInfo.."no= "..inboundData.taskId
        end
    end
    --创建入库单码盘
    nRet, strRetInfo =create_inbound_pallet(strLuaDEID,inboundData,items)
    if (nRet==nil or nRet ~= 0 ) then
        return 1, "创建码盘明细失败!"..strRetInfo.."no= "..inboundData.taskId
    end
    --创建容器料格信息
    nRet, strRetInfo = create_container(strLuaDEID,inboundData,items)
    if (nRet==nil or nRet ~= 0 ) then
        return 1, "创建容器失败!"..strRetInfo.."no= "..inboundData.taskId
    end
    return 0,""
end
function Inbound_Sync(strLuaDEID)
    local nRet, strRetInfo
    local err_msg
    --m3.PrintLuaDEInfo(strLuaDEID)
    local soap_xml
    nRet,soap_xml=mobox.getCurEditDataPacket(strLuaDEID)
    if ( nRet ~=0 ) then
        return_value(strLuaDEID, false,  "无法获取数据包 !"..soap_xml)
        return
    end
    local parsed_data
    nRet, parsed_data = xml.parse(soap_xml)
    if ( nRet ~= 0 ) then
        return_value(strLuaDEID, false, "接口输入的XML格式非法!")
        return
    end
    local inboundItems=parsed_data.Envelope.Body.InTaskCreationReq.TaskCreation_Input.InputParameters.TaskCreation_TB
    local inboundSet={}
    if inboundItems==nil then
        return_value(strLuaDEID, false, "接口输入的XML格式非法!")
        return
    end
    if inboundItems[1]==nil then
        inboundSet={inboundItems}
    else
        inboundSet=inboundItems
    end
    for i, inboundData in ipairs(inboundSet) do
         if inboundData.TaskCreation_TB_ITEM==nil then
            err_msg = "入库明细为空"
            return_value(strLuaDEID, false, err_msg)
            return
        end
          --判断InBound是否存在,取消状态后面考虑
        local id
        local strCondition =string.format("S_NO='%s' AND N_B_STATE<>91",inboundData.taskId)
        nRet,id,strRetInfo=mobox.getDataObjAttrByKeyAttr(strLuaDEID,"GK_IN_TASK",strCondition)
        if nRet==0 then
            err_msg = "入库单号已存在!"..inboundData.taskId
            return_value(strLuaDEID, true, err_msg)
            return
        end
        --判断料箱是否存在,不存在拒绝
        local detailItems = inboundData.TaskCreation_TB_ITEM
        local items={}
        if detailItems==nil then
            return 1, "入库单明细为空"
        end
        if detailItems[1]==nil then
            items={detailItems}
        else
            items=detailItems
        end
        local by_pallet=group_by(items,"cid")
        local emptyCellCount=0
        for cid, group in pairs(by_pallet) do
            for _,cell in ipairs(group) do
                if cell.boxCode==nil or cell.boxCode=="" then
                    emptyCellCount=emptyCellCount+1
                end
            end
            if cid~=nil and cid~="" then
                --判断料箱是否存在
                local strCondition =string.format("S_CODE='%s'",cid)
                nRet,strRetInfo=mobox.existThisData(strLuaDEID,"Container",strCondition)
                if nRet~=0 then
                    err_msg = "查询数据出错!"..cid
                    return_value(strLuaDEID, false, err_msg)
                    return
                end
                if strRetInfo=="no" then
                    err_msg = "料箱不存在!"..cid
                    return_value(strLuaDEID, false, err_msg)
                    return
                end
                --判断料箱是否完成入库
                 local strCondition =string.format("S_CNTR_CODE='%s' AND N_B_STATE NOT IN(30,40)",cid)
                nRet,strRetInfo=mobox.existThisData(strLuaDEID,"Inbound_Palletization",strCondition)
                if nRet~=0 then
                    err_msg = "查询数据出错!"..cid
                    return_value(strLuaDEID, false, err_msg)
                    return
                end
                if strRetInfo=="yes" then
                    err_msg = "该料箱码盘数据未完成!"..cid
                    return_value(strLuaDEID, false, err_msg)
                    return
                end
                --判断料箱是否存在库存
                strCondition=string.format("S_CNTR_CODE='%s' AND F_QTY>0",cid)
                nRet,strRetInfo=mobox.existThisData(strLuaDEID,"INV_Detail",strCondition)
                if nRet~=0 then
                    err_msg = "查询数据出错!"..cid
                    return_value(strLuaDEID, false, err_msg..strRetInfo)
                    return
                end
                if strRetInfo=="yes" then
                    lua.DebugEx(strLuaDEID, "料箱存在库存")
                    err_msg = "料箱存在库存"..cid
                    return_value(strLuaDEID, false, err_msg..strRetInfo)
                    return
                end
            end
        end
        nRet,err_msg= gk_create_inbound(strLuaDEID, inboundData,emptyCellCount)
        if (nRet==nil or nRet~=0) then
            return_value(strLuaDEID, false, err_msg)
        else
            return_value(strLuaDEID, true, "")
        end
    end
end
lua_code/Lua/GK-API-06-Outbound_Sync.lua
New file
@@ -0,0 +1,187 @@
--[[
    编码: GK-API-06
    名称: 出库单创建接口
    作者: yuanfeng
    日期: 2025-7-24
    入口函数: Outbound_Sync
    功能说明:
        1. 接收上游系统的入库任务XML数据
        2. 检查入库单是否已存在
        3. 创建Outbound_Order主表和Outbound_Detail子表记录
        4. 返回处理结果
    更新
        2025-07-12 V1.1 新增出库单主表,state 默认为 "定版"
    输入XML示例:
--]] wms_base = require("wms_base")
xml = require("oi_base_xml")
xml2lua = require("xml2lua")
-- 创建出库单主表记录
local function create_outbound_order(strLuaDEID, order_data)
    local nRet1, CONST_FACTORY = wms_base.Get_sConst2("WMS_Default_Factory")
    -- 默认工厂标识
    local nRet2, CONST_WH = wms_base.Get_sConst2("WMS_Default_Warehouse")
    if nRet1 ~= 0 or nRet2 ~= 0 then
        return 1, "获取工厂或仓库常量失败"
    end
    local order = m3.AllocObject(strLuaDEID, "Outbound_Order")
    order.no = order_data.taskId
    order.bs_type = order_data.taskType
    order.bs_no = order_data.orderNo
    order.wave_no = order_data.waveId
    order.op_date = order_data.orderDate
    order.priority = order_data.priority
    order.note = order_data.memo or ""
    order.wh_code = CONST_WH
    order.area_code = ""
    order.factory = CONST_FACTORY
    -- 新增出库单主表状态字段
    order.state = "定版"
    -- 新增货主编码
    order.storer = order_data.storerId
    -- 新增源单号
    order.source_no = order_data.sourceBillNo
    -- lua.DebugEx(strLuaDEID, "预创建出库单主表", order)
    local nRet, result = m3.CreateDataObj(strLuaDEID, order)
    return nRet, result
end
-- 创建出库单明细记录
-- @param:item_date 是传入的物料明细实体数据
-- @param:order_data 传入出库单主表实体数据
local function create_outbound_detail(strLuaDEID, order_no, item_data, order_data)
    local detail = m3.AllocObject(strLuaDEID, "Outbound_Detail")
    -- 明细表字段映射
    detail.oo_no = order_no
    detail.storer = order_data.storerId
    detail.owner = order_data.ownerId
    detail.bs_row_no = item_data.orderItemId -- 订单行号
    detail.item_code = item_data.skuId
    detail.item_state = item_data.skuStatus
    detail.qty = lua.Get_NumAttrValue(item_data.qty)
    detail.batch_no = item_data.produceCode
    detail.wms_bn = item_data.batchNo
    detail.prd_date = item_data.productDate
    detail.exp_date = item_data.expiryDate
    detail.udf01 = item_data.registerNo or ""
    detail.udf02 = item_data.cid or ""
    -- lua.DebugEx(strLuaDEID, "预创建出库单明细", detail)
    local nRet, result = m3.CreateDataObj(strLuaDEID, detail)
    return nRet, result
end
function Outbound_Sync(strLuaDEID)
    local nRet, strRetInfo, err_msg
    local soap_xml, parsed_data, task_data, input_params, task_tb, order_no
    local strCondition, id, task_items
    local isStop = 0
    local err = {}
    local items = {}
    m3.PrintLuaDEInfo(strLuaDEID)
    local result = {
        flag = "success",
        code = "0",
        message = "成功"
    }
    -- lua.Debug(strLuaDEID, debug.getinfo(1), "出库单数据:", result)
    -- 1. 获取接口输入数据
    nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
    if nRet ~= 0 then
        -- lua.Stop(strLuaDEID, "无法获取数据包: " .. soap_xml)
        result.flag = "failure"
        result.code = "1"
        result.message = "无法获取数据包: "
        goto api_call_return
        -- return
    end
    -- 2. 解析XML
    nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
        -- lua.Stop(strLuaDEID, "接口输入的XML格式非法!")
        result.flag = "failure"
        result.code = "2"
        result.message = "接口输入的XML格式非法: "
        goto api_call_return
        -- return
    end
    -- 3. 提取任务数据
    task_data = parsed_data.Envelope.Body.InTaskCreationReq.TaskCreation_Input
    input_params = task_data.InputParameters
    task_tb = input_params.TaskCreation_TB
    -- 4. 检查出库单是否已存在
    order_no = task_tb.taskId
    strCondition = string.format("S_NO = '%s'", order_no)
    nRet, id, strRetInfo = mobox.getDataObjAttrByKeyAttr(strLuaDEID, "Outbound_Order", strCondition)
    -- lua.Debug(strLuaDEID, debug.getinfo(1), "查询出来的出库单数据:", strRetInfo)
    if nRet > 1 then
        result.flag = "failure"
        result.code = "3"
        result.message = "检查出库单是否存在时出错: " .. strRetInfo
        goto api_call_return
    elseif nRet == 0 then
        result.flag = "failure"
        result.code = "4"
        result.message = "出库单已存在 "
        goto api_call_return
    end
    -- 5. 创建出库单主表
    nRet, strRetInfo = create_outbound_order(strLuaDEID, task_tb)
    if nRet ~= 0 then
        -- lua.Stop(strLuaDEID, "创建出库单主表失败:" .. strRetInfo)
        result.flag = "failure"
        result.code = "5"
        result.message = "创建出库单主表失败:" .. strRetInfo
        isStop = 3
        goto api_call_return
        -- return
    end
    -- 6. 处理明细数据
    task_items = task_tb.TaskCreation_TB_ITEM
    -- 情况2:多条明细(数组形式)
    if task_items[1] ~= nil then
        items = task_items
        -- 情况3:单条明细(非数组形式)
    else
        items = {task_items}
    end
    for i, item in ipairs(items) do
        nRet, strRetInfo = create_outbound_detail(strLuaDEID, order_no, item, task_tb)
        if nRet ~= 0 then
            err_msg = string.format("创建出库单明细失败(行号:%d): %s", item.orderItemId, strRetInfo)
            result.flag = "failure"
            result.code = "5"
            result.message = err_msg
            isStop = 3
            goto api_call_return
        end
    end
    ::api_call_return::
    -- 7. 返回处理结果
    -- local result = {
    -- flag = nErr > 0 and "failure" or "success",
    -- code = nErr,
    -- message = nErr > 0 and table.concat(err, "; ") or "成功",
    -- }
    local xml_result = xml.json_to_xml(result, "response")
    mobox.returnValue(strLuaDEID, 0, xml_result, isStop)
    -- m3.EPI_Return(strLuaDEID, result)
    -- ApiReturn(strLuaDEID, 0, result, isStop)
end
lua_code/Lua/GK-API-07-Order_Cancel.lua
File was renamed from lua_code/Lua/GK-API-07.lua
@@ -1,25 +1,17 @@
---
--- Created by wsz.
--- DateTime: 2025/5/15 下午3:31
---
--[[
 编码: GK-API-07
 名称: 订单取消接口
 作者: wsz
 入口函数:Order_Sync
 功能说明: 接收报文创建 Order_Hold 实体  ,对于已存在的数据无条件更新,返回报无需创建还货单 ;新增的数据设置需要创建还货单
 功能说明: 接收报文创建 Order_Hold 实体,对于已存在的数据无条件更新,返回报无需创建还货单 ;新增的数据设置需要创建还货单
 变更历史:v0.0.1
注意:
    1、按照此报文形式,这应是单条发送
    2、报文解析项少了来源单号、拦截类型、:存储时 来源单号不填,拦截类型填Interface
    3、needReturnOrder 仅 根据出库单的状态会涉及到
   订单类型: asn 入库订单 so出库订单 inventory 盘点单
   三者单据的主键no-与报文的taskId 对应
   入库单和盘点单已执行不允许取消,出库单订单未完成都允许取消,允许取消才插入Order_Hold
-----------------------------------------
入库单 Inbound_Order   no | b_state
lua_code/Lua/GK-API-08-Outbound_Priority_Chg.lua
New file
@@ -0,0 +1,334 @@
--- Created by w1832.
--- DateTime: 2025/5/20 上午8:57
---
--[[
 编码: GK-API-08
 名称: Outbound_Priority_Change
 作者:wsz
 入口函数:Outbound_Priority_Change
 功能说明:更新发货单 Shipping_Order 优先级,已分配后不允许更新
 变更历史:
v0.0.1 初始
v0.0.2  CLSID 为出库单 Outbound_Order,非Shipping_Order发货单
 注意:
 1、此报文结构应是单条推送
 2、S_BS_NO查到的数据不一定唯一,同S_BS_NO数据的N_B_STATE状态也不一定全部满足要求,仅处理满足条件的数据
v0.0.3 修改映射字段 S_BS_NO -> S_NO
v0.0.4 重回映射字段 S_NO -> S_BS_NO 现逻辑保持在v0.0.2为准
 --]]
 json = require("json")
 mobox = require("OILua_JavelinExt")
 m3 = require("oi_base_mobox")
 xml = require("oi_base_xml")
--- 实体标识-出库订单优先级调整接口
local CLSID_OutboundOrder = "Outbound_Order"
-- 表名
local TB_NAME_OutboundOrder = "TN_Outbound_Order"
local CLSID_OutboundOrder_desc = "出库订单优先级调整接口"
local luaDEID
--根标签
local RootTag =  "response"
-- 简化 debug.info记录
local function DebugInfo(desc, param)
    if param == nil then
        param = ""
    end
    lua.Debug(luaDEID, debug.getinfo(1), desc, param)
end
--[[
-- 执行成功
]]
local function result_success(strLuaDEID)
    local result = {}
    result.flag = "success" -- success|failure
    result.code = 0
    result.message = ""
    do
        local nRet = mobox.returnValue(strLuaDEID, 1, xml.json_to_xml(result,RootTag), result.code)
        if nRet ~= 0 then
            lua.Error(strLuaDEID, debug.getinfo(1), 'result_success-执行mobox.returnValue失败 ' .. nRet)
        end
    end
end
--[[
-- 事务回滚-返回信息后终止执行
]]
local function result_transaction_back(strLuaDEID, msg)
    -- 回滚当次处理
    lua.Stop(strLuaDEID, msg)
    --
    --local result = {}
    --result.flag = "failure" -- success|failure
    --result.code = 5
    --result.message = msg
    --
    --local xmlstr = xml.json_to_xml(result)
    --
    --do
    --    local nRet = mobox.returnValue(strLuaDEID, 1, xmlstr, result.code)
    --    if nRet ~= 0 then
    --        lua.Error(strLuaDEID, debug.getinfo(1), 'result_transaction_back-执行mobox.returnValue失败 ' .. nRet)
    --    end
    --end
    error(msg, 0)
    -- lua.Error(strLuaDEID, debug.getinfo(1), xmlstr)
end
--[[
param
    CLSID:目标实例calssid
    id目标数据的S_ID
    temp:待更新字段对象,lua属性table
--]]
local function butchUpdateData(strLuaDEID,CLSID,id,temp)
    local nRet, str_data_attrset = mobox.luaJsonToObjAttrs(CLSID_OutboundOrder, json.encode(temp))
    if nRet ~= 0 then
        result_transaction_back(strLuaDEID, "luaJsonToObjAttrs 函数 转化格式失败" .. nRet .. str_data_attrset)
    end
    -- 反序列化为目标格式
    local attrValueObj = json.decode(str_data_attrset)
    -- 组装批量更新的数据格式
    local updateObj = {}
    local updateObj_item = {}
    updateObj_item.id = id
    updateObj_item.attrs = attrValueObj
    table.insert(updateObj, updateObj_item)
    local updateStrDataJson = lua.table2str(updateObj)
    lua.Debug(strLuaDEID, debug.getinfo(1), CLSID .. "update准备覆盖已有数据", updateStrDataJson)
    local nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, CLSID, updateStrDataJson)
    if (nRet ~= 0) then
        result_transaction_back(strLuaDEID, string.format(CLSID.."更新操作失败!code:%s,msg:%s", nRet, strRetInfo))
    end
end
--[[
业务数据处理
1、查询  orderNo:S_BS_NO 记录  来源单号非主键,可能存在多条数据
2.判断 N_B_STATE 状态  更新出库单 Outbound_Order 优先级,已分配后不允许更新(N_B_STATE>=25 AND N_B_STATE<=55)
]]
local function bussHandle(strLuaDEID, dataSet)
    local strCondition
    do
        local filters = {}
        table.insert(filters, string.format([[ %s = '%s' ]], "S_BS_NO", lua.Get_StrAttrValue(dataSet.orderNo)))
        strCondition = table.concat(filters, " and ")
        lua.Debug(strLuaDEID, debug.getinfo(1), "filters-组装的Shipping_Order的where条件", strCondition)
    end
    -- 查找总数量
    local nRetCount
    do
        local nRet, nCount = mobox.getDataObjCount(strLuaDEID, CLSID_OutboundOrder, strCondition)
        if nRet == 0 then
            nRetCount = nCount
            DebugInfo(string.format("%s-查询总数量为%s", CLSID_OutboundOrder, nCount))
        else
            result_transaction_back(strLuaDEID, string.format("查询目标数据失败,code:%s,msg:%s", nRet, nCount))
        end
        if nCount == "0" then
            result_transaction_back(strLuaDEID, dataSet.orderNo .. "查找不到报文对应的出库单数据!")
        end
    end
    -- 查询目标数据, 存储lua数组,内部为数据库字段格式对象
    local datas = {}
    do
        local strFieldList = { "S_ID", "S_NO", "S_BS_NO", "N_PRIORITY", "S_NOTE", "N_PRIORITY" }
        -- 返回 json型 str,格式 [ { "attr1": "xxx", "attr2": "xxx1", … }, …
        local nRet, strRetInfo = mobox.queryTable(strLuaDEID, TB_NAME_OutboundOrder, json.encode(strFieldList), nRetCount, strCondition)
        if nRet == 0 then
            DebugInfo(string.format("%s-queryTable函数返回", CLSID_OutboundOrder), strRetInfo)
            local tableData = json.decode(strRetInfo)
            DebugInfo("tableData", tableData)
            datas = tableData
            --for n = 1, #tableData do
            --    -- local nRet1, strObjJson = mobox.objAttrToObjJson(CLSID_ShippingOrder, tableData[n])
            --    -- local nRet1, strObjJson = mobox.objJsonToLuaJson(CLSID_ShippingOrder, tableData[n])
            --
            --    if nRet1 == 0 then
            --        table.insert(datas, strObjJson)
            --    else
            --        result_transaction_back(strLuaDEID, string.format("queryTable-objAttrToObjJson转化失败,code:%s,msg:%s", nRet1, strObjJson))
            --    end
            --end
        else
            result_transaction_back(strLuaDEID, string.format("queryTable查询目标数据失败,code:%s,msg:%s", nRet, strRetInfo))
        end
    end
    DebugInfo(string.format("%s-queryTable-取得datas数据", CLSID_OutboundOrder), datas)
    -- 迭代更新满足条件的数据
    for n = 1, #datas do
        local b_state = lua.Get_NumAttrValue(datas[n].N_B_STATE)
        local id = datas[n].S_ID
        local flag = b_state >= 25 and b_state <= 55
        DebugInfo("b_state", b_state)
        DebugInfo("id", id)
        DebugInfo("flag", flag)
        if not flag then
            -- 执行更新
            local temp = {}
            temp.priority = dataSet.priority --优先级
            temp.note = dataSet.memo -- 备注
            butchUpdateData(strLuaDEID, CLSID_OutboundOrder,id,temp)
            --local nRet, str_data_attrset = mobox.luaJsonToObjAttrs(CLSID_ShippingOrder, json.encode(temp))
            --if nRet ~= 0 then
            --    result_transaction_back(strLuaDEID, "luaJsonToObjAttrs 函数 转化格式失败" .. nRet .. str_data_attrset)
            --end
            ---- 反序列化为目标格式
            --local attrValueObj = json.decode(str_data_attrset)
            --
            ---- 组装批量更新的数据格式
            --local updateObj = {}
            --
            --local updateObj_item = {}
            --updateObj_item.id = id
            --updateObj_item.attrs = attrValueObj
            --table.insert(updateObj, updateObj_item)
            --
            --local updateStrDataJson = lua.table2str(updateObj)
            --lua.Debug(strLuaDEID, debug.getinfo(1), CLSID_ShippingOrder .. "update准备覆盖已有数据", updateStrDataJson)
            --local nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, CLSID_ShippingOrder, updateStrDataJson)
            --if (nRet ~= 0) then
            --    result_transaction_back(strLuaDEID, string.format("发货单更新操作失败!code:%s,msg:%s", nRet, strRetInfo))
            --end
        end
    end
end
local function Outbound_Priority_Change_main(strLuaDEID)
    -- 1.1 getxml
    local soap_xml
    do
        local nRet, data = mobox.getCurEditDataPacket(strLuaDEID)
        if (nRet ~= 0) then
            result_transaction_back(strLuaDEID, "无法获取数据包 datajson !" .. data)
        end
        soap_xml = data
        lua.Debug(strLuaDEID, debug.getinfo(1), "GK-API-08xml报文", soap_xml)
    end
    -- 1.2 xml->luaobj
    local parsed_data
    do
        local nRet, data = xml.parse(soap_xml)
        if (nRet ~= 0) then
            result_transaction_back(strLuaDEID, "接口输入的XML格式非法!")
        end
        parsed_data = data
    end
    -- 1.3 取得商品批次表item的tableObj
    local dataSet = parsed_data["Envelope"]["Body"]
    ["OrderAdjustmentsReq"]
    ["OrderAdjustments_Input"]
    ["InputParameters"]
    ["OrderAdjustments_TB"]
    if nil == dataSet then
        -- wms_base.Warning(strLuaDEID, 2, 201, CLSID_OutboundOrder_desc .. "-未解析到soap目标节点!", json.encode(dataSet), "", CLSID_OutboundOrder_desc .. "-dataSet")
        result_transaction_back(strLuaDEID, CLSID_OutboundOrder_desc .. "-未解析到soap目标节点!")
    elseif #dataSet == 0 then
        -- 仅单条数据
        lua.Debug(strLuaDEID, debug.getinfo(1), "单条", dataSet)
        bussHandle(strLuaDEID, dataSet)
    else
        for i = 1, #dataSet do
            lua.Debug(strLuaDEID, debug.getinfo(1), "多条", "")
            bussHandle(strLuaDEID, dataSet[i])
        end
    end
    result_success(strLuaDEID)
end
--[[
固定-错误捕获处理
]]
local ERR
local function errorHandler(err)
    ERR = err
    lua.Debug(luaDEID, debug.getinfo(1), "err-记录", err)
    return err
end
--[[ 入口函数 ]]
function Outbound_Priority_Change(strLuaDEID)
    m3.PrintLuaDEInfo(strLuaDEID)
    luaDEID = strLuaDEID
    --Outbound_Priority_Change_main(strLuaDEID)
    local success, result = xpcall(Outbound_Priority_Change_main, errorHandler, strLuaDEID)
    if not success then
        local result = {}
        result.flag = "failure" -- success|failure
        result.code = 5
        result.message = ERR
        local xmlstr = xml.json_to_xml(result,RootTag)
        do
            local nRet = mobox.returnValue(strLuaDEID, 1, xmlstr, result.code)
            if nRet ~= 0 then
                lua.Error(strLuaDEID, debug.getinfo(1), 'result_transaction_back-执行mobox.returnValue失败 ' .. nRet)
            end
        end
        -- lua.Error(strLuaDEID, debug.getinfo(1), ERR)
    end
end
lua_code/Lua/GK-API-09-Count_Pan_Sync.lua
New file
@@ -0,0 +1,297 @@
--[[
 编码: GK-API-09
 名称: 盘点计划同步接口
 作者: 袁峰
 入口函数:Count_Pan_Sync
 功能说明: 该接口是同步接口,
    上游系统调用该接口后,WES的响应报文success说明WES已经将该报文接收成功;
    需要同步盘点任务表和盘点任务明细表xml结构。
 变更历史:
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:v1="http://www.gkht.com/Inventory/INV/Ebs/Schemas/InventoryTasks/V1.0">
    <soapenv:Header/>
    <soapenv:Body>
        <v1:InventoryTasksReq>
            <v1:InventoryTasks_Input>
                <v1:RESTHeader>
                    <v1:NLSLanguage>SIMPLIFIED CHINESE</v1:NLSLanguage>
                    <v1:Org_Id>0</v1:Org_Id>
                </v1:RESTHeader>
                <v1:InputParameters>
                    <!-- 第一个盘点任务 -->
                    <v1:InventoryTasks_TB>
                        <v1:taskId>TASK30000100</v1:taskId>
                        <v1:inventoryMode>01</v1:inventoryMode>
                        <v1:workMode>Manual</v1:workMode>
                        <v1:MaintenanceNumber>10</v1:MaintenanceNumber>
                        <v1:inventoryType>YH</v1:inventoryType>
                        <v1:orderNo>YH100001</v1:orderNo>
                        <v1:storerId>CGKHTY</v1:storerId>
                        <v1:ownerId>CGKHTY</v1:ownerId>
                        <v1:mtBeginDate>2025-05-06</v1:mtBeginDate>
                        <v1:mtEndDate>2025-05-06</v1:mtEndDate>
                        <!-- 第一个任务的明细数据 -->
                        <v1:InventoryTasks_TB_ITEM>
                            <v1:orderItemId>100</v1:orderItemId>
                            <v1:skuId>XR33201-2L080B</v1:skuId>
                            <v1:qty>2</v1:qty>
                            <v1:batchNo>PHI00000000000001309</v1:batchNo>
                        </v1:InventoryTasks_TB_ITEM>
                    </v1:InventoryTasks_TB>
                    <!-- 第二个盘点任务 -->
                    <v1:InventoryTasks_TB>
                        <v1:taskId>TASK300000101</v1:taskId>
                        <v1:inventoryMode>02</v1:inventoryMode>
                        <v1:workMode>Auto</v1:workMode>
                        <v1:MaintenanceNumber>5</v1:MaintenanceNumber>
                        <v1:inventoryType>PD</v1:inventoryType>
                        <v1:orderNo>PD200001</v1:orderNo>
                        <v1:storerId>GKHT</v1:storerId>
                        <v1:ownerId>GKHT</v1:ownerId>
                        <v1:mtBeginDate>2025-05-07</v1:mtBeginDate>
                        <v1:mtEndDate>2025-05-07</v1:mtEndDate>
                        <!-- 第二个任务的明细数据 -->
                        <v1:InventoryTasks_TB_ITEM>
                            <v1:orderItemId>110</v1:orderItemId>
                            <v1:skuId>ABC123</v1:skuId>
                            <v1:qty>10</v1:qty>
                            <v1:batchNo>BATCH2025001</v1:batchNo>
                        </v1:InventoryTasks_TB_ITEM>
                        <v1:InventoryTasks_TB_ITEM>
                            <v1:orderItemId>111</v1:orderItemId>
                            <v1:skuId>XYZ456</v1:skuId>
                            <v1:qty>3</v1:qty>
                            <v1:batchNo>BATCH2025002</v1:batchNo>
                        </v1:InventoryTasks_TB_ITEM>
                    </v1:InventoryTasks_TB>
                </v1:InputParameters>
            </v1:InventoryTasks_Input>
        </v1:InventoryTasksReq>
    </soapenv:Body>
</soapenv:Envelope>
响应示例:
<response>
    <flag>success</flag>
    <code>0</code>
    <message>成功</message>
</response>
]] --
wms_base = require("wms_base")
xml = require("oi_base_xml")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
wms = require("OILua_WMS")
local filedMapping = {
    ["货品盘点"] = 1,
    ["容器盘点"] = 2,
    ["货位盘点"] = 3,
    ["OVERALL全盘"] = 4,
    ["MT动碰盘点"] = 5,
    ["SPECIAL抽盘"] = 6,
    ["YH"] = 7
}
-- 创建统一返回结果(调整为3个参数)
function Create_result(flag, code, msg)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or ""
    }
end
function GK_Count_Pan_Sync(strLuaDEID)
    -- 初始化最终结果
    local FinalRes = Create_result()
    -- 1. 获取 xml 数据包
    local nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "201", "无法获取数据包: " .. soap_xml)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取数据包失败", soap_xml)
        return
    end
    lua.DebugEx(strLuaDEID, "获取到的数据包", soap_xml)
    -- 2. 解析 xml
    local nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
        FinalRes = Create_result("failure", "202", "xml 格式非法: " .. parsed_data)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml格式非法", parsed_data)
        return
    end
    -- 3. 提取主表数据
    local receipt_data = parsed_data.Envelope.Body.InventoryTasksReq.InventoryTasks_Input
    local input_params = receipt_data.InputParameters
    -- 检查是否存在 InventoryTasks_TB
    if not input_params or not input_params.InventoryTasks_TB then
        FinalRes = Create_result("failure", "203", "xml 数据格式错误,缺少 InventoryTasks_TB")
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "xml 数据格式错误", xml_result)
        return
    end
    -- 4. 统一处理:确保 mainTables 是数组(即使只有一个主表)
    local mainTables = input_params.InventoryTasks_TB
    if mainTables[1] == nil then
        mainTables = {mainTables}
    end
    -- 5. 获取系统常量
    local RetWH_COE, CONST_WH = wms_base.Get_sConst2("WMS_Default_Factory")
    if RetWH_COE ~= 0 then
        FinalRes = Create_result("failure", "204", "获取仓库常量失败: " .. CONST_WH)
        local xml_result = xml.json_to_xml(FinalRes, "response")
        mobox.returnValue(strLuaDEID, 0, xml_result, 0)
        lua.Stop(strLuaDEID, "获取系统常量失败", xml_result)
        return
    end
    -- 6. 遍历所有盘点任务
    for i = 1, #mainTables do
        local mainData = mainTables[i]
        -- 检查盘点任务是否已存在
        local strCondition = string.format("S_CP_NO = '%s'", mainData.taskId)
        lua.DebugEx(strLuaDEID, "查询盘点任务条件", strCondition)
        local nRet, retCountPlan = m3.GetDataObjByCondition(strLuaDEID, "Count_Plan", strCondition)
        lua.DebugEx(strLuaDEID, "查询结果", retCountPlan)
        if nRet == 0 then
            -- 查询成功且找到记录,说明盘点任务已存在
            FinalRes = Create_result("failure", "1", "盘点任务[" .. mainData.taskId .. "]已存在")
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "盘点任务已存在", retCountPlan)
            return
        elseif nRet ~= 1 then
            -- 查询出错
            FinalRes = Create_result("failure", "206", "检查盘点任务[" .. mainData.taskId ..
                "]是否存在时出错: " .. retCountPlan)
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "检查盘点任务是否存在时出错", xml_result)
            return
        end
        -- 创建主表数据
        local count_plan = m3.AllocObject(strLuaDEID, "Count_Plan")
        count_plan.cp_no = mainData.taskId
        count_plan.inventory_mode = mainData.inventoryMode
        count_plan.work_mode = mainData.workMode
        count_plan.count_limit = mainData.MaintenanceNumber
        -- 根据inventoryType设置type和type_desc
        local panTypeValue = mainData.inventoryType or ""
        local foundType = false
        local typeValue = 0
        -- 遍历字典查找匹配项
        for k, v in pairs(filedMapping) do
            if string.find(panTypeValue, k) then
                typeValue = v
                foundType = true
                break
            end
        end
        -- 如果没有找到匹配项,使用默认值
        if not foundType then
            -- typeValue = 0
            -- typeDesc = "未知类型"
            -- lua.DebugEx(strLuaDEID, "警告:未找到匹配的盘点类型", panTypeValue)
            FinalRes = Create_result("failure", "1", "盘点类型[" .. panTypeValue .. "]不存在")
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "盘点类型不存在", panTypeValue)
        end
        count_plan.type = typeValue
        lua.DebugEx(strLuaDEID, "容器类型数值:", count_plan.type)
        count_plan.bs_no = mainData.orderNo
        -- count_plan.storer = mainData.storerId
        -- count_plan.owner = mainData.ownerId
        count_plan.begin_time = mainData.mtBeginDate
        count_plan.end_time = mainData.mtEndDate
        count_plan.wh_code = CONST_WH
        count_plan.plan_total = 0
        count_plan.b_state = 0
        count_plan.acc_finish = 0
        lua.DebugEx(strLuaDEID, "获取创建数据:", count_plan)
        -- 检查是否已存在相同关键字的记录
        local nRet, ret_info = m3.CreateDataObj(strLuaDEID, count_plan)
        lua.DebugEx(strLuaDEID, "创建结果", ret_info)
        if nRet ~= 0 then
            -- 再次检查是否真的存在
            local nRetCheck, retCountPlanCheck = m3.GetDataObjByCondition(strLuaDEID, "Count_Plan", strCondition)
            lua.DebugEx(strLuaDEID, "再次查询结果", retCountPlanCheck)
            if nRetCheck == 0 then
                FinalRes = Create_result("failure", "205", "盘点任务[" .. mainData.taskId .. "]已存在")
            else
                FinalRes = Create_result("failure", "207",
                    "盘点任务[" .. mainData.taskId .. "]创建失败: " .. ret_info)
            end
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "创建盘点任务主表失败", ret_info)
            return
        end
        -- 检查明细数据是否存在
        if mainData.InventoryTasks_TB_ITEM == nil then
            lua.DebugEx(strLuaDEID, "警告:盘点任务 " .. mainData.taskId .. " 无明细数据")
        else
            -- 7. 遍历当前盘点任务的所有明细数据
            local details = mainData.InventoryTasks_TB_ITEM
            -- 确保 details 是数组(即使只有一个明细)
            if details[1] == nil then
                details = {details}
            end
            for j = 1, #details do
                local item = details[j]
                -- 创建明细数据
                local count_plan_item = m3.AllocObject(strLuaDEID, "Count_Plan_Detail")
                count_plan_item.cp_no = mainData.taskId
                count_plan_item.row_no = item.orderItemId
                count_plan_item.qty = lua.Get_NumAttrValue(item.qty) or 0
                count_plan_item.wms_bn = item.batchNo
                count_plan_item.batch_no = item.produceCode or ""
                count_plan_item.prd_date = item.productDate or ""
                count_plan_item.exp_date = item.expiryDate or ""
                count_plan_item.reg_no = item.registerNo or ""
                count_plan_item.storer = mainData.storerId
                count_plan_item.owner = mainData.ownerId
                local nRet, ret_info = m3.CreateDataObj(strLuaDEID, count_plan_item)
                if nRet ~= 0 then
                    FinalRes = Create_result("failure", "208", "创建盘点明细失败: " .. ret_info)
                    local xml_result = xml.json_to_xml(FinalRes, "response")
                    mobox.returnValue(strLuaDEID, 0, xml_result, 0)
                    lua.Stop(strLuaDEID, "创建盘点明细失败", ret_info)
                    return
                end
            end
        end
    end
    -- 8. 返回成功
    FinalRes = Create_result("success", "0", "盘点任务创建成功")
    local xml_result = xml.json_to_xml(FinalRes, "response")
    mobox.returnValue(strLuaDEID, 0, xml_result, 0)
end
lua_code/Lua/GK-API-11-StockQuery.lua
New file
@@ -0,0 +1,251 @@
wms_base = require("wms_base")
xml = require("oi_base_xml")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
-- XML特殊字符转义函数
local function xml_escape(str)
    if not str then
        return ""
    end
    return tostring(str):gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;"):gsub("\"", "&quot;"):gsub("'", "&apos;")
end
-- 硬编码XML生成器
local function generate_raw_xml(result)
    local xml = [[<?xml version="1.0" encoding="UTF-8"?>
<response>
    <flag>]] .. xml_escape(result.flag) .. [[</flag>
    <code>]] .. xml_escape(result.code) .. [[</code>
    <message>]] .. xml_escape(result.message) .. [[</message>]]
    if result.data and #result.data > 0 then
        xml = xml .. [[
    <data>]]
        for _, item in ipairs(result.data) do
            xml = xml .. [[
        <item>
            <productLine>]] .. xml_escape(item.productLine) .. [[</productLine>
            <storerId>]] .. xml_escape(item.storerId) .. [[</storerId>
            <cid>]] .. xml_escape(item.cid) .. [[</cid>
            <ownerId>]] .. xml_escape(item.ownerId) .. [[</ownerId>
            <skuId>]] .. xml_escape(item.skuId) .. [[</skuId>
            <boxCode>]] .. xml_escape(item.boxCode) .. [[</boxCode>
            <qty>]] .. xml_escape(item.qty) .. [[</qty>
            <registerNo>]] .. xml_escape(item.registerNo) .. [[</registerNo>
            <skuStatus>]] .. xml_escape(item.skuStatus) .. [[</skuStatus>
            <expiryDate>]] .. xml_escape(item.expiryDate) .. [[</expiryDate>
            <batchNo>]] .. xml_escape(item.batchNo) .. [[</batchNo>
            <productDate>]] .. xml_escape(item.productDate) .. [[</productDate>
            <produceCode>]] .. xml_escape(item.produceCode) .. [[</produceCode>
            <locationId>]] .. xml_escape(item.locationId) .. [[</locationId>
        </item>]]
        end
        xml = xml .. [[
    </data>]]
    else
        xml = xml .. [[
    <data/>]]
    end
    xml = xml .. [[
</response>]]
    return xml
end
-- 创建统一返回结果
local function Create_result(flag, code, msg, data)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or "",
        data = data or {}
    }
end
-- 深度遍历查找XML节点(最稳定的方法)
local function deep_find_xml_node(data, node_names)
    if type(data) ~= "table" then
        return nil
    end
    -- 先尝试直接路径查找
    local current = data
    for _, name in ipairs(node_names) do
        if type(current) ~= "table" then
            break
        end
        -- 尝试带v1命名空间和不带命名空间的节点
        current = current["v1:" .. name] or current[name]
        if not current then
            break
        end
    end
    if current then
        return current
    end
    -- 深度遍历整个XML树查找
    local function deep_search(t, names, index)
        if index > #names then
            return t
        end
        local current_name = names[index]
        for k, v in pairs(t) do
            if (k == current_name or k == "v1:" .. current_name) and type(v) == "table" then
                local result = deep_search(v, names, index + 1)
                if result then
                    return result
                end
            elseif type(v) == "table" then
                local result = deep_search(v, names, index)
                if result then
                    return result
                end
            end
        end
        return nil
    end
    return deep_search(data, node_names, 1)
end
-- 查询库存明细
local function query_inventory_detail(strLuaDEID, query_params)
    local strCondition = string.format(" S_STORER = '%s' AND S_OWNER = '%s' AND S_ITEM_CODE = '%s'",
        query_params.storerId, query_params.ownerId, query_params.skuId)
    if query_params.locationId and query_params.locationId ~= "" then
        strCondition = strCondition .. string.format(" AND S_LOC_CODE = '%s'", query_params.locationId)
    end
    if query_params.produceCode and query_params.produceCode ~= "" then
        strCondition = strCondition .. string.format(" AND S_BATCH_NO = '%s'", query_params.produceCode)
    end
    local nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition)
    if nRet ~= 0 then
        return nRet, data_objects
    end
    local formatted_result = {}
    for n = 1, #data_objects do
        local obj_attrs = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
        local stock = {
            storerId = obj_attrs.S_STORER or "",
            ownerId = obj_attrs.S_OWNER or "",
            skuId = obj_attrs.S_ITEM_CODE or "",
            skuStatus = obj_attrs.S_ITEM_STATE or "",
            qty = lua.Get_NumAttrValue(obj_attrs.F_QTY) or 0,
            locationId = obj_attrs.S_LOC_CODE or "",
            cid = obj_attrs.S_CNTR_CODE or "",
            boxCode = obj_attrs.S_CELL_NO or "",
            batchNo = obj_attrs.S_WMS_BN or "",
            produceCode = obj_attrs.S_BATCH_NO or "",
            productDate = obj_attrs.D_PRD_DATE or "",
            expiryDate = obj_attrs.D_EXP_DATE or "",
            productLine = obj_attrs.S_UDF01 or "",
            registerNo = obj_attrs.S_PROD_LINE or ""
        }
        table.insert(formatted_result, stock)
    end
    return 0, formatted_result
end
-- Main函数
function Stock_Query(strLuaDEID)
    m3.PrintLuaDEInfo(strLuaDEID)
    local FinalRes = Create_result()
    local xml_result = ""
    -- 使用pcall捕获所有可能的错误
    local status, err = pcall(function()
        -- 1. 获取xml数据包
        local nRet, soap_xml = mobox.getCurEditDataPacket(strLuaDEID)
        if nRet ~= 0 then
            FinalRes = Create_result("failure", "201", "无法获取数据包: " .. tostring(soap_xml))
            error(FinalRes.message)
        end
        -- 2. 解析xml
        local nRet, parsed_data = xml.parse(soap_xml)
        if nRet ~= 0 then
            FinalRes = Create_result("failure", "202", "xml格式非法")
            error(FinalRes.message)
        end
        -- 3. 使用深度遍历查找关键节点
        local inventory_input = deep_find_xml_node(parsed_data, {"Envelope", "Body", "InventoryReq", "Inventory_Input"})
        if not inventory_input then
            FinalRes = Create_result("failure", "206", "XML结构错误: 缺少Inventory_Input节点")
            error(FinalRes.message)
        end
        local input_params = deep_find_xml_node(inventory_input, {"InputParameters"})
        if not input_params then
            FinalRes = Create_result("failure", "207", "XML结构错误: 缺少InputParameters节点")
            error(FinalRes.message)
        end
        local inventory_tb = deep_find_xml_node(input_params, {"Inventory_TB"})
        if not inventory_tb then
            FinalRes = Create_result("failure", "208", "XML结构错误: 缺少Inventory_TB节点")
            error(FinalRes.message)
        end
        -- 4. 提取查询参数
        local query_params = {
            storerId = deep_find_xml_node(inventory_tb, {"storerId"}) or "",
            ownerId = deep_find_xml_node(inventory_tb, {"ownerId"}) or "",
            skuId = deep_find_xml_node(inventory_tb, {"skuId"}) or "",
            productLine = deep_find_xml_node(inventory_tb, {"productLine"}) or "",
            locationId = deep_find_xml_node(inventory_tb, {"locationId"}) or "",
            produceCode = deep_find_xml_node(inventory_tb, {"produceCode"}) or ""
        }
        -- 5. 参数校验
        if query_params.storerId == "" then
            FinalRes = Create_result("failure", "203", "storerId不能为空")
            error(FinalRes.message)
        end
        if query_params.skuId == "" then
            FinalRes = Create_result("failure", "204", "skuId不能为空")
            error(FinalRes.message)
        end
        -- 6. 查询库存明细
        local nRet, query_result = query_inventory_detail(strLuaDEID, query_params)
        if nRet ~= 0 then
            FinalRes = Create_result("failure", "205", "查询库存明细失败: " .. tostring(query_result))
            error(FinalRes.message)
        end
        -- 7. 处理查询结果
        if not query_result or #query_result == 0 then
            FinalRes = Create_result("success", "0", "未查询到符合条件的库存记录", {})
        else
            FinalRes = Create_result("success", "0", "查询成功!", query_result)
        end
    end)
    -- 处理错误情况
    if not status then
        if FinalRes.code == "0" then
            FinalRes = Create_result("failure", "299", "系统内部错误: " .. tostring(err))
        end
    end
    -- 生成XML结果
    xml_result = generate_raw_xml(FinalRes)
    -- 返回结果
    mobox.returnValue(strLuaDEID, 0, xml_result, 0)
    -- 记录错误日志
    if not status then
        lua.Stop(strLuaDEID, "处理过程中发生错误", FinalRes)
    end
end