隔月补单造成关账报负库存问题的控制方法(二)——代码

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

隔月补单造成关账报负库存问题的控制方法(二)——代码

1、服务插件用校验器,主要C#代码如下(所有出库单审核按钮都要配置,本代码是通用代码,各单据都是注册同一个DLL):

 

using System;

using System.Data;

using System.Collections.Generic;

using System.ComponentModel;

using Kingdee.BOS.Core.DynamicForm.PlugIn;

using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;

using Kingdee.BOS.Core;

using Kingdee.BOS.Orm.DataEntity;

using Kingdee.BOS.App.Data;

using Kingdee.BOS.Core.Validation;

using Kingdee.BOS;


namespace WFHH.K3CLOUD.SCM.STK.StockCheckService.Plugin

{

    [Description("隔月入库致出库负库存检查")]

    [Kingdee.BOS.Util.HotUpdate]

    public class StockCheck : AbstractOperationServicePlugIn

    {

        public class OtherMonthValidator : AbstractValidator

        {

            //单据体实体名、数量字段名 各单据不统一,请根据业务实际增加启用的单据数据

            Dictionary<string, string> dcQty = new Dictionary<string, string>()

                {

                    { "其他出库单","BASEQTY"},

                    { "销售出库单","BASEUNITQTY"},

                    { "直接调拨单","BaseQty"},//FSrcStockID,FDestStockID

                    { "其他入库单","BaseQty"}


                };

            Dictionary<string, string> dcEntry = new Dictionary<string, string>()

                {

                    { "其他出库单","BillEntry"},

                    { "销售出库单","SAL_OUTSTOCKENTRY"},                  

                    { "直接调拨单","TransferDirectEntry"},//FSrcStockID,FDestStockID

                    { "其他入库单","BillEntry"}


                };

            public override void InitializeConfiguration(ValidateContext validateContext, Context ctx)

            {

                base.InitializeConfiguration(validateContext, ctx);

                if (validateContext.BusinessInfo != null)

                {

                    EntityKey = validateContext.BusinessInfo.GetEntity(0).Key;

                }

            }

            public override void Validate(ExtendedDataEntity[] dataEntities, ValidateContext validateContext, Context ctx)

            {

                if (validateContext.IgnoreWarning) return; // 警告已经被用户忽略,就不需要再次执行了 

                if (dataEntities == null || dataEntities.Length <= 0) return;

                // 循环校验每一个数据包(一个数据包对应一张单据)

                foreach (ExtendedDataEntity et in dataEntities)

                {

                    bool hasError = false;

                    // 进行数据校验

                    string formName = validateContext.BusinessInfo.GetForm().Name;

                    //对其他出入库单、直接调拨单做特别处理(INT/OUT双向单据都要做)

                    string stockDirect = "";

                    switch (formName)

                    {

                        case "其他出库单":

                            stockDirect = et.DataEntity["StockDirect"].ToString();

                            if (stockDirect.Equals("RETURN")) return;//退货方向入库,无需校验

                            break;

                        case "其他入库单":

                            stockDirect = et.DataEntity["StockDirect"].ToString();

                            if (stockDirect.Equals("GENERAL")) return; //普通方向入库,无需校验

                            break;

                        case "直接调拨单":

                            stockDirect = et.DataEntity["TransferDirect"].ToString();

                            break;

                        //请根据业务实际增加其他有双向出入库单据的库存方向处理

                    }

                    string entryName = dcEntry[formName].ToString();

                    string qtyField = dcQty[formName].ToString();

                    DateTime billDate = Convert.ToDateTime(et.DataEntity["Date"]);

                    DynamicObjectCollection detail = et.DataEntity[entryName] as DynamicObjectCollection;//单据体

                    foreach (DynamicObject d in detail)

                    {

                        int Id = Convert.ToInt32(d["Id"]);

                        double qty = Convert.ToDouble(d[qtyField]);

                        DynamicObject material = d["MaterialId"] as DynamicObject;

                        int materialId = Convert.ToInt32(material["Id"]);

                        DynamicObject stock = null;

                        #region 

                        //请根据业务实际增加其他有调拨出入库单据的出库仓库取值

                        if (formName.Equals("直接调拨单") && stockDirect.Equals("GENERAL"))

                            stock = d["SrcStockID"] as DynamicObject;

                        else if (formName.Equals("直接调拨单") && stockDirect.Equals("RETURN"))

                            stock = d["DestStockID"] as DynamicObject;

                        else stock = d["StockID"] as DynamicObject;

                        #endregion

                        int stockId = Convert.ToInt32(stock["Id"]);

                        int lot = Convert.ToInt32(d["Lot"]);

                        List<SqlParam> sqlParams = new List<SqlParam>();

                        sqlParams.Add(new SqlParam("@date", KDDbType.Date, billDate));

                        sqlParams.Add(new SqlParam("@materialId", KDDbType.Int32, materialId));

                        sqlParams.Add(new SqlParam("@stockId", KDDbType.Int32, stockId));

                        sqlParams.Add(new SqlParam("@lot", KDDbType.Int32, lot));

                        sqlParams.Add(new SqlParam("@qty", KDDbType.Double, qty));

                        sqlParams.Add(new SqlParam("@result", KDDbType.Double, -1) { Direction = ParameterDirection.Output });

                        List<SqlParam> sp = DBUtils.ExecuteStoreProcedure(ctx, "CHR_STOCK_CHECK", sqlParams);

                        if ((double)sp[0].Value < 0) hasError = true;//@result<0   

                        // 如果校验不通过,抛出校验异常

                        if (hasError)

                        {

                            validateContext.AddError(et, new ValidationErrorInfo(

                                                         "", // 出错字段Key,可以空

                                                        Convert.ToString(et.DataEntity[0]), // 数据包内码,必填,后续操作会据此内码避开此数据包

                                                        et.DataEntityIndex, // 出错的数据包在全部数据包中的顺序

                                                        et.RowIndex, // 出错的数据行在全部数据行中的顺序,如果校验基于单据头,此为0

                                                        "OTHER-MONTH-01", // 错误编码,可以任意设定一个字符,主要用于追查错误来源

                                                        "检查到有下月及后续的入库,本月库存不足,无法审核。物料编码=" + material["NUMBER"].ToString() + ",仓库=" + stock["NAME"].ToString(), // 错误的详细提示信息

                                                        "隔月入库检查", // 错误的简明提示信息

                                                        ErrorLevel.Error // 错误级别:警告、错误

                                                    ));

                        }

                    }

                }

            }

        }

