金蝶对接电商ERP库存数据,实现监听库存变化
金蝶云星空实时库存专题
通过向金蝶库存单据注册Python脚本,用于实时监听库存单据审核/反审核,并且将数据发送到轻易云系统集成平台
。通过集成平台将数据分发到对应的目标系统。
向金蝶的库存单据注册脚本
注意修改脚本中的三个变量参数,MAIN_NAME表描述,MAIN_TABLE主表名,ENTRY_TABLE分录表名。关键库存单据表名参考:
库存变动表单 | 表头 | 表体 |
---|---|---|
采购入库单 | t_STK_InStock | T_STK_INSTOCKENTRY |
采购退料单 | t_PUR_MRB | T_PUR_MRBENTRY |
销售出库单 | T_SAL_OUTSTOCK | T_SAL_OUTSTOCKENTRY |
销售退货单 | T_SAL_RETURNSTOCK | T_SAL_RETURNSTOCKENTRY |
更多表单名与字段可以自行查阅金蝶BOS平台,不在此赘述。
注意正式运行时将python脚本中的 SHOW_DEBUG 设置为 False 当然这不是必需的
注册金蝶列表插件
ListViewBarItemClick.py
import clr clr.AddReference("System") clr.AddReference("System.Web.Extensions") clr.AddReference("Kingdee.BOS.Core") clr.AddReference("Kingdee.BOS") clr.AddReference('Kingdee.BOS.App') clr.AddReference("Kingdee.BOS.DataEntity") clr.AddReference("Kingdee.BOS.ServiceHelper") clr.AddReference("Newtonsoft.Json") import sys from System import * from System.Collections.Generic import * from System.Web.Script.Serialization import * from System.Security.Cryptography import * from System.Text import * from System.Net import * from System.IO import * from System.Threading import * from System.Collections.Generic import Dictionary from Newtonsoft.Json import * from Newtonsoft.Json.Linq import * from Kingdee.BOS.ServiceHelper import * from Kingdee.BOS.Core.DynamicForm import * from Kingdee.BOS.App.Data import * reload(sys) sys.setdefaultencoding('utf-8') # 金蝶Cloud列表插件 # 用于监听审核、反审核事件后查询变动物料的库存数据,并将数据发送到DATAHUB # 在应用到需要监听的不同表单时,注意需要更换 MAIN_NAME,MAIN_TABLE,ENTRY_TABLE 三个变量 # 开启debug调试消息提醒 SHOW_DEBUG = True # DataHub Host 服务器主机 DATAHUB_HOST = 'https://www.qliang.cloud' # StrategyId 集线器ID STRATEGY_ID = '2509d92a-a91e-30a3-ae4e-33eead036a97' # 当前表名 MAIN_NAME = '外购入库'; # 当前表的主表名 MAIN_TABLE = 't_STK_InStock' # 当前表的分录表名 ENTRY_TABLE = 'T_STK_INSTOCKENTRY' # 当前表的单据编码字段 BILL_FIELD = 'FBILLNO'; # 物料字段名,通常情况下应该是 FMATERIALID 不需要修改 MATERIAL_FIELD = 'FMATERIALID' # 仓库字段名,通常情况下应该是 FSTOCKID 不需要修改 STOCK_FIELD = 'FSTOCKID' # 库位字段名,通常情况下应该是 FSTOCKLOCID 不需要修改 STOCKLOC_FIELD = 'FSTOCKLOCID' # 批次号字段名,通常情况下应该是 FLOT 不需要修改 LOT_FIELD = 'FLOT' # 货主字段名,通常情况下应该是 FOWNERID 不需要修改 OWNER_FIELD = 'FOWNERID' class Inventory: def __init__(self,BarItemKey): keys = this.ListView.SelectedRowsInfo.GetPrimaryKeyValues() self.BarItemKey = BarItemKey self.PKS = ','.join(keys) def fetch(self): return DBServiceHelper.ExecuteDynamicObject( this.Context, self.__generateSQL()) def collectionToJson(self, collection): json = '[' for row in collection: json += '{"FID":"'+row[0].ToString() + '"' \ ',"FStockId":"'+row[1].ToString()+'"' \ ',"FMaterialId":"'+row[2].ToString()+'"' \ ',"FBaseQty":"'+row[3].ToString()+'"' \ ',"FBaseAVBQty":"'+row[4].ToString()+'"' \ ',"FLot":"'+row[5].ToString()+'"' \ ',"FUpdateTime":"'+row[6].ToString()+'"' \ ',"FOwnerId":"'+row[7].ToString()+'"' \ ',"FKeeperId":"'+row[8].ToString()+'"' \ ',"FStockOrgId":"'+row[9].ToString()+'"' \ ',"FOwnerTypeId":"'+row[10].ToString()+'"' \ ',"FMaterialId_FNumber":"'+row[11].ToString()+'"' \ ',"FOwnerId_FNumber":"'+row[12].ToString()+'"' \ ',"FKeeperId_FNumber":"'+row[13].ToString()+'"' \ ',"FStockOrgId_FNumber":"'+row[14].ToString()+'"' \ ',"FProduceDate":"'+row[15].ToString()+'"' \ ',"FMtoNo":"'+row[16].ToString()+'"' \ ',"FStockStatusId":"'+row[17].ToString()+'"' \ ',"FBILLNO":"'+row[18].ToString()+'"' \ ',"id":"'+row[0].ToString()+'"' \ ',"FormName":"'+MAIN_NAME+'"},' json = json.rstrip(',') + ']' return '{"idCheck":false,"content":'+json+',"multiple":true,"id":1}'; def __generateSQL(self): sqlArray = [ 'SELECT DISTINCT', 'INV.FID,', 'INV.FStockId,', 'INV.FMaterialId,', 'INV.FBaseQty,', 'INV.FBaseAVBQty,', 'INV.FLot,', 'INV.FUpdateTime,', 'INV.FOwnerId,', 'INV.FKeeperId,', 'INV.FStockOrgId,', 'INV.FOwnerTypeId,', 'MATE.FNUMBER AS FMaterialId_FNumber,', 'ORG1.FNUMBER AS FOwnerId_FNumber,', 'ORG2.FNUMBER AS FKeeperId_FNumber,', 'ORG3.FNUMBER AS FStockOrgId_FNumber,', 'INV.FProduceDate,', 'INV.FMtoNo,', 'INV.FStockStatusId,', # 如果现实单据号,则可能导致会重复发送多一些数据.但是这并不影响真实库存.如果非要传入单号,可以开启 # 'MAIN.'+BILL_FIELD, # 将单号现实为0,避免POST数据中存在重复 "'0' AS FBillNo", 'FROM T_STK_INVENTORY INV', 'LEFT JOIN '+ENTRY_TABLE+' BILL ON', 'INV.FMATERIALID = BILL.'+MATERIAL_FIELD, 'AND INV.FSTOCKID = BILL.'+STOCK_FIELD, 'AND INV.FSTOCKLOCID = BILL.'+STOCKLOC_FIELD, 'AND INV.FLOT = BILL.'+LOT_FIELD, 'AND INV.FOWNERID = BILL.'+OWNER_FIELD, 'LEFT JOIN t_bd_material MATE ON INV.FMATERIALID = MATE.FMATERIALID', 'LEFT JOIN t_ORG_Organizations ORG1 ON INV.FOwnerId = ORG1.FORGID', 'LEFT JOIN t_ORG_Organizations ORG2 ON INV.FKeeperId = ORG2.FORGID', 'LEFT JOIN t_ORG_Organizations ORG3 ON INV.FStockOrgId = ORG3.FORGID', 'LEFT JOIN '+MAIN_TABLE+' MAIN ON BILL.FID = MAIN.FID', 'WHERE BILL.FID IN ('+self.PKS+')', ] sql = ' '.join(sqlArray) if self.BarItemKey == 'tbApprove': sql += " AND MAIN.FDOCUMENTSTATUS = 'C'" else: sql += " AND MAIN.FDOCUMENTSTATUS = 'D'" # this.View.ShowMessage(sql) return sql def AfterBarItemClick(e): if e.BarItemKey == 'tbApprove' or e.BarItemKey == 'tbReject': if len(this.ListView.SelectedRowsInfo.GetPrimaryKeyValues()) > 0: handle(e.BarItemKey) def handle(BarItemKey): inv = Inventory(BarItemKey) url = DATAHUB_HOST + '/api/open/operation/'+STRATEGY_ID collection = inv.fetch() if len(collection) == 0: return; webRequest = post(url, inv.collectionToJson(collection)) result = JObject.Parse(webRequest) if SHOW_DEBUG == True: this.View.ShowMessage(webRequest) def get(url): webRequest = WebRequest.Create(url) webRequest.Method = "GET" webResponse = webRequest.GetResponse() stream = webResponse.GetResponseStream() streamReader = StreamReader(stream, Encoding.GetEncoding("utf-8")) result = streamReader.ReadToEnd() return result def post(url, postdata): webRequest = HttpWebRequest.Create(url) webRequest.Method = "POST" webRequest.Accept = "application/json, text/plain, */*" webRequest.ContentType = "application/json;charset=UTF-8" data = Encoding.ASCII.GetBytes(postdata) webRequest.ContentLength = data.Length webRequest.GetRequestStream().Write(data, 0, data.Length) webRequest.GetRequestStream().Flush() webRequest.GetRequestStream().Close() webResponse = webRequest.GetResponse() streamReader = StreamReader( webResponse .GetResponseStream(), Encoding.GetEncoding("utf-8")) result = streamReader.ReadToEnd() return result
注册金蝶表单插件
DynamicFormDataChanged.py
import clr clr.AddReference("System") clr.AddReference("System.Web.Extensions") clr.AddReference("Kingdee.BOS.Core") clr.AddReference("Kingdee.BOS") clr.AddReference('Kingdee.BOS.App') clr.AddReference("Kingdee.BOS.DataEntity") clr.AddReference("Kingdee.BOS.ServiceHelper") clr.AddReference("Newtonsoft.Json") import sys from System import * from System.Collections.Generic import * from System.Web.Script.Serialization import * from System.Security.Cryptography import * from System.Text import * from System.Net import * from System.IO import * from System.Threading import * from System.Collections.Generic import Dictionary from Newtonsoft.Json import * from Newtonsoft.Json.Linq import * from Kingdee.BOS.ServiceHelper import * from Kingdee.BOS.Core.DynamicForm import * from Kingdee.BOS.App.Data import * reload(sys) sys.setdefaultencoding('utf-8') # 金蝶Cloud表单插件 # 用于监听数据变动审核、反审核事件后查询变动物料的库存数据,并将数据发送到DATAHUB # 在应用到需要监听的不同表单时,注意需要更换 MAIN_NAME,MAIN_TABLE,ENTRY_TABLE 三个变量 # 开启debug调试消息提醒 SHOW_DEBUG = True # DataHub Host 服务器主机 DATAHUB_HOST = 'https://www.qliang.cloud' # StrategyId 集线器ID STRATEGY_ID = '2509d92a-a91e-30a3-ae4e-33eead036a97' # 当前表名 MAIN_NAME = '外购入库'; # 当前表的主表名 MAIN_TABLE = 't_STK_InStock' # 当前表的分录表名 ENTRY_TABLE = 'T_STK_INSTOCKENTRY' # 当前表的单据编码字段 BILL_FIELD = 'FBILLNO'; # 物料字段名,通常情况下应该是 FMATERIALID 不需要修改 MATERIAL_FIELD = 'FMATERIALID' # 仓库字段名,通常情况下应该是 FSTOCKID 不需要修改 STOCK_FIELD = 'FSTOCKID' # 库位字段名,通常情况下应该是 FSTOCKLOCID 不需要修改 STOCKLOC_FIELD = 'FSTOCKLOCID' # 批次号字段名,通常情况下应该是 FLOT 不需要修改 LOT_FIELD = 'FLOT' # 货主字段名,通常情况下应该是 FOWNERID 不需要修改 OWNER_FIELD = 'FOWNERID' class Inventory: def __init__(self): self.PK = this.View.Model.GetPKValue().ToString() def fetch(self): return DBServiceHelper.ExecuteDynamicObject( this.Context, self.__generateSQL()) def collectionToJson(self, collection): json = '[' for row in collection: json += '{"FID":"'+row[0].ToString() + '"' \ ',"FStockId":"'+row[1].ToString()+'"' \ ',"FMaterialId":"'+row[2].ToString()+'"' \ ',"FBaseQty":"'+row[3].ToString()+'"' \ ',"FBaseAVBQty":"'+row[4].ToString()+'"' \ ',"FLot":"'+row[5].ToString()+'"' \ ',"FUpdateTime":"'+row[6].ToString()+'"' \ ',"FOwnerId":"'+row[7].ToString()+'"' \ ',"FKeeperId":"'+row[8].ToString()+'"' \ ',"FStockOrgId":"'+row[9].ToString()+'"' \ ',"FOwnerTypeId":"'+row[10].ToString()+'"' \ ',"FMaterialId_FNumber":"'+row[11].ToString()+'"' \ ',"FOwnerId_FNumber":"'+row[12].ToString()+'"' \ ',"FKeeperId_FNumber":"'+row[13].ToString()+'"' \ ',"FStockOrgId_FNumber":"'+row[14].ToString()+'"' \ ',"FProduceDate":"'+row[15].ToString()+'"' \ ',"FMtoNo":"'+row[16].ToString()+'"' \ ',"FStockStatusId":"'+row[17].ToString()+'"' \ ',"FBILLNO":"'+row[18].ToString()+'"' \ ',"id":"'+row[0].ToString()+'"' \ ',"FormName":"'+MAIN_NAME+'"},' json = json.rstrip(',') + ']' return '{"idCheck":false,"content":'+json+',"multiple":true,"id":1}'; def __generateSQL(self): sqlArray = [ 'SELECT DISTINCT', 'INV.FID,', 'INV.FStockId,', 'INV.FMaterialId,', 'INV.FBaseQty,', 'INV.FBaseAVBQty,', 'INV.FLot,', 'INV.FUpdateTime,', 'INV.FOwnerId,', 'INV.FKeeperId,', 'INV.FStockOrgId,', 'INV.FOwnerTypeId,', 'MATE.FNUMBER AS FMaterialId_FNumber,', 'ORG1.FNUMBER AS FOwnerId_FNumber,', 'ORG2.FNUMBER AS FKeeperId_FNumber,', 'ORG3.FNUMBER AS FStockOrgId_FNumber,', 'INV.FProduceDate,', 'INV.FMtoNo,', 'INV.FStockStatusId,', 'MAIN.'+BILL_FIELD, 'FROM T_STK_INVENTORY INV', 'LEFT JOIN '+ENTRY_TABLE+' BILL ON', 'INV.FMATERIALID = BILL.'+MATERIAL_FIELD, 'AND INV.FSTOCKID = BILL.'+STOCK_FIELD, 'AND INV.FSTOCKLOCID = BILL.'+STOCKLOC_FIELD, 'AND INV.FLOT = BILL.'+LOT_FIELD, 'AND INV.FOWNERID = BILL.'+OWNER_FIELD, 'LEFT JOIN t_bd_material MATE ON INV.FMATERIALID = MATE.FMATERIALID', 'LEFT JOIN t_ORG_Organizations ORG1 ON INV.FOwnerId = ORG1.FORGID', 'LEFT JOIN t_ORG_Organizations ORG2 ON INV.FKeeperId = ORG2.FORGID', 'LEFT JOIN t_ORG_Organizations ORG3 ON INV.FStockOrgId = ORG3.FORGID', 'LEFT JOIN '+MAIN_TABLE+' MAIN ON BILL.FID = MAIN.FID', 'WHERE BILL.FID = '+self.PK, ] return ' '.join(sqlArray) def DataChanged(e): if e.Key.ToString() == 'FDocumentStatus' and e.OldValue.ToString() == 'B' and e.NewValue.ToString() == 'C': handle() elif e.Key.ToString() == 'FDocumentStatus' and e.OldValue.ToString() == 'C' and e.NewValue.ToString() == 'D': handle() def handle(): inv = Inventory() url = DATAHUB_HOST + '/api/open/operation/'+STRATEGY_ID collection = inv.fetch() if len(collection) == 0: return; webRequest = post(url, inv.collectionToJson(collection)) result = JObject.Parse(webRequest) if SHOW_DEBUG == True: this.View.ShowMessage(webRequest) def get(url): webRequest = WebRequest.Create(url) webRequest.Method = "GET" webResponse = webRequest.GetResponse() stream = webResponse.GetResponseStream() streamReader = StreamReader(stream, Encoding.GetEncoding("utf-8")) result = streamReader.ReadToEnd() return result def post(url, postdata): webRequest = HttpWebRequest.Create(url) webRequest.Method = "POST" webRequest.Accept = "application/json, text/plain, */*" webRequest.ContentType = "application/json;charset=UTF-8" data = Encoding.ASCII.GetBytes(postdata) webRequest.ContentLength = data.Length webRequest.GetRequestStream().Write(data, 0, data.Length) webRequest.GetRequestStream().Flush() webRequest.GetRequestStream().Close() webResponse = webRequest.GetResponse() streamReader = StreamReader( webResponse .GetResponseStream(), Encoding.GetEncoding("utf-8")) result = streamReader.ReadToEnd() return result
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!