单据添加仓库负责人校验器原创
金蝶云社区-邱育华
邱育华
7人赞赏了该文章 1,127次浏览 未经作者许可,禁止转载编辑于2022年01月17日 16:38:47

【业务背景】想控制每个负责自己仓库的人只能出这个仓库的货

【需求描述】一个仓库只有一个具体的负责人,一个负责人可能不止负责一个仓库。而负责人只能出自己负责的仓库的物料,例如销售订单下推销售出库单,一个单据有自己库的物料还有不是自己库的物料,这时候下推以后不能保存审核。


【问题分析】当前标准产品未对仓库负责人做相关的业务逻辑校验,所以一些"数据规则选项勾选"、“设置数据规则"等设置均无法实现效果,且单据下推仓库也无法做过滤,下推完以后也能保存审核。

这里主要涉及两个业务场景:

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;
        }
    }
}


[%WG5~78D}C]@QKE{6DUQ_E.jpg




这里二开校验器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);




【实现效果】

6B(J(%]L0{27@JKQE53XVVR.png


EY@44HG9B{KMKXJR[2ATSV0.png

图标赞 7
7人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!