通过向金蝶库存单据注册Python脚本,用于实时监听库存单据审核/反审核,并且将数据发送到DATAHUB

向金蝶的库存单据注册脚本

注意修改脚本中的三个变量参数,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

  1. import clr
  2. clr.AddReference("System")
  3. clr.AddReference("System.Web.Extensions")
  4. clr.AddReference("Kingdee.BOS.Core")
  5. clr.AddReference("Kingdee.BOS")
  6. clr.AddReference('Kingdee.BOS.App')
  7. clr.AddReference("Kingdee.BOS.DataEntity")
  8. clr.AddReference("Kingdee.BOS.ServiceHelper")
  9. clr.AddReference("Newtonsoft.Json")
  10. import sys
  11. from System import *
  12. from System.Collections.Generic import *
  13. from System.Web.Script.Serialization import *
  14. from System.Security.Cryptography import *
  15. from System.Text import *
  16. from System.Net import *
  17. from System.IO import *
  18. from System.Threading import *
  19. from System.Collections.Generic import Dictionary
  20. from Newtonsoft.Json import *
  21. from Newtonsoft.Json.Linq import *
  22. from Kingdee.BOS.ServiceHelper import *
  23. from Kingdee.BOS.Core.DynamicForm import *
  24. from Kingdee.BOS.App.Data import *
  25. reload(sys)
  26. sys.setdefaultencoding('utf-8')
  27. # 金蝶Cloud列表插件
  28. # 用于监听审核、反审核事件后查询变动物料的库存数据,并将数据发送到DATAHUB
  29. # 在应用到需要监听的不同表单时,注意需要更换 MAIN_NAME,MAIN_TABLE,ENTRY_TABLE 三个变量
  30. # 开启debug调试消息提醒
  31. SHOW_DEBUG = True
  32. # DataHub Host 服务器主机
  33. DATAHUB_HOST = 'http://datahub-service.kdyunchuang.com'
  34. # StrategyId 集线器ID
  35. STRATEGY_ID = '2509d92a-a91e-30a3-ae4e-33eead036a97'
  36. # 当前表名
  37. MAIN_NAME = '外购入库';
  38. # 当前表的主表名
  39. MAIN_TABLE = 't_STK_InStock'
  40. # 当前表的分录表名
  41. ENTRY_TABLE = 'T_STK_INSTOCKENTRY'
  42. # 当前表的单据编码字段
  43. BILL_FIELD = 'FBILLNO';
  44. # 物料字段名,通常情况下应该是 FMATERIALID 不需要修改
  45. MATERIAL_FIELD = 'FMATERIALID'
  46. # 仓库字段名,通常情况下应该是 FSTOCKID 不需要修改
  47. STOCK_FIELD = 'FSTOCKID'
  48. # 库位字段名,通常情况下应该是 FSTOCKLOCID 不需要修改
  49. STOCKLOC_FIELD = 'FSTOCKLOCID'
  50. # 批次号字段名,通常情况下应该是 FLOT 不需要修改
  51. LOT_FIELD = 'FLOT'
  52. # 货主字段名,通常情况下应该是 FOWNERID 不需要修改
  53. OWNER_FIELD = 'FOWNERID'
  54. class Inventory:
  55. def __init__(self,BarItemKey):
  56. keys = this.ListView.SelectedRowsInfo.GetPrimaryKeyValues()
  57. self.BarItemKey = BarItemKey
  58. self.PKS = ','.join(keys)
  59. def fetch(self):
  60. return DBServiceHelper.ExecuteDynamicObject(
  61. this.Context, self.__generateSQL())
  62. def collectionToJson(self, collection):
  63. json = '['
  64. for row in collection:
  65. json += '{"FID":"'+row[0].ToString() + '"' \
  66. ',"FStockId":"'+row[1].ToString()+'"' \
  67. ',"FMaterialId":"'+row[2].ToString()+'"' \
  68. ',"FBaseQty":"'+row[3].ToString()+'"' \
  69. ',"FBaseAVBQty":"'+row[4].ToString()+'"' \
  70. ',"FLot":"'+row[5].ToString()+'"' \
  71. ',"FUpdateTime":"'+row[6].ToString()+'"' \
  72. ',"FOwnerId":"'+row[7].ToString()+'"' \
  73. ',"FKeeperId":"'+row[8].ToString()+'"' \
  74. ',"FStockOrgId":"'+row[9].ToString()+'"' \
  75. ',"FOwnerTypeId":"'+row[10].ToString()+'"' \
  76. ',"FMaterialId_FNumber":"'+row[11].ToString()+'"' \
  77. ',"FOwnerId_FNumber":"'+row[12].ToString()+'"' \
  78. ',"FKeeperId_FNumber":"'+row[13].ToString()+'"' \
  79. ',"FStockOrgId_FNumber":"'+row[14].ToString()+'"' \
  80. ',"FProduceDate":"'+row[15].ToString()+'"' \
  81. ',"FMtoNo":"'+row[16].ToString()+'"' \
  82. ',"FStockStatusId":"'+row[17].ToString()+'"' \
  83. ',"FBILLNO":"'+row[18].ToString()+'"' \
  84. ',"id":"'+row[0].ToString()+'"' \
  85. ',"FormName":"'+MAIN_NAME+'"},'
  86. json = json.rstrip(',') + ']'
  87. return '{"idCheck":false,"content":'+json+',"multiple":true,"id":1}';
  88. def __generateSQL(self):
  89. sqlArray = [
  90. 'SELECT DISTINCT',
  91. 'INV.FID,',
  92. 'INV.FStockId,',
  93. 'INV.FMaterialId,',
  94. 'INV.FBaseQty,',
  95. 'INV.FBaseAVBQty,',
  96. 'INV.FLot,',
  97. 'INV.FUpdateTime,',
  98. 'INV.FOwnerId,',
  99. 'INV.FKeeperId,',
  100. 'INV.FStockOrgId,',
  101. 'INV.FOwnerTypeId,',
  102. 'MATE.FNUMBER AS FMaterialId_FNumber,',
  103. 'ORG1.FNUMBER AS FOwnerId_FNumber,',
  104. 'ORG2.FNUMBER AS FKeeperId_FNumber,',
  105. 'ORG3.FNUMBER AS FStockOrgId_FNumber,',
  106. 'INV.FProduceDate,',
  107. 'INV.FMtoNo,',
  108. 'INV.FStockStatusId,',
  109. # 如果现实单据号,则可能导致会重复发送多一些数据.但是这并不影响真实库存.如果非要传入单号,可以开启
  110. # 'MAIN.'+BILL_FIELD,
  111. # 将单号现实为0,避免POST数据中存在重复
  112. "'0' AS FBillNo",
  113. 'FROM T_STK_INVENTORY INV',
  114. 'LEFT JOIN '+ENTRY_TABLE+' BILL ON',
  115. 'INV.FMATERIALID = BILL.'+MATERIAL_FIELD,
  116. 'AND INV.FSTOCKID = BILL.'+STOCK_FIELD,
  117. 'AND INV.FSTOCKLOCID = BILL.'+STOCKLOC_FIELD,
  118. 'AND INV.FLOT = BILL.'+LOT_FIELD,
  119. 'AND INV.FOWNERID = BILL.'+OWNER_FIELD,
  120. 'LEFT JOIN t_bd_material MATE ON INV.FMATERIALID = MATE.FMATERIALID',
  121. 'LEFT JOIN t_ORG_Organizations ORG1 ON INV.FOwnerId = ORG1.FORGID',
  122. 'LEFT JOIN t_ORG_Organizations ORG2 ON INV.FKeeperId = ORG2.FORGID',
  123. 'LEFT JOIN t_ORG_Organizations ORG3 ON INV.FStockOrgId = ORG3.FORGID',
  124. 'LEFT JOIN '+MAIN_TABLE+' MAIN ON BILL.FID = MAIN.FID',
  125. 'WHERE BILL.FID IN ('+self.PKS+')',
  126. ]
  127. sql = ' '.join(sqlArray)
  128. if self.BarItemKey == 'tbApprove':
  129. sql += " AND MAIN.FDOCUMENTSTATUS = 'C'"
  130. else:
  131. sql += " AND MAIN.FDOCUMENTSTATUS = 'D'"
  132. # this.View.ShowMessage(sql)
  133. return sql
  134. def AfterBarItemClick(e):
  135. if e.BarItemKey == 'tbApprove' or e.BarItemKey == 'tbReject':
  136. if len(this.ListView.SelectedRowsInfo.GetPrimaryKeyValues()) > 0:
  137. handle(e.BarItemKey)
  138. def handle(BarItemKey):
  139. inv = Inventory(BarItemKey)
  140. url = DATAHUB_HOST + '/api/open/operation/'+STRATEGY_ID
  141. collection = inv.fetch()
  142. if len(collection) == 0:
  143. return;
  144. webRequest = post(url, inv.collectionToJson(collection))
  145. result = JObject.Parse(webRequest)
  146. if SHOW_DEBUG == True:
  147. this.View.ShowMessage(webRequest)
  148. def get(url):
  149. webRequest = WebRequest.Create(url)
  150. webRequest.Method = "GET"
  151. webResponse = webRequest.GetResponse()
  152. stream = webResponse.GetResponseStream()
  153. streamReader = StreamReader(stream, Encoding.GetEncoding("utf-8"))
  154. result = streamReader.ReadToEnd()
  155. return result
  156. def post(url, postdata):
  157. webRequest = HttpWebRequest.Create(url)
  158. webRequest.Method = "POST"
  159. webRequest.Accept = "application/json, text/plain, */*"
  160. webRequest.ContentType = "application/json;charset=UTF-8"
  161. data = Encoding.ASCII.GetBytes(postdata)
  162. webRequest.ContentLength = data.Length
  163. webRequest.GetRequestStream().Write(data, 0, data.Length)
  164. webRequest.GetRequestStream().Flush()
  165. webRequest.GetRequestStream().Close()
  166. webResponse = webRequest.GetResponse()
  167. streamReader = StreamReader(
  168. webResponse .GetResponseStream(), Encoding.GetEncoding("utf-8"))
  169. result = streamReader.ReadToEnd()
  170. return result

