Files
f9-web-ai-test/backend/F9WebAiTest.Application/Services/Wms/WmsService.cs
T
yanpeng200212 1f1b184411 新增工资/仓储/生产日报等模块,移除旧考勤模块
- 新增 PayRoll(工资)、Wms(仓储)、Prod(车速/缫丝/考勤/丝片检测/丝片台账/纤检日报/质量日报) 模块
- 新增对应 Domain 实体、Application DTO/Service、Api Controller 与前端页面
- 移除旧 Attendance 考勤模块(Controller/Service/Entity/DTO 及 5 个前端页面),由 Prod/Kaoqin 取代
- 调整路由、仪表盘、Lsjl 流丝记录实体与服务
- 更新业务种子数据与 DbSeeder
- 补充工作进度文档 14-27
2026-10-27 09:09:57 +08:00

1904 lines
91 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using F9WebAiTest.Application.Common;
using F9WebAiTest.Application.Dtos.Wms;
using F9WebAiTest.Domain.Common;
using F9WebAiTest.Domain.Entities.Base;
using F9WebAiTest.Domain.Entities.Wms;
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments.Department;
using SysGys = F9WebAiTest.Domain.Entities.SYS.Gongyingshangs.Gongyingshang;
using SysWz = F9WebAiTest.Domain.Entities.SYS.WuziMulus.WuziMulu;
using SysYlzk = F9WebAiTest.Domain.Entities.SYS.YuanliaoZhuangkous.YuanliaoZhuangkou;
using WuziGroup = F9WebAiTest.Domain.Entities.SYS.WuziMulus.WuziGroup;
namespace F9WebAiTest.Application.Services.Wms;
/// <summary>
/// 仓储(WMS)统一服务:原料 / 副产品 / 在制品 / 生丝 / 物资 五个模块。
/// 对齐 F9 WinClient\F9.Business.WMS:入库、出库、库存台账(在制品另有笼筒架台账),
/// 以及各模块扩展:原料采购 / 翻包计划、生丝批次包件、物资库存 / 出入库流水 / 采购计划 / 采购报销。
/// 五个模块结构一致,用 Module(Wms_Module_Enum)+ Kind(0库存台账 1笼筒架台账)区分。
/// </summary>
public interface IWmsService
{
Task<WmsOptionsDto> GetOptionsAsync(int module);
Task<PageResult<WmsRukuListDto>> GetRukuPagedAsync(int module, DateTime? start, DateTime? end, int? type, string like, int pageIndex, int pageSize);
Task<WmsRukuEditDto> GetRukuAsync(long id);
Task<long> SaveRukuAsync(WmsRukuSaveDto dto);
Task DeleteRukuAsync(long id);
Task<int> BaoxiaoAsync(List<long> rukuIds);
Task<PageResult<WmsChukuListDto>> GetChukuPagedAsync(int module, DateTime? start, DateTime? end, int? type, string like, int pageIndex, int pageSize);
Task<WmsChukuEditDto> GetChukuAsync(long id);
Task<long> SaveChukuAsync(WmsChukuSaveDto dto);
Task DeleteChukuAsync(long id);
Task<List<WmsTzDateDto>> GetTzDatesAsync(int module, int kind);
Task<WmsTzEditDto> GetTaizhangAsync(int module, int kind, DateTime date, bool regenerate);
Task<long> SaveTaizhangAsync(WmsTzSaveDto dto);
Task DeleteTaizhangAsync(long id);
Task<List<WmsKucunDto>> GetKucunAsync(int module, string like);
Task<List<WmsCrkDto>> GetCrkAsync(int module, DateTime start, DateTime end, long? wzmlId);
Task<PageResult<WmsPiciListDto>> GetPiciPagedAsync(DateTime? start, DateTime? end, int kcStatus, string like, int pageIndex, int pageSize);
Task<WmsPiciSaveDto> GetPiciAsync(long id);
Task<long> SavePiciAsync(WmsPiciSaveDto dto);
Task DeletePiciAsync(long id);
Task<List<WmsPiciChukuSelectDto>> GetPiciChukuSelectAsync(long wzmlId, string excludePiciIds);
Task<WmsPiciQcDto> GetPiciQcAsync(long piciId);
Task SavePiciQcAsync(WmsPiciQcDto dto);
Task<List<WmsPiciQcXdjyDto>> GetPiciQcXdjyAsync(long piciId);
Task SavePiciQcXdjyAsync(List<WmsPiciQcXdjyDto> items);
Task<List<WmsCaigouDto>> GetCaigouAsync(DateTime? start, DateTime? end);
Task<long> SaveCaigouAsync(WmsCaigouDto dto);
Task DeleteCaigouAsync(long id);
Task<List<WmsFbplanDto>> GetFbplanAsync(DateTime? start, DateTime? end);
Task<long> SaveFbplanAsync(WmsFbplanDto dto);
Task DeleteFbplanAsync(long id);
Task<List<WmsCgplanListDto>> GetCgplanAsync(DateTime? start, DateTime? end);
Task<WmsCgplanSaveDto> GetCgplanAsync(long id);
Task<long> SaveCgplanAsync(WmsCgplanSaveDto dto);
Task DeleteCgplanAsync(long id);
}
public class WmsService : IWmsService
{
private readonly IRepository<WmsRuku> _rkRepo;
private readonly IRepository<WmsRukuDetail> _rkDetailRepo;
private readonly IRepository<WmsBangmadan> _bmdRepo;
private readonly IRepository<WmsChuku> _ckRepo;
private readonly IRepository<WmsChukuDetail> _ckDetailRepo;
private readonly IRepository<WmsKcTzDate> _tzDateRepo;
private readonly IRepository<WmsKcTzItem> _tzItemRepo;
private readonly IRepository<WmsPici> _piciRepo;
private readonly IRepository<WmsPiciBaojian> _baojianRepo;
private readonly IRepository<WmsPiciChuku> _piciChukuRepo;
private readonly IRepository<WmsPiciQc> _piciQcRepo;
private readonly IRepository<WmsPiciQcXdjy> _piciQcXdjyRepo;
private readonly IRepository<WmsKucun> _kucunRepo;
private readonly IRepository<WmsCaigou> _caigouRepo;
private readonly IRepository<WmsFbplan> _fbplanRepo;
private readonly IRepository<WmsFbjl> _fbjlRepo;
private readonly IRepository<WmsCgplan> _cgplanRepo;
private readonly IRepository<WmsCgplanItem> _cgplanItemRepo;
private readonly IRepository<SysWz> _wzRepo;
private readonly IRepository<WuziGroup> _wzGroupRepo;
private readonly IRepository<SysYlzk> _ylzkRepo;
private readonly IRepository<SysGys> _gysRepo;
private readonly IRepository<SysDept> _depRepo;
private readonly IRepository<CraftZhuangKou> _gyzkRepo;
public WmsService(
IRepository<WmsRuku> rkRepo, IRepository<WmsRukuDetail> rkDetailRepo, IRepository<WmsBangmadan> bmdRepo,
IRepository<WmsChuku> ckRepo, IRepository<WmsChukuDetail> ckDetailRepo,
IRepository<WmsKcTzDate> tzDateRepo, IRepository<WmsKcTzItem> tzItemRepo,
IRepository<WmsPici> piciRepo, IRepository<WmsPiciBaojian> baojianRepo,
IRepository<WmsPiciChuku> piciChukuRepo, IRepository<WmsPiciQc> piciQcRepo, IRepository<WmsPiciQcXdjy> piciQcXdjyRepo,
IRepository<WmsKucun> kucunRepo,
IRepository<WmsCaigou> caigouRepo, IRepository<WmsFbplan> fbplanRepo, IRepository<WmsFbjl> fbjlRepo,
IRepository<WmsCgplan> cgplanRepo, IRepository<WmsCgplanItem> cgplanItemRepo,
IRepository<SysWz> wzRepo, IRepository<WuziGroup> wzGroupRepo, IRepository<SysYlzk> ylzkRepo,
IRepository<SysGys> gysRepo, IRepository<SysDept> depRepo, IRepository<CraftZhuangKou> gyzkRepo)
{
_rkRepo = rkRepo; _rkDetailRepo = rkDetailRepo; _bmdRepo = bmdRepo;
_ckRepo = ckRepo; _ckDetailRepo = ckDetailRepo;
_tzDateRepo = tzDateRepo; _tzItemRepo = tzItemRepo;
_piciRepo = piciRepo; _baojianRepo = baojianRepo;
_piciChukuRepo = piciChukuRepo; _piciQcRepo = piciQcRepo; _piciQcXdjyRepo = piciQcXdjyRepo;
_kucunRepo = kucunRepo;
_caigouRepo = caigouRepo; _fbplanRepo = fbplanRepo; _fbjlRepo = fbjlRepo;
_cgplanRepo = cgplanRepo; _cgplanItemRepo = cgplanItemRepo;
_wzRepo = wzRepo; _wzGroupRepo = wzGroupRepo; _ylzkRepo = ylzkRepo;
_gysRepo = gysRepo; _depRepo = depRepo; _gyzkRepo = gyzkRepo;
}
// ==================== 模块元数据(对齐 F9 枚举) ====================
private static readonly Dictionary<int, (int[] Rk, string[] RkNames, int[] Ck, string[] CkNames)> ModuleTypes = new()
{
// 原料:YLRk_Type 采购1/调拨2/其他3;YLCk_Type 领料1/销售2/样茧3/其他4
{ 1, (new[] { 1, 2, 3 }, new[] { "采购", "调拨", "其他" }, new[] { 1, 2, 3, 4 }, new[] { "领料", "销售", "样茧", "其他" }) },
// 副产品:FcpRk_Type 生产1/试缫2/其他5;FcpCk_Type 销售1/调拨2/其他3
{ 2, (new[] { 1, 2, 5 }, new[] { "生产", "试缫", "其他" }, new[] { 1, 2, 3 }, new[] { "销售", "调拨", "其他" }) },
// 在制品:ZJP_Rk_Type 选茧0/试缫2/退料3/其他5;ZJP_Ck_Type 领料0/销售1/其他2
{ 3, (new[] { 0, 2, 3, 5 }, new[] { "选茧", "试缫", "退料", "其他" }, new[] { 0, 1, 2 }, new[] { "领料", "销售", "其他" }) },
// 生丝:SSRk_Type 生产0/退货1/其他2;SSCk_Type 销售1/其他5
{ 4, (new[] { 0, 1, 2 }, new[] { "生产", "退货", "其他" }, new[] { 1, 5 }, new[] { "销售", "其他" }) },
// 物资:WJRk_Type 采购1/冲账2/盘盈3/其他4;WJCk_Type 领料1/销售2/调拨3/报废4/其他5
{ 5, (new[] { 1, 2, 3, 4 }, new[] { "采购", "冲账", "盘盈", "其他" }, new[] { 1, 2, 3, 4, 5 }, new[] { "领料", "销售", "调拨", "报废", "其他" }) }
};
private static string RkTypeName(int module, int type)
{
if (!ModuleTypes.TryGetValue(module, out var t)) return type.ToString();
var i = Array.IndexOf(t.Rk, type);
return i >= 0 ? t.RkNames[i] : type.ToString();
}
private static string CkTypeName(int module, int type)
{
if (!ModuleTypes.TryGetValue(module, out var t)) return type.ToString();
var i = Array.IndexOf(t.Ck, type);
return i >= 0 ? t.CkNames[i] : type.ToString();
}
/// <summary>台账项目口径:原料=原料庄口;笼筒架=工艺庄口;其余=物资目录</summary>
private static bool IsYlzkItem(int module, int kind) => module == (int)Wms_Module_Enum.原料 && kind == 0;
private static bool IsGyzkItem(int module, int kind) => module == (int)Wms_Module_Enum.中间品 && kind == 1;
/// <summary>台账两列口径:物资=数量/金额;其余=件数/净重</summary>
private static bool IsWujizl(int module) => module == (int)Wms_Module_Enum.物资;
/// <summary>仓储模块 → 物资分组的业务类型(原料1 / 中间品2 / 生丝3 / 副产品4 / 五金5)</summary>
private static int WuziBizTypeOfModule(int module) => module switch
{
(int)Wms_Module_Enum.原料 => 1,
(int)Wms_Module_Enum.中间品 => 2,
(int)Wms_Module_Enum.生丝 => 3,
(int)Wms_Module_Enum.副产品 => 4,
_ => 5
};
private static string BxStatusName(int v) => v switch { 1 => "待报销", 2 => "已报销", _ => "无" };
private static string RkStatusName(int v) => v == 1 ? "已入库" : "未入库";
private static string CkStatusName(int v) => v == 1 ? "已出库" : "未出库";
private static string FbTypeName(int v) => v switch { 1 => "翻包中", 2 => "完成", _ => "暂停" };
private static string RukuStatusName(int v) => v switch { 1 => "有效", 2 => "作废", _ => "无" };
private static string KcStatusName(int v) => v switch { 1 => "待入库", 2 => "已入库", 3 => "部分出库", 4 => "已出库", _ => "全部" };
private static string CgplanStatusName(int v) => v switch { 1 => "已确认", 2 => "已完成", _ => "草稿" };
// ==================== 基础数据 ====================
public async Task<WmsOptionsDto> GetOptionsAsync(int module)
{
var result = new WmsOptionsDto();
if (ModuleTypes.TryGetValue(module, out var t))
{
for (var i = 0; i < t.Rk.Length; i++) result.RukuTypes.Add(new WmsTypeOptionDto { Value = t.Rk[i], Name = t.RkNames[i] });
for (var i = 0; i < t.Ck.Length; i++) result.ChukuTypes.Add(new WmsTypeOptionDto { Value = t.Ck[i], Name = t.CkNames[i] });
}
// 各仓储模块只加载自己对应的物资分组(物资分组 BusinessType:原料1/中间品2/生丝3/副产品4/五金5)
var bizType = WuziBizTypeOfModule(module);
var groupIds = (await _wzGroupRepo.GetAllAsync())
.Where(g => (int)g.BusinessType == bizType).Select(g => g.Id).ToHashSet();
var wzs = (await _wzRepo.GetAllAsync()).Where(x => groupIds.Contains(x.GroupId)).ToList();
result.Wuzi = wzs.OrderBy(x => x.Code).Select(x => new WmsOptionDto { Id = x.Id, Name = x.Name, Code = x.Code, Ext = x.Unit }).ToList();
var ylzks = await _ylzkRepo.GetAllAsync();
result.YuanliaoZhuangkou = ylzks.OrderByDescending(x => x.Year).ThenBy(x => x.Code)
.Select(x => new WmsOptionDto { Id = x.Id, Name = x.Name, Code = x.Code }).ToList();
var gyzks = await _gyzkRepo.GetAllAsync();
result.CraftZhuangkou = gyzks.Select(x => new WmsOptionDto { Id = x.Id, Name = x.ZhuangKouName }).ToList();
var deps = await _depRepo.GetAllAsync();
result.Departments = deps.Select(x => new WmsOptionDto { Id = x.Id, Name = x.Name }).ToList();
var gyss = await _gysRepo.GetAllAsync();
result.Gongyingshangs = gyss.Select(x => new WmsOptionDto { Id = x.Id, Name = x.Name, Ext = x.Phone }).ToList();
return result;
}
private async Task<Dictionary<long, string>> WzDictAsync()
=> (await _wzRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
private async Task<Dictionary<long, (string Name, string Code, string Unit, string Guige)>> WzFullDictAsync()
=> (await _wzRepo.GetAllAsync()).ToDictionary(x => x.Id, x => (x.Name, x.Code, x.Unit, x.Guige));
// ==================== 入库 ====================
public async Task<PageResult<WmsRukuListDto>> GetRukuPagedAsync(int module, DateTime? start, DateTime? end, int? type, string like, int pageIndex, int pageSize)
{
var all = await _rkRepo.GetAllAsync();
var q = all.AsQueryable().Where(x => x.Module == (Wms_Module_Enum)module);
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
if (type.HasValue) q = q.Where(x => x.Type == type.Value);
var gysDict = (await _gysRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var ylzkDict = (await _ylzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var details = await _rkDetailRepo.GetAllAsync();
var sumDict = details.Where(d => d.RukuId > 0)
.GroupBy(d => d.RukuId)
.ToDictionary(g => g.Key, g => new
{
Jianshu = g.Sum(x => x.Jianshu),
BaojianNum = g.Sum(x => x.BaojianNum),
Jingzhong = g.Sum(x => x.Jingzhong),
Shuliang = g.Sum(x => x.Shuliang),
Jine = g.Sum(x => x.Jine),
Count = g.Count()
});
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
q = q.Where(x => x.Code.Contains(key) || x.Kuguan.Contains(key) || x.Description.Contains(key)
|| x.Caigouyuan.Contains(key)
|| (x.GysId > 0 && gysDict.ContainsKey(x.GysId) && gysDict[x.GysId].Contains(key)));
}
var total = q.LongCount();
var items = q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<WmsRukuListDto>
{
Items = items.Select(x =>
{
sumDict.TryGetValue(x.Id, out var s);
return new WmsRukuListDto
{
Id = x.Id, Module = module, Date = x.Date, Type = x.Type, TypeName = RkTypeName(module, x.Type),
Code = x.Code, GysId = x.GysId, GysName = gysDict.GetValueOrDefault(x.GysId, ""),
KhId = x.KhId, KhName = gysDict.GetValueOrDefault(x.KhId, ""),
GyzkId = x.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(x.GyzkId, ""),
DepId = x.DepId, DepName = depDict.GetValueOrDefault(x.DepId, ""),
Lianxiren = x.Lianxiren, Phone = x.Phone, Carno = x.Carno, Kuguan = x.Kuguan,
Caigouyuan = x.Caigouyuan, PayStatus = x.PayStatus,
RkStatus = x.RkStatus, RkStatusName = RkStatusName(x.RkStatus),
BxStatus = x.BxStatus, BxStatusName = BxStatusName(x.BxStatus),
Description = x.Description,
Jianshu = s?.Jianshu ?? 0, BaojianNum = s?.BaojianNum ?? 0, Jingzhong = s?.Jingzhong ?? 0,
Shuliang = s?.Shuliang ?? 0, Jine = s?.Jine ?? 0, DetailCount = s?.Count ?? 0
};
}).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
public async Task<WmsRukuEditDto> GetRukuAsync(long id)
{
var ruku = await _rkRepo.GetByIdAsync(id);
if (ruku == null) throw new ServiceException("入库单不存在", 404);
var module = (int)ruku.Module;
var details = await _rkDetailRepo.FindAsync(x => x.RukuId == id);
var bmds = await _bmdRepo.GetAllAsync();
var bmdDict = bmds.Where(b => !b.IsChuku).GroupBy(b => b.DetailId)
.ToDictionary(g => g.Key, g => g.OrderBy(x => x.Id).ToList());
var wzDict = await WzFullDictAsync();
var ylzkDict = (await _ylzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gysDict = (await _gysRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
return new WmsRukuEditDto
{
Id = ruku.Id, Module = module, Date = ruku.Date, Type = ruku.Type, Code = ruku.Code,
GysId = ruku.GysId, GysName = gysDict.GetValueOrDefault(ruku.GysId, ""),
KhId = ruku.KhId, KhName = gysDict.GetValueOrDefault(ruku.KhId, ""),
GyzkId = ruku.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(ruku.GyzkId, ""),
DepId = ruku.DepId, DepName = depDict.GetValueOrDefault(ruku.DepId, ""),
Lianxiren = ruku.Lianxiren, Phone = ruku.Phone, Carno = ruku.Carno, Kuguan = ruku.Kuguan,
Caigouyuan = ruku.Caigouyuan, PayStatus = ruku.PayStatus,
RkStatus = ruku.RkStatus, BxStatus = ruku.BxStatus, Description = ruku.Description,
Details = details.OrderBy(x => x.Id).Select(d =>
{
wzDict.TryGetValue(d.WzmlId, out var wz);
return new WmsRukuDetailDto
{
Id = d.Id, RukuId = d.RukuId, WzmlId = d.WzmlId,
WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "", Unit = wz.Unit ?? "",
YlzkId = d.YlzkId, YlzkName = ylzkDict.GetValueOrDefault(d.YlzkId, ""),
GyzkId = d.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(d.GyzkId, ""),
DepId = d.DepId, DepName = depDict.GetValueOrDefault(d.DepId, ""),
Jianshu = d.Jianshu, Maozhong = d.Maozhong, Pizhong = d.Pizhong, Jingzhong = d.Jingzhong,
BiaobaoZhongliang = d.BiaobaoZhongliang, BaozhuangZhongliang = d.BaozhuangZhongliang,
Shuliang = d.Shuliang, Danjia = d.Danjia, Jine = d.Jine, Sunhao = d.Sunhao,
PiciCodes = d.PiciCodes, PiciNum = d.PiciNum, BaojianNum = d.BaojianNum,
PiciIds = d.PiciIds,
Banci = d.Banci, JidiWaigou = d.JidiWaigou,
Bangmadans = bmdDict.TryGetValue(d.Id, out var list)
? list.Select(b => new WmsBangmadanDto
{
Id = b.Id, DetailId = b.DetailId, IsChuku = b.IsChuku, Jianshu = b.Jianshu,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong, GuobangShijian = b.GuobangShijian
}).ToList()
: new List<WmsBangmadanDto>()
};
}).ToList()
};
}
public async Task<long> SaveRukuAsync(WmsRukuSaveDto dto)
{
var module = dto.Module;
if (module < 1 || module > 5) throw new ServiceException("模块无效");
if (dto.Date == default) throw new ServiceException("请选择入库日期");
if (dto.Details == null || dto.Details.Count == 0) throw new ServiceException("请至少录入一条入库明细");
WmsRuku entity;
var isNew = dto.Id == 0;
if (isNew)
{
entity = new WmsRuku { Module = (Wms_Module_Enum)module };
}
else
{
entity = await _rkRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("入库单不存在", 404);
// 物资模块:先回滚库存,再按新明细重算
if (module == (int)Wms_Module_Enum.物资) await RollbackWjKucunAsync(dto.Id, true);
}
entity.Date = dto.Date.Date;
entity.Type = dto.Type;
entity.Code = (dto.Code ?? "").Trim();
entity.GysId = dto.GysId;
entity.KhId = dto.KhId;
entity.GyzkId = dto.GyzkId;
entity.DepId = dto.DepId;
entity.Lianxiren = dto.Lianxiren ?? "";
entity.Phone = dto.Phone ?? "";
entity.Carno = dto.Carno ?? "";
entity.Kuguan = dto.Kuguan ?? "";
entity.Caigouyuan = dto.Caigouyuan ?? "";
entity.PayStatus = dto.PayStatus ?? "";
entity.RkStatus = dto.RkStatus;
entity.BxStatus = dto.BxStatus;
entity.Description = dto.Description ?? "";
// 单据编号唯一(同模块 + 同日期)
if (!string.IsNullOrWhiteSpace(entity.Code))
{
var dup = await _rkRepo.FirstOrDefaultAsync(x => x.Id != entity.Id && x.Module == (Wms_Module_Enum)module
&& x.Date == entity.Date && x.Code == entity.Code);
if (dup != null) throw new ServiceException($"单据编号「{entity.Code}」在该日期已存在");
}
if (isNew) await _rkRepo.AddAsync(entity); else await _rkRepo.UpdateAsync(entity);
// 明细先删后建(物理删除,避免软删残留影响汇总)
var olds = await _rkDetailRepo.FindAsync(x => x.RukuId == entity.Id);
if (olds.Count > 0)
{
// 生丝:编辑前回滚旧批次的入库状态
if (module == (int)Wms_Module_Enum.生丝)
await RollbackPiciRukuAsync(olds);
var oldBmds = await _bmdRepo.GetAllAsync();
var del = oldBmds.Where(b => !b.IsChuku && olds.Any(o => o.Id == b.DetailId)).ToList();
if (del.Count > 0) await _bmdRepo.HardDeleteRangeAsync(del);
await _rkDetailRepo.HardDeleteRangeAsync(olds);
}
foreach (var d in dto.Details)
{
if (d.WzmlId == 0 && d.YlzkId == 0 && d.GyzkId == 0) throw new ServiceException("入库明细必须选择物资 / 庄口");
// 磅码单汇总:件数 / 毛重 / 皮重 / 净重
var bmds = d.Bangmadans ?? new List<WmsBangmadanDto>();
foreach (var b in bmds) b.Jingzhong = Round2(b.Maozhong - b.Pizhong);
var detail = new WmsRukuDetail
{
RukuId = entity.Id,
WzmlId = d.WzmlId, YlzkId = d.YlzkId, GyzkId = d.GyzkId, DepId = d.DepId,
BiaobaoZhongliang = d.BiaobaoZhongliang, BaozhuangZhongliang = d.BaozhuangZhongliang,
Shuliang = d.Shuliang, Danjia = d.Danjia,
Sunhao = d.Sunhao, PiciCodes = d.PiciCodes ?? "", PiciNum = d.PiciNum, BaojianNum = d.BaojianNum,
PiciIds = d.PiciIds ?? "",
Banci = d.Banci ?? "", JidiWaigou = d.JidiWaigou ?? ""
};
if (bmds.Count > 0)
{
detail.Jianshu = bmds.Sum(b => b.Jianshu);
detail.Maozhong = Round2(bmds.Sum(b => b.Maozhong));
detail.Pizhong = Round2(bmds.Sum(b => b.Pizhong));
detail.Jingzhong = Round2(bmds.Sum(b => b.Jingzhong));
}
else
{
detail.Jianshu = d.Jianshu;
detail.Maozhong = d.Maozhong;
detail.Pizhong = d.Pizhong;
detail.Jingzhong = d.Jingzhong;
}
// 净重 = 毛重 − 皮重 − 包装重量 − 运损(各模块未使用的项为 0)
if (detail.Jingzhong == 0)
detail.Jingzhong = Round2(detail.Maozhong - detail.Pizhong - detail.BaozhuangZhongliang - detail.Sunhao);
// 物资:金额 = 数量 × 单价(冲账允许负数)
if (module == (int)Wms_Module_Enum.物资)
{
if (detail.Sunhao == 0 && Math.Abs(d.Jine) > 0)
detail.Jine = d.Jine;
else
detail.Jine = Round2(detail.Shuliang * detail.Danjia);
if (detail.Shuliang != 0 && detail.Jine != 0 && detail.Danjia == 0)
detail.Danjia = Round4(detail.Jine / detail.Shuliang);
}
else
{
detail.Jine = d.Jine;
}
await _rkDetailRepo.AddAsync(detail);
// 生丝入库:按批次ID更新批次入库状态(对齐 F9 SSRk_CreateCommand.UpdateRk)
if (module == (int)Wms_Module_Enum.生丝 && !string.IsNullOrWhiteSpace(detail.PiciIds))
await UpdatePiciRukuAsync(detail.PiciIds, detail.Id, entity.Date);
foreach (var b in bmds)
{
await _bmdRepo.AddAsync(new WmsBangmadan
{
DetailId = detail.Id, IsChuku = false, Jianshu = b.Jianshu,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong,
GuobangShijian = b.GuobangShijian
});
}
// 物资模块:入库即增库存
if (module == (int)Wms_Module_Enum.物资)
await ApplyWjKucunAsync(detail.WzmlId, detail.Shuliang, detail.Jine);
}
return entity.Id;
}
public async Task DeleteRukuAsync(long id)
{
var ruku = await _rkRepo.GetByIdAsync(id);
if (ruku == null) throw new ServiceException("入库单不存在", 404);
if (ruku.Module == Wms_Module_Enum.物资) await RollbackWjKucunAsync(id, true);
var details = await _rkDetailRepo.FindAsync(x => x.RukuId == id);
if (ruku.Module == Wms_Module_Enum.生丝) await RollbackPiciRukuAsync(details);
var bmds = await _bmdRepo.GetAllAsync();
var del = bmds.Where(b => !b.IsChuku && details.Any(d => d.Id == b.DetailId)).ToList();
if (del.Count > 0) await _bmdRepo.HardDeleteRangeAsync(del);
if (details.Count > 0) await _rkDetailRepo.HardDeleteRangeAsync(details);
// 物理删除:单据编号有唯一索引,软删残留会阻止重新创建同号单据
await _rkRepo.HardDeleteAsync(ruku);
}
/// <summary>采购报销:把入库单置为「已报销」(物资模块)</summary>
public async Task<int> BaoxiaoAsync(List<long> rukuIds)
{
if (rukuIds == null || rukuIds.Count == 0) throw new ServiceException("请选择要报销的入库单");
var count = 0;
foreach (var id in rukuIds)
{
var ruku = await _rkRepo.GetByIdAsync(id);
if (ruku == null) continue;
ruku.BxStatus = 2;
await _rkRepo.UpdateAsync(ruku);
count++;
}
return count;
}
// ==================== 出库 ====================
public async Task<PageResult<WmsChukuListDto>> GetChukuPagedAsync(int module, DateTime? start, DateTime? end, int? type, string like, int pageIndex, int pageSize)
{
var all = await _ckRepo.GetAllAsync();
var q = all.AsQueryable().Where(x => x.Module == (Wms_Module_Enum)module);
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
if (type.HasValue) q = q.Where(x => x.Type == type.Value);
var gysDict = (await _gysRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var wzDict = await WzDictAsync();
var details = await _ckDetailRepo.GetAllAsync();
var sumDict = details.GroupBy(d => d.ChukuId)
.ToDictionary(g => g.Key, g => new
{
Jianshu = g.Sum(x => x.Jianshu),
Jingzhong = g.Sum(x => x.Jingzhong),
Shuliang = g.Sum(x => x.Shuliang),
Jine = g.Sum(x => x.Jine),
Count = g.Count()
});
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
q = q.Where(x => x.Code.Contains(key) || x.Kuguan.Contains(key) || x.Lingliaoren.Contains(key)
|| x.Description.Contains(key)
|| (x.WldwId > 0 && gysDict.ContainsKey(x.WldwId) && gysDict[x.WldwId].Contains(key)));
}
var total = q.LongCount();
var items = q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<WmsChukuListDto>
{
Items = items.Select(x =>
{
sumDict.TryGetValue(x.Id, out var s);
return new WmsChukuListDto
{
Id = x.Id, Module = module, Date = x.Date, Type = x.Type, TypeName = CkTypeName(module, x.Type),
Code = x.Code,
WldwId = x.WldwId, WldwName = gysDict.GetValueOrDefault(x.WldwId, ""),
KhId = x.KhId, KhName = gysDict.GetValueOrDefault(x.KhId, ""),
GyzkId = x.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(x.GyzkId, ""),
LingliaoDepId = x.LingliaoDepId, LingliaoDepName = depDict.GetValueOrDefault(x.LingliaoDepId, ""),
UseDepId = x.UseDepId, UseDepName = depDict.GetValueOrDefault(x.UseDepId, ""),
DbdepId = x.DbdepId, DbdepName = depDict.GetValueOrDefault(x.DbdepId, ""),
Lingliaoren = x.Lingliaoren, Kuguan = x.Kuguan, UseLocation = x.UseLocation,
CkStatus = x.CkStatus, CkStatusName = CkStatusName(x.CkStatus), IsHongchong = x.IsHongchong,
Description = x.Description,
Jianshu = s?.Jianshu ?? 0, Jingzhong = s?.Jingzhong ?? 0,
Shuliang = s?.Shuliang ?? 0, Jine = s?.Jine ?? 0, DetailCount = s?.Count ?? 0
};
}).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
public async Task<WmsChukuEditDto> GetChukuAsync(long id)
{
var chuku = await _ckRepo.GetByIdAsync(id);
if (chuku == null) throw new ServiceException("出库单不存在", 404);
var details = await _ckDetailRepo.FindAsync(x => x.ChukuId == id);
var bmds = await _bmdRepo.GetAllAsync();
var bmdDict = bmds.Where(b => b.IsChuku).GroupBy(b => b.DetailId)
.ToDictionary(g => g.Key, g => g.OrderBy(x => x.Id).ToList());
var wzDict = await WzFullDictAsync();
var ylzkDict = (await _ylzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var gysDict = (await _gysRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
return new WmsChukuEditDto
{
Id = chuku.Id, Module = (int)chuku.Module, Date = chuku.Date, Type = chuku.Type, Code = chuku.Code,
WldwId = chuku.WldwId, WldwName = gysDict.GetValueOrDefault(chuku.WldwId, ""),
KhId = chuku.KhId, KhName = gysDict.GetValueOrDefault(chuku.KhId, ""),
GyzkId = chuku.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(chuku.GyzkId, ""),
LingliaoDepId = chuku.LingliaoDepId, LingliaoDepName = depDict.GetValueOrDefault(chuku.LingliaoDepId, ""),
UseDepId = chuku.UseDepId, UseDepName = depDict.GetValueOrDefault(chuku.UseDepId, ""),
DbdepId = chuku.DbdepId, DbdepName = depDict.GetValueOrDefault(chuku.DbdepId, ""),
Lingliaoren = chuku.Lingliaoren, Kuguan = chuku.Kuguan, UseLocation = chuku.UseLocation,
CkStatus = chuku.CkStatus, IsHongchong = chuku.IsHongchong, HongchongId = chuku.HongchongId,
Description = chuku.Description,
Details = details.OrderBy(x => x.Id).Select(d =>
{
wzDict.TryGetValue(d.WzmlId, out var wz);
return new WmsChukuDetailDto
{
Id = d.Id, ChukuId = d.ChukuId, WzmlId = d.WzmlId,
WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "", Unit = wz.Unit ?? "",
YlzkId = d.YlzkId, YlzkName = ylzkDict.GetValueOrDefault(d.YlzkId, ""),
GyzkId = d.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(d.GyzkId, ""),
DepId = d.DepId, DepName = depDict.GetValueOrDefault(d.DepId, ""),
Jianshu = d.Jianshu, Maozhong = d.Maozhong, Pizhong = d.Pizhong, Jingzhong = d.Jingzhong,
BiaobaoZhongliang = d.BiaobaoZhongliang, BaozhuangZhongliang = d.BaozhuangZhongliang,
Shuliang = d.Shuliang, Danjia = d.Danjia, Jine = d.Jine, KucunNum = d.KucunNum,
PiciCodes = d.PiciCodes, PiciNum = d.PiciNum, PcAndBjIds = d.PcAndBjIds,
Lingyongren = d.Lingyongren, Banci = d.Banci,
JidiWaigou = d.JidiWaigou,
Bangmadans = bmdDict.TryGetValue(d.Id, out var list)
? list.Select(b => new WmsBangmadanDto
{
Id = b.Id, DetailId = b.DetailId, IsChuku = b.IsChuku, Jianshu = b.Jianshu,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong, GuobangShijian = b.GuobangShijian
}).ToList()
: new List<WmsBangmadanDto>()
};
}).ToList()
};
}
public async Task<long> SaveChukuAsync(WmsChukuSaveDto dto)
{
var module = dto.Module;
if (module < 1 || module > 5) throw new ServiceException("模块无效");
if (dto.Date == default) throw new ServiceException("请选择出库日期");
if (dto.Details == null || dto.Details.Count == 0) throw new ServiceException("请至少录入一条出库明细");
WmsChuku entity;
var isNew = dto.Id == 0;
if (isNew)
{
entity = new WmsChuku { Module = (Wms_Module_Enum)module };
}
else
{
entity = await _ckRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("出库单不存在", 404);
if (module == (int)Wms_Module_Enum.物资) await RollbackWjKucunAsync(dto.Id, false);
}
entity.Date = dto.Date.Date;
entity.Type = dto.Type;
entity.Code = (dto.Code ?? "").Trim();
entity.WldwId = dto.WldwId;
entity.KhId = dto.KhId;
entity.GyzkId = dto.GyzkId;
entity.LingliaoDepId = dto.LingliaoDepId;
entity.UseDepId = dto.UseDepId;
entity.DbdepId = dto.DbdepId;
entity.Lingliaoren = dto.Lingliaoren ?? "";
entity.Kuguan = dto.Kuguan ?? "";
entity.UseLocation = dto.UseLocation ?? "";
entity.CkStatus = dto.CkStatus;
entity.CkTime = dto.CkStatus == 1 ? DateTime.Now : null;
entity.Description = dto.Description ?? "";
if (!string.IsNullOrWhiteSpace(entity.Code))
{
var dup = await _ckRepo.FirstOrDefaultAsync(x => x.Id != entity.Id && x.Module == (Wms_Module_Enum)module
&& x.Date == entity.Date && x.Code == entity.Code);
if (dup != null) throw new ServiceException($"单据编号「{entity.Code}」在该日期已存在");
}
if (isNew) await _ckRepo.AddAsync(entity); else await _ckRepo.UpdateAsync(entity);
var olds = await _ckDetailRepo.FindAsync(x => x.ChukuId == entity.Id);
if (olds.Count > 0)
{
// 生丝:编辑前回滚旧批次的包件出库状态
if (module == (int)Wms_Module_Enum.生丝)
await RollbackPiciChukuAsync(olds);
var oldBmds = await _bmdRepo.GetAllAsync();
var del = oldBmds.Where(b => b.IsChuku && olds.Any(o => o.Id == b.DetailId)).ToList();
if (del.Count > 0) await _bmdRepo.HardDeleteRangeAsync(del);
await _ckDetailRepo.HardDeleteRangeAsync(olds);
}
var kucunDict = (await _kucunRepo.GetAllAsync()).ToDictionary(x => x.WzmlId);
foreach (var d in dto.Details)
{
if (d.WzmlId == 0 && d.YlzkId == 0 && d.GyzkId == 0) throw new ServiceException("出库明细必须选择物资 / 庄口");
var bmds = d.Bangmadans ?? new List<WmsBangmadanDto>();
foreach (var b in bmds) b.Jingzhong = Round2(b.Maozhong - b.Pizhong);
var detail = new WmsChukuDetail
{
ChukuId = entity.Id,
WzmlId = d.WzmlId, YlzkId = d.YlzkId, GyzkId = d.GyzkId, DepId = d.DepId,
BiaobaoZhongliang = d.BiaobaoZhongliang, BaozhuangZhongliang = d.BaozhuangZhongliang,
Shuliang = d.Shuliang, Danjia = d.Danjia, KucunNum = d.KucunNum,
PiciCodes = d.PiciCodes ?? "", PiciNum = d.PiciNum, PcAndBjIds = d.PcAndBjIds ?? "",
Lingyongren = d.Lingyongren ?? "",
Banci = d.Banci ?? "", JidiWaigou = d.JidiWaigou ?? ""
};
if (bmds.Count > 0)
{
detail.Jianshu = bmds.Sum(b => b.Jianshu);
detail.Maozhong = Round2(bmds.Sum(b => b.Maozhong));
detail.Pizhong = Round2(bmds.Sum(b => b.Pizhong));
detail.Jingzhong = Round2(bmds.Sum(b => b.Jingzhong));
}
else
{
detail.Jianshu = d.Jianshu;
detail.Maozhong = d.Maozhong;
detail.Pizhong = d.Pizhong;
detail.Jingzhong = d.Jingzhong;
}
if (detail.Jingzhong == 0)
detail.Jingzhong = Round2(detail.Maozhong - detail.Pizhong - detail.BaozhuangZhongliang);
if (module == (int)Wms_Module_Enum.物资)
{
if (detail.Shuliang <= 0) throw new ServiceException("出库数量必须大于0");
// 库存校验 + 金额口径(F9 WJ_Chuku_Detail.CalJine)
kucunDict.TryGetValue(detail.WzmlId, out var kc);
var kcNum = kc?.Shuliang ?? d.KucunNum;
var kcJine = kc?.Jine ?? 0;
var kcDanjia = kc?.Danjia ?? 0;
detail.KucunNum = kcNum;
if (detail.Shuliang > kcNum) throw new ServiceException("库存数量不足");
if (detail.Shuliang == kcNum)
{
detail.Jine = kcJine;
detail.Danjia = detail.Shuliang != 0 ? Round4(detail.Jine / detail.Shuliang) : 0;
}
else
{
detail.Danjia = kcDanjia;
detail.Jine = Round2(detail.Shuliang * detail.Danjia);
}
await ApplyWjKucunAsync(detail.WzmlId, -detail.Shuliang, -detail.Jine);
}
else
{
detail.Jine = d.Jine;
}
await _ckDetailRepo.AddAsync(detail);
foreach (var b in bmds)
{
await _bmdRepo.AddAsync(new WmsBangmadan
{
DetailId = detail.Id, IsChuku = true, Jianshu = b.Jianshu,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong,
GuobangShijian = b.GuobangShijian
});
}
// 生丝出库:按批次+包件更新(对齐 F9 SSCk_CreateCommand.Update_Ck)
if (module == (int)Wms_Module_Enum.生丝 && !string.IsNullOrWhiteSpace(detail.PcAndBjIds))
await UpdatePiciChukuAsync(detail.PcAndBjIds, detail.Id, entity.Date);
}
return entity.Id;
}
public async Task DeleteChukuAsync(long id)
{
var chuku = await _ckRepo.GetByIdAsync(id);
if (chuku == null) throw new ServiceException("出库单不存在", 404);
if (chuku.Module == Wms_Module_Enum.物资) await RollbackWjKucunAsync(id, false);
var details = await _ckDetailRepo.FindAsync(x => x.ChukuId == id);
if (chuku.Module == Wms_Module_Enum.生丝) await RollbackPiciChukuAsync(details);
var bmds = await _bmdRepo.GetAllAsync();
var del = bmds.Where(b => b.IsChuku && details.Any(d => d.Id == b.DetailId)).ToList();
if (del.Count > 0) await _bmdRepo.HardDeleteRangeAsync(del);
if (details.Count > 0) await _ckDetailRepo.HardDeleteRangeAsync(details);
await _ckRepo.HardDeleteAsync(chuku);
}
// ==================== 库存台账 ====================
public async Task<List<WmsTzDateDto>> GetTzDatesAsync(int module, int kind)
{
var dates = await _tzDateRepo.GetAllAsync();
var items = await _tzItemRepo.GetAllAsync();
var countDict = items.GroupBy(x => x.TzDateId).ToDictionary(g => g.Key, g => g.Count());
return dates.Where(x => x.Module == (Wms_Module_Enum)module && x.Kind == kind)
.OrderByDescending(x => x.Date)
.Select(x => new WmsTzDateDto
{
Id = x.Id, Module = module, Kind = kind, Date = x.Date, LastDate = x.LastDate,
ItemCount = countDict.GetValueOrDefault(x.Id, 0)
}).ToList();
}
/// <summary>读取(或生成)指定日期台账</summary>
public async Task<WmsTzEditDto> GetTaizhangAsync(int module, int kind, DateTime date, bool regenerate)
{
var tzDate = date.Date;
var existing = await _tzDateRepo.FirstOrDefaultAsync(x => x.Module == (Wms_Module_Enum)module
&& x.Kind == kind && x.Date == tzDate);
if (existing == null)
{
var latest = (await _tzDateRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Kind == kind)
.OrderByDescending(x => x.Date).FirstOrDefault();
if (latest != null && tzDate <= latest.Date)
throw new ServiceException($"只能生成晚于 {latest.Date:yyyy-MM-dd} 的台账");
// F9:无上一台账时 LastDate 取 1970-01-01,即首份台账汇总全部历史出入库
var lastDate = latest?.Date ?? new DateTime(1970, 1, 1);
var generated = await GenerateItemsAsync(module, kind, tzDate, lastDate);
return new WmsTzEditDto
{
TzDateId = 0, Module = module, Kind = kind, Date = tzDate, LastDate = lastDate, Items = generated
};
}
if (regenerate)
{
var generated = await GenerateItemsAsync(module, kind, tzDate, existing.LastDate);
return new WmsTzEditDto
{
TzDateId = existing.Id, Module = module, Kind = kind, Date = tzDate, LastDate = existing.LastDate, Items = generated
};
}
var items = await _tzItemRepo.FindAsync(x => x.TzDateId == existing.Id);
return new WmsTzEditDto
{
TzDateId = existing.Id, Module = module, Kind = kind, Date = existing.Date, LastDate = existing.LastDate,
Items = items.OrderBy(x => x.Id).Select(ToTzItemDto).ToList()
};
}
/// <summary>
/// 生成台账:上日结存(上一台账中「件数或净重不为 0」的行)+ 区间入库/出库汇总 → 计算今日结存。
/// 对齐 F9 YL_Kucun_Taizhang_Edit.GenerateTaizhang / CalTdayJiecun。
/// </summary>
private async Task<List<WmsTzItemDto>> GenerateItemsAsync(int module, int kind, DateTime tzDate, DateTime lastDate)
{
var from = kind == 1 ? lastDate : lastDate.AddDays(1); // 笼筒架台账含上日(F9 区间 Yesterday~Date)
var to = tzDate.AddDays(1).AddMilliseconds(-1);
var result = new List<WmsTzItemDto>();
var lastItems = await _tzItemRepo.GetAllAsync();
var lastDict = lastItems.Where(x => x.Module == (Wms_Module_Enum)module && x.Kind == kind && x.Date == lastDate)
.ToList();
foreach (var last in lastDict)
{
if (last.Tdayjingzhong != 0 || last.Tdayjianshu != 0)
{
result.Add(new WmsTzItemDto
{
ItemId = last.ItemId, SubKey = last.SubKey, ItemName = last.ItemName, ItemCode = last.ItemCode,
Ydayjianshu = last.Tdayjianshu, Ydayjingzhong = last.Tdayjingzhong,
Yangjianjianshu = last.Yangjianjianshu
});
}
}
var rukuSum = await SumRukuAsync(module, kind, from, to);
var chukuSum = await SumChukuAsync(module, kind, from, to);
void Merge(List<SumRow> rows, bool isRuku)
{
foreach (var r in rows)
{
var hit = result.FirstOrDefault(x => x.ItemId == r.ItemId && x.SubKey == r.SubKey);
if (hit == null)
{
hit = new WmsTzItemDto { ItemId = r.ItemId, SubKey = r.SubKey };
result.Add(hit);
}
if (isRuku) { hit.Tdayrukujianshu = r.Jianshu; hit.Tdayrukujingzhong = r.Jingzhong; }
else { hit.Tdaychukujianshu = r.Jianshu; hit.Tdaychukujingzhong = r.Jingzhong; }
}
}
Merge(rukuSum, true);
Merge(chukuSum, false);
var nameDict = await BuildItemNameDictAsync(module, kind, result.Select(x => x.ItemId).Distinct().ToList());
foreach (var item in result)
{
if (nameDict.TryGetValue(item.ItemId, out var nm)) { item.ItemName = nm.Name; item.ItemCode = nm.Code; }
CalTdayJiecun(item, module);
}
return result.OrderBy(x => x.ItemName).ToList();
}
/// <summary>今日结存 = 上日 + 入库 − 出库 + 盈亏;全部出库 = 上日有结存且今日结存为 0(对齐 F9 CalTdayJiecun)</summary>
private static void CalTdayJiecun(WmsTzItemDto item, int module)
{
item.Tdayjianshu = item.Ydayjianshu + item.Tdayrukujianshu - item.Tdaychukujianshu;
item.Tdayjingzhong = Round2(item.Ydayjingzhong + item.Tdayrukujingzhong - item.Tdaychukujingzhong + item.Yingkui);
if (module != (int)Wms_Module_Enum.物资)
item.AllChuku = item.Ydayjingzhong != 0 && item.Tdayjingzhong == 0;
}
public async Task<long> SaveTaizhangAsync(WmsTzSaveDto dto)
{
var module = dto.Module;
var tzDate = dto.Date.Date;
if (dto.Items == null || dto.Items.Count == 0) throw new ServiceException("台账明细不能为空");
// 校验(对齐 F9 YL_Kucun_Taizhang_Edit 校验规则)
foreach (var it in dto.Items)
{
var expect = Round2(it.Ydayjingzhong + it.Tdayrukujingzhong - it.Tdaychukujingzhong + it.Yingkui);
if (expect != Round2(it.Tdayjingzhong))
throw new ServiceException($"「{it.ItemName}」今日结存重量有误(应为 {expect})");
if (it.Tdayjingzhong < 0)
throw new ServiceException($"「{it.ItemName}」结存重量不能小于0");
if (module == (int)Wms_Module_Enum.原料 && it.AllChuku && it.Yangjianjianshu != 0)
throw new ServiceException($"「{it.ItemName}」全部出库时样茧件数必须为0");
}
WmsKcTzDate tz;
if (dto.TzDateId > 0)
{
tz = await _tzDateRepo.GetByIdAsync(dto.TzDateId);
if (tz == null) throw new ServiceException("台账不存在", 404);
}
else
{
var exist = await _tzDateRepo.FirstOrDefaultAsync(x => x.Module == (Wms_Module_Enum)module
&& x.Kind == dto.Kind && x.Date == tzDate);
if (exist != null) throw new ServiceException("该日期台账已存在");
var latest = (await _tzDateRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Kind == dto.Kind)
.OrderByDescending(x => x.Date).FirstOrDefault();
if (latest != null && tzDate <= latest.Date)
throw new ServiceException($"只能生成晚于 {latest.Date:yyyy-MM-dd} 的台账");
tz = new WmsKcTzDate
{
Module = (Wms_Module_Enum)module, Kind = dto.Kind, Date = tzDate,
LastDate = latest?.Date ?? new DateTime(1970, 1, 1)
};
await _tzDateRepo.AddAsync(tz);
}
var olds = await _tzItemRepo.FindAsync(x => x.TzDateId == tz.Id);
if (olds.Count > 0) await _tzItemRepo.HardDeleteRangeAsync(olds);
foreach (var it in dto.Items)
{
await _tzItemRepo.AddAsync(new WmsKcTzItem
{
TzDateId = tz.Id, Module = (Wms_Module_Enum)module, Kind = dto.Kind, Date = tzDate,
ItemId = it.ItemId, SubKey = it.SubKey ?? "", ItemName = it.ItemName ?? "", ItemCode = it.ItemCode ?? "",
Ydayjianshu = it.Ydayjianshu, Ydayjingzhong = it.Ydayjingzhong,
Tdayrukujianshu = it.Tdayrukujianshu, Tdayrukujingzhong = it.Tdayrukujingzhong,
Tdaychukujianshu = it.Tdaychukujianshu, Tdaychukujingzhong = it.Tdaychukujingzhong,
Yingkui = it.Yingkui, Tdayjianshu = it.Tdayjianshu, Tdayjingzhong = it.Tdayjingzhong,
Yangjianjianshu = it.Yangjianjianshu, AllChuku = it.AllChuku, Description = it.Description ?? ""
});
}
return tz.Id;
}
public async Task DeleteTaizhangAsync(long id)
{
var tz = await _tzDateRepo.GetByIdAsync(id);
if (tz == null) throw new ServiceException("台账不存在", 404);
var items = await _tzItemRepo.FindAsync(x => x.TzDateId == id);
if (items.Count > 0) await _tzItemRepo.HardDeleteRangeAsync(items);
await _tzDateRepo.HardDeleteAsync(tz);
}
// ==================== 汇总辅助 ====================
private class SumRow
{
public long ItemId { get; set; }
public string SubKey { get; set; } = "";
public decimal Jianshu { get; set; }
public decimal Jingzhong { get; set; }
}
private long ItemKeyOf(WmsRukuDetail d, int module, int kind)
=> IsYlzkItem(module, kind) ? d.YlzkId : (IsGyzkItem(module, kind) ? d.GyzkId : d.WzmlId);
private string SubKeyOf(WmsRukuDetail d, int module)
=> module == (int)Wms_Module_Enum.生丝 ? (d.JidiWaigou ?? "") : "";
private long ItemKeyOf(WmsChukuDetail d, int module, int kind)
=> IsYlzkItem(module, kind) ? d.YlzkId : (IsGyzkItem(module, kind) ? d.GyzkId : d.WzmlId);
private string SubKeyOf(WmsChukuDetail d, int module)
=> module == (int)Wms_Module_Enum.生丝 ? (d.JidiWaigou ?? "") : "";
private async Task<List<SumRow>> SumRukuAsync(int module, int kind, DateTime from, DateTime to)
{
var rukus = (await _rkRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Date >= from && x.Date <= to).ToList();
if (rukus.Count == 0) return new List<SumRow>();
var ids = rukus.Select(x => x.Id).ToHashSet();
var details = (await _rkDetailRepo.GetAllAsync()).Where(x => ids.Contains(x.RukuId)).ToList();
if (IsGyzkItem(module, kind))
{
var ltjWzmlId = await GetLtjWzmlIdAsync();
if (ltjWzmlId > 0) details = details.Where(x => x.WzmlId == ltjWzmlId).ToList();
}
return details.GroupBy(d => new { Key = ItemKeyOf(d, module, kind), Sub = SubKeyOf(d, module) })
.Select(g => new SumRow
{
ItemId = g.Key.Key,
SubKey = g.Key.Sub,
Jianshu = module == (int)Wms_Module_Enum.物资 ? g.Sum(x => x.Shuliang)
: module == (int)Wms_Module_Enum.生丝 ? g.Sum(x => x.PiciNum)
: g.Sum(x => x.Jianshu),
Jingzhong = module == (int)Wms_Module_Enum.物资 ? Round2(g.Sum(x => x.Jine)) : Round2(g.Sum(x => x.Jingzhong))
})
.Where(x => x.ItemId != 0)
.ToList();
}
private async Task<List<SumRow>> SumChukuAsync(int module, int kind, DateTime from, DateTime to)
{
var chukus = (await _ckRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Date >= from && x.Date <= to).ToList();
if (chukus.Count == 0) return new List<SumRow>();
var ids = chukus.Select(x => x.Id).ToHashSet();
var details = (await _ckDetailRepo.GetAllAsync()).Where(x => ids.Contains(x.ChukuId)).ToList();
if (IsGyzkItem(module, kind))
{
var ltjWzmlId = await GetLtjWzmlIdAsync();
if (ltjWzmlId > 0) details = details.Where(x => x.WzmlId == ltjWzmlId).ToList();
}
return details.GroupBy(d => new { Key = ItemKeyOf(d, module, kind), Sub = SubKeyOf(d, module) })
.Select(g => new SumRow
{
ItemId = g.Key.Key,
SubKey = g.Key.Sub,
Jianshu = module == (int)Wms_Module_Enum.物资 ? g.Sum(x => x.Shuliang)
: module == (int)Wms_Module_Enum.生丝 ? g.Sum(x => x.PiciNum)
: g.Sum(x => x.Jianshu),
Jingzhong = module == (int)Wms_Module_Enum.物资 ? Round2(g.Sum(x => x.Jine)) : Round2(g.Sum(x => x.Jingzhong))
})
.Where(x => x.ItemId != 0)
.ToList();
}
/// <summary>笼筒架台账只统计「联统茧」物资(注册表「仓储\联统茧物资ID」)</summary>
private async Task<long> GetLtjWzmlIdAsync()
{
var wzs = await _wzRepo.GetAllAsync();
var hit = wzs.FirstOrDefault(x => x.Name.Contains("联统"));
return hit?.Id ?? 0;
}
private async Task<Dictionary<long, (string Name, string Code)>> BuildItemNameDictAsync(int module, int kind, List<long> ids)
{
var dict = new Dictionary<long, (string, string)>();
if (IsYlzkItem(module, kind))
{
foreach (var x in await _ylzkRepo.GetAllAsync()) dict[x.Id] = (x.Name, x.Code);
}
else if (IsGyzkItem(module, kind))
{
foreach (var x in await _gyzkRepo.GetAllAsync()) dict[x.Id] = (x.ZhuangKouName, "");
}
else
{
foreach (var x in await _wzRepo.GetAllAsync()) dict[x.Id] = (x.Name, x.Code);
}
return dict;
}
private static WmsTzItemDto ToTzItemDto(WmsKcTzItem x) => new()
{
Id = x.Id, TzDateId = x.TzDateId, ItemId = x.ItemId, SubKey = x.SubKey,
ItemName = x.ItemName, ItemCode = x.ItemCode,
Ydayjianshu = x.Ydayjianshu, Ydayjingzhong = x.Ydayjingzhong,
Tdayrukujianshu = x.Tdayrukujianshu, Tdayrukujingzhong = x.Tdayrukujingzhong,
Tdaychukujianshu = x.Tdaychukujianshu, Tdaychukujingzhong = x.Tdaychukujingzhong,
Yingkui = x.Yingkui, Tdayjianshu = x.Tdayjianshu, Tdayjingzhong = x.Tdayjingzhong,
Yangjianjianshu = x.Yangjianjianshu, AllChuku = x.AllChuku, Description = x.Description
};
// ==================== 物资库存 / 出入库流水 ====================
/// <summary>物资模块库存:按物资目录汇总(入库 − 出库)</summary>
public async Task<List<WmsKucunDto>> GetKucunAsync(int module, string like)
{
var wzFull = await WzFullDictAsync();
var result = new List<WmsKucunDto>();
if (module == (int)Wms_Module_Enum.物资)
{
var kcs = await _kucunRepo.GetAllAsync();
foreach (var kc in kcs)
{
wzFull.TryGetValue(kc.WzmlId, out var wz);
result.Add(new WmsKucunDto
{
WzmlId = kc.WzmlId, WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "",
Unit = wz.Unit ?? "", Guige = wz.Guige ?? "",
Shuliang = kc.Shuliang, Danjia = kc.Danjia, Jine = kc.Jine
});
}
}
else
{
// 其余模块:用最近一次台账的「今日结存」作为库存
var dates = (await _tzDateRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Kind == 0)
.OrderByDescending(x => x.Date).FirstOrDefault();
if (dates != null)
{
var items = await _tzItemRepo.FindAsync(x => x.TzDateId == dates.Id);
foreach (var it in items.Where(x => x.Tdayjianshu != 0 || x.Tdayjingzhong != 0))
{
wzFull.TryGetValue(it.ItemId, out var wz);
result.Add(new WmsKucunDto
{
WzmlId = it.ItemId, WzmlName = it.ItemName, WzmlCode = it.ItemCode,
Unit = wz.Unit ?? "", Guige = wz.Guige ?? "",
Shuliang = it.Tdayjianshu, Jine = it.Tdayjingzhong
});
}
}
}
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
result = result.Where(x => x.WzmlName.Contains(key) || x.WzmlCode.Contains(key)).ToList();
}
return result.OrderBy(x => x.WzmlCode).ToList();
}
/// <summary>出入库流水(物资模块)</summary>
public async Task<List<WmsCrkDto>> GetCrkAsync(int module, DateTime start, DateTime end, long? wzmlId)
{
var wzFull = await WzFullDictAsync();
var list = new List<WmsCrkDto>();
var rukus = (await _rkRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Date.Date >= start.Date && x.Date.Date <= end.Date).ToList();
var rkIds = rukus.Select(x => x.Id).ToHashSet();
var rkDetails = (await _rkDetailRepo.GetAllAsync()).Where(x => rkIds.Contains(x.RukuId)).ToList();
var rkDict = rukus.ToDictionary(x => x.Id);
foreach (var d in rkDetails)
{
if (wzmlId.HasValue && d.WzmlId != wzmlId.Value) continue;
wzFull.TryGetValue(d.WzmlId, out var wz);
rkDict.TryGetValue(d.RukuId, out var rk);
list.Add(new WmsCrkDto
{
Date = rk?.Date ?? default, IsRuku = true, Code = rk?.Code ?? "",
TypeName = RkTypeName(module, rk?.Type ?? 0),
WzmlId = d.WzmlId, WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "", Unit = wz.Unit ?? "",
Shuliang = d.Shuliang, Danjia = d.Danjia, Jine = d.Jine, Description = ""
});
}
var chukus = (await _ckRepo.GetAllAsync())
.Where(x => x.Module == (Wms_Module_Enum)module && x.Date.Date >= start.Date && x.Date.Date <= end.Date).ToList();
var ckIds = chukus.Select(x => x.Id).ToHashSet();
var ckDetails = (await _ckDetailRepo.GetAllAsync()).Where(x => ckIds.Contains(x.ChukuId)).ToList();
var ckDict = chukus.ToDictionary(x => x.Id);
foreach (var d in ckDetails)
{
if (wzmlId.HasValue && d.WzmlId != wzmlId.Value) continue;
wzFull.TryGetValue(d.WzmlId, out var wz);
ckDict.TryGetValue(d.ChukuId, out var ck);
list.Add(new WmsCrkDto
{
Date = ck?.Date ?? default, IsRuku = false, Code = ck?.Code ?? "",
TypeName = CkTypeName(module, ck?.Type ?? 0),
WzmlId = d.WzmlId, WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "", Unit = wz.Unit ?? "",
Shuliang = -d.Shuliang, Danjia = d.Danjia, Jine = -d.Jine, Description = ""
});
}
return list.OrderByDescending(x => x.Date).ThenBy(x => x.Code).ToList();
}
/// <summary>物资库存增减(入库为正、出库为负)</summary>
private async Task ApplyWjKucunAsync(long wzmlId, decimal shuliang, decimal jine)
{
if (wzmlId == 0) return;
var kc = await _kucunRepo.FirstOrDefaultAsync(x => x.WzmlId == wzmlId);
if (kc == null)
{
kc = new WmsKucun { WzmlId = wzmlId };
await _kucunRepo.AddAsync(kc);
}
// 出库前已做库存校验,正常业务不会出现负数;删除单据回滚时可能出现负数,
// 这里不做截断,保证「入库回滚」与「出库回滚」严格对称,不会造成库存漂移。
kc.Shuliang = Round4(kc.Shuliang + shuliang);
kc.Jine = Round2(kc.Jine + jine);
kc.Danjia = kc.Shuliang != 0 ? Round4(kc.Jine / kc.Shuliang) : 0;
await _kucunRepo.UpdateAsync(kc);
}
/// <summary>回滚单据对物资库存的影响(修改 / 删除前调用)</summary>
private async Task RollbackWjKucunAsync(long billId, bool isRuku)
{
if (isRuku)
{
var details = await _rkDetailRepo.FindAsync(x => x.RukuId == billId);
foreach (var d in details) await ApplyWjKucunAsync(d.WzmlId, -d.Shuliang, -d.Jine);
}
else
{
var details = await _ckDetailRepo.FindAsync(x => x.ChukuId == billId);
foreach (var d in details) await ApplyWjKucunAsync(d.WzmlId, d.Shuliang, d.Jine);
}
}
// ==================== 生丝批次 / 包件 ====================
public async Task<PageResult<WmsPiciListDto>> GetPiciPagedAsync(DateTime? start, DateTime? end, int kcStatus, string like, int pageIndex, int pageSize)
{
var all = await _piciRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
if (kcStatus > 0) q = q.Where(x => x.KcStatus == kcStatus);
var wzDict = await WzDictAsync();
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var baojianDict = (await _baojianRepo.GetAllAsync()).GroupBy(x => x.PiciId)
.ToDictionary(g => g.Key, g => g.Count());
if (!string.IsNullOrWhiteSpace(like))
{
var key = like.Trim();
q = q.Where(x => x.Code.Contains(key) || x.Guige.Contains(key));
}
var total = q.LongCount();
var items = q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code)
.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return new PageResult<WmsPiciListDto>
{
Items = items.Select(x => new WmsPiciListDto
{
Id = x.Id, Code = x.Code, CodeType = x.CodeType, Year = x.Year, Date = x.Date, ScDate = x.ScDate,
GyzkId = x.GyzkId, GyzkName = gyzkDict.GetValueOrDefault(x.GyzkId, ""),
WzmlId = x.WzmlId, WzmlName = wzDict.GetValueOrDefault(x.WzmlId, ""),
DepId = x.DepId, DepName = depDict.GetValueOrDefault(x.DepId, ""),
Guige = x.Guige, JidiWaigou = x.JidiWaigou, YoujiPutong = x.YoujiPutong,
Baoshu = x.Baoshu, StartBaojianNum = x.StartBaojianNum,
Maozhong = x.Maozhong, Pizhong = x.Pizhong, Jingzhong = x.Jingzhong, Gongliang = x.Gongliang,
KcStatus = x.KcStatus, KcStatusName = KcStatusName(x.KcStatus),
RkItemId = x.RkItemId, RkDate = x.RkDate,
CkLastDate = x.CkLastDate, CkBaoshu = x.CkBaoshu, CkJingzhong = x.CkJingzhong, KcBaoshu = x.KcBaoshu,
Dengji = x.Dengji, Qingjie = x.Qingjie, Jiejing = x.Jiejing,
Piancha = x.Piancha, Zongcha = x.Zongcha, Qieduan = x.Qieduan,
Description = x.Description, BaojianNum = baojianDict.GetValueOrDefault(x.Id, 0)
}).ToList(),
Total = total, PageIndex = pageIndex, PageSize = pageSize
};
}
public async Task<WmsPiciSaveDto> GetPiciAsync(long id)
{
var pici = await _piciRepo.GetByIdAsync(id);
if (pici == null) throw new ServiceException("批次不存在", 404);
var baojians = await _baojianRepo.FindAsync(x => x.PiciId == id);
return new WmsPiciSaveDto
{
Id = pici.Id, Code = pici.Code, CodeType = pici.CodeType, Date = pici.Date, ScDate = pici.ScDate,
GyzkId = pici.GyzkId, WzmlId = pici.WzmlId, DepId = pici.DepId, Guige = pici.Guige,
JidiWaigou = pici.JidiWaigou, YoujiPutong = pici.YoujiPutong,
Baoshu = pici.Baoshu, StartBaojianNum = pici.StartBaojianNum,
Maozhong = pici.Maozhong, Pizhong = pici.Pizhong, Jingzhong = pici.Jingzhong, Gongliang = pici.Gongliang,
KcStatus = pici.KcStatus, Dengji = pici.Dengji, Qingjie = pici.Qingjie, Jiejing = pici.Jiejing,
Piancha = pici.Piancha, Zongcha = pici.Zongcha, Qieduan = pici.Qieduan, Description = pici.Description,
Baojians = baojians.OrderBy(x => x.Baojianhao).Select(b => new WmsPiciBaojianDto
{
Id = b.Id, PiciId = b.PiciId, Baojianhao = b.Baojianhao,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong,
Gongliang = b.Gongliang, Huichaolv = b.Huichaolv,
CkIsck = b.CkIsck, CkItemId = b.CkItemId, CkDate = b.CkDate, CkJingzhong = b.CkJingzhong,
Description = b.Description
}).ToList()
};
}
public async Task<long> SavePiciAsync(WmsPiciSaveDto dto)
{
if (string.IsNullOrWhiteSpace(dto.Code)) throw new ServiceException("请填写批号");
// 批号格式校验(对齐 F9 Page_PiciBaojian_Edit):正品 10 位数字;其他 31EE-7 位数字
var codeOk = dto.CodeType == 0
? System.Text.RegularExpressions.Regex.IsMatch(dto.Code.Trim(), @"^\d{10}$")
: System.Text.RegularExpressions.Regex.IsMatch(dto.Code.Trim(), @"^31EE-\d{7}$");
if (!codeOk)
throw new ServiceException("编号格式错误:正品编号类型 示例6100000001;其他编号类型 示例31EE-2600001");
WmsPici entity;
var isNew = dto.Id == 0;
if (isNew)
{
var exist = await _piciRepo.FirstOrDefaultAsync(x => x.Code == dto.Code);
if (exist != null) throw new ServiceException("批号已存在");
entity = new WmsPici();
}
else
{
entity = await _piciRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("批次不存在", 404);
}
entity.Code = dto.Code.Trim();
entity.CodeType = dto.CodeType;
entity.Date = dto.Date.Date;
entity.Year = dto.Date.Year;
entity.ScDate = dto.ScDate;
entity.GyzkId = dto.GyzkId;
entity.WzmlId = dto.WzmlId;
entity.DepId = dto.DepId;
entity.Guige = dto.Guige ?? "";
entity.JidiWaigou = dto.JidiWaigou ?? "";
entity.YoujiPutong = dto.YoujiPutong ?? "";
entity.Baoshu = dto.Baoshu;
entity.StartBaojianNum = dto.StartBaojianNum;
entity.Dengji = dto.Dengji ?? "";
entity.Qingjie = dto.Qingjie;
entity.Jiejing = dto.Jiejing;
entity.Piancha = dto.Piancha;
entity.Zongcha = dto.Zongcha;
entity.Qieduan = dto.Qieduan;
entity.Description = dto.Description ?? "";
// 包件:净重 = 毛重 − 皮重;公量 = 净重 × (1 + 11%) / (1 + 回潮率%)
var baojians = dto.Baojians ?? new List<WmsPiciBaojianDto>();
foreach (var b in baojians)
{
b.Jingzhong = Round2(b.Maozhong - b.Pizhong);
b.Gongliang = Round2(b.Jingzhong * 1.11m / (1 + b.Huichaolv / 100m));
}
if (baojians.Count > 0)
{
entity.Maozhong = Round2(baojians.Sum(b => b.Maozhong));
entity.Pizhong = Round2(baojians.Sum(b => b.Pizhong));
entity.Jingzhong = Round2(baojians.Sum(b => b.Jingzhong));
entity.Gongliang = Round2(baojians.Sum(b => b.Gongliang));
entity.Baoshu = baojians.Count;
}
else
{
entity.Maozhong = dto.Maozhong;
entity.Pizhong = dto.Pizhong;
entity.Jingzhong = dto.Jingzhong;
entity.Gongliang = dto.Gongliang;
}
if (entity.KcStatus == 0) entity.KcStatus = 1; // 待入库
if (isNew) await _piciRepo.AddAsync(entity); else await _piciRepo.UpdateAsync(entity);
var olds = await _baojianRepo.FindAsync(x => x.PiciId == entity.Id);
if (olds.Count > 0) await _baojianRepo.HardDeleteRangeAsync(olds);
foreach (var b in baojians)
{
await _baojianRepo.AddAsync(new WmsPiciBaojian
{
PiciId = entity.Id, Baojianhao = b.Baojianhao,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong,
Gongliang = b.Gongliang, Huichaolv = b.Huichaolv, Description = b.Description ?? ""
});
}
return entity.Id;
}
public async Task DeletePiciAsync(long id)
{
var pici = await _piciRepo.GetByIdAsync(id);
if (pici == null) throw new ServiceException("批次不存在", 404);
var baojians = await _baojianRepo.FindAsync(x => x.PiciId == id);
if (baojians.Count > 0) await _baojianRepo.HardDeleteRangeAsync(baojians);
// 物理删除:批号有唯一索引,软删残留会阻止重新创建同号批次
await _piciRepo.HardDeleteAsync(pici);
}
// ==================== 生丝批次:入库 / 出库状态流转(对齐 F9 SSRk/SSCk CQRS) ====================
private class PcAndBjRef { public long PiciId { get; set; } public List<long> PiciBaojianIds { get; set; } = new(); }
/// <summary>入库:按批次ID把批次置为「已入库」(对齐 F9 SSRk_CreateCommand.UpdateRk)</summary>
private async Task UpdatePiciRukuAsync(string piciIds, long rkItemId, DateTime rkDate)
{
foreach (var pid in SplitLongs(piciIds))
{
var pici = await _piciRepo.GetByIdAsync(pid);
if (pici == null) continue;
pici.RkItemId = rkItemId;
pici.RkDate = rkDate;
pici.KcStatus = 2; // 已入库
pici.KcBaoshu = pici.Baoshu;
pici.CkBaoshu = 0;
pici.CkJingzhong = 0;
await _piciRepo.UpdateAsync(pici);
}
}
/// <summary>入库回滚:把已入库且未出库的批次退回「待入库」(编辑/删除入库单时调用)</summary>
private async Task RollbackPiciRukuAsync(List<WmsRukuDetail> details)
{
var ids = details.Where(d => !string.IsNullOrWhiteSpace(d.PiciIds))
.SelectMany(d => SplitLongs(d.PiciIds)).Distinct().ToList();
foreach (var pid in ids)
{
var pici = await _piciRepo.GetByIdAsync(pid);
if (pici == null) continue;
if (pici.CkBaoshu > 0 || pici.KcStatus == 3 || pici.KcStatus == 4)
throw new ServiceException($"批次「{pici.Code}」已出库,不能修改/删除入库单");
pici.RkItemId = 0;
pici.RkDate = null;
pici.KcStatus = 1; // 待入库
pici.KcBaoshu = 0;
await _piciRepo.UpdateAsync(pici);
}
}
/// <summary>出库:按「批次+包件」标记包件已出库并更新批次库存状态(对齐 F9 SSCk_CreateCommand.Update_Ck)</summary>
private async Task UpdatePiciChukuAsync(string pcAndBjIds, long ckItemId, DateTime ckDate)
{
List<PcAndBjRef> refs;
try
{
refs = System.Text.Json.JsonSerializer.Deserialize<List<PcAndBjRef>>(pcAndBjIds,
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new();
}
catch { refs = new(); }
var ckDate0 = ckDate.Date;
foreach (var r in refs)
{
if (r.PiciBaojianIds == null || r.PiciBaojianIds.Count == 0) continue;
var pici = await _piciRepo.GetByIdAsync(r.PiciId);
if (pici == null) continue;
// 校验(对齐 F9):待入库不可出库;已全部出库不可再出库
if (pici.KcStatus == 1) throw new ServiceException($"批次「{pici.Code}」尚未入库,不能出库");
if (pici.KcStatus == 4) throw new ServiceException($"批次「{pici.Code}」已全部出库");
var baojians = await _baojianRepo.FindAsync(x => x.PiciId == r.PiciId);
var selected = baojians.Where(x => r.PiciBaojianIds.Contains(x.Id)).ToList();
if (selected.Count == 0) continue;
foreach (var bj in selected)
{
if (bj.CkIsck) continue;
bj.CkIsck = true;
bj.CkItemId = ckItemId;
bj.CkDate = ckDate0;
bj.CkJingzhong = bj.Jingzhong;
await _baojianRepo.UpdateAsync(bj);
}
var ckBaoshu = selected.Count;
var ckJingzhong = Round2(selected.Sum(x => x.Jingzhong));
await _piciChukuRepo.AddAsync(new WmsPiciChuku
{
PiciId = pici.Id, CkItemId = ckItemId, CkBaoshu = ckBaoshu,
CkDate = ckDate0, CkJingzhong = ckJingzhong
});
pici.CkItemId = ckItemId;
pici.CkLastDate = ckDate0;
pici.CkBaoshu += ckBaoshu;
pici.CkJingzhong = Round2(pici.CkJingzhong + ckJingzhong);
pici.KcBaoshu = pici.Baoshu - pici.CkBaoshu;
pici.KcStatus = pici.CkBaoshu >= pici.Baoshu ? 4 : 3; // 全部出库 / 部分出库
await _piciRepo.UpdateAsync(pici);
}
}
/// <summary>出库回滚:撤销包件出库标记并回退批次库存状态(编辑/删除出库单时调用)</summary>
private async Task RollbackPiciChukuAsync(List<WmsChukuDetail> details)
{
var itemIds = details.Select(d => d.Id).ToList();
if (itemIds.Count == 0) return;
var bjs = await _baojianRepo.GetAllAsync();
var hitBjs = bjs.Where(x => itemIds.Contains(x.CkItemId)).ToList();
foreach (var bj in hitBjs)
{
bj.CkIsck = false;
bj.CkItemId = 0;
bj.CkDate = null;
bj.CkJingzhong = 0;
await _baojianRepo.UpdateAsync(bj);
}
var chukus = await _piciChukuRepo.GetAllAsync();
var hitCks = chukus.Where(x => itemIds.Contains(x.CkItemId)).ToList();
if (hitCks.Count > 0) await _piciChukuRepo.HardDeleteRangeAsync(hitCks);
// 按批次重算库存状态
var piciIds = hitCks.Select(x => x.PiciId).Distinct().ToList();
foreach (var pid in piciIds)
{
var pici = await _piciRepo.GetByIdAsync(pid);
if (pici == null) continue;
var remain = chukus.Where(x => x.PiciId == pid && !hitCks.Contains(x)).ToList();
pici.CkBaoshu = remain.Sum(x => x.CkBaoshu);
pici.CkJingzhong = Round2(remain.Sum(x => x.CkJingzhong));
pici.KcBaoshu = pici.Baoshu - pici.CkBaoshu;
pici.CkItemId = remain.Count > 0 ? remain.Max(x => x.CkItemId) : 0;
pici.CkLastDate = remain.Count > 0 ? (remain.Max(x => x.CkDate) ?? default) : default;
pici.KcStatus = pici.CkBaoshu == 0 ? 2 : (pici.CkBaoshu >= pici.Baoshu ? 4 : 3);
await _piciRepo.UpdateAsync(pici);
}
}
private static List<long> SplitLongs(string s)
=> (s ?? "").Split(new[] { ',', ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => { long.TryParse(x.Trim(), out var v); return v; })
.Where(v => v > 0).Distinct().ToList();
/// <summary>生丝出库选择批次+包件(对齐 F9 SSPici_GetChukuSelectAsync)</summary>
public async Task<List<WmsPiciChukuSelectDto>> GetPiciChukuSelectAsync(long wzmlId, string excludePiciIds)
{
var all = await _piciRepo.GetAllAsync();
var q = all.Where(x => x.KcStatus == 2 || x.KcStatus == 3); // 已入库 / 部分出库
if (wzmlId > 0) q = q.Where(x => x.WzmlId == wzmlId);
var excl = SplitLongs(excludePiciIds);
if (excl.Count > 0) q = q.Where(x => !excl.Contains(x.Id));
var wzDict = await WzDictAsync();
var gyzkDict = (await _gyzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.ZhuangKouName);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var baojians = await _baojianRepo.GetAllAsync();
return q.OrderByDescending(x => x.Date).ThenBy(x => x.Code).Select(p =>
{
var bjs = baojians.Where(b => b.PiciId == p.Id).OrderBy(b => b.Baojianhao).ToList();
var kcBjs = bjs.Where(b => !b.CkIsck).ToList();
return new WmsPiciChukuSelectDto
{
PiciId = p.Id, PiciCode = p.Code, Date = p.Date, Guige = p.Guige,
WzmlName = wzDict.GetValueOrDefault(p.WzmlId, ""),
GyzkName = gyzkDict.GetValueOrDefault(p.GyzkId, ""),
DepName = depDict.GetValueOrDefault(p.DepId, ""),
JidiWaigou = p.JidiWaigou,
Baoshu = p.Baoshu, KcBaoshu = kcBjs.Count,
Jingzhong = p.Jingzhong, KcJingzhong = Round2(kcBjs.Sum(x => x.Jingzhong)),
Description = p.Description,
Baojians = bjs.Select(b => new WmsPiciBaojianDto
{
Id = b.Id, PiciId = b.PiciId, Baojianhao = b.Baojianhao,
Maozhong = b.Maozhong, Pizhong = b.Pizhong, Jingzhong = b.Jingzhong,
Gongliang = b.Gongliang, Huichaolv = b.Huichaolv,
CkIsck = b.CkIsck, CkItemId = b.CkItemId, CkDate = b.CkDate, CkJingzhong = b.CkJingzhong,
Description = b.Description
}).ToList()
};
}).ToList();
}
/// <summary>读取批次质检</summary>
public async Task<WmsPiciQcDto> GetPiciQcAsync(long piciId)
{
var qc = await _piciQcRepo.FirstOrDefaultAsync(x => x.PiciId == piciId);
return qc == null ? new WmsPiciQcDto { PiciId = piciId } : ToQcDto(qc);
}
public async Task SavePiciQcAsync(WmsPiciQcDto dto)
{
if (dto.PiciId <= 0) throw new ServiceException("批次无效");
var qc = await _piciQcRepo.FirstOrDefaultAsync(x => x.PiciId == dto.PiciId);
var isNew = qc == null;
if (isNew) qc = new WmsPiciQc { PiciId = dto.PiciId };
qc.Pingwei = dto.Pingwei ?? ""; qc.Seze = dto.Seze ?? "";
qc.AvgHuichaolv = dto.AvgHuichaolv; qc.AvgGongliangXiandu = dto.AvgGongliangXiandu;
qc.XianduPiancha = dto.XianduPiancha; qc.XianduZdpiancha = dto.XianduZdpiancha;
qc.Qingjie = dto.Qingjie; qc.Jiejing = dto.Jiejing;
qc.JunyunYd = dto.JunyunYd; qc.JunyunEd = dto.JunyunEd; qc.JunyunSd = dto.JunyunSd;
qc.Qieduan = dto.Qieduan;
qc.DuanlieQiangdu = dto.DuanlieQiangdu ?? ""; qc.DuanlieShenchanglv = dto.DuanlieShenchanglv ?? "";
qc.Baohe = dto.Baohe;
qc.WgWg = dto.WgWg ?? ""; qc.WgYanse = dto.WgYanse ?? ""; qc.WgGuangze = dto.WgGuangze ?? ""; qc.WgShougan = dto.WgShougan ?? "";
qc.QjZhuyao = dto.QjZhuyao; qc.QjCiyaoFeisi = dto.QjCiyaoFeisi; qc.QjCiyaoDacao = dto.QjCiyaoDacao;
qc.QjCiyaoNianfucao = dto.QjCiyaoNianfucao; qc.QjCiyaoDachangjie = dto.QjCiyaoDachangjie;
qc.QjCiyaoZhongluoxuan = dto.QjCiyaoZhongluoxuan;
qc.QjPutongXiaocao = dto.QjPutongXiaocao; qc.QjPutongChangjie = dto.QjPutongChangjie;
qc.QjPutongLuoxuan = dto.QjPutongLuoxuan; qc.QjPutongHuanjie = dto.QjPutongHuanjie;
qc.Jj100 = dto.Jj100; qc.Jj95 = dto.Jj95; qc.Jj90 = dto.Jj90; qc.Jj85 = dto.Jj85; qc.Jj80 = dto.Jj80;
qc.Jj75 = dto.Jj75; qc.Jj70 = dto.Jj70; qc.Jj65 = dto.Jj65; qc.Jj60 = dto.Jj60; qc.Jj55 = dto.Jj55;
qc.Jj50 = dto.Jj50; qc.Jj45 = dto.Jj45;
qc.JjYuanyin = dto.JjYuanyin ?? ""; qc.Description = dto.Description ?? "";
if (isNew) await _piciQcRepo.AddAsync(qc); else await _piciQcRepo.UpdateAsync(qc);
}
private static WmsPiciQcDto ToQcDto(WmsPiciQc qc) => new()
{
Id = qc.Id, PiciId = qc.PiciId, Pingwei = qc.Pingwei, Seze = qc.Seze,
AvgHuichaolv = qc.AvgHuichaolv, AvgGongliangXiandu = qc.AvgGongliangXiandu,
XianduPiancha = qc.XianduPiancha, XianduZdpiancha = qc.XianduZdpiancha,
Qingjie = qc.Qingjie, Jiejing = qc.Jiejing,
JunyunYd = qc.JunyunYd, JunyunEd = qc.JunyunEd, JunyunSd = qc.JunyunSd, Qieduan = qc.Qieduan,
DuanlieQiangdu = qc.DuanlieQiangdu, DuanlieShenchanglv = qc.DuanlieShenchanglv, Baohe = qc.Baohe,
WgWg = qc.WgWg, WgYanse = qc.WgYanse, WgGuangze = qc.WgGuangze, WgShougan = qc.WgShougan,
QjZhuyao = qc.QjZhuyao, QjCiyaoFeisi = qc.QjCiyaoFeisi, QjCiyaoDacao = qc.QjCiyaoDacao,
QjCiyaoNianfucao = qc.QjCiyaoNianfucao, QjCiyaoDachangjie = qc.QjCiyaoDachangjie,
QjCiyaoZhongluoxuan = qc.QjCiyaoZhongluoxuan,
QjPutongXiaocao = qc.QjPutongXiaocao, QjPutongChangjie = qc.QjPutongChangjie,
QjPutongLuoxuan = qc.QjPutongLuoxuan, QjPutongHuanjie = qc.QjPutongHuanjie,
Jj100 = qc.Jj100, Jj95 = qc.Jj95, Jj90 = qc.Jj90, Jj85 = qc.Jj85, Jj80 = qc.Jj80,
Jj75 = qc.Jj75, Jj70 = qc.Jj70, Jj65 = qc.Jj65, Jj60 = qc.Jj60, Jj55 = qc.Jj55,
Jj50 = qc.Jj50, Jj45 = qc.Jj45, JjYuanyin = qc.JjYuanyin, Description = qc.Description
};
/// <summary>读取批次纤度检验</summary>
public async Task<List<WmsPiciQcXdjyDto>> GetPiciQcXdjyAsync(long piciId)
{
var list = await _piciQcXdjyRepo.FindAsync(x => x.PiciId == piciId);
return list.OrderBy(x => x.Id).Select(x => new WmsPiciQcXdjyDto
{ Id = x.Id, PiciId = x.PiciId, Xiandu = x.Xiandu, Num = x.Num }).ToList();
}
public async Task SavePiciQcXdjyAsync(List<WmsPiciQcXdjyDto> items)
{
var piciId = items?.FirstOrDefault()?.PiciId ?? 0;
if (piciId <= 0) throw new ServiceException("批次无效");
var olds = await _piciQcXdjyRepo.FindAsync(x => x.PiciId == piciId);
if (olds.Count > 0) await _piciQcXdjyRepo.HardDeleteRangeAsync(olds);
foreach (var it in items ?? new List<WmsPiciQcXdjyDto>())
{
await _piciQcXdjyRepo.AddAsync(new WmsPiciQcXdjy
{ PiciId = piciId, Xiandu = it.Xiandu, Num = it.Num });
}
}
// ==================== 原料采购 ====================
public async Task<List<WmsCaigouDto>> GetCaigouAsync(DateTime? start, DateTime? end)
{
var all = await _caigouRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
var gysDict = (await _gysRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var ylzkDict = (await _ylzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
return q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code).Select(x => new WmsCaigouDto
{
Id = x.Id, Date = x.Date, Code = x.Code,
GysId = x.GysId, GysName = gysDict.GetValueOrDefault(x.GysId, ""),
YlzkId = x.YlzkId, YlzkName = ylzkDict.GetValueOrDefault(x.YlzkId, ""),
Jianshu = x.Jianshu, Maozhong = x.Maozhong, Pizhong = x.Pizhong, Jingzhong = x.Jingzhong,
Danjia = x.Danjia, Jine = x.Jine, Description = x.Description
}).ToList();
}
public async Task<long> SaveCaigouAsync(WmsCaigouDto dto)
{
WmsCaigou entity;
var isNew = dto.Id == 0;
if (isNew) entity = new WmsCaigou();
else
{
entity = await _caigouRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("采购记录不存在", 404);
}
entity.Date = dto.Date.Date;
entity.Code = dto.Code ?? "";
entity.GysId = dto.GysId;
entity.YlzkId = dto.YlzkId;
entity.Jianshu = dto.Jianshu;
entity.Maozhong = dto.Maozhong;
entity.Pizhong = dto.Pizhong;
entity.Jingzhong = dto.Jingzhong != 0 ? dto.Jingzhong : Round2(dto.Maozhong - dto.Pizhong);
entity.Danjia = dto.Danjia;
entity.Jine = dto.Jine != 0 ? dto.Jine : Round2(entity.Jingzhong * dto.Danjia);
entity.Description = dto.Description ?? "";
if (isNew) await _caigouRepo.AddAsync(entity); else await _caigouRepo.UpdateAsync(entity);
return entity.Id;
}
public async Task DeleteCaigouAsync(long id)
{
var entity = await _caigouRepo.GetByIdAsync(id);
if (entity == null) throw new ServiceException("采购记录不存在", 404);
await _caigouRepo.DeleteAsync(entity);
}
// ==================== 原料翻包计划 ====================
public async Task<List<WmsFbplanDto>> GetFbplanAsync(DateTime? start, DateTime? end)
{
var all = await _fbplanRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
var ylzkDict = (await _ylzkRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var wzDict = await WzDictAsync();
var jls = await _fbjlRepo.GetAllAsync();
var jlDict = jls.GroupBy(x => x.FbplanId).ToDictionary(g => g.Key, g => g.OrderBy(x => x.Date).ToList());
return q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code).ToList().Select(x =>
{
jlDict.TryGetValue(x.Id, out var records);
records ??= new List<WmsFbjl>();
return new WmsFbplanDto
{
Id = x.Id, Date = x.Date, Code = x.Code,
YlzkId = x.YlzkId, YlzkName = ylzkDict.GetValueOrDefault(x.YlzkId, ""),
WzmlId = x.WzmlId, WzmlName = wzDict.GetValueOrDefault(x.WzmlId, ""),
Jianshu = x.Jianshu, Jingzhong = x.Jingzhong,
FbType = x.FbType, FbTypeName = FbTypeName(x.FbType),
RukuStatus = x.RukuStatus, RukuStatusName = RukuStatusName(x.RukuStatus),
Description = x.Description,
YiwanchengJianshu = records.Sum(r => r.Jianshu),
YiwanchengJingzhong = records.Sum(r => r.Jingzhong),
Records = records.Select(r => new WmsFbjlDto
{
Id = r.Id, FbplanId = r.FbplanId, Date = r.Date,
Jianshu = r.Jianshu, Jingzhong = r.Jingzhong, Description = r.Description
}).ToList()
};
}).ToList();
}
public async Task<long> SaveFbplanAsync(WmsFbplanDto dto)
{
WmsFbplan entity;
var isNew = dto.Id == 0;
if (isNew) entity = new WmsFbplan();
else
{
entity = await _fbplanRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("翻包计划不存在", 404);
}
entity.Date = dto.Date.Date;
entity.Code = dto.Code ?? "";
entity.YlzkId = dto.YlzkId;
entity.WzmlId = dto.WzmlId;
entity.Jianshu = dto.Jianshu;
entity.Jingzhong = dto.Jingzhong;
entity.FbType = dto.FbType == 0 ? 1 : dto.FbType;
entity.RukuStatus = dto.RukuStatus == 0 ? 1 : dto.RukuStatus;
entity.Description = dto.Description ?? "";
if (isNew) await _fbplanRepo.AddAsync(entity); else await _fbplanRepo.UpdateAsync(entity);
// 翻包记录整体覆盖
var olds = await _fbjlRepo.FindAsync(x => x.FbplanId == entity.Id);
if (olds.Count > 0) await _fbjlRepo.HardDeleteRangeAsync(olds);
foreach (var r in dto.Records ?? new List<WmsFbjlDto>())
{
await _fbjlRepo.AddAsync(new WmsFbjl
{
FbplanId = entity.Id, Date = r.Date.Date, Jianshu = r.Jianshu, Jingzhong = r.Jingzhong,
Description = r.Description ?? ""
});
}
return entity.Id;
}
public async Task DeleteFbplanAsync(long id)
{
var entity = await _fbplanRepo.GetByIdAsync(id);
if (entity == null) throw new ServiceException("翻包计划不存在", 404);
var jls = await _fbjlRepo.FindAsync(x => x.FbplanId == id);
if (jls.Count > 0) await _fbjlRepo.HardDeleteRangeAsync(jls);
await _fbplanRepo.DeleteAsync(entity);
}
// ==================== 物资采购计划 ====================
public async Task<List<WmsCgplanListDto>> GetCgplanAsync(DateTime? start, DateTime? end)
{
var all = await _cgplanRepo.GetAllAsync();
var q = all.AsQueryable();
if (start.HasValue) q = q.Where(x => x.Date.Date >= start.Value.Date);
if (end.HasValue) q = q.Where(x => x.Date.Date <= end.Value.Date);
var depDict = (await _depRepo.GetAllAsync()).ToDictionary(x => x.Id, x => x.Name);
var items = await _cgplanItemRepo.GetAllAsync();
var itemDict = items.GroupBy(x => x.CgplanId).ToDictionary(g => g.Key, g => g.ToList());
return q.OrderByDescending(x => x.Date).ThenByDescending(x => x.Code).ToList().Select(x =>
{
itemDict.TryGetValue(x.Id, out var list);
list ??= new List<WmsCgplanItem>();
return new WmsCgplanListDto
{
Id = x.Id, Date = x.Date, Code = x.Code,
DepId = x.DepId, DepName = depDict.GetValueOrDefault(x.DepId, ""),
Status = x.Status, StatusName = CgplanStatusName(x.Status),
Description = x.Description,
ItemCount = list.Count,
Shuliang = list.Sum(i => i.Shuliang),
Jine = list.Sum(i => i.Jine)
};
}).ToList();
}
public async Task<WmsCgplanSaveDto> GetCgplanAsync(long id)
{
var plan = await _cgplanRepo.GetByIdAsync(id);
if (plan == null) throw new ServiceException("采购计划不存在", 404);
var items = await _cgplanItemRepo.FindAsync(x => x.CgplanId == id);
var wzFull = await WzFullDictAsync();
return new WmsCgplanSaveDto
{
Id = plan.Id, Date = plan.Date, Code = plan.Code, DepId = plan.DepId, Status = plan.Status,
Description = plan.Description,
Items = items.OrderBy(x => x.Id).Select(i =>
{
wzFull.TryGetValue(i.WzmlId, out var wz);
return new WmsCgplanItemDto
{
Id = i.Id, CgplanId = i.CgplanId, WzmlId = i.WzmlId,
WzmlName = wz.Name ?? "", WzmlCode = wz.Code ?? "", Unit = wz.Unit ?? "",
Shuliang = i.Shuliang, Danjia = i.Danjia, Jine = i.Jine, Description = i.Description
};
}).ToList()
};
}
public async Task<long> SaveCgplanAsync(WmsCgplanSaveDto dto)
{
WmsCgplan entity;
var isNew = dto.Id == 0;
if (isNew) entity = new WmsCgplan();
else
{
entity = await _cgplanRepo.GetByIdAsync(dto.Id);
if (entity == null) throw new ServiceException("采购计划不存在", 404);
}
entity.Date = dto.Date.Date;
entity.Code = dto.Code ?? "";
entity.DepId = dto.DepId;
entity.Status = dto.Status;
entity.Description = dto.Description ?? "";
if (isNew) await _cgplanRepo.AddAsync(entity); else await _cgplanRepo.UpdateAsync(entity);
var olds = await _cgplanItemRepo.FindAsync(x => x.CgplanId == entity.Id);
if (olds.Count > 0) await _cgplanItemRepo.HardDeleteRangeAsync(olds);
foreach (var i in dto.Items ?? new List<WmsCgplanItemDto>())
{
if (i.WzmlId == 0) continue;
var jine = i.Jine != 0 ? i.Jine : Round2(i.Shuliang * i.Danjia);
await _cgplanItemRepo.AddAsync(new WmsCgplanItem
{
CgplanId = entity.Id, WzmlId = i.WzmlId, Shuliang = i.Shuliang,
Danjia = i.Danjia, Jine = jine, Description = i.Description ?? ""
});
}
return entity.Id;
}
public async Task DeleteCgplanAsync(long id)
{
var entity = await _cgplanRepo.GetByIdAsync(id);
if (entity == null) throw new ServiceException("采购计划不存在", 404);
var items = await _cgplanItemRepo.FindAsync(x => x.CgplanId == id);
if (items.Count > 0) await _cgplanItemRepo.HardDeleteRangeAsync(items);
await _cgplanRepo.DeleteAsync(entity);
}
// ==================== 工具 ====================
private static decimal Round2(decimal v) => Math.Round(v, 2, MidpointRounding.AwayFromZero);
private static decimal Round4(decimal v) => Math.Round(v, 4, MidpointRounding.AwayFromZero);
}