#使用技巧#如何利用二开插件实现自动核销原创
金蝶云社区-张大星
张大星
24人赞赏了该文章 471次浏览 未经作者许可,禁止转载编辑于2022年12月14日 16:16:11

一、业务背景

大家都知道系统有自动核销的功能

例如正负自动核销,但是系统的自动核销只支持下面的场景

image.png

如果换了其他的场景,就无法支持了,比如客户找不到源单的情况下退料,然后再进行补料(创建补料订单),如下图

image.png

这个也是系统标准流程哦,同样有着上下游的关联关系,但是却无法进行自动核销,(下面有解释原因)


二、关键步骤

1、通过分析系统标准的核销插件,在应付单--审核上有自动核销的插件,发现系统只是对源单为采购退料单的应付做了自动核销的处理,所以导致无法进行,因为我们流程最后做的采购入库单

2、所以我们通过修改标准的插件的取数逻辑,用我们的sql替换掉标准的sql,结果发现提示“没有需要进行核销的数据”,此处是因为标准插件核销方式methodid是71,71不支持我们的逻辑,在后续接口里面会把我们的数据过滤掉。所以我们要用72,此处71表示自动核销,72是手工核销;但是将methodid改为72 以后,又报错“给定的关键字不在字典中”;这是因为需要在参数里面多两个字段两个字段FBUSINESSTYPE(如果是费用的BUSINESSTYPE就是FY,标准应收就是BZ,标准应付就是CG)跟FMatchAmountFor(价税合计);


PS:系统之前是有核销接口如下面小伙伴的帖子https://vip.kingdee.com/article/43729857239646208?productLineId=1 但是升级升级8.0以后就不能用了,而且总部研发也表示此接口后续不会再维护;