注册金蝶表单插件

DynamicFormDataChanged.py

  1. import clr
  2. clr.AddReference("System")
  3. clr.AddReference("System.Web.Extensions")
  4. clr.AddReference("Kingdee.BOS.Core")
  5. clr.AddReference("Kingdee.BOS")
  6. clr.AddReference('Kingdee.BOS.App')
  7. clr.AddReference("Kingdee.BOS.DataEntity")
  8. clr.AddReference("Kingdee.BOS.ServiceHelper")
  9. clr.AddReference("Newtonsoft.Json")
  10. import sys
  11. from System import *
  12. from System.Collections.Generic import *
  13. from System.Web.Script.Serialization import *
  14. from System.Security.Cryptography import *
  15. from System.Text import *
  16. from System.Net import *
  17. from System.IO import *
  18. from System.Threading import *
  19. from System.Collections.Generic import Dictionary
  20. from Newtonsoft.Json import *
  21. from Newtonsoft.Json.Linq import *
  22. from Kingdee.BOS.ServiceHelper import *
  23. from Kingdee.BOS.Core.DynamicForm import *
  24. from Kingdee.BOS.App.Data import *
  25. reload(sys)
  26. sys.setdefaultencoding('utf-8')
  27. # 金蝶Cloud表单插件
  28. # 用于监听数据变动审核、反审核事件后查询变动物料的库存数据,并将数据发送到DATAHUB
  29. # 在应用到需要监听的不同表单时,注意需要更换 MAIN_NAME,MAIN_TABLE,ENTRY_TABLE 三个变量
  30. # 开启debug调试消息提醒
  31. SHOW_DEBUG = True
  32. # DataHub Host 服务器主机
  33. DATAHUB_HOST = 'http://datahub-service.kdyunchuang.com'
  34. # StrategyId 集线器ID
  35. STRATEGY_ID = '2509d92a-a91e-30a3-ae4e-33eead036a97'
  36. # 当前表名
  37. MAIN_NAME = '外购入库';
  38. # 当前表的主表名
  39. MAIN_TABLE = 't_STK_InStock'
  40. # 当前表的分录表名
  41. ENTRY_TABLE = 'T_STK_INSTOCKENTRY'
  42. # 当前表的单据编码字段
  43. BILL_FIELD = 'FBILLNO';
  44. # 物料字段名,通常情况下应该是 FMATERIALID 不需要修改
  45. MATERIAL_FIELD = 'FMATERIALID'
  46. # 仓库字段名,通常情况下应该是 FSTOCKID 不需要修改
  47. STOCK_FIELD = 'FSTOCKID'
  48. # 库位字段名,通常情况下应该是 FSTOCKLOCID 不需要修改
  49. STOCKLOC_FIELD = 'FSTOCKLOCID'
  50. # 批次号字段名,通常情况下应该是 FLOT 不需要修改
  51. LOT_FIELD = 'FLOT'
  52. # 货主字段名,通常情况下应该是 FOWNERID 不需要修改
  53. OWNER_FIELD = 'FOWNERID'
  54. class Inventory:
  55. def __init__(self):
  56. self.PK = this.View.Model.GetPKValue().ToString()
  57. def fetch(self):
  58. return DBServiceHelper.ExecuteDynamicObject(
  59. this.Context, self.__generateSQL())
  60. def collectionToJson(self, collection):
  61. json = '['
  62. for row in collection:
  63. json += '{"FID":"'+row[0].ToString() + '"' \
  64. ',"FStockId":"'+row[1].ToString()+'"' \
  65. ',"FMaterialId":"'+row[2].ToString()+'"' \
  66. ',"FBaseQty":"'+row[3].ToString()+'"' \
  67. ',"FBaseAVBQty":"'+row[4].ToString()+'"' \
  68. ',"FLot":"'+row[5].ToString()+'"' \
  69. ',"FUpdateTime":"'+row[6].ToString()+'"' \
  70. ',"FOwnerId":"'+row[7].ToString()+'"' \
  71. ',"FKeeperId":"'+row[8].ToString()+'"' \
  72. ',"FStockOrgId":"'+row[9].ToString()+'"' \
  73. ',"FOwnerTypeId":"'+row[10].ToString()+'"' \
  74. ',"FMaterialId_FNumber":"'+row[11].ToString()+'"' \
  75. ',"FOwnerId_FNumber":"'+row[12].ToString()+'"' \
  76. ',"FKeeperId_FNumber":"'+row[13].ToString()+'"' \
  77. ',"FStockOrgId_FNumber":"'+row[14].ToString()+'"' \
  78. ',"FProduceDate":"'+row[15].ToString()+'"' \
  79. ',"FMtoNo":"'+row[16].ToString()+'"' \
  80. ',"FStockStatusId":"'+row[17].ToString()+'"' \
  81. ',"FBILLNO":"'+row[18].ToString()+'"' \
  82. ',"id":"'+row[0].ToString()+'"' \
  83. ',"FormName":"'+MAIN_NAME+'"},'
  84. json = json.rstrip(',') + ']'
  85. return '{"idCheck":false,"content":'+json+',"multiple":true,"id":1}';
  86. def __generateSQL(self):
  87. sqlArray = [
  88. 'SELECT DISTINCT',
  89. 'INV.FID,',
  90. 'INV.FStockId,',
  91. 'INV.FMaterialId,',
  92. 'INV.FBaseQty,',
  93. 'INV.FBaseAVBQty,',
  94. 'INV.FLot,',
  95. 'INV.FUpdateTime,',
  96. 'INV.FOwnerId,',
  97. 'INV.FKeeperId,',
  98. 'INV.FStockOrgId,',
  99. 'INV.FOwnerTypeId,',
  100. 'MATE.FNUMBER AS FMaterialId_FNumber,',
  101. 'ORG1.FNUMBER AS FOwnerId_FNumber,',
  102. 'ORG2.FNUMBER AS FKeeperId_FNumber,',
  103. 'ORG3.FNUMBER AS FStockOrgId_FNumber,',
  104. 'INV.FProduceDate,',
  105. 'INV.FMtoNo,',
  106. 'INV.FStockStatusId,',
  107. 'MAIN.'+BILL_FIELD,
  108. 'FROM T_STK_INVENTORY INV',
  109. 'LEFT JOIN '+ENTRY_TABLE+' BILL ON',
  110. 'INV.FMATERIALID = BILL.'+MATERIAL_FIELD,
  111. 'AND INV.FSTOCKID = BILL.'+STOCK_FIELD,
  112. 'AND INV.FSTOCKLOCID = BILL.'+STOCKLOC_FIELD,
  113. 'AND INV.FLOT = BILL.'+LOT_FIELD,
  114. 'AND INV.FOWNERID = BILL.'+OWNER_FIELD,
  115. 'LEFT JOIN t_bd_material MATE ON INV.FMATERIALID = MATE.FMATERIALID',
  116. 'LEFT JOIN t_ORG_Organizations ORG1 ON INV.FOwnerId = ORG1.FORGID',
  117. 'LEFT JOIN t_ORG_Organizations ORG2 ON INV.FKeeperId = ORG2.FORGID',
  118. 'LEFT JOIN t_ORG_Organizations ORG3 ON INV.FStockOrgId = ORG3.FORGID',
  119. 'LEFT JOIN '+MAIN_TABLE+' MAIN ON BILL.FID = MAIN.FID',
  120. 'WHERE BILL.FID = '+self.PK,
  121. ]
  122. return ' '.join(sqlArray)
  123. def DataChanged(e):
  124. if e.Key.ToString() == 'FDocumentStatus' and e.OldValue.ToString() == 'B' and e.NewValue.ToString() == 'C':
  125. handle()
  126. elif e.Key.ToString() == 'FDocumentStatus' and e.OldValue.ToString() == 'C' and e.NewValue.ToString() == 'D':
  127. handle()
  128. def handle():
  129. inv = Inventory()
  130. url = DATAHUB_HOST + '/api/open/operation/'+STRATEGY_ID
  131. collection = inv.fetch()
  132. if len(collection) == 0:
  133. return;
  134. webRequest = post(url, inv.collectionToJson(collection))
  135. result = JObject.Parse(webRequest)
  136. if SHOW_DEBUG == True:
  137. this.View.ShowMessage(webRequest)
  138. def get(url):
  139. webRequest = WebRequest.Create(url)
  140. webRequest.Method = "GET"
  141. webResponse = webRequest.GetResponse()
  142. stream = webResponse.GetResponseStream()
  143. streamReader = StreamReader(stream, Encoding.GetEncoding("utf-8"))
  144. result = streamReader.ReadToEnd()
  145. return result
  146. def post(url, postdata):
  147. webRequest = HttpWebRequest.Create(url)
  148. webRequest.Method = "POST"
  149. webRequest.Accept = "application/json, text/plain, */*"
  150. webRequest.ContentType = "application/json;charset=UTF-8"
  151. data = Encoding.ASCII.GetBytes(postdata)
  152. webRequest.ContentLength = data.Length
  153. webRequest.GetRequestStream().Write(data, 0, data.Length)
  154. webRequest.GetRequestStream().Flush()
  155. webRequest.GetRequestStream().Close()
  156. webResponse = webRequest.GetResponse()
  157. streamReader = StreamReader(
  158. webResponse .GetResponseStream(), Encoding.GetEncoding("utf-8"))
  159. result = streamReader.ReadToEnd()
  160. return result

