校验器.二开案例.下游单据保存时上游单据状态校验
【场景】存在场景,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用户保存时导致的单据异常;包括可能:网控被用户误操作移除,历史版本下推操作网...
点击下载文档
本文2024-09-16 18:22:30发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-21722.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章