diff --git a/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmRecordOutboundController.cs b/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmRecordOutboundController.cs index 685812d..cec69b1 100644 --- a/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmRecordOutboundController.cs +++ b/DOAN.Admin.WebApi/Controllers/MES/Material/productionMaterial/MmRecordOutboundController.cs @@ -1,8 +1,10 @@ -using Microsoft.AspNetCore.Mvc; -using DOAN.Model.BZFM.Dto; -using DOAN.Model.BZFM; -using DOAN.Service.BZFM.IBZFMService; using DOAN.Admin.WebApi.Filters; +using DOAN.Model; +using DOAN.Model.BZFM; +using DOAN.Model.BZFM.Dto; +using DOAN.Service.BZFM; +using DOAN.Service.BZFM.IBZFMService; +using Microsoft.AspNetCore.Mvc; //创建时间:2025-12-25 namespace DOAN.Admin.WebApi.Controllers.BZFM @@ -98,5 +100,56 @@ namespace DOAN.Admin.WebApi.Controllers.BZFM return ToResponse(_MmRecordOutboundService.Delete(idArr)); } + /// + /// 导入 + /// + /// 使用IFromFile必须使用name属性否则获取不到文件 + /// + [HttpPost("importData")] + [Log(Title = "出库记录导入", BusinessType = BusinessType.IMPORT, IsSaveRequestData = false, IsSaveResponseData = true)] + [ActionPermissionFilter(Permission = "mmrecordoutbound:import")] + public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile) + { + if (formFile == null) + { + return SUCCESS(null); + } + ImportResultDto response = _MmRecordOutboundService.ImportRecordOutbound(formFile, HttpContext.GetName()); + + return SUCCESS(response); + } + + /// + /// 下载出库记录导入模板 + /// + /// + [HttpGet("importTemplate")] + [Log(Title = "出库记录模板", BusinessType = BusinessType.EXPORT)] + [AllowAnonymous] + public IActionResult ImportTemplateExcel() + { + // create an empty sample list of export DTO to generate header row + var sample = new List(); + var result = DownloadImportTemplate(sample, "recordoutbound"); + return ExportExcel(result.Item2, result.Item1); + } + + /// + /// 出库记录导出 + /// + /// + /// + [HttpGet("export")] + [Log(Title = "出库记录导出", BusinessType = BusinessType.EXPORT)] + [ActionPermissionFilter(Permission = "mmrecordoutbound:export")] + public IActionResult MaterialExport([FromQuery] MmRecordOutboundQueryDto recordoutbound) + { + var list = _MmRecordOutboundService.SelectRecordoutboundList(recordoutbound, new PagerInfo(1, 10000)); + + var data = (list?.Result ?? new List()); + + var result = ExportExcelMini(data, "recordoutbound", "出库记录"); + return ExportExcel(result.Item2, result.Item1); + } } } \ No newline at end of file diff --git a/DOAN.Model/MES/Material/Dto/MmRecordInboundDto.cs b/DOAN.Model/MES/Material/Dto/MmRecordInboundDto.cs index 1e93a71..04eb49f 100644 --- a/DOAN.Model/MES/Material/Dto/MmRecordInboundDto.cs +++ b/DOAN.Model/MES/Material/Dto/MmRecordInboundDto.cs @@ -121,7 +121,7 @@ namespace DOAN.Model.BZFM.Dto public int Id { get; set; } [ExcelColumn(Name = "入库单号")] - [SugarColumn(ColumnName = "Inbound_no")] + [SugarColumn(ColumnName = "inbound_no")] public string InboundNo { get; set; } [ExcelColumn(Name = "物料编码")] diff --git a/DOAN.Model/MES/Material/Dto/MmRecordOutboundDto.cs b/DOAN.Model/MES/Material/Dto/MmRecordOutboundDto.cs index 4a198af..c77d57e 100644 --- a/DOAN.Model/MES/Material/Dto/MmRecordOutboundDto.cs +++ b/DOAN.Model/MES/Material/Dto/MmRecordOutboundDto.cs @@ -107,4 +107,69 @@ namespace DOAN.Model.BZFM.Dto // 1-蓝单正向 2-红单逆向 public int ReceiptType { get; set; } = 1; } + + // + /// 出库记录导入导出 + /// + [SugarTable("mm_recordoutbound", "出库记录")] + public class MmRecordOutboundExcelDto + { + [ExcelColumn(Name = "id")] + [SugarColumn(IsIdentity = true, IsPrimaryKey = true)] + public int Id { get; set; } + + [ExcelColumn(Name = "出库单号")] + [SugarColumn(ColumnName = "outbound_no")] + public string OutboundNo { get; set; } + + [ExcelColumn(Name = "物料编码")] + [SugarColumn(ColumnName = "material_code")] + public string MaterialCode { get; set; } + + [ExcelColumn(Name = "物料名称")] + [SugarColumn(ColumnName = "material_name")] + public string MaterialName { get; set; } + + [ExcelColumn(Name = "批次号")] + [SugarColumn(ColumnName = "batch_no")] + public string BatchNo { get; set; } + + [ExcelColumn(Name = "出库数量")] + [SugarColumn(ColumnName = "quantity")] + public decimal Quantity { get; set; } + + [ExcelColumn(Name = "出库类型")] + [SugarColumn(ColumnName = "transaction_Type")] + public string TransactionType { get; set; } + + [ExcelColumn(Name = "操作员")] + [SugarColumn(ColumnName = "operator")] + public string Operator { get; set; } + + [ExcelColumn(Name = "仓库编码")] + [SugarColumn(ColumnName = "warehouse_code")] + public string WarehouseCode { get; set; } + + [ExcelColumn(Name = "仓库名称")] + [SugarColumn(ColumnName = "warehouse_name")] + public string WarehouseName { get; set; } + + [ExcelColumn(Name = "库位编码")] + [SugarColumn(ColumnName = "location_code")] + public string LocationCode { get; set; } + + [ExcelColumn(Name = "库位名称")] + [SugarColumn(ColumnName = "location_name")] + public string LocationName { get; set; } + + [ExcelColumn(Name = "关联订单号")] + [SugarColumn(ColumnName = "order_no")] + public string OrderNo { get; set; } + + public string SupplierName { get; set; } + + [ExcelColumn(Name = "创建时间")] + [SugarColumn(ColumnName = "created_time")] + public DateTime? CreatedTime { get; set; } + } } \ No newline at end of file diff --git a/DOAN.Service/MES/Material/IService/IMmRecordOutboundService.cs b/DOAN.Service/MES/Material/IService/IMmRecordOutboundService.cs index 75c8623..d6ff6d9 100644 --- a/DOAN.Service/MES/Material/IService/IMmRecordOutboundService.cs +++ b/DOAN.Service/MES/Material/IService/IMmRecordOutboundService.cs @@ -1,5 +1,6 @@ -using DOAN.Model.BZFM.Dto; using DOAN.Model.BZFM; +using DOAN.Model.BZFM.Dto; +using Microsoft.AspNetCore.Http; namespace DOAN.Service.BZFM.IBZFMService { @@ -16,6 +17,13 @@ namespace DOAN.Service.BZFM.IBZFMService MmRecordOutbound AddMmRecordOutbound(MmRecordOutbound parm); int UpdateMmRecordOutbound(MmRecordOutbound parm); + // + /// 导入 + /// + /// + /// + ImportResultDto ImportRecordOutbound(IFormFile formFile, string username); + public PagedInfo SelectRecordoutboundList(MmRecordOutboundQueryDto recordoutbound, PagerInfo pager); } } diff --git a/DOAN.Service/MES/Material/MmRecordOutboundService.cs b/DOAN.Service/MES/Material/MmRecordOutboundService.cs index 68d0b91..9eca396 100644 --- a/DOAN.Service/MES/Material/MmRecordOutboundService.cs +++ b/DOAN.Service/MES/Material/MmRecordOutboundService.cs @@ -5,6 +5,9 @@ using DOAN.Service.BZFM.IBZFMService; using Infrastructure.Attribute; using Infrastructure.Converter; using Infrastructure.Extensions; +using Microsoft.AspNetCore.Http; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; namespace DOAN.Service.BZFM { @@ -96,5 +99,262 @@ namespace DOAN.Service.BZFM return predicate; } + + /// + /// 导入数据 + /// + /// + /// + public ImportResultDto ImportRecordOutbound(IFormFile formFile, string username) + { + string message = "导出成功"; + List recordoutboundList = new(); + using (var stream = formFile.OpenReadStream()) + { + try + { + IWorkbook workbook = new XSSFWorkbook(stream); + ISheet sheet = workbook.GetSheetAt(0); + + #region 读取excel + + // 遍历每一行 + for (int row = 1; row <= sheet.LastRowNum; row++) + { + IRow currentRow = sheet.GetRow(row); + if (currentRow != null) // 确保行不为空 + { + MmRecordOutboundExcelDto recordoutbound = new MmRecordOutboundExcelDto(); + + //00 ID + NPOI.SS.UserModel.ICell currentCell_00 = currentRow.GetCell(0); + recordoutbound.Id = (int)currentCell_00?.NumericCellValue; + + //01 创建时间 + NPOI.SS.UserModel.ICell currentCell_01 = currentRow.GetCell(1); + recordoutbound.CreatedTime = currentCell_01?.DateCellValue ?? DateTime.Now; + + //02 出库单号 + NPOI.SS.UserModel.ICell currentCell_02 = currentRow.GetCell(2); + recordoutbound.OutboundNo = currentCell_02?.ToString(); + if ( + currentCell_02 == null + || string.IsNullOrEmpty(recordoutbound.OutboundNo) + ) + { + message = $"出库单号不可为空,第{row + 1}行"; + break; + } + + //03 物料编码 + NPOI.SS.UserModel.ICell currentCell_03 = currentRow.GetCell(3); + recordoutbound.MaterialCode = currentCell_03?.ToString(); + if ( + currentCell_03 == null + || string.IsNullOrEmpty(recordoutbound.MaterialCode) + ) + { + message = $"物料编码不可为空,第{row + 1}行"; + break; + } + + //04 物料名称 + NPOI.SS.UserModel.ICell currentCell_04 = currentRow.GetCell(4); + recordoutbound.MaterialName = currentCell_04?.ToString(); + if ( + currentCell_04 == null + || string.IsNullOrEmpty(recordoutbound.MaterialName) + ) + { + message = $"物料名称不可为空,第{row + 1}行"; + break; + } + + //05 仓库编码 + NPOI.SS.UserModel.ICell currentCell_05 = currentRow.GetCell(5); + recordoutbound.WarehouseCode = currentCell_05?.ToString(); + if ( + currentCell_05 == null + || string.IsNullOrEmpty(recordoutbound.WarehouseCode) + ) + { + message = $"仓库编码不可为空,第{row + 1}行"; + break; + } + + //06 仓库名称 + NPOI.SS.UserModel.ICell currentCell_06 = currentRow.GetCell(6); + recordoutbound.WarehouseName = currentCell_06?.ToString(); + if ( + currentCell_06 == null + || string.IsNullOrEmpty(recordoutbound.WarehouseName) + ) + { + message = $"仓库名称不可为空,第{row + 1}行"; + break; + } + + //07 库位编码 + NPOI.SS.UserModel.ICell currentCell_07 = currentRow.GetCell(7); + recordoutbound.LocationCode = currentCell_05?.ToString(); + if ( + currentCell_07 == null + || string.IsNullOrEmpty(recordoutbound.LocationCode) + ) + { + message = $"库位编码不可为空,第{row + 1}行"; + break; + } + + //08 库位名称 + NPOI.SS.UserModel.ICell currentCell_08 = currentRow.GetCell(8); + recordoutbound.LocationName = currentCell_06?.ToString(); + if ( + currentCell_08 == null + || string.IsNullOrEmpty(recordoutbound.LocationName) + ) + { + message = $"库位名称不可为空,第{row + 1}行"; + break; + } + + //09 出库数量 + NPOI.SS.UserModel.ICell currentCell_09 = currentRow.GetCell(9); + string QuantityStr = currentCell_09?.ToString(); + + if (currentCell_09 == null || string.IsNullOrWhiteSpace(QuantityStr)) + { + message = $"出库数量不可为空,第{row + 1}行"; + break; + } + + // 尝试转换为decimal + if (!decimal.TryParse(QuantityStr, out decimal Quantity)) + { + message = $"出库数量格式错误(必须是数字),第{row + 1}行"; + break; + } + + //验证数值范围(可根据业务需求调整) + if (Quantity < 0) + { + message = $"出库数量不能为负数,第{row + 1}行"; + break; + } + recordoutbound.Quantity = Quantity; + + //10 出库类型 + NPOI.SS.UserModel.ICell currentCell_10 = currentRow.GetCell(10); + recordoutbound.TransactionType = currentCell_08?.ToString(); + if ( + currentCell_10 == null + || string.IsNullOrEmpty(recordoutbound.TransactionType) + ) + { + message = $"入库类型不可为空,第{row + 1}行"; + break; + } + + //11关联订单号 + NPOI.SS.UserModel.ICell currentCell_11 = currentRow.GetCell(11); + recordoutbound.OrderNo = currentCell_11?.ToString() ?? string.Empty; + + //12 操作员 + NPOI.SS.UserModel.ICell currentCell_12 = currentRow.GetCell(12); + recordoutbound.Operator = currentCell_12?.ToString() ?? string.Empty; + + recordoutboundList.Add(recordoutbound); + } + } + + #endregion + } + catch (Exception ex) + { + return null; + } + + + } + // TODO 3.调用SplitInsert方法实现导入操作,注意主键列的配置(建议优化为,ID相同则修改,不同则新增) + + var x = Context + .Storageable(recordoutboundList) + //.SplitInsert(it => !it.Any()) + //.WhereColumns(it => it.Id) //如果不是主键可以这样实现(多字段it=>new{it.x1,it.x2}) + .ToStorage(); + var result = x.AsInsertable.ExecuteCommand(); + var result2 = x.AsUpdateable.IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommand(); //插入可插入部分; + + var importResult = new ImportResultDto + { + Message = message, + Inserted = x.InsertList.Count, + Updated = x.UpdateList.Count, + ErrorCount = x.ErrorList.Count, + IgnoredCount = x.IgnoreList.Count, + Deleted = x.DeleteList.Count, + Total = x.TotalList.Count, + }; + //输出统计 + Console.WriteLine(importResult); + // 4.收集错误与忽略信息,返回导入结果ImportResultDto 提示,需要修改IServer相关返回格式 + foreach (var item in x.ErrorList) + { + importResult.Errors.Add( + new ImportErrorDto + { + MaterialCode = item.Item.MaterialCode, + Message = item.StorageMessage, + } + ); + } + foreach (var item in x.IgnoreList) + { + importResult.Ignored.Add( + new ImportErrorDto + { + MaterialCode = item.Item.MaterialCode, + Message = item.StorageMessage, + } + ); + } + + return importResult; + } + + /// + /// 导出入库记录列表 + /// + /// + public PagedInfo SelectRecordoutboundList( + MmRecordOutboundQueryDto recordoutbound, + PagerInfo pager + ) + { + // Use the same predicate builder as GetList to support consistent filtering + var predicate = QueryExp(recordoutbound); + + var query = Queryable() + .Where(predicate.ToExpression()) + .Select(it => new MmRecordOutboundExcelDto + { + Id = it.Id, + CreatedTime = it.CreatedTime, + OutboundNo = it.OutboundNo, + MaterialCode = it.MaterialCode, + MaterialName = it.MaterialName, + WarehouseCode = it.WarehouseCode, + WarehouseName = it.WarehouseName, + LocationCode = it.LocationCode, + LocationName = it.LocationName, + Quantity = it.Quantity, + TransactionType = it.TransactionType, + OrderNo = it.OrderNo, + Operator = it.Operator, + }); + + return query.ToPage(pager); + } } } \ No newline at end of file