diff --git a/DataBase/版本日志/SGGLDB_V2026-09-14-lpf(新增入库条码查询菜单).sql b/DataBase/版本日志/SGGLDB_V2026-09-14-lpf(新增入库条码查询菜单).sql new file mode 100644 index 00000000..f2c2c527 --- /dev/null +++ b/DataBase/版本日志/SGGLDB_V2026-09-14-lpf(新增入库条码查询菜单).sql @@ -0,0 +1,57 @@ +SET XACT_ABORT ON; +BEGIN TRANSACTION; + +DECLARE @MenuId NVARCHAR(50) = N'74048E7D-C9D3-45A1-B985-DCE1DB5723F5'; +DECLARE @InPlanMenuId NVARCHAR(50) = N'324C72AF-447A-4308-AFB7-ABF788C58240'; +DECLARE @SuperMenu NVARCHAR(50); +DECLARE @MenuType NVARCHAR(50); +DECLARE @IsOffice BIT; + +SELECT + @SuperMenu = SuperMenu, + @MenuType = MenuType, + @IsOffice = IsOffice +FROM dbo.Sys_Menu +WHERE MenuId = @InPlanMenuId; + +IF @SuperMenu IS NULL +BEGIN + RAISERROR(N'未找到“入库申请”菜单,无法确定入库条码查询菜单位置。', 16, 1); + ROLLBACK TRANSACTION; + RETURN; +END; + +IF NOT EXISTS (SELECT 1 FROM dbo.Sys_Menu WHERE MenuId = @MenuId) +BEGIN + INSERT INTO dbo.Sys_Menu + (MenuId, MenuName, Icon, Url, SortIndex, SuperMenu, MenuType, IsOffice, IsEnd, IsUsed) + VALUES + (@MenuId, N'入库条码查询', NULL, N'CLGL/InputBarCodeQuery.aspx', + 30, @SuperMenu, @MenuType, @IsOffice, 1, 1); +END +ELSE +BEGIN + UPDATE dbo.Sys_Menu + SET MenuName = N'入库条码查询', + Url = N'CLGL/InputBarCodeQuery.aspx', + SortIndex = 30, + SuperMenu = @SuperMenu, + MenuType = @MenuType, + IsEnd = 1, + IsUsed = 1 + WHERE MenuId = @MenuId; +END; + +INSERT INTO dbo.Sys_RolePower (RolePowerId, RoleId, MenuId, MenuType, IsOffice) +SELECT CONVERT(NVARCHAR(50), NEWID()), source.RoleId, @MenuId, @MenuType, source.IsOffice +FROM dbo.Sys_RolePower source +WHERE source.MenuId = @InPlanMenuId + AND NOT EXISTS + ( + SELECT 1 + FROM dbo.Sys_RolePower target + WHERE target.RoleId = source.RoleId + AND target.MenuId = @MenuId + ); + +COMMIT TRANSACTION; diff --git a/SGGL/BLL/CLGL/TwArrivalStatisticsService.cs b/SGGL/BLL/CLGL/TwArrivalStatisticsService.cs index 8f7d339f..1daa3004 100644 --- a/SGGL/BLL/CLGL/TwArrivalStatisticsService.cs +++ b/SGGL/BLL/CLGL/TwArrivalStatisticsService.cs @@ -149,18 +149,89 @@ namespace BLL } } + // 以下三个集合只服务于“按材料 Code 挑选材料主编码”的场景: + // remainingRequiredNumByCode:当前明细及后续明细的剩余总需求; + // stocksByCode:同一 Code 下所有可用的材料主编码库存; + // usedMaterialCodesByCode:本轮匹配已经使用过的材料主编码,用于优先继续消耗其余量。 + Dictionary remainingRequiredNumByCode = null; + Dictionary> stocksByCode = null; + Dictionary> usedMaterialCodesByCode = null; + if (matchByMaterialCodeLibCode) + { + // 按 Code 汇总全部需求,选择新主编码时可判断一个库存能否覆盖剩余需求。 + remainingRequiredNumByCode = requiredMaterials + .Where(x => !string.IsNullOrEmpty(x.MaterialCode)) + .GroupBy(x => x.MaterialCode) + .ToDictionary(x => x.Key, x => x.Sum(y => y.NeedNum ?? 0)); + + // 出库申请预扣后,仅保留仍有可用数量的主编码库存。 + stocksByCode = stockList + .Where(x => !string.IsNullOrEmpty(x.Code) && (x.StockNum ?? 0) > 0) + .GroupBy(x => x.Code) + .ToDictionary(x => x.Key, x => x.ToList()); + + // 每个 Code 独立记录已经启用的主编码,避免不同材料之间相互影响。 + usedMaterialCodesByCode = stocksByCode.ToDictionary(x => x.Key, x => new HashSet()); + } + foreach (var material in requiredMaterials) { material.Id = Guid.NewGuid().ToString(); // 材料导入只保存材料编码 Code,匹配时按 Code 从库存中挑选实际材料主编码 PipeLineMatCode。 - var stock = matchByMaterialCodeLibCode - ? stockList - .Where(x => x.Code == material.MaterialCode && (x.StockNum ?? 0) > 0) - // 优先选择库存能覆盖本条需求的材料主编码,再按主编码稳定排序,保证匹配结果可复现。 - .OrderByDescending(x => (x.StockNum ?? 0) >= (material.NeedNum ?? 0)) - .ThenBy(x => x.PipeLineMatCode) - .FirstOrDefault() - : stockList.FirstOrDefault(x => x.PipeLineMatCode == material.MaterialCode); + Tw_MaterialStockOutput stock = null; + if (matchByMaterialCodeLibCode) + { + if (!string.IsNullOrEmpty(material.MaterialCode) && stocksByCode.ContainsKey(material.MaterialCode)) + { + decimal needNum = material.NeedNum ?? 0; + decimal remainingRequiredNum = remainingRequiredNumByCode[material.MaterialCode]; + // StockNum 会在每条明细匹配后原地扣减,这里读取的是本轮实时剩余库存。 + var candidates = stocksByCode[material.MaterialCode] + .Where(x => (x.StockNum ?? 0) > 0) + .ToList(); + var usedMaterialCodes = usedMaterialCodesByCode[material.MaterialCode]; + + // 第一优先级:复用已使用且能完整满足当前明细的主编码,减少主编码种类; + // 在多个已用主编码都满足时,选择库存最小的,使其更接近用完。 + stock = candidates + .Where(x => usedMaterialCodes.Contains(x.PipeLineMatCode) && (x.StockNum ?? 0) >= needNum) + .OrderBy(x => x.StockNum ?? 0) + .ThenBy(x => x.PipeLineMatCode) + .FirstOrDefault() + // 第二优先级:启用一个能覆盖剩余总需求的新主编码,保证后续仍只使用这一种主编码; + // 若有多个可选,选择库存最小的,减少最终余料。 + ?? candidates + .Where(x => !usedMaterialCodes.Contains(x.PipeLineMatCode) && (x.StockNum ?? 0) >= remainingRequiredNum) + .OrderBy(x => x.StockNum ?? 0) + .ThenBy(x => x.PipeLineMatCode) + .FirstOrDefault() + // 第三优先级:无法一次覆盖剩余总需求时,选择刚好能满足当前明细且余量最小的新主编码。 + ?? candidates + .Where(x => !usedMaterialCodes.Contains(x.PipeLineMatCode) && (x.StockNum ?? 0) >= needNum) + .OrderBy(x => x.StockNum ?? 0) + .ThenBy(x => x.PipeLineMatCode) + .FirstOrDefault() + // 所有库存都不足以完整满足当前明细时,选择库存最大的主编码以保证匹配量最大; + // 库存相同时优先已使用的主编码,最后按主编码排序保证结果稳定。 + ?? candidates + .OrderByDescending(x => x.StockNum ?? 0) + .ThenByDescending(x => usedMaterialCodes.Contains(x.PipeLineMatCode)) + .ThenBy(x => x.PipeLineMatCode) + .FirstOrDefault(); + + if (stock != null) + { + usedMaterialCodes.Add(stock.PipeLineMatCode); + } + // 当前明细处理完后,只保留后续明细的总需求供下一轮选码判断。 + remainingRequiredNumByCode[material.MaterialCode] -= needNum; + } + } + else + { + // 旧调用传入的是材料主编码,保持原来的精确匹配逻辑。 + stock = stockList.FirstOrDefault(x => x.PipeLineMatCode == material.MaterialCode); + } var thisMaterialStockNum = stock?.StockNum ?? 0; if (thisMaterialStockNum >= material.NeedNum) { diff --git a/SGGL/BLL/CLGL/TwInputdetailBarCodeService.cs b/SGGL/BLL/CLGL/TwInputdetailBarCodeService.cs index bc1bbae9..c973e575 100644 --- a/SGGL/BLL/CLGL/TwInputdetailBarCodeService.cs +++ b/SGGL/BLL/CLGL/TwInputdetailBarCodeService.cs @@ -39,13 +39,16 @@ namespace BLL (string.IsNullOrEmpty(table.Id) || x.Id.Contains(table.Id)) && (string.IsNullOrEmpty(table.InputDetailId) || x.InputDetailId.Contains(table.InputDetailId)) && (string.IsNullOrEmpty(table.InputMasterId) || x.InputMasterId.Contains(table.InputMasterId)) && - (string.IsNullOrEmpty(table.MaterialCode) || x.MaterialCode.Contains(table.MaterialCode)) + (string.IsNullOrEmpty(table.ProjectId) || master.ProjectId == table.ProjectId) && + (string.IsNullOrEmpty(table.MaterialCode) || x.MaterialCode.Contains(table.MaterialCode)) && + (string.IsNullOrEmpty(table.BarCode) || x.BarCode.Contains(table.BarCode)) orderby master.CusBillCode, x.MaterialCode, x.Id select new Tw_InputDetailBarCodeOutput { Id = x.Id, InputDetailId = x.InputDetailId, InputMasterId = x.InputMasterId, + ProjectId = master.ProjectId, CusBillCode = master.CusBillCode, MaterialCode = x.MaterialCode, Code = mat.Code, diff --git a/SGGL/BLL/HJGL/BaseInfo/MaterialCodeLibService.cs b/SGGL/BLL/HJGL/BaseInfo/MaterialCodeLibService.cs index 7def0725..fe386918 100644 --- a/SGGL/BLL/HJGL/BaseInfo/MaterialCodeLibService.cs +++ b/SGGL/BLL/HJGL/BaseInfo/MaterialCodeLibService.cs @@ -20,7 +20,7 @@ /// public static Model.MaterialCodeLibBarCodeOutput GetBarCodeMaterialInfo(string barCode) { - return (from detailBarCode in Funs.DB.Tw_InputDetailBarCode + var result= (from detailBarCode in Funs.DB.Tw_InputDetailBarCode join x in Funs.DB.HJGL_MaterialCodeLib on detailBarCode.MaterialCode equals x.MaterialCode where detailBarCode.BarCode == barCode select new Model.MaterialCodeLibBarCodeOutput @@ -36,6 +36,24 @@ BarCode = detailBarCode.BarCode, Quantity = detailBarCode.Quantity }).FirstOrDefault(); + + if (result==null) + { + result = (from x in Funs.DB.HJGL_MaterialCodeLib + where x.MaterialCode == barCode + select new Model.MaterialCodeLibBarCodeOutput + { + MaterialCode = x.MaterialCode, + Code = x.Code, + HeatNo = x.HeatNo, + BatchNo = x.BatchNo, + MaterialName = x.MaterialName, + MaterialDef = x.MaterialDef, + MaterialSpec = x.MaterialSpec, + MaterialUnit = x.MaterialUnit + }).FirstOrDefault(); + } + return result; } /// diff --git a/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx new file mode 100644 index 00000000..9eeea439 --- /dev/null +++ b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx @@ -0,0 +1,133 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="InputBarCodeQuery.aspx.cs" Inherits="FineUIPro.Web.CLGL.InputBarCodeQuery" %> + + + + + + 入库条码查询 + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.cs b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.cs new file mode 100644 index 00000000..784e49ed --- /dev/null +++ b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.cs @@ -0,0 +1,84 @@ +using BLL; +using System; +using System.Data; +using System.IO; + +namespace FineUIPro.Web.CLGL +{ + public partial class InputBarCodeQuery : PageBase + { + protected void Page_Load(object sender, EventArgs e) + { + if (!IsPostBack) + { + ddlPageSize.SelectedValue = Grid1.PageSize.ToString(); + BindGrid(); + } + } + + private void BindGrid() + { + var query = new Model.Tw_InputDetailBarCodeOutput + { + ProjectId = CurrUser.LoginProjectId, + MaterialCode = txtMaterialCode.Text.Trim(), + BarCode = txtBarCode.Text.Trim() + }; + Grid1.DataSource = TwInputdetailBarCodeService.GetListData(query, Grid1); + Grid1.RecordCount = TwInputdetailBarCodeService.Count; + Grid1.DataBind(); + } + + protected void btnSearch_Click(object sender, EventArgs e) + { + Grid1.PageIndex = 0; + BindGrid(); + } + + protected void Grid1_PageIndexChange(object sender, GridPageEventArgs e) + { + Grid1.PageIndex = e.NewPageIndex; + BindGrid(); + } + + protected void Grid1_RowCommand(object sender, GridCommandEventArgs e) + { + if (e.CommandName != "btnBarCodePrint") + { + return; + } + + FastReportService.ResetData(); + var query = new Model.Tw_InputDetailBarCodeOutput + { + Id = e.RowID, + ProjectId = CurrUser.LoginProjectId + }; + var list = TwInputdetailBarCodeService.GetListData(query); + if (list.Count == 0) + { + ShowNotify("未找到入库条码明细。", MessageBoxIcon.Warning); + return; + } + + DataTable table = LINQToDataTable(list); + table.TableName = "Table1"; + FastReportService.AddFastreportTable(table); + + string rootPath = Server.MapPath("~/"); + string templatePath = "File\\Fastreport\\材料入库条码.frx"; + if (File.Exists(rootPath + templatePath)) + { + string printUrl = ResolveUrl(FastReportService.ExportReport(rootPath + templatePath)); + PageContext.RegisterStartupScript(string.Format("printByHiddenFrame('{0}');", printUrl)); + } + } + + protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e) + { + Grid1.PageSize = Convert.ToInt32(ddlPageSize.SelectedValue); + Grid1.PageIndex = 0; + BindGrid(); + } + } +} diff --git a/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.designer.cs b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.designer.cs new file mode 100644 index 00000000..09837e63 --- /dev/null +++ b/SGGL/FineUIPro.Web/CLGL/InputBarCodeQuery.aspx.designer.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <自动生成> +// 此代码由工具生成。 +// +//------------------------------------------------------------------------------ + +namespace FineUIPro.Web.CLGL +{ + public partial class InputBarCodeQuery + { + protected global::System.Web.UI.HtmlControls.HtmlForm form1; + protected global::FineUIPro.PageManager PageManager1; + protected global::FineUIPro.Panel Panel1; + protected global::FineUIPro.Grid Grid1; + protected global::FineUIPro.Toolbar Toolbar1; + protected global::FineUIPro.TextBox txtMaterialCode; + protected global::FineUIPro.TextBox txtBarCode; + protected global::FineUIPro.ToolbarFill ToolbarFill1; + protected global::FineUIPro.Button btnSearch; + protected global::System.Web.UI.WebControls.Label lblNumber; + protected global::FineUIPro.ToolbarSeparator ToolbarSeparator1; + protected global::FineUIPro.ToolbarText ToolbarText1; + protected global::FineUIPro.DropDownList ddlPageSize; + } +} diff --git a/SGGL/FineUIPro.Web/CLGL/MaterialStock.aspx b/SGGL/FineUIPro.Web/CLGL/MaterialStock.aspx index 67eb51c9..b823c5bd 100644 --- a/SGGL/FineUIPro.Web/CLGL/MaterialStock.aspx +++ b/SGGL/FineUIPro.Web/CLGL/MaterialStock.aspx @@ -91,7 +91,7 @@ - +