- 新增 PayRoll(工资)、Wms(仓储)、Prod(车速/缫丝/考勤/丝片检测/丝片台账/纤检日报/质量日报) 模块 - 新增对应 Domain 实体、Application DTO/Service、Api Controller 与前端页面 - 移除旧 Attendance 考勤模块(Controller/Service/Entity/DTO 及 5 个前端页面),由 Prod/Kaoqin 取代 - 调整路由、仪表盘、Lsjl 流丝记录实体与服务 - 更新业务种子数据与 DbSeeder - 补充工作进度文档 14-27
1904 lines
91 KiB
C#
1904 lines
91 KiB
C#
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);
|
||
}
|