        public override void OnAddValidators(AddValidatorsEventArgs e)

        {

            base.OnAddValidators(e);

            // 二、采用校验器模式

            var omv = new OtherMonthValidator();

            e.Validators.Add(omv);

        }

    }

}



2、插件涉及的存储过程CHR_STOCK_CHECK 的SQL语句(如公司业务涉及其他入库单,请自行UNION到RKD内):



ALTER PROCEDURE [dbo].[CHR_STOCK_CHECK] 

-- Add the parameters for the stored procedure here

@date DATE,@materialId INT , @stockId INT  ,@lot INT ,@qty FLoat,@result Float OUTPUT

AS

BEGIN

-- SET NOCOUNT ON added to prevent extra result sets from

-- interfering with SELECT statements.

SET NOCOUNT ON;


    -- Insert statements for procedure here

--SET @date='2023-8-1'

IF(MONTH(@date) BETWEEN 1 AND 11) BEGIN SET @date=CAST(YEAR(@date) AS VARCHAR(4))+'-' +CAST((MONTH(@date)+1) AS VARCHAR(2))  + '-01' END

ELSE  SET @date=CAST(YEAR(@date)+1 AS VARCHAR(4))+ '-01-01'


;WITH RKD AS(

SELECT FMATERIALID,FSTOCKID,FLOT,ISNULL(SUM(FREALQTY),0) FREALQTY FROM (

--采购入库单  

SELECT BE.FMATERIALID,BE.FSTOCKID,BE.FREALQTY,BE.FLOT_TEXT,BE.FLOT

FROM T_STK_INSTOCKENTRY BE

INNER JOIN T_STK_INSTOCK BM ON BM.FID=BE.FID

WHERE BM.FDocumentStatus='C'

AND BM.FCancelStatus='A'

AND BM.FDATE>=@date

AND BE.FMATERIALID=@materialId

AND BE.FSTOCKID=@stockId

AND BE.FLOT=@lot


UNION ALL

--委外退料单

SELECT SE.FMATERIALID,SE.FSTOCKID,SE.FBASEQTY,SE.FLOT_TEXT,SE.FLOT 

FROM T_SUB_RETURNMTRLENTRY SE

INNER JOIN T_SUB_RETURNMTRL SM ON SM.FID=SE.FID

WHERE SM.FDocumentStatus='C'

AND SM.FCancelStatus='A'

AND SM.FDATE>=@date

AND SE.FMATERIALID=@materialId

AND SE.FSTOCKID=@stockId

AND SE.FLOT=@lot


UNION ALL

--生产入库单

SELECT PE.FMATERIALID,PE.FSTOCKID,PE.FREALQTY,PE.FLOT_TEXT,PE.FLOT 

FROM T_PRD_INSTOCKENTRY PE 

INNER JOIN T_PRD_INSTOCK PM ON PM.FID=PE.FID

WHERE PM.FDocumentStatus='C'

AND PM.FCancelStatus='A'

AND PM.FDATE>=@date

AND PE.FMATERIALID=@materialId

AND PE.FSTOCKID=@stockId

AND PE.FLOT=@lot


UNION ALL

--生产退料单

SELECT RE.FMATERIALID,RE.FSTOCKID,RE.FBASEQTY,RE.FLOT_TEXT,RE.FLOT 

FROM T_PRD_ReturnMtrlENTRY RE 

INNER JOIN T_PRD_ReturnMtrl RM ON RM.FID=RE.FID

WHERE RM.FDocumentStatus='C'

AND RM.FCancelStatus='A'

AND RM.FDATE>=@date

AND RE.FMATERIALID=@materialId

AND RE.FSTOCKID=@stockId

AND RE.FLOT=@lot


UNION ALL

--销售退货单

SELECT SRE.FMATERIALID,SRE.FSTOCKID,SRE.FREALQTY,SRE.FLOT_TEXT,SRE.FLOT 

FROM T_SAL_RETURNSTOCKENTRY SRE 

INNER JOIN T_SAL_RETURNSTOCK SRM ON SRM.FID=SRE.FID

WHERE SRM.FDocumentStatus='C'

AND SRM.FCancelStatus='A'

AND SRM.FDATE>=@date

AND SRE.FMATERIALID=@materialId

AND SRE.FSTOCKID=@stockId

AND SRE.FLOT=@lot


UNION ALL

--其他入库单

SELECT ME.FMATERIALID,ME.FSTOCKID,ME.FQTY,ME.FLOT_TEXT,ME.FLOT

FROM T_STK_MISCELLANEOUSENTRY ME 

INNER JOIN T_STK_MISCELLANEOUS MM ON MM.FID=ME.FID

WHERE MM.FStockDirect='GENERAL'--RETURN 普通/退货

AND MM.FDocumentStatus='C'

AND MM.FCancelStatus='A'

AND MM.FDATE>=@date

AND ME.FMATERIALID=@materialId

AND ME.FSTOCKID=@stockId

AND ME.FLOT=@lot


UNION ALL

--其他出库单

SELECT DE.FMATERIALID,DE.FSTOCKID,DE.FQTY,DE.FLOT_TEXT,DE.FLOT

FROM T_STK_MISDELIVERYENTRY DE

INNER JOIN T_STK_MISDELIVERY DM ON DM.FID=DE.FID

WHERE DM.FStockDirect='RETURN'--RETURN 普通/退货

AND DM.FDocumentStatus='C'

AND DM.FCancelStatus='A'

AND DM.FDATE>=@date

AND DE.FMATERIALID=@materialId

AND DE.FSTOCKID=@stockId

AND DE.FLOT=@lot


UNION ALL

--直接调拨单

SELECT TE.FMATERIALID,TE.FDESTSTOCKID,TE.FQty,TE.FLOT_TEXT,TE.FLOT --DESTSTOCK是入库

FROM T_STK_STKTRANSFERINENTRY TE   

INNER JOIN T_STK_STKTRANSFERIN TM ON TM.FID=TE.FID

WHERE TM.FTRANSFERDIRECT='GENERAL'--RETURN 普通/退货

AND TM.FDocumentStatus='C'

AND TM.FCancelStatus='A'

AND TM.FDATE>=@date

AND TE.FMATERIALID=@materialId

AND TE.FDESTSTOCKID=@stockId

AND TE.FLOT=@lot


UNION ALL

--直接调拨单

SELECT TE.FMATERIALID,TE.FSRCSTOCKID,TE.FQty,TE.FLOT_TEXT,TE.FLOT --DESTSTOCK是入库

FROM T_STK_STKTRANSFERINENTRY TE 

INNER JOIN T_STK_STKTRANSFERIN TM ON TM.FID=TE.FID

WHERE TM.FTRANSFERDIRECT='RETURN'--GENERAL 退货/普通

AND TM.FDocumentStatus='C'

AND TM.FCancelStatus='A'

AND TM.FDATE>=@date

AND TE.FMATERIALID=@materialId

AND TE.FSRCSTOCKID=@stockId

AND TE.FLOT=@lot) RK GROUP BY FMATERIALID,FSTOCKID,FLOT

)