上代码如下:分别是核销与反核销

 public class YFDZDHX: AbstractOperationServicePlugIn
    {
        List<long> ids = new List<long>();
        public override void OnPreparePropertys(PreparePropertysEventArgs e)
        {
            base.OnPreparePropertys(e);
            if (!e.FieldKeys.Contains("FSetAccountType"))
            {
                e.FieldKeys.Add("FSetAccountType");
            }
            if (!e.FieldKeys.Contains("FSOURCETYPE"))
            {
                e.FieldKeys.Add("FSOURCETYPE");
            }
            if (!e.FieldKeys.Contains("SourceBillNo"))
            {
                e.FieldKeys.Add("SourceBillNo");
            }
        }

        public virtual CalParameter GetZtCalPar(DynamicObject[] dataEntry)
        {
            CalParameter result = new CalParameter();
            Dictionary<string, List<long>> dictionary = new Dictionary<string, List<long>>();
            if (dataEntry != null && dataEntry.Length > 0)
            {
                List<DynamicObject> list = (from p in dataEntry
                                            where Convert.ToString(p["FSetAccountType"]) == "2" 
                                            select p).ToList<DynamicObject>();
                foreach (DynamicObject current in list)
                {
                    List<DynamicObject> list2 = (from p in current["AP_PAYABLEENTRY"] as DynamicObjectCollection//源单类型=采购入库单 或者源单类型=采购退料单且源单编号不为空
                                                 where (Convert.ToString(p["FSOURCETYPE"]) == "STK_InStock" || Convert.ToString(p["FSOURCETYPE"]) == "PUR_MRB") && Convert.ToString(p["SourceBillNo"]).Trim().Length > 0
                                                 select p).ToList<DynamicObject>();
                    if (list2 != null && list2.Count > 0)
                    {
                        string key = Convert.ToString(list2[0]["FSOURCETYPE"]);
                        if (dictionary.ContainsKey(key))
                        {
                            if (!dictionary[key].Contains(Convert.ToInt64(current["Id"])))
                            {
                                dictionary[key].Add(Convert.ToInt64(current["Id"]));
                            }
                        }
                        else
                        {
                            dictionary[key] = new List<long>
                            {
                                Convert.ToInt64(current["Id"])
                            };
                        }
                    }
                }
            }
            if (dictionary.Count > 0)
            {
                result = this.GetZtCalPar(dictionary);
            }
            return result;
        }

        public virtual CalParameter GetZtCalPar(Dictionary<string, List<long>> dctData)
        {
            CalParameter calParameter = new CalParameter();
            IDBService service = ServiceHelper.GetService<IDBService>();
            //创建临时表
            string text = service.CreateTemporaryTableName(base.Context);
            //取出单据  
            this.GetOutReturnData(text, dctData);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("select distinct FID,FEntryID,FMatchQty,FBUSINESSTYPE,FMatchAmountFor from {0} ", text);//主要位置

            DynamicObjectCollection dynamicObjectCollection = DBUtils.ExecuteDynamicObject(base.Context, stringBuilder.ToString(), null, null, CommandType.Text, new SqlParam[0]);
            if (dynamicObjectCollection != null && dynamicObjectCollection.Count > 0)
            {
                List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
                List<Dictionary<string, object>> list2 = new List<Dictionary<string, object>>();
                foreach (DynamicObject current in dynamicObjectCollection)
                {
                    list.Add(new Dictionary<string, object>
                    {
                        {
                            "FID",
                            current["FID"]
                        },
                        {
                            "FEntryID",
                            current["FEntryID"]
                        },
                        {
                            "FMatchQty",
                            current["FMatchQty"]
                        },
                       {
                            "FBUSINESSTYPE",
                            current["FBUSINESSTYPE"]
                        },
                       {
                            "FMatchAmountFor",
                            current["FMatchAmountFor"]
                        }
                    });
                }

                calParameter.SrcDataDebit = list.ToArray();
                calParameter.SrcDataCredit = list2.ToArray();

            }
            service.DeleteTemporaryTableName(base.Context, new string[]
            {
                text
            });
            return calParameter;
        }

        public virtual void GetOutReturnData(string tableName, Dictionary<string, List<long>> dctData)
        {
            this.CreateTempTable(tableName);
            if (dctData.ContainsKey("STK_InStock"))
            {
                //第一步
                this.GetInStockAR(tableName, dctData["STK_InStock"]);
            }
            DynamicObjectCollection DynamicObjectCollection= Gettempdata(tableName);
            ids = dctData["STK_InStock"];//把ids取出来,执行成功后,更新手动核销为自动核销           

            //第二步
            this.GetInStockARBySide(tableName);
            DynamicObjectCollection DynamicObjectCollection1 = Gettempdata(tableName);

        }

        private DynamicObjectCollection Gettempdata(string tableName)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("select * from {0} ", tableName);           
            DynamicObjectCollection dynamicObjectCollection = DBUtils.ExecuteDynamicObject(base.Context, stringBuilder.ToString(), null, null, CommandType.Text, new SqlParam[0]);
            return dynamicObjectCollection;
        }
        //第二步 获取上游退料单跟退料单的应付
        public virtual void GetInStockARBySide(string tableName)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("insert into {0}(FID,FENTRYID,FMATCHQTY,FSOURCETYPE,FOUTID,FOUTENTRYID,FMatchAmountFor) ", tableName);
            stringBuilder.AppendLine("select t1.FID,t2.FENTRYID,t2.FBASICUNITQTY-t2.FBUYIVBASICQTY as FMatchQty,t2.FSOURCETYPE,trout.FID AS FOUTID,trout.FENTRYID AS FOUTENTRYID,A1.FMatchAmountFor*-1 ");
            stringBuilder.AppendLine("from T_AP_PAYABLE t1 ");
            stringBuilder.AppendLine("inner join T_AP_PAYABLEENTRY t2 on t1.fid=t2.fid and t1.fdocumentstatus='C' and t1.FSETACCOUNTTYPE='2' and t1.FByVerify='0' and t1.FRedBlue='0' and t2.FBASICUNITQTY-t2.FBUYIVBASICQTY<>0 ");
            stringBuilder.AppendLine("inner join T_AP_PAYABLE_LK lk1 on t2.FENTRYID=lk1.FENTRYID ");
            stringBuilder.AppendLine("inner join T_PUR_MRBENTRY tretu on lk1.FSBILLID=tretu.FID and lk1.FSID=tretu.FENTRYID and lk1.FSTABLENAME like 'T_PUR_MRBENTRY%' ");
            stringBuilder.AppendLine("INNER JOIN T_PUR_POORDERENTRY_LK B1 ON B1.FSBILLID=tretu.FID AND B1.FSID=tretu.FENTRYID ");
            stringBuilder.AppendLine("INNER JOIN t_PUR_POOrderEntry B2 ON B2.FENTRYID=B1.FENTRYID");
            stringBuilder.AppendLine("INNER JOIN T_STK_INSTOCKENTRY trout on   trout.FPOORDERENTRYID=B2.FENTRYID  ");
            stringBuilder.AppendFormat("inner join {0} a1 on a1.FOUTID=trout.FID and a1.FOUTENTRYID=trout.FENTRYID and a1.fsourcetype='{1}' ", tableName, "STK_InStock");
            DBUtils.Execute(base.Context, stringBuilder.ToString());
        }       

        public virtual void CreateTempTable(string tableName)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("create table {0} ( ", tableName);
            stringBuilder.AppendLine(" FID                      int                  not null default 0 ,");
            stringBuilder.AppendLine(" FENTRYID                 int                  not null default 0 ,");
            stringBuilder.AppendLine(" FMATCHQTY                decimal(23,10)       not null default 0 ,");
            stringBuilder.AppendLine(" FSOURCETYPE              varchar(80)          not null default ' ' ,");
            stringBuilder.AppendLine(" FOUTID                   int                  not null default 0 ,");
            stringBuilder.AppendLine(" FOUTENTRYID              int                  not null default 0 ,");
            stringBuilder.AppendLine(" FBUSINESSTYPE            varchar(80)          not null default 'CG' ,");
            stringBuilder.AppendLine(" FMatchAmountFor          decimal(23,10)       not null default 0 )");
            DBUtils.Execute(base.Context, stringBuilder.ToString());
        }
        //第一步,把当前的应付单跟上游的入库单插入临时表
        public virtual void GetInStockAR(string tableName, List<long> lstIds)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("insert into {0}(FID,FENTRYID,FMATCHQTY,FSOURCETYPE,FOUTID,FOUTENTRYID,FMatchAmountFor) ", tableName);
            stringBuilder.AppendLine("select t1.FID,t2.FENTRYID,t2.FBASICUNITQTY-t2.FBUYIVBASICQTY as FMatchQty,t2.FSOURCETYPE,tout.FID,tout.FENTRYID,T2.FALLAMOUNTFOR ");
            stringBuilder.AppendLine("from T_AP_PAYABLE t1 ");
            stringBuilder.AppendFormat("inner join ({0}) tid on t1.fid=tid.fid ", StringUtils.GetSqlWithCardinality(lstIds.Count, "@FID", 1, true));
            stringBuilder.AppendLine("inner join T_AP_PAYABLEENTRY t2 on t1.fid=t2.fid and t1.fdocumentstatus='C' and t1.FSETACCOUNTTYPE='2' and t1.FByVerify='0' and t1.FRedBlue='0' and t2.FBASICUNITQTY-t2.FBUYIVBASICQTY<>0 ");
            stringBuilder.AppendLine("inner join T_AP_PAYABLE_LK lk1 on t2.FENTRYID=lk1.FENTRYID ");
            stringBuilder.AppendLine("inner join T_STK_INSTOCKENTRY tout on lk1.FSBILLID=tout.FID and lk1.FSID=tout.FENTRYID and lk1.FSTABLENAME like 'T_STK_INSTOCKENTRY%' ");
            DBUtils.Execute(base.Context, stringBuilder.ToString(), new SqlParam("@FID", KDDbType.udt_inttable, lstIds.ToArray()));
        }      

        public override void EndOperationTransaction(EndOperationTransactionArgs e)
        {
            try
            {
                base.EndOperationTransaction(e);
                if (base.BusinessInfo.GetForm().Id.EqualsIgnoreCase("AP_Payable"))
                {
                    CalParameter ztCalPar = this.GetZtCalPar(e.DataEntitys);
                    if (ztCalPar.SrcDataDebit != null && ztCalPar.SrcDataDebit.Count<Dictionary<string, object>>() > 0)
                    {
                       
                        FinMatch finMatch = new FinMatch();
                        ztCalPar.Ctx = base.Context;
                        ztCalPar.FormID = base.BusinessInfo.GetForm().Id;
                        ztCalPar.FinMatchMethod = 72; //71自动核销  72手动核销
                        ztCalPar.MatchType = "3";
                        finMatch.para = ztCalPar;
                        IOperationResult operationResult = finMatch.FinMatchProcess();
                        this.AfterFinMatch(operationResult.FuncResult);
                        StringBuilder stringBuilder = new StringBuilder();
                        if (!operationResult.IsSuccess)
                        {
                            stringBuilder.AppendLine(ResManager.LoadKDString("核销失败", "003176000001825", SubSystemType.FIN, new object[0]));
                            foreach (OperateResult current in operationResult.OperateResult)
                            {
                                stringBuilder.AppendLine(current.Message);
                            }
                            throw new KDException("", stringBuilder.ToString());
                        }
                        if (operationResult.IsSuccess)
                        {
                            stringBuilder.AppendLine(ResManager.LoadKDString("核销成功", "003182000009431", SubSystemType.FIN, new object[0]));
                            foreach (OperateResult current2 in operationResult.OperateResult)
                            {
                                stringBuilder.AppendLine(current2.Message);
                            }
                            if (operationResult.OperateResult.Count>1)
                            {
                                if (ids.Count>0)
                                {
                                    string strids = string.Join<long>(",", ids);                                    
                                    //--应付付款核销记录
                                    StringBuilder stringBuilder1 = new StringBuilder();
                                    stringBuilder1.AppendLine("/*dialect*/UPDATE A SET A.FISJOINMATCH=1,A.FMATCHMETHODID=71");
                                    stringBuilder1.AppendLine("from T_AP_PAYABLE t1 ");
                                    stringBuilder1.AppendFormat("inner join (SELECT FID FROM fn_StrSplit('{0}',',') ) tid on t1.fid=tid.fid  ", strids);
                                    stringBuilder1.AppendLine("INNER JOIN T_AP_PAYMATCHLOGENTRY A1 ON t1.FID=A1.FSRCBILLID ");
                                    stringBuilder1.AppendLine("INNER JOIN T_AP_PAYMATCHLOG A ON  A.FID=A1.FID ");                                    
                                    DBUtils.Execute(base.Context, stringBuilder1.ToString(), new SqlParam("@FID", KDDbType.udt_inttable, ids.ToArray()));

                                    //--应付开票核销记录
                                    StringBuilder stringBuilder2 = new StringBuilder();
                                    stringBuilder2.AppendLine("/*dialect*/UPDATE A SET A.FISJOINMATCH=1,A.FMATCHMETHODID=71");
                                    stringBuilder2.AppendLine("from T_AP_PAYABLE t1 ");
                                    stringBuilder2.AppendFormat("inner join (SELECT FID FROM fn_StrSplit('{0}',',') ) tid on t1.fid=tid.fid  ", strids);
                                    stringBuilder2.AppendLine("INNER JOIN T_AP_BILLINGMATCHLOGENTRY A1 ON t1.FID=A1.FSRCBILLID ");
                                    stringBuilder2.AppendLine("INNER JOIN T_AP_BILLINGMATCHLOG A ON A.FID=A1.FID ");
                                    DBUtils.Execute(base.Context, stringBuilder2.ToString(), new SqlParam("@FID", KDDbType.udt_inttable, ids.ToArray()));


                                }

                            }
                            APCommonFunction.WriteLog(base.Context, ResManager.LoadKDString("审核", "003185000018287", SubSystemType.FIN, new object[0]), base.BusinessInfo.GetForm().Id, base.BusinessInfo.GetForm().SubsysId, stringBuilder.ToString());
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ARAP", ex.Message, ex);
                throw new KDException("", ex.Message);
            }
        }

     
        public virtual void AfterFinMatch(object objOpenMatchIDs)
        {
        }
    }



反核销插件要简单的多了

 public override void EndOperationTransaction(EndOperationTransactionArgs e)
        {
            try
            {
                base.EndOperationTransaction(e);
                if (base.BusinessInfo.GetForm().Id.EqualsIgnoreCase("AP_Payable") && e.DataEntitys.Length > 0)// && base.FormOperation.OperationId == FormOperation.Operation_UnAudit)
                {
                    long[] zfBillIds = this.GetZfBillIds(e.DataEntitys);//自己组装数据
                    if (zfBillIds != null && zfBillIds.Length > 0)
                    {
                        HashSet<long> billingIds = this.GetBillingIds(zfBillIds);
                        if (billingIds != null && billingIds.Count > 0)
                        {
                            IOperationResult operationResult = new FinMatch
                            {
                                para = new CalParameter
                                {
                                    Ctx = base.Context,
                                    FormID = base.BusinessInfo.GetForm().Id,
                                    FinMatchMethod = 72,
                                    MatchLogs = billingIds
                                }
                            }.UnFinMatchProcess();
                            bool arg_C3_0 = operationResult.IsSuccess;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ARAP", ex.Message, ex);
                throw new KDException("", ex.Message);
            }
        }

3、由于我们插件内调用的手工核销,所以在核销记录上会显示手工核销,如果不把手工核销伪装成自动核销,那么反核销插件就无法使用,而且反审核校验的时候也过不去;

4、插件分别放置在应付单的审核操作与反审核操作上,此处注意一定要加前置条件与标准的自动核销插件进行区分,防止后续功能冲突


三、小结

此种功能理论上还是需要总部进行完善的,毕竟采购退料流程都有两个,相应的核销功能也要跟上,不能做供应链的只管供应链,做应付的只负责应付....


赞 24