隔月补单造成关账报负库存问题的控制方法(二)——代码
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、插件部署好后,实际运行效果如下图:
大佬,请接受我的赞美
这两个里面的值是什么?
占楼 好贴
SQL 代码已更新,原先即时库存结果可能为null导致返回result也为null ,转换int的时候会报转换错误。
好帖
大佬,请接受我的赞美
这么好的帖子没人占沙发吗
隔月补单造成关账报负库存问题的控制方法(二)——代码
本文2024-09-16 17:40:18发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-17216.html