【业务背景】想控制每个负责自己仓库的人只能出这个仓库的货
【需求描述】一个仓库只有一个具体的负责人,一个负责人可能不止负责一个仓库。而负责人只能出自己负责的仓库的物料,例如销售订单下推销售出库单,一个单据有自己库的物料还有不是自己库的物料,这时候下推以后不能保存审核。
【问题分析】当前标准产品未对仓库负责人做相关的业务逻辑校验,所以一些"数据规则选项勾选"、“设置数据规则"等设置均无法实现效果,且单据下推仓库也无法做过滤,下推完以后也能保存审核。
这里主要涉及两个业务场景:
1、单据下推批号拣货,需要根据当前登录用户所负责的仓库列表,过滤即时库存范围
2、单据保存或审核时校验控制
仓库资料中仓库负责人的基础资料类型是"员工任岗信息",这涉及到"用户-人员-员工任岗关系"之间的一个逻辑关联关系,参考【技术答疑.常用SQL.用户-人员-员工任岗关系】,根据当前登录用户内码,通过用户-关联对象“员工”,和仓库上的负责人对应的“员工任岗信息”中的员工做匹配,获取到对应负责的仓库集合
SELECT d.FSTOCKID, d.FNUMBER FROM T_SEC_USER a -- 用户表 INNER JOIN T_BD_PERSON b -- 人员表 ON a.FLINKOBJECT = b.FPERSONID INNER JOIN T_BD_STAFF c -- 员工任岗表 ON b.FPERSONID = c.FPERSONID INNER JOIN T_BD_STOCK d ON d.FPRINCIPAL = c.FSTAFFID WHERE a.FUSERID = @userId
综上,整体的实现思路为:
1、通过用户-关联对象“员工”,和仓库上的负责人对应的“员工任岗信息”中的员工做匹配,获取到对应负责的仓库集合
2、单据下推批号拣货过滤仓库,参考二开实现【有源单批号拣货仓库过滤】
3、单据保存或审核时校验控制,通过定义校验器并注册实现
Python:
import clr clr.AddReference('System') clr.AddReference('Kingdee.BOS') clr.AddReference('Kingdee.BOS.Core') clr.AddReference('Kingdee.BOS.ServiceHelper') from System import * from System import StringComparison from Kingdee.BOS.Core import * from Kingdee.BOS.Core.Metadata.EntityElement import * from Kingdee.BOS.Core.Validation import * from Kingdee.BOS.Log import Logger from System.Collections.Generic import * from Kingdee.BOS.ServiceHelper import * def OnAddValidators(e): validator = StockPrincipalValidator() validator.AlwaysValidate = True validator.EntityKey = "FBillHead" e.Validators.Add(validator) class StockPrincipalValidator(AbstractValidator): def Validate(self, dataEntities, validateContext, ctx): if len(dataEntities) == 0: return dataRuleStockIds = List[Int64]() stockSql = '''SELECT d.FSTOCKID, d.FNUMBER FROM T_SEC_USER a -- 用户表 INNER JOIN T_BD_PERSON b -- 人员表 ON a.FLINKOBJECT = b.FPERSONID INNER JOIN T_BD_STAFF c -- 员工任岗表 ON b.FPERSONID = c.FPERSONID INNER JOIN T_BD_STOCK d ON d.FPRINCIPAL = c.FSTAFFID WHERE a.FUSERID = {0}'''.format(str(ctx.UserId)) dyObjs = DBServiceHelper.ExecuteDynamicObject(ctx, stockSql) if dyObjs.Count > 0: Logger.Info("LZZ",dyObjs.Count) Logger.Info("LZZ",dyObjs["FSTOCKID"]) dataRuleStockIds.Add(Convert.ToInt64(dyObjs[0]["FSTOCKID"])) for bill in dataEntities: billEntities = bill["BillEntry"] for item in billEntities: if dataRuleStockIds.Contains(Convert.ToInt64(item["StockId"]["Id"])) == False: errorMsg = "仓库【{0}】【{1}】不属于当前用户的负责仓库,校验失败".format(Convert.ToString(item["StockId"]["NUMBER"]), Convert.ToString(item["StockId"]["NAME"])) info = ValidationErrorInfo("",str(bill["Id"]), bill.DataEntityIndex, bill.RowIndex, "StockPrincipal", errorMsg, str(bill["BillNo"]), ErrorLevel.Error) validateContext.AddError(None,info)
C#
using Kingdee.BOS.App.Data; using Kingdee.BOS.Orm.DataEntity; using Kingdee.BOS.Util; using System; using System.Collections.Generic; using System.Data; using System.Linq; using Kingdee.K3.SCM.App.Validator; namespace Kingdee.K3.SCM.Stock.App.CustomizePlugIn.Validator { public class StockPrincipalValidator : AbstractServiceValidator { #region 属性 /// <summary> /// 单据FormId /// </summary> public string FormId { get; set; } /// <summary> /// 表体仓库 /// </summary> public string StockId { get; set; } /// <summary> /// 单据体名称 /// </summary> public string EntryName { get; set; } #endregion public override void Validate(BOS.Core.ExtendedDataEntity[] dataEntities, BOS.Core.Validation.ValidateContext validateContext, BOS.Context ctx) { if (dataEntities.IsNullOrEmpty() || dataEntities.Length == 0) { return; } List<long> dataRuleStockIds = new List<long>(); string stockSql = string.Format(@"SELECT d.FSTOCKID, d.FNUMBER FROM T_SEC_USER a -- 用户表 INNER JOIN T_BD_PERSON b -- 人员表 ON a.FLINKOBJECT = b.FPERSONID INNER JOIN T_BD_STAFF c -- 员工任岗表 ON b.FPERSONID = c.FPERSONID INNER JOIN T_BD_STOCK d ON d.FPRINCIPAL = c.FSTAFFID WHERE a.FUSERID = {0} ", this.Context.UserId); using (IDataReader dataReader = DBUtils.ExecuteReader(ctx, stockSql)) { while (dataReader.Read()) { dataRuleStockIds.Add(Convert.ToInt64(dataReader["FSTOCKID"])); } dataReader.Close(); } string str = string.Empty; foreach (BOS.Core.ExtendedDataEntity bill in dataEntities) { DynamicObjectCollection dataEntity = bill[EntryName] as DynamicObjectCollection; IDictionary<long, DynamicObject> stockIdsDic = dataEntity.ToDictionaryEx(p => Convert.ToInt64(((DynamicObject)p[StockId])["Id"]), v => v); foreach (var item in stockIdsDic) { if (!dataRuleStockIds.Contains(item.Key)) { DynamicObject stock = item.Value[StockId] as DynamicObject; str = Kingdee.BOS.Resource.ResManager.LoadKDString(string.Format("仓库【{0}】【{1}】不属于当前用户的负责仓库,校验失败", stock["NUMBER"].ToString(), stock["NAME"].ToString()), "004107000033549", Kingdee.BOS.Resource.SubSystemType.SCM); validateContext.AddError(null, new BOS.Core.Validation.ValidationErrorInfo("", Convert.ToString(bill["Id"]), 0, 0, "StockPrincipal", str, bill.BillNo, BOS.Core.Validation.ErrorLevel.Error)); } } } } } static class ToDictionaryExtentions { public static IDictionary<TKey, TValue> ToDictionaryEx<TElement, TKey, TValue>( this IEnumerable<TElement> source, Func<TElement, TKey> keyGetter, Func<TElement, TValue> valueGetter) { IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(); foreach (var e in source) { var key = keyGetter(e); if (dict.ContainsKey(key)) { continue; } dict.Add(key, valueGetter(e)); } return dict; } } }
这里二开校验器StockPrincipalValidator,编译组件,放到系统运行bin目录,重启IIS。采用python脚本注册校验器,这种方式比较灵活,方便修改仓库Key和单据明细的名称,不同的单据这两个值不太一样,注册脚本针对性修改即可。
# 审核时添加仓库负责人校验器 clr.AddReference('Kingdee.BOS') clr.AddReference('Kingdee.BOS.Core') clr.AddReference('Kingdee.K3.BD.App.Core') clr.AddReference('Kingdee.K3.SCM.App') clr.AddReference('Kingdee.K3.SCM.Stock.App.CustomizePlugIn') from Kingdee.BOS import * from Kingdee.BOS.Core import * from Kingdee.BOS.Core.Metadata import * from Kingdee.BOS.Core.Metadata.FormValidationElement import AbstractValidation from Kingdee.K3.BD.App.Core import * from Kingdee.K3.BD.App.Core.Validator import * from Kingdee.K3.SCM.Stock.App.CustomizePlugIn.Validator import StockPrincipalValidator from Kingdee.K3.SCM.App import * def OnAddValidators(e): # 其他出库单 misDelivery_StockPrincipalValidator = StockPrincipalValidator(); misDelivery_StockPrincipalValidator.AlwaysValidate = True; misDelivery_StockPrincipalValidator.EntityKey = "FBillHead"; misDelivery_StockPrincipalValidator.StockId = "StockId"; misDelivery_StockPrincipalValidator.EntryName = "BillEntry"; e.Validators.Add(misDelivery_StockPrincipalValidator);
【实现效果】
StockPrincipalValidator.rar(2.45KB)
推荐阅读