金蝶发送get post请求

  1. import clr
  2. clr.AddReference("System")
  3. clr.AddReference("System.Web.Extensions")
  4. clr.AddReference("Kingdee.BOS.Core")
  5. clr.AddReference("Kingdee.BOS")
  6. clr.AddReference('Kingdee.BOS.App')
  7. clr.AddReference("Kingdee.BOS.DataEntity")
  8. clr.AddReference("Kingdee.BOS.ServiceHelper")
  9. clr.AddReference("Newtonsoft.Json")
  10. import sys
  11. from System import *
  12. from System.Collections.Generic import *
  13. from System.Web.Script.Serialization import *
  14. from System.Security.Cryptography import *
  15. from System.Text import *
  16. from System.Net import *
  17. from System.IO import *
  18. from System.Threading import *
  19. from System.Collections.Generic import Dictionary
  20. from Newtonsoft.Json import *
  21. from Newtonsoft.Json.Linq import *
  22. from Kingdee.BOS.ServiceHelper import *
  23. from Kingdee.BOS.Core.DynamicForm import *
  24. from Kingdee.BOS.App.Data import *
  25. reload(sys)
  26. sys.setdefaultencoding('utf-8')
  27. # 开启debug调试消息提醒
  28. SHOW_DEBUG = True
  29. # DataHub Host 服务器主机
  30. DATAHUB_HOST = 'http://datahub-service.kdyunchuang.com'
  31. def DataChanged(e):
  32. if e.Key.ToString() == 'FDocumentStatus' and e.OldValue.ToString() == 'B' and e.NewValue.ToString() == 'C':
  33. handle()
  34. def handle():
  35. # 拿到当前页面那两个事件
  36. # self.PK = this.View.Model.GetPKValue().ToString()
  37. begin = this.Model.GetValue('')
  38. end = this.Model.GetValue('')
  39. url = DATAHUB_HOST + '/api/open/operation/'+begin
  40. webRequest = get(url);
  41. if SHOW_DEBUG == True:
  42. this.View.ShowMessage(webRequest)
  43. def get(url):
  44. webRequest = WebRequest.Create(url)
  45. webRequest.Method = "GET"
  46. webResponse = webRequest.GetResponse()
  47. stream = webResponse.GetResponseStream()
  48. streamReader = StreamReader(stream, Encoding.GetEncoding("utf-8"))
  49. result = streamReader.ReadToEnd()
  50. return result
  51. def post(url, postdata):
  52. webRequest = HttpWebRequest.Create(url)
  53. webRequest.Method = "POST"
  54. webRequest.Accept = "application/json, text/plain, */*"
  55. webRequest.ContentType = "application/json;charset=UTF-8"
  56. data = Encoding.ASCII.GetBytes(postdata)
  57. webRequest.ContentLength = data.Length
  58. webRequest.GetRequestStream().Write(data, 0, data.Length)
  59. webRequest.GetRequestStream().Flush()
  60. webRequest.GetRequestStream().Close()
  61. webResponse = webRequest.GetResponse()
  62. streamReader = StreamReader(
  63. webResponse .GetResponseStream(), Encoding.GetEncoding("utf-8"))
  64. result = streamReader.ReadToEnd()
  65. return result