using DOAN.Model; using DOAN.Model.BZFM; using DOAN.Model.BZFM.Dto; using DOAN.Model.MES.order; using DOAN.Model.MES.product; using DOAN.Repository; using DOAN.Service.BZFM.IBZFMService; using Infrastructure.Attribute; using Infrastructure.Converter; using Infrastructure.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.IdentityModel.Tokens; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; namespace DOAN.Service.BZFM { /// /// 库存表Service业务层处理 /// [AppService(ServiceType = typeof(IMmInventoryService), ServiceLifetime = LifeTime.Transient)] public class MmInventoryService : BaseService, IMmInventoryService { /// /// 查询库存表列表 /// /// /// public PagedInfo GetList(MmInventoryQueryDto parm) { var predicate = QueryExp(parm); var response = Queryable() .Where(predicate.ToExpression()) .ToPage(parm); return response; } /// /// 获取详情 /// /// /// public MmInventory GetInfo(int Id) { var response = Queryable().Where(x => x.Id == Id).First(); return response; } /// /// 添加库存表 /// /// /// public MmInventory AddMmInventory(MmInventory model) { return Insertable(model).ExecuteReturnEntity(); } /// /// 修改库存表 /// /// /// public int UpdateMmInventory(MmInventory model) { return Update(model, true); } /// /// 查询导出表达式 /// /// /// private static Expressionable QueryExp(MmInventoryQueryDto parm) { var predicate = Expressionable .Create() .AndIF( !string.IsNullOrEmpty(parm.MaterialCode), m => m.MaterialCode == parm.MaterialCode ) .AndIF( !string.IsNullOrEmpty(parm.SupplierCode), m => m.SupplierCode == parm.SupplierCode ) .AndIF( !string.IsNullOrEmpty(parm.WarehouseName), m => m.WarehouseName.Contains(parm.WarehouseName) ) .AndIF( !string.IsNullOrEmpty(parm.WarehouseCode), m => m.WarehouseCode.Contains(parm.WarehouseCode) ) .AndIF( !string.IsNullOrEmpty(parm.LocationCode), m => m.LocationCode.Contains(parm.LocationCode) ) .AndIF( !string.IsNullOrEmpty(parm.SupplierName), m => m.SupplierName.Contains(parm.SupplierName) ) .AndIF(!string.IsNullOrEmpty(parm.BatchNo), m => m.BatchNo.Contains(parm.BatchNo)); return predicate; } public List GetMaterialOption() { try { return Context .Queryable() .Where(it => it.Status == "启用") .Select(it => new MmMaterialOption { Id = it.Id, MaterialCode = it.MaterialCode, MaterialName = it.MaterialName, CategoryCode = it.CategoryCode, Specification = it.Specification, SupplierCode = it.SupplierCode, SupplierName = it.SupplierName, Type = it.Type, }) .OrderBy(it => it.Type) .ToList(); } catch (Exception) { throw; } } public List GetLocationOption() { try { return Context .Queryable() .Where(it => it.Status == "启用") .Select(it => new MmLocationOption { Id = it.Id, WarehouseCode = it.WarehouseCode, WarehouseName = it.WarehouseName, LocationCode = it.LocationCode, LocationName = it.LocationName, LocationType = it.LocationType, }) .ToList(); } catch (Exception) { throw; } } public List GetTransactionOption() { try { return Context .Queryable() .Where(it => it.Status == "启用") .Select(it => new MmTransactionOption { Label = it.TypeName, Value = it.TypeCode, }) .ToList(); } catch (Exception) { throw; } } // 入库单 public string CreateInboundReceipt(InboundReceiptDto parm) { try { DateTime nowDate = DateTime.Now; // 计算有符号变动量(蓝单为正,红单为负) decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity); // 校验物料和库位 var mmMaterial = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .WhereIF( !string.IsNullOrEmpty(parm.SupplierCode), it => it.SupplierCode == parm.SupplierCode ) .First(); if (mmMaterial == null) return "物料不存在!"; var mmLocation = Context .Queryable() .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); if (mmLocation == null) return "仓库编码或库位编码不存在!"; // 启用事务 Context.Ado.BeginTran(); // 获取现有库存(同物料、批次、库位) var mmInventory = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .Where(it => it.BatchNo == parm.BatchNo) .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); // 若不存在则新增;存在则更新 if (mmInventory == null) { var newInventory = new MmInventory() { MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, SupplierCode = mmMaterial.SupplierCode, SupplierName = mmMaterial.SupplierName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, BatchNo = parm.BatchNo, CurrentQty = delta, Unit = parm.Unit, ExpiryDate = parm.ExpiryDate, LastUpdatedTime = null, ProductionDate = parm.ProductionDate, CreatedTime = nowDate, }; Context.Insertable(newInventory).ExecuteCommand(); } else { mmInventory.CurrentQty += delta; Context .Updateable(mmInventory) .UpdateColumns(it => it.CurrentQty) .ExecuteCommand(); } // 插入入库记录,入库单号使用自动增长策略(同日期按序号) var inboundNo = GenerateReceiptNo("RK"); MmRecordInbound newRecord = new() { InboundNo = inboundNo, BatchNo = parm.BatchNo, Operator = parm.Operator, StoveCode = parm.StoveCode, MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, SupplierCode = mmMaterial.SupplierCode, SupplierName = mmMaterial.SupplierName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, //TODO 待调整(可能涉及记录汇总) Quantity = delta, Unit = parm.Unit, ProductionDate = parm.ProductionDate, Workorder = parm.Workorder, WorkorderRaw = parm.WorkorderRaw, ExpiryDate = parm.ExpiryDate, CreatedTime = nowDate, TransactionType = parm.TransactionType, Remarks = parm.Remarks, }; Context.Insertable(newRecord).ExecuteCommand(); Context.Ado.CommitTran(); return "ok"; } catch (Exception ex) { // 回滚操作 Context.Ado.RollbackTran(); return ex.Message; } } // 出库单 public string CreateOutboundReceipt(OutboundReceiptDto parm) { try { DateTime nowDate = DateTime.Now; // 计算有符号变动量(蓝单为正,红单为负) decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity); Context.Ado.BeginTran(); MmInventory mmInventory = null; if (parm.InventoryId == -1) { var mmMaterial = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .WhereIF( !string.IsNullOrEmpty(parm.SupplierCode), it => it.SupplierCode == parm.SupplierCode ) .First(); if (mmMaterial == null) return "物料不存在!"; var mmLocation = Context .Queryable() .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); if (mmLocation == null) return "仓库编码或库位编码不存在!"; mmInventory = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .Where(it => it.BatchNo == parm.BatchNo) .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); if (mmInventory == null) { if (parm.ReceiptType == 1) { //库存为0或者不存在,不允许出库 Context.Ado.RollbackTran(); return "库存不存在,禁止出库!"; } var newInventory = new MmInventory() { MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, SupplierCode = mmMaterial.SupplierCode, SupplierName = mmMaterial.SupplierName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, BatchNo = parm.BatchNo, CurrentQty = -delta, Unit = parm.Unit, LastUpdatedTime = null, CreatedTime = nowDate, }; Context.Insertable(newInventory).ExecuteCommand(); mmInventory = newInventory; } else { if (mmInventory.CurrentQty - delta < 0) { Context.Ado.RollbackTran(); return "库存不足,无法出库!"; } mmInventory.CurrentQty -= delta; Context .Updateable(mmInventory) .UpdateColumns(it => it.CurrentQty) .ExecuteCommand(); } var outboundNo = GenerateReceiptNo("CK"); MmRecordOutbound newRecord = new() { OutboundNo = outboundNo, BatchNo = parm.BatchNo, StoveCode = parm.StoveCode, MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, SupplierCode = mmMaterial.SupplierCode, SupplierName = mmMaterial.SupplierName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, //TODO 待调整(可能涉及记录汇总) Quantity = -delta, Unit = parm.Unit, CreatedTime = nowDate, TransactionType = parm.TransactionType, Workorder = parm.Workorder, WorkorderRaw = parm.WorkorderRaw, OrderNo = parm.OrderNo, Remarks = parm.Remarks, Operator = parm.Operator, }; Context.Insertable(newRecord).ExecuteCommand(); Context.Ado.CommitTran(); return "ok"; } else { mmInventory = Context .Queryable() .Where(it => it.Id == parm.InventoryId) .First(); if (mmInventory == null) { if (parm.ReceiptType == 1) { //库存为0或者不存在,不允许出库 Context.Ado.RollbackTran(); return "库存不存在,禁止出库!"; } var newInventory = new MmInventory() { MaterialCode = mmInventory.MaterialCode, MaterialName = mmInventory.MaterialName, SupplierCode = mmInventory.SupplierCode, SupplierName = mmInventory.SupplierName, LocationCode = mmInventory.LocationCode, LocationName = mmInventory.LocationName, WarehouseCode = mmInventory.WarehouseCode, WarehouseName = mmInventory.WarehouseName, BatchNo = parm.BatchNo, CurrentQty = -delta, Unit = parm.Unit, LastUpdatedTime = null, CreatedTime = nowDate, }; Context.Insertable(newInventory).ExecuteCommand(); mmInventory = newInventory; } else { if (mmInventory.CurrentQty - delta < 0) { Context.Ado.RollbackTran(); return "库存不足,无法出库!"; } mmInventory.CurrentQty -= delta; Context .Updateable(mmInventory) .UpdateColumns(it => it.CurrentQty) .ExecuteCommand(); } var outboundNo = GenerateReceiptNo("CK"); MmRecordOutbound newRecord = new() { OutboundNo = outboundNo, BatchNo = parm.BatchNo, StoveCode = parm.StoveCode, Operator = parm.Operator, MaterialCode = mmInventory.MaterialCode, MaterialName = mmInventory.MaterialName, SupplierCode = mmInventory.SupplierCode, SupplierName = mmInventory.SupplierName, LocationCode = mmInventory.LocationCode, LocationName = mmInventory.LocationName, WarehouseCode = mmInventory.WarehouseCode, WarehouseName = mmInventory.WarehouseName, //TODO 待调整(可能涉及记录汇总) Quantity = -delta, Unit = parm.Unit, CreatedTime = nowDate, TransactionType = parm.TransactionType, Workorder = parm.Workorder, WorkorderRaw = parm.WorkorderRaw, OrderNo = parm.OrderNo, Remarks = parm.Remarks, }; Context.Insertable(newRecord).ExecuteCommand(); Context.Ado.CommitTran(); return "ok"; } } catch (Exception ex) { // 回滚操作 Context.Ado.RollbackTran(); return ex.Message; } } /// /// 根据单据类型与数量计算有符号变动量(蓝单为正,红单为负) /// /// 1 表示蓝单(正),其它为红单(负) /// 1 表示蓝单(正),其它为红单(负) private static decimal GetSignedQuantity(int receiptType, decimal quantity) { return receiptType == 1 ? Math.Abs(quantity) : -Math.Abs(quantity); } /// /// 生成单据编号,格式:{prefix}{yyyyMMdd}-{nnn} /// 例如:RK20251225-001 /// private string GenerateReceiptNo(string prefix) { var datePart = DateTime.Now.ToString("yyyyMMdd"); var baseNo = prefix + datePart + "-"; // 尝试从入库/出库表中获取当天最大的编号后缀 try { if (prefix == "RK") { var last = Context .Queryable() .Where(it => it.InboundNo.StartsWith(prefix + datePart)) .OrderByDescending(it => it.InboundNo) .Select(it => it.InboundNo) .First(); if (string.IsNullOrEmpty(last)) { return baseNo + "001"; } var suf = last.Substring((prefix + datePart).Length).TrimStart('-', '_'); if (int.TryParse(suf, out var n)) { return baseNo + (n + 1).ToString("D3"); } return baseNo + "001"; } else { var last = Context .Queryable() .Where(it => it.OutboundNo.StartsWith(prefix + datePart)) .OrderByDescending(it => it.OutboundNo) .Select(it => it.OutboundNo) .First(); if (string.IsNullOrEmpty(last)) { return baseNo + "001"; } var suf = last.Substring((prefix + datePart).Length).TrimStart('-', '_'); if (int.TryParse(suf, out var n)) { return baseNo + (n + 1).ToString("D3"); } return baseNo + "001"; } } catch { return baseNo + "001"; } } /// /// 导入数据 /// /// /// public ImportResultDto ImportInventory(IFormFile formFile, string username) { string message = "导入成功"; List inventoryList = 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) // 确保行不为空 { MmInventoryExcelDto inventory = new MmInventoryExcelDto(); //00 ID NPOI.SS.UserModel.ICell currentCell_00 = currentRow.GetCell(0); inventory.Id = (int)currentCell_00?.NumericCellValue; //02物料编码 NPOI.SS.UserModel.ICell currentCell_01 = currentRow.GetCell(1); inventory.MaterialCode = currentCell_01?.ToString(); if ( currentCell_01 == null || string.IsNullOrEmpty(inventory.MaterialCode) ) { message = $"物料编码不可为空,第{row + 1}行"; break; } //03批次号 NPOI.SS.UserModel.ICell currentCell_02 = currentRow.GetCell(2); inventory.BatchNo = currentCell_02?.ToString() ?? string.Empty; //04当前库存量 NPOI.SS.UserModel.ICell currentCell_03 = currentRow.GetCell(3); string currentQtyStr = currentCell_03?.ToString(); if (currentCell_03 == null || string.IsNullOrWhiteSpace(currentQtyStr)) { message = $"当前库存量不可为空,第{row + 1}行"; break; } // 尝试转换为decimal if (!decimal.TryParse(currentQtyStr, out decimal currentQty)) { message = $"当前库存量格式错误(必须是数字),第{row + 1}行"; break; } // 验证数值范围(可根据业务需求调整) //if (currentQty < 0) //{ // message = $"当前库存量不能为负数,第{row + 1}行"; // break; //} inventory.CurrentQty = currentQty; //05 仓库编码 NPOI.SS.UserModel.ICell currentCell_04 = currentRow.GetCell(4); inventory.WarehouseCode = currentCell_04?.ToString(); if ( currentCell_04 == null || string.IsNullOrEmpty(inventory.WarehouseCode) ) { message = $"仓库编码不可为空,第{row + 1}行"; break; } //06 仓库名称 NPOI.SS.UserModel.ICell currentCell_05 = currentRow.GetCell(5); inventory.WarehouseName = currentCell_05?.ToString(); if ( currentCell_05 == null || string.IsNullOrEmpty(inventory.WarehouseName) ) { message = $"仓库名称不可为空,第{row + 1}行"; break; } //07 库位编码 NPOI.SS.UserModel.ICell currentCell_06 = currentRow.GetCell(6); inventory.LocationCode = currentCell_06?.ToString(); if ( currentCell_06 == null || string.IsNullOrEmpty(inventory.LocationCode) ) { message = $"仓库编码不可为空,第{row + 1}行"; break; } //08 库位名称 NPOI.SS.UserModel.ICell currentCell_07 = currentRow.GetCell(7); inventory.LocationName = currentCell_07?.ToString(); if ( currentCell_07 == null || string.IsNullOrEmpty(inventory.LocationName) ) { message = $"库位名称不可为空,第{row + 1}行"; break; } //09 创建时间 NPOI.SS.UserModel.ICell currentCell_08 = currentRow.GetCell(8); inventory.CreatedTime = currentCell_08?.DateCellValue ?? DateTime.Now; inventoryList.Add(inventory); } } #endregion } catch (Exception ex) { return null; } } // TODO 3.调用SplitInsert方法实现导入操作,注意主键列的配置(建议优化为,ID相同则修改,不同则新增) var x = Context .Storageable(inventoryList) //.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 SelectInventoryList( MmInventoryQueryDto inventory, PagerInfo pager ) { // Use the same predicate builder as GetList to support consistent filtering var predicate = QueryExp(inventory); var query = Queryable() .Where(predicate.ToExpression()) .Select(it => new MmInventoryExcelDto { Id = it.Id, MaterialCode = it.MaterialCode, BatchNo = it.BatchNo, CurrentQty = it.CurrentQty, WarehouseCode = it.WarehouseCode, WarehouseName = it.WarehouseName, LocationCode = it.LocationCode, LocationName = it.LocationName, CreatedTime = it.CreatedTime, }); return query.ToPage(pager); } /// /// 查询出/入记录列表 /// /// /// public PagedInfo GetInOrOutRecord(MmInventoryRecordQueryDto parm) { PagedInfo result = new PagedInfo(); // 处理日期 if (parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue) { parm.StartTime = DOANConvertDate.ConvertLocalDate(parm.StartTime.Value); } if (parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue) { parm.EndTime = DOANConvertDate.ConvertLocalDate(parm.EndTime.Value); } // 查询入库记录 if (parm.SearchType == 1) { result = Context .Queryable() .WhereIF( parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue, it => it.CreatedTime >= parm.StartTime ) .WhereIF( parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue, it => it.CreatedTime <= parm.EndTime ) .WhereIF( !string.IsNullOrEmpty(parm.TransactionType), it => it.TransactionType == parm.TransactionType ) .Where(it => it.MaterialCode == parm.MaterialCode) .Where(it => it.SupplierCode == parm.SupplierCode) .Where(it => it.LocationCode == parm.LocationCode) .Where(it => it.BatchNo == parm.BatchNo) .Select(it => new MmInventoryRecordDto() { Id = it.Id, InboundNo = it.InboundNo, MaterialCode = it.MaterialCode, MaterialName = it.MaterialName, SupplierCode = it.SupplierCode, SupplierName = it.SupplierName, LocationCode = it.LocationCode, LocationName = it.LocationName, WarehouseCode = it.WarehouseCode, WarehouseName = it.WarehouseName, BatchNo = it.BatchNo, Quantity = it.Quantity, TransactionType = it.TransactionType, Operator = it.Operator, CreatedTime = it.CreatedTime, Workorder = it.Workorder, WorkorderRaw = it.WorkorderRaw, StoveCode = it.StoveCode, Remarks = it.Remarks, }) .OrderByDescending(it => it.CreatedTime) .ToPage(parm); } // 查询出库记录 else if (parm.SearchType == 2) { result = Context .Queryable() .WhereIF( parm.StartTime != null && parm.StartTime.Value > DateTime.MinValue, it => it.CreatedTime >= parm.StartTime ) .WhereIF( parm.EndTime != null && parm.EndTime.Value > DateTime.MinValue, it => it.CreatedTime <= parm.EndTime ) .WhereIF( !string.IsNullOrEmpty(parm.TransactionType), it => it.TransactionType == parm.TransactionType ) .Where(it => it.MaterialCode == parm.MaterialCode) //.Where(it => it.SupplierCode == parm.SupplierCode) .Where(it => it.LocationCode == parm.LocationCode) .Where(it => it.BatchNo == parm.BatchNo) .Select(it => new MmInventoryRecordDto() { Id = it.Id, OutboundNo = it.OutboundNo, MaterialCode = it.MaterialCode, MaterialName = it.MaterialName, LocationCode = it.LocationCode, LocationName = it.LocationName, WarehouseCode = it.WarehouseCode, WarehouseName = it.WarehouseName, BatchNo = it.BatchNo, Quantity = it.Quantity, TransactionType = it.TransactionType, Operator = it.Operator, CreatedTime = it.CreatedTime, Workorder = it.Workorder, WorkorderRaw = it.WorkorderRaw, OrderNo = it.OrderNo, Remarks = it.Remarks, }) .OrderByDescending(it => it.CreatedTime) .ToPage(parm); } return result; } /// /// 撤销单据,传入单据类型(1-入库单,2-出库单)和ID /// /// /// 返回ok即为成功其他都是不成功 /// public string RevokeReceipt(MmInventoryRevokeDto parm) { try { int _type = parm.Type; int _id = parm.Id; if (_type < -1 || _id < -1) { return $"传入参数有误,请检查:type-{_type},id-{_id}"; } // type == 1 入库单 if (_type == 1) { MmRecordInbound recordInbound = Context .Queryable() .Where(it => it.Id == _id) .First(); if (recordInbound == null) { return $"无此入库记录,请检查:type-{_type},id-{_id}"; } if (recordInbound.Remarks == "已撤销") { return $"此记录已撤销过,不可重复撤销"; } //做入库红单 InboundReceiptDto revokeRecepitDto = new() { ReceiptType = 2, MaterialCode = recordInbound.MaterialCode, BatchNo = recordInbound.BatchNo, LocationCode = recordInbound.LocationCode, WarehouseCode = recordInbound.WarehouseCode, SupplierCode = recordInbound.SupplierCode, StoveCode = recordInbound.StoveCode, Workorder = recordInbound.Workorder, WorkorderRaw = recordInbound.WorkorderRaw, Operator = recordInbound.Operator, Quantity = recordInbound.Quantity, TransactionType = "入库红单", Remarks = $"撤销操作,入库单号:{recordInbound.InboundNo}", }; string result = CreateInboundReceipt(revokeRecepitDto); if (result != "ok") { return result; } // 如果是成品入库则还要减少工单记录数量 Context.Ado.BeginTran(); if (recordInbound.TransactionType == "生产入库") { var workorderInfo = Context .Queryable() .Where(it => it.Workorder == recordInbound.Workorder) .First(); if (workorderInfo == null) { Context.Ado.RollbackTran(); return "工单不存在"; } // 数量调整 workorderInfo.ProductNum -= Math.Abs((int)recordInbound.Quantity); if (workorderInfo.ProductNum < 0) { workorderInfo.ProductNum = 0; } Context.Updateable(workorderInfo).ExecuteCommand(); } recordInbound.Remarks = "已撤销"; Context.Updateable(recordInbound).ExecuteCommand(); Context.Ado.CommitTran(); return result; } else { MmRecordOutbound recordOutbound = Context .Queryable() .Where(it => it.Id == _id) .First(); if (recordOutbound == null) { return $"无此出库记录,请检查:type-{_type},id-{_id}"; } if (recordOutbound.Remarks == "已撤销") { return $"此记录已撤销过,不可重复撤销"; } //做出库红单 OutboundReceiptDto revokeRecepitDto = new() { ReceiptType = 2, MaterialCode = recordOutbound.MaterialCode, BatchNo = recordOutbound.BatchNo, LocationCode = recordOutbound.LocationCode, WarehouseCode = recordOutbound.WarehouseCode, OrderNo = recordOutbound.OrderNo, Workorder = recordOutbound.Workorder, WorkorderRaw = recordOutbound.WorkorderRaw, Operator = recordOutbound.Operator, Quantity = recordOutbound.Quantity, TransactionType = "出库红单", Remarks = $"撤销操作,出库单号:{recordOutbound.OutboundNo}", }; string result = CreateOutboundReceipt(revokeRecepitDto); if (result != "ok") { return result; } Context.Ado.BeginTran(); // 如果是根据工单领料撤销,还需要调整领料记录 if ( recordOutbound.TransactionType == "领料出库" && !string.IsNullOrEmpty(recordOutbound.WorkorderRaw) ) { var workorderInfo = Context .Queryable() .Where(it => it.Workorder == recordOutbound.WorkorderRaw) .First(); if (workorderInfo == null) { Context.Ado.RollbackTran(); return "工单不存在"; } workorderInfo.ShipmentNum -= Math.Abs((int)recordOutbound.Quantity); if (workorderInfo.ShipmentNum < 0) { workorderInfo.ShipmentNum = 0; } Context .Updateable(workorderInfo) .UpdateColumns(it => new { it.ShipmentNum }) .ExecuteCommand(); } // 如果是出货则还要减少出货单库存和工单出货数量 if (recordOutbound.TransactionType == "出货出库") { var workorderInfo = Context .Queryable() .Where(it => it.Workorder == recordOutbound.Workorder) .First(); if (workorderInfo == null) { Context.Ado.RollbackTran(); return "工单不存在"; } // 判断订单号是否存在 var orderPurchase = Context .Queryable() .Where(o => o.OrderNoMes == workorderInfo.CustomerOrder) .First(); if (orderPurchase == null) { Context.Ado.RollbackTran(); return "订单号不存在"; } // 数量调整 workorderInfo.ShipmentNum -= Math.Abs((int)recordOutbound.Quantity); if (workorderInfo.ShipmentNum < 0) { workorderInfo.ShipmentNum = 0; } workorderInfo.CustomerOrder = string.Empty; Context.Updateable(workorderInfo).ExecuteCommand(); // 修改采购订单是否完成 //int newQuantity = Context // .Queryable() // .Where(it => it.CustomerOrder == orderPurchase.OrderNoMes) // .Sum(it => it.ShipmentNum); //orderPurchase.DeliveryQuantity = newQuantity; orderPurchase.DeliveryQuantity -= Math.Abs((int)recordOutbound.Quantity); if (orderPurchase.DeliveryQuantity > orderPurchase.DemandQuantity) { // 订单超额了 Context.Ado.RollbackTran(); return "订单超额了"; } if (orderPurchase.DeliveryQuantity == orderPurchase.DemandQuantity) { orderPurchase.Orderindicator = 1; } else { orderPurchase.Orderindicator = -1; } int res = Context.Updateable(orderPurchase).ExecuteCommand(); } recordOutbound.Remarks = "已撤销"; Context.Updateable(recordOutbound).ExecuteCommand(); Context.Ado.CommitTran(); return result; } } catch (Exception e) { Context.Ado.RollbackTran(); return e.Message; } } /// /// 出货操作 成功返回ok /// /// /// public string Shipment(ShipmentDto parm) { try { DateTime nowDate = DateTime.Now; // 计算有符号变动量(蓝单为正,红单为负) decimal delta = GetSignedQuantity(parm.ReceiptType, parm.Quantity); // 校验物料和库位 var mmMaterial = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .First(); if (mmMaterial == null) return "物料不存在!"; var mmLocation = Context .Queryable() .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); if (mmLocation == null) return "仓库编码或库位编码不存在!"; // 启用事务 Context.Ado.BeginTran(); // 订单关联处理 if (!string.IsNullOrEmpty(parm.CustomerOrder)) { // 判断订单号是否存在 var orderPurchase = Context .Queryable() .Where(o => o.OrderNoMes == parm.CustomerOrder) .First(); if (orderPurchase == null) { Context.Ado.RollbackTran(); return "订单号不存在"; } // 判断工单是否存在 if (!string.IsNullOrEmpty(parm.Workorder)) { var workorderInfo = Context .Queryable() .Where(it => it.Workorder == parm.Workorder) .First(); if (workorderInfo == null) { Context.Ado.RollbackTran(); return "工单不存在"; } // 判断工单主体型号和订单物料号是否匹配 if (workorderInfo.productionCode != orderPurchase.MaterialCode) { Context.Ado.RollbackTran(); return "工单主体型号和订单物料号不匹配"; } } } // 获取现有库存 var mmInventory = Context .Queryable() .Where(it => it.MaterialCode == parm.MaterialCode) .Where(it => it.BatchNo == parm.BatchNo) .Where(it => it.WarehouseCode == parm.WarehouseCode) .Where(it => it.LocationCode == parm.LocationCode) .First(); // 库存检查 if (mmInventory == null) { if (parm.ReceiptType == 1) { Context.Ado.RollbackTran(); return "库存不存在,禁止出货!"; } // 红单处理 var newInventory = new MmInventory() { MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, SupplierCode = mmMaterial.SupplierCode, SupplierName = mmMaterial.SupplierName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, BatchNo = parm.BatchNo, CurrentQty = -delta, Unit = mmMaterial.Unit, LastUpdatedTime = null, CreatedTime = nowDate, }; Context.Insertable(newInventory).ExecuteCommand(); } else { if (mmInventory.CurrentQty - delta < 0) { Context.Ado.RollbackTran(); return "库存不足,无法出货!"; } // 更新库存 mmInventory.CurrentQty -= delta; Context .Updateable(mmInventory) .UpdateColumns(it => it.CurrentQty) .ExecuteCommand(); } // 生成出货单号 var shipmentNo = GenerateReceiptNo("CH"); // 创建出货记录 MmRecordOutbound newRecord = new() { OutboundNo = shipmentNo, BatchNo = parm.BatchNo, Operator = parm.Operator, MaterialCode = mmMaterial.MaterialCode, MaterialName = mmMaterial.MaterialName, LocationCode = mmLocation.LocationCode, LocationName = mmLocation.LocationName, WarehouseCode = mmLocation.WarehouseCode, WarehouseName = mmLocation.WarehouseName, Quantity = -delta, Unit = mmMaterial.Unit, CreatedTime = nowDate, TransactionType = parm.TransactionType, Workorder = parm.Workorder, WorkorderRaw = parm.WorkorderRaw, OrderNo = parm.CustomerOrder, Remarks = parm.Remarks, }; Context.Insertable(newRecord).ExecuteCommand(); // 更新工单和订单信息 if ( !string.IsNullOrEmpty(parm.CustomerOrder) && !string.IsNullOrEmpty(parm.Workorder) ) { // 获取当前工单信息 var workorderInfo = Context .Queryable() .Where(it => it.Workorder == parm.Workorder) .First(); // 计算累计出货数量(使用delta值,考虑单据类型的影响) int currentShipmentNum = workorderInfo.ShipmentNum; int newShipmentNum = currentShipmentNum + (int)delta; // 验证出货数量有效性 if (newShipmentNum < 0) { Context.Ado.RollbackTran(); return "累计出货数量不能为负数"; } // 更新工单信息 Context .Updateable() .Where(it => it.Workorder == parm.Workorder) .SetColumns(it => it.ShipmentNum == newShipmentNum) .SetColumns(it => it.CustomerOrder == parm.CustomerOrder) .ExecuteCommand(); // 修改采购订单信息 var orderPurchase = Context .Queryable() .Where(o => o.OrderNoMes == parm.CustomerOrder) .First(); if (orderPurchase != null) { int newQuantity = Context .Queryable() .Where(it => it.CustomerOrder == parm.CustomerOrder) .Sum(it => it.ShipmentNum); orderPurchase.DeliveryQuantity = newQuantity; if (orderPurchase.DeliveryQuantity > orderPurchase.DemandQuantity) { Context.Ado.RollbackTran(); return "交货数量超过订单需求数量"; } if (orderPurchase.DeliveryQuantity == orderPurchase.DemandQuantity) { orderPurchase.Orderindicator = 1; } else { orderPurchase.Orderindicator = -1; } Context.Updateable(orderPurchase).ExecuteCommand(); } } // 提交事务 Context.Ado.CommitTran(); return "ok"; } catch (Exception ex) { // 回滚操作 Context.Ado.RollbackTran(); return ex.Message; } } } }