校验器.二开案例.下游单据保存时上游单据状态校验

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-16浏览:1

校验器.二开案例.下游单据保存时上游单据状态校验

【场景】存在场景,A用户下推未保存,B用户反审核,而后A用户保存时导致的单据异常; 包括可能:网控被用户误操作移除,历史版本下推操作网控控制不准,自定义二开逻辑没有关联网控,api反审核等; 导致最终单据未审核但是存在下游的的场景 【方案】在下游单据保存操作服务插件时,强校验源单的单据状态为已审核 ![B(CR7LDU{$V{_CBOCBS)91M.webp](/download/0100aa76b1c8868a4d9fbe719473513a11f8.webp) 代码实现逻辑:根据link找到上游单据内码,在事务内去数据库查询比对一次,有必要的话可以升级成行锁 ```csharp using Kingdee.BOS; using Kingdee.BOS.App.Data; using Kingdee.BOS.BusinessEntity.BusinessFlow; using Kingdee.BOS.Core.DynamicForm.PlugIn; using Kingdee.BOS.Core.DynamicForm.PlugIn.Args; using Kingdee.BOS.Core.Metadata; using Kingdee.BOS.Core.Metadata.EntityElement; using Kingdee.BOS.Orm.DataEntity; using Kingdee.BOS.ServiceHelper; using Kingdee.BOS.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DynamicFormPlugIn.Operation { [Kingdee.BOS.Util.HotUpdate] [System.ComponentModel.Description("校验源单状态 操作服务插件")] public class ValidateSrcBillServicePlugIn : AbstractOperationServicePlugIn { /* * 部分场景下下游单据保存时上游已经反审核,导致的数据异常 * * 方案: * 下游保存增加校验器,校验上游的状态 */ /// <summary> /// 事务内事件,要保证强一致应该放在BeiginOperationTransaction /// </summary> /// <param name="e"></param> public override void BeginOperationTransaction(BeginOperationTransactionArgs e) { /* * 根据link实体,获取源单类型和源单内码,然后获取一次单据状态,验证等于审核 */ var billForm = this.BusinessInfo.GetForm(); if (billForm.LinkSet == null || billForm.LinkSet.LinkEntitys.Count == 0) return; Dictionary<string, List<int>> srcFormBillIds = new Dictionary<string, List<int>>(); // <0>收集关联源单对象和单据内码 foreach(var linkEntity in billForm.LinkSet.LinkEntitys) { if (linkEntity == null) continue; string entityKey = linkEntity.ParentEntityKey; var entity = this.BusinessInfo.GetEntryEntity(entityKey); if (entity == null) continue; for (int i = 0, sz = e.DataEntitys.Length; i < sz; ++i) { var billData = e.DataEntitys[i]; if (billData == null) continue; // <1>获取link 实体的父实体数据包 var entityObjColl = GetEntityDynamicObject(billData, entity); for (int rowIdx = 0, rowCnt = entityObjColl.Count; rowIdx < rowCnt; ++rowIdx) { var entityRow = entityObjColl[rowIdx]; if (entityRow == null) continue; if (!entityRow.DynamicObjectType.Properties.ContainsKey(linkEntity.Key)) continue; // <2>根据link实体 收集 源单内码 源单对象 var linkObjCol = entityRow[linkEntity.Key] as DynamicObjectCollection; if (linkObjCol == null || linkObjCol.Count <= 0) continue; foreach (var linkObj in linkObjCol) { if (linkObj == null) continue; var linkSTableName = ObjectUtils.Object2String(linkObj["STableName"]); var linkSBillid = ObjectUtils.Object2Int(linkObj["SBillId"]); if (linkSTableName.IsNullOrEmptyOrWhiteSpace()) continue; if (linkSBillid == 0) continue; if (!srcFormBillIds.ContainsKey(linkSTableName)) srcFormBillIds[linkSTableName] = new List<int>(); srcFormBillIds[linkSTableName].Add(linkSBillid); } } } } // <3>根据源单计算 foreach (var tableKey in srcFormBillIds.Keys) { var tableDefine = LoadTableDefine(tableKey); if (tableDefine == null) continue; string formId = tableDefine.FormId; var metaData = MetaDataServiceHelper.Load(this.Context, formId) as FormMetadata; if (metaData == null) continue; var headEntity = metaData.BusinessInfo.GetEntity(0); if (headEntity == null || headEntity.TableName.IsNullOrEmptyOrWhiteSpace()) continue; var docStatausKey = metaData.BusinessInfo.GetForm().DocumentStatusFieldKey; if (docStatausKey == null || docStatausKey.IsNullOrEmptyOrWhiteSpace()) continue; var docStatusField = metaData.BusinessInfo.GetField(docStatausKey); if (docStatusField == null) continue; var distBillIds = srcFormBillIds[tableKey].Distinct().ToArray(); string pkField = metaData.BusinessInfo.GetForm().PkFieldName; List<SqlParam> paramList = new List<SqlParam>(); var sqlBuilder = new StringBuilder(); sqlBuilder.AppendFormat("SELECT {0} FROM {1}", pkField, headEntity.TableName); if (distBillIds.Length == 1) { sqlBuilder.AppendFormat(" WHERE {0} = @FID", pkField, distBillIds[0]); paramList.Add(new SqlParam("@FID", KDDbType.Int32, distBillIds[0])); } else if (distBillIds.Length <= 20) { sqlBuilder.AppendFormat(" WHERE {0} in ({1})", pkField, string.Join(",", distBillIds)); } else { var tempTb1 = StringUtils.GetSqlWithCardinality(distBillIds.Length, "@FID", 1); paramList.Add(new SqlParam("@FID", KDDbType.udt_inttable, distBillIds)); sqlBuilder.AppendFormat(" INNER JOIN {0} t2 ON (t2.FID = t0.FSId ) ", tempTb1); sqlBuilder.Append(" WHERE 1=1"); } sqlBuilder.AppendFormat(" AND {0} <> 'C' ", docStatusField.FieldName); using (var dr = DBUtils.ExecuteReader(this.Context, sqlBuilder.ToString(), paramList)) { if (dr.Read()) { throw new Exception(string.Format("存在单据非已审核状态,{0},{1}", formId, dr["FID"])); } } } } /// <summary> /// 加载表格定义对象 /// </summary> /// <param name="tableNumber">表格编码</param> /// <returns></returns> private TableDefine LoadTableDefine(string tableNumber) { TableDefine tableDefine = null; tableDefine = BusinessFlowServiceHelper.LoadTableDefine(this.Context, tableNumber); return tableDefine; } /// <summary> /// 获取目标实体的数据 /// </summary> /// <param name="billObj"></param> /// <param name="targetEntity"></param> /// <returns></returns> private List<DynamicObject> GetEntityDynamicObject(DynamicObject billObj, Entity targetEntity) { if (billObj == null) return null; List<DynamicObject> result = new List<DynamicObject>(); if (targetEntity is HeadEntity) { result.Add(billObj); return result; } if (targetEntity is SubHeadEntity) { if (billObj.DynamicObjectType.Properties.ContainsKey(targetEntity.EntryName)) { DynamicObjectCollection subHeadObj = billObj[targetEntity.EntryName] as DynamicObjectCollection; if (subHeadObj == null || subHeadObj.Count <= 0) return null; foreach (var subHeadObjItem in subHeadObj) { result.Add(subHeadObjItem); } return result; } return null; } Entity headEntity = BusinessInfo.Entrys.FirstOrDefault(x => x is HeadEntity); if (headEntity == null) return null; SubEntryEntity targetSubEntryEntity = targetEntity as SubEntryEntity; string parentEntityKey = targetSubEntryEntity == null ? headEntity.Key : targetSubEntryEntity.ParentEntityKey; ///实现思路,假设 A(单据头) B(单据体) C(子单据体) D(子子单据体)四级关系 ///从单据数据包 目标实体 和目标实体父实体的内码 获取 目标实体的所有数据 ///第一步获取搜索路径,第二步根据搜索路径存入所有目标实体的父实体数据包,将父实体数据包的所有子分录收集 EntryEntity entryEntity = targetEntity as EntryEntity;//先获取对应的搜索路径 Stack<EntryEntity> pathEntity = new Stack<EntryEntity>(); while ((entryEntity as SubEntryEntity) != null)//递归子单据体 { entryEntity = (entryEntity as SubEntryEntity).ParentEntity; pathEntity.Push(entryEntity); } List<DynamicObjectCollection> lastEntityCollection = new List<DynamicObjectCollection>(); DynamicObjectCollection billObjCollection = new DynamicObjectCollection(billObj.DynamicObjectType); billObjCollection.Add(billObj); lastEntityCollection.Add(billObjCollection); DynamicObjectCollection tempCollection = null; while (pathEntity.Count != 0) { EntryEntity tempEntity = pathEntity.Pop(); //递归BFS List<DynamicObjectCollection> tempEntityCollection = new List<DynamicObjectCollection>(); foreach (var lastEntityObjs in lastEntityCollection) { if (!lastEntityObjs.DynamicCollectionItemPropertyType.Properties.ContainsKey(tempEntity.EntryName)) continue; foreach (var lastEntityObj in lastEntityObjs) { tempCollection = lastEntityObj[tempEntity.EntryName] as DynamicObjectCollection; if (tempCollection == null || tempCollection.Count == 0) continue; tempEntityCollection.Add(tempCollection); } } lastEntityCollection = tempEntityCollection; } //这里是目标实体的父实体数据包集合,兼容了目标实体是单据体 foreach (var lastEntityObjs in lastEntityCollection) { if (!lastEntityObjs.DynamicCollectionItemPropertyType.Properties.ContainsKey(targetEntity.EntryName)) continue; foreach (var lastEntityObj in lastEntityObjs) { DynamicObjectCollection objColl = lastEntityObj[targetEntity.EntryName] as DynamicObjectCollection; if (objColl == null) continue; foreach(var obj in objColl) { result.Add(obj); } } } return result; } } } ``` 【效果】当上游已经非审核状态时,下游保存报错 ![image.webp](/download/010026f0cf26a1954c86b46a123ea7c3ba8a.webp)

校验器.二开案例.下游单据保存时上游单据状态校验

【场景】存在场景,A用户下推未保存,B用户反审核,而后A用户保存时导致的单据异常;包括可能:网控被用户误操作移除,历史版本下推操作网...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息