,STK AS(

SELECT FSTOCKID,FMATERIALID,FLOT, ISNULL(SUM(FBaseQty),0) STK

FROM T_STK_INVENTORY 

WHERE  FMATERIALID=@materialId

AND FSTOCKID=@stockId

AND FLOT=@lot

GROUP BY FSTOCKID,FMATERIALID,FLOT)



SELECT @result=ISNULL(STK.STK,0)-ISNULL(RKD.FREALQTY ,0) 

FROM STK

LEFT JOIN RKD ON RKD.FMATERIALID=STK.FMATERIALID AND RKD.FSTOCKID=STK.FSTOCKID AND RKD.FLOT =STK.FLOT


SET @result=ISNULL(@result,0)-@qty

SELECT @result RESULT


END



3、插件部署好后,实际运行效果如下图:

上传图片



大佬,请接受我的赞美


image.webp这两个里面的值是什么?


占楼 好贴


SQL 代码已更新,原先即时库存结果可能为null导致返回result也为null ,转换int的时候会报转换错误。


好帖


大佬,请接受我的赞美


这么好的帖子没人占沙发吗

隔月补单造成关账报负库存问题的控制方法(二)——代码

1、服务插件用校验器,主要C#代码如下(所有出库单审核按钮都要配置,本代码是通用代码,各单据都是注册同一个DLL): using System;using...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息