fy36
2025-06-24 a3f50e5bd0d5ecbd0c5992da042fe4292dbb6911
lua_code/Lua/SKU_Sync.lua
@@ -11,104 +11,169 @@
        1. 接收来自上游系统的 XML 格式数据,并解析该数据, 创建SKU及SKU_UPC
    更新:
        2025/06/09 更新了udf01~udf04的属性变更
        <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>KH32803017</v1:skuId>
                                    <v1:storerId>CGKHTY</v1:storerId>
                                    <v1:skuName>螺旋刀片式髓内钉</v1:skuName>
                                    <v1:skuDec>螺旋刀片式股骨近端髓内钉10×170</v1:skuDec>
                                    <v1:spec>JGDⅥ φ10×170</v1:spec>
                                    <v1:packageCode>个</v1:packageCode>
                                    <v1:packageQty>1</v1:packageQty>
                                    <v1:goodsUnit>件</v1:goodsUnit>
                                    <v1:length>1</v1:length>
                                    <v1:width>2</v1:width>
                                    <v1:height>3</v1:height>
                                    <v1:abcType></v1:abcType>
                                    <v1:isBatchMgr>1</v1:isBatchMgr>
                                    <v1:isSnMgr>1</v1:isSnMgr>
                                    <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                                    <v1:imgUrl></v1:imgUrl>
                                    <v1:cidtype>A</v1:cidtype>
                                    <v1:productLine>KH-创伤</v1:productLine>
                                    <v1:storageConditions>常温</v1:storageConditions>
                                    <v1:skuType>small</v1:skuType>
                                    <v1:maxCount>50</v1:maxCount>
                                    <v1:sptm></v1:sptm>
                                    <v1:barcode1></v1:barcode1>
                                    <v1:barcode2></v1:barcode2>
                                    <v1:barcode3></v1:barcode3>
                                    <v1:barcode_pk></v1:barcode_pk>
                                </v1:COMMODITY_TB_ITEM>
                            </v1:COMMODITY_TB>
                        </v1:InputParameters>
                    </v1:COMMODITY_Input>
                </v1:inCommodityReq>
            </soapenv:Body>
        </soapenv:Envelope>
    更改记录:
       V2.0 HAN 20250402  代码规范
       V2.1 Yuanfeng
       1. 统一了返回结果格式,使用Create_result函数创建标准化的返回结构
       2. 优化了代码结构
--]] --[[
    编码: GK-API-001
    名称: 盘点计划同步
    作者: HAN
    日期: 2025-1-29
    入口函数: SKU_Sync
    来源项目:  国科项目
    功能说明:
        1. 接收来自上游系统的 XML 格式数据,并解析该数据, 创建SKU及SKU_UPC
    更改记录:
       V2.0 HAN 20250402  代码规范       
       V2.1 Yuanfeng 优化返回结果处理
       V2.2 优化错误处理和返回逻辑
       ]] wms_base = require("wms_base")
       V2.3 移除error字段,合并到message中
       V2.4 新增SKU_GRID_PARM字段
<?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>XR4121-3204A</v1:skuId>
                            <v1:storerId>CGKHTY</v1:storerId>
                            <v1:skuName>金属解剖型接骨板</v1:skuName>
                            <v1:skuDec>金属解剖型接骨板YSQ20 4孔</v1:skuDec>
                            <v1:spec>规格测试</v1:spec>
                            <v1:packageCode>块</v1:packageCode>
                            <v1:packageQty>1</v1:packageQty>
                            <v1:goodsUnit>块</v1:goodsUnit>
                            <v1:length>3.5</v1:length>
                            <v1:width>3.5</v1:width>
                            <v1:height>3.5</v1:height>
                            <v1:abcType/>
                            <v1:isBatchMgr>0</v1:isBatchMgr>
                            <v1:isSnMgr>0</v1:isSnMgr>
                            <v1:isSnStorageMgr>0</v1:isSnStorageMgr>
                            <v1:imgUrl/>
                            <v1:cidtype>11111A</v1:cidtype>
                            <v1:productLine>XR-创伤</v1:productLine>
                            <v1:storageConditions>恒温</v1:storageConditions>
                            <v1:skuType>small</v1:skuType>
                            <v1:maxCount/>
                            <v1:bMaxCount>10</v1:bMaxCount>
                            <v1:cMaxCount>11</v1:cMaxCount>
                             <v1:dMaxCount>12</v1:dMaxCount>
                            <v1:eMaxCount/>
                            <v1:fMaxCount/>
                            <v1:upc>1</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")
xml = require("oi_base_xml")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
-- 创建统一返回结果
function Create_result(flag, code, msg, error)
-- 创建统一返回结果(修改后版本,移除error字段)
function Create_result(flag, code, msg)
    return {
        flag = flag or "success",
        code = code or "0",
        message = msg or "",
        error = error or ""
        message = msg or ""
    }
end
-- 新增sku_grid_parm JSON字符串,在SKU 创建的时候插入json数据
local function build_sku_grid_parm(ctd_code, sku_input_data)
    -- 定义基础json框架结构(放在数组中)
    local grid_def = {{
        ctd_code = ctd_code,
        cell_def = {}
    }}
    -- 定义料箱类型与对应字段的映射
    local grid_mapping = {{
        cell_type = "A",
        loading_limit = sku_input_data.maxCount
    }, {
        cell_type = "B",
        loading_limit = sku_input_data.bMaxCount
    }, {
        cell_type = "C",
        loading_limit = sku_input_data.cMaxCount
    }, {
        cell_type = "D",
        loading_limit = sku_input_data.dMaxCount
    }, {
        cell_type = "E",
        loading_limit = sku_input_data.eMaxCount
    }, {
        cell_type = "F",
        loading_limit = sku_input_data.fMaxCount
    }}
    -- 构建cell_def数组
    for _, grid in ipairs(grid_mapping) do
        local limit = lua.Get_NumAttrValue(grid.loading_limit) or 0
        table.insert(grid_def[1].cell_def, { -- 注意这里改为grid_def[1]
            cell_type = grid.cell_type,
            loading_limit = limit,
            load_capacity = limit -- 这里假设load_capacity和loading_limit相同
        })
    end
    -- 转换为JSON字符串
    return lua.table2str(grid_def)
end
-- 创建或更新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)
    if nRet > 1 then
        return 1, "检查GK_PROLINE是否存在时失败: " .. strRetInfo
    end
    if nRet == 1 then
        -- 不存在,创建新记录
        nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, proline)
        if nRet ~= 0 then
            return 1, "创建GK_PROLINE失败: " .. strRetInfo
        end
    else
        -- 已存在,更新记录
        local update_proline_obj = {{
            id = id,
            attrs = {{
                attr = "S_PROLINE_NAME",
                value = proline_name
            }}
        }}
        nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "GK_PROLINE", lua.table2str(update_proline_obj))
        if nRet ~= 0 then
            return 1, "更新GK_PROLINE失败: " .. strRetInfo
        end
    end
    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
    end
    -- 创建SKU_UPC
    local sku_upc = m3.AllocObject(strLuaDEID, "SKU_UPC")
    sku_upc.storer = storer
    sku_upc.item_code = item_code
@@ -119,80 +184,105 @@
end
-- 创建/更新SKU对应的货品料箱格参数
-- 输入参数:strLuaDEID - 执行环境ID
--          skuModel - SKU数据模型(包含料格参数)
local function create_or_update_sku_gridbox(strLuaDEID, skuModel)
local function create_or_update_sku_gridbox(strLuaDEID, skuModel, sku_input_data)
    local nRet, strRetInfo
    local err_msg = ''
    -- 获取默认容器类型定义编码
    -- 获取默认容器类型编码
    local nConstRet, CONST_CTD_CODE = wms_base.Get_sConst2("WMS_Default_CNTR_Type")
    if nConstRet ~= 0 then
        return 1, "获取默认容器类型定义编码失败: " .. CONST_CTD_CODE
    end
    -- 检查必填字段
    if not skuModel.item_code or not skuModel.storer then
        return 1, "缺少必要参数: item_code或storer"
    end
    -- 定义料箱格类型与对应字段的映射关系
    local gridbox_types = {{
        cell_type = "A",
        loading_limit = sku_input_data.maxCount
    }, {
        cell_type = "B",
        loading_limit = sku_input_data.bMaxCount
    }, {
        cell_type = "C",
        loading_limit = sku_input_data.cMaxCount
    }, {
        cell_type = "D",
        loading_limit = sku_input_data.dMaxCount
    }, {
        cell_type = "E",
        loading_limit = sku_input_data.eMaxCount
    }, {
        cell_type = "F",
        loading_limit = sku_input_data.fMaxCount
    }}
    -- 设置默认料格类型
    skuModel.ctd_code = CONST_CTD_CODE
    skuModel.cell_type = skuModel.cell_type -- 料格类型
    -- 处理每种料箱格类型
    for _, gridbox_type in ipairs(gridbox_types) do
        -- 设置装载上限,如果没有则设为0
        local loading_limit = 0
        if gridbox_type.loading_limit and gridbox_type.loading_limit ~= "" then
            loading_limit = lua.Get_NumAttrValue(gridbox_type.loading_limit) or 0
        end
    -- 根据商品编码(item_code)和货主(storer)以及容器类型定义编码(ctd_code)关联查询货品料箱格参数
    local strCondition = string.format("S_ITEM_CODE = '%s' AND S_STORER = '%s' AND S_CTD_CODE='%s'", skuModel.item_code,
        skuModel.storer, skuModel.ctd_code)
    lua.DebugEx(strLuaDEID, "查询SKU_GridBox_Parm条件", strCondition)
    local nRet, gridBoxParams = m3.GetDataObjByCondition(strLuaDEID, "SKU_GridBox_Parm", strCondition)
    lua.DebugEx(strLuaDEID, "查询SKU_GridBox_Parm结果", gridBoxParams)
    if nRet == 0 then
        -- 记录已存在,执行更新操作
        local update_gridbox_obj = {{
            id = gridBoxParams.id,
            attrs = {{
                attr = "N_LOADING_LIMIT",
                value = skuModel.loading_limit -- 更新容器装载上限
            }, {
                attr = "S_CELL_TYPE",
                value = skuModel.cell_type -- 更新料格类型
        -- 精确查询该类型的料箱格记录
        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, gridbox_type.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
                }}
            }}
        }}
        lua.DebugEx(strLuaDEID, "准备更新SKU_GridBox_Parm属性", update_gridbox_obj)
        nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "SKU_GridBox_Parm", lua.table2str(update_gridbox_obj))
        lua.DebugEx(strLuaDEID, "更新SKU_GridBox_Parm结果", nRet .. "    返回值" .. strRetInfo)
        if nRet ~= 0 then
            return 1, "更新SKU_GridBox_Parm属性失败: " .. strRetInfo
            nRet, strRetInfo = mobox.updateDataObj(strLuaDEID, "SKU_GridBox_Parm", lua.table2str(update_gridbox_obj))
            if nRet ~= 0 then
                return 1, string.format("更新%s类型料箱格属性失败: %s", gridbox_type.cell_type, strRetInfo)
            end
        else
            -- 创建新记录
            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 = gridbox_type.cell_type
            nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, gridbox)
            if nRet ~= 0 then
                return 1, string.format("创建%s类型料箱格失败: %s", gridbox_type.cell_type, strRetInfo)
            end
        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 = skuModel.ctd_code -- 容器类型定义编码
        gridbox.loading_limit = skuModel.loading_limit -- 设置容器装载上限
        gridbox.cell_type = skuModel.cell_type -- 格类型
        lua.DebugEx(strLuaDEID, "准备创建SKU_GridBox_Parm", gridbox)
        nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, gridbox)
        if nRet ~= 0 then
            return 1, "创建SKU_GridBox_Parm失败: " .. strRetInfo
        end
    else
        -- 查询出错
        return 1, "查询SKU_GridBox_Parm时出错: " .. strRetInfo
    end
    return 0, strRetInfo
    return 0, "料箱格参数处理完成"
end
-- 创建或更新SKU主数据
local function create_sku(strLuaDEID, sku_input_data)
    local nRet, strRetInfo
    local err_msg = ''
    -- 获取系统常量
    -- 获取默认容器类型编码
    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对象
    -- 首先检查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 "未知错误")
    end
    -- 分配SKU对象并设置属性
    local sku = m3.AllocObject(strLuaDEID, "SKU")
    lua.DebugEx(strLuaDEID, "SKU分配对象", sku)
    sku.item_code = sku_input_data.skuId
    sku.storer = sku_input_data.storerId
    sku.short_name = sku_input_data.skuName
@@ -202,41 +292,33 @@
    sku.long = lua.Get_NumAttrValue(sku_input_data.length) or 0
    sku.middle = lua.Get_NumAttrValue(sku_input_data.width) or 0
    sku.short = lua.Get_NumAttrValue(sku_input_data.height) or 0
    sku.count_method = "Limit";
    sku.count_method = "Limit"
    sku.abc_type = sku_input_data.abcType or ""
    sku.is_life_mgt = (sku_input_data.isBatchMgr == "1" and 'Y') or 'N'
    sku.is_sn_mgt = (sku_input_data.isSnMgr == "1" and 'Y') or 'N'
    sku.img_url = sku_input_data.imgUrl or ""
    lua.DebugEx(strLuaDEID, "cell_type", sku_input_data.cidtype)
    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
    --- 2025/6/9 新增/更改字段
    sku.udf01 = sku_input_data.productLine or "";
    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;
    lua.DebugEx(strLuaDEID, "SKU赋值后", sku)
    -- 检查SKU是否已存在
    local id
    local strCondition = string.format("S_ITEM_CODE = '%s' AND S_STORER = '%s'", sku.item_code, sku.storer)
    nRet, id, strRetInfo = mobox.getDataObjAttrByKeyAttr(strLuaDEID, "SKU", strCondition)
    if nRet > 1 then
        return 1, "检查SKU是否存在时失败: " .. strRetInfo
    end
    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)
    lua.DebugEx(strLuaDEID, "更新/创建数据", sku.cell_type)
    if nRet == 1 then
        -- SKU不存在,创建新SKU
        -- SKU不存在,创建新记录
        nRet, strRetInfo = m3.CreateDataObj(strLuaDEID, sku)
        if nRet ~= 0 then
            return 1, "创建SKU失败: " .. strRetInfo .. " skuId = " .. sku_input_data.skuId
        end
    else
        -- SKU已存在,更新属性
        -- SKU已存在,更新记录
        local update_sku_obj = {{
            id = id,
            id = existing_sku.id,
            attrs = {{
                attr = "S_ITEM_NAME",
                value = sku.item_name
@@ -294,16 +376,29 @@
            }, {
                attr = "S_UDF05",
                value = sku.udf05
            }, {
                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)
        if nRet ~= 0 then
            return 1, "更新SKU属性失败: " .. strRetInfo
        end
    end
    -- 处理SKU_UPC
    -- 处理产品线数据
    if sku_input_data.productLine and sku_input_data.productLine ~= "" then
        local proline_no = sku_input_data.productLine
        local proline_name = string.match(proline_no, "KH%-(.+)") or proline_no
        nRet, strRetInfo = create_or_update_proline(strLuaDEID, proline_no, proline_name)
        if nRet ~= 0 then
            return 1, "处理产品线数据失败: " .. strRetInfo
        end
    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}
@@ -316,8 +411,8 @@
        end
    end
    -- 处理SKU_GridBox_Parm
    nRet, strRetInfo = create_or_update_sku_gridbox(strLuaDEID, sku)
    -- 处理SKU_GridBox_Parm料箱格参数
    nRet, strRetInfo = create_or_update_sku_gridbox(strLuaDEID, sku, sku_input_data)
    if nRet ~= 0 then
        return 1, "处理SKU_GridBox_Parm失败: " .. strRetInfo
    end
@@ -340,8 +435,6 @@
        return
    end
    lua.DebugEx(strLuaDEID, "获取到的数据包", soap_xml)
    -- 2. 解析xml
    local nRet, parsed_data = xml.parse(soap_xml)
    if nRet ~= 0 then
@@ -362,7 +455,7 @@
        return
    end
    -- 4. 统一处理:确保sku_items是数组(即使只有一个SKU)
    -- 4. 统一处理:确保sku_items是数组
    local sku_items = sku_data.COMMODITY_TB_ITEM
    if sku_items[1] == nil then
        sku_items = {sku_items}
@@ -371,22 +464,17 @@
    -- 5. 遍历所有SKU数据
    for i = 1, #sku_items do
        local sku_item = sku_items[i]
        -- 创建SKU及其UPC
        local nRet, err_msg = create_sku(strLuaDEID, sku_item)
        if nRet ~= 0 then
            -- 记录警告日志
            wms_base.Warning(strLuaDEID, 1, 601, err_msg, "从GK-WMS系统同步SKU信息")
            -- 设置错误结果并停止处理
            FinalRes = Create_result("failure", "204", "SKU同步失败", err_msg)
            FinalRes = Create_result("failure", "204", "SKU同步失败: " .. err_msg)
            local xml_result = xml.json_to_xml(FinalRes, "response")
            mobox.returnValue(strLuaDEID, 0, xml_result, 0)
            lua.Stop(strLuaDEID, "SKU同步失败: " .. sku_item.skuId, FinalRes)
            return
        end
    end
    -- 6. 返回成功
    FinalRes = Create_result("success", "0", "SKU同步成功")
    local xml_result = xml.json_to_xml(FinalRes, "response")