using DS.Module.Core; using DS.Module.Core.Condition; using DS.Module.Core.Data; using DS.Module.Core.Extensions; using DS.WMS.Core.Code.Entity; using DS.WMS.Core.Fee.Dtos; using DS.WMS.Core.Fee.Entity; using DS.WMS.Core.Fee.Interface; using DS.WMS.Core.Info.Entity; using DS.WMS.Core.Op.Dtos; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Sys.Entity; using DS.WMS.Core.TaskInteraction.Interface; using Masuit.Tools; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using SqlSugar; namespace DS.WMS.Core.Fee.Method { /// /// 往来单位费用模板 /// public class FeeCustTemplateService : FeeServiceBase, IFeeCustTemplateService { Lazy actionService; Lazy feeService; Lazy exchangeService; /// /// 初始化 /// /// public FeeCustTemplateService(IServiceProvider serviceProvider) : base(serviceProvider) { actionService = new Lazy(serviceProvider.GetRequiredService()); feeService = new Lazy(serviceProvider.GetRequiredService()); exchangeService = new Lazy(serviceProvider.GetRequiredService()); } /// /// 根据开船日生成费用 /// /// 开船日 /// public async Task GenerateFeesAsync(DateTime etd) { DateTime dt = DateTime.Now; var list = await TenantDb.Queryable() .Where(x => !x.IsDisabled && SqlFunc.Between(dt, x.StartTime, x.EndTime) && (x.IsShared || x.CustomerId != null)) .Select(x => new FeeCustTemplate { Id = x.Id, CustomerId = x.CustomerId, FeeType = x.FeeType, FeeCategoryId = x.FeeCategoryId, FeeCategoryName = x.FeeCategoryName, Priority = x.Priority, IsShared = x.IsShared, POLCode = x.POLCode, PODCode = x.PODCode, LaneId = x.LaneId, SourceId = x.SourceId, CarrierId = x.CarrierId, ForwarderId = x.ForwarderId, MBLFrtCode = x.MBLFrtCode, Condition = x.Condition }).ToListAsync(); if (list.Count == 0) return; if (etd == default) etd = dt.Date; var tids = list.Select(x => x.Id); var orders = await TenantDb.Queryable().Where(x => SqlFunc.DateIsSame(x.ETD, etd) && SqlFunc.Subqueryable().Where(y => y.BusinessId == x.Id && y.BusinessType == BusinessType.OceanShippingExport && tids.Contains(y.TemplateId)).NotAny()) .Select(x => new SeaExportRes { CtnInfo = SqlFunc.Subqueryable().Where(y => y.BSNO == x.Id.ToString()).ToList(y => new OpCtnRes { Ctn = y.Ctn, CtnNum = SqlFunc.IsNull(y.CtnNum.Value, 0) }) }, true).ToListAsync(); if (orders.Count == 0) return; List feeList = []; await TenantDb.Ado.BeginTranAsync(); try { var localCurrecy = await Db.Queryable().Where(x => x.Id == User.OrgId).Select(x => x.LocalCurrency).FirstAsync(); foreach (var order in orders) { var custList = list.Where(x => x.CustomerId == order.CustomerId).OrderBy(x => x.Priority); foreach (var template in custList) //遍历客户费用模板,查找匹配项 { var fees = await CreateFeesIfMatchAsync(order, template, localCurrecy); if (fees != null) { feeList.AddRange(fees); var record = new FeeCustTemplateRecord { BusinessId = order.Id, BusinessType = BusinessType.OceanShippingExport, CreateBy = long.Parse(User.UserId), CreateTime = dt, FeeCategoryId = string.IsNullOrEmpty(template.FeeCategoryId) ? null : template.FeeCategoryId, FeeType = template.FeeType, TemplateId = template.Id }; await TenantDb.Insertable(record).ExecuteCommandAsync(); } } //未找到客户模板,开始匹配共享模板 if (feeList.Count == 0) { var sharedList = list.Where(x => x.IsShared).OrderBy(x => x.Priority).ToList(); foreach (var template in sharedList) { var fees = await CreateFeesIfMatchAsync(order, template, localCurrecy); if (fees != null) { feeList.AddRange(fees); var record = new FeeCustTemplateRecord { BusinessId = order.Id, BusinessType = BusinessType.OceanShippingExport, CreateBy = long.Parse(User.UserId), CreateTime = dt, FeeCategoryId = string.IsNullOrEmpty(template.FeeCategoryId) ? null : template.FeeCategoryId, FeeType = template.FeeType, TemplateId = template.Id }; await TenantDb.Insertable(record).ExecuteCommandAsync(); break; } } } //写入当前业务的费用 if (feeList.Count > 0) { var result = await feeService.Value.SaveAsync(feeList, true, false); if (!result.Succeeded) { //记录失败日志 await new ApplicationException(result.Message).LogAsync(Db); } feeList.Clear(); } } await TenantDb.Ado.CommitTranAsync(); } catch (Exception ex) { await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); } } private async Task?> CreateFeesIfMatchAsync(SeaExportRes order, FeeCustTemplate template, string localCurrecy) { if (!string.IsNullOrEmpty(template.POLCode) && template.POLCode != order.LoadPortCode) return null; if (!string.IsNullOrEmpty(template.PODCode) && template.PODCode != order.DischargePortCode) return null; if (!string.IsNullOrEmpty(template.MBLFrtCode) && template.MBLFrtCode != order.MBLFrt) return null; if (template.LaneId.HasValue && template.LaneId != order.LaneId) return null; if (template.CarrierId.HasValue && template.CarrierId != order.CarrierId) return null; if (template.SourceId.HasValue && template.SourceId != order.SourceId) return null; if (template.ForwarderId.HasValue && template.ForwarderId != order.ForwarderId) return null; if (!string.IsNullOrEmpty(template.Condition)) //设置了自定义匹配条件 { var conditionModel = JsonConvert.DeserializeObject(template.Condition); if (!actionService.Value.IsMatch(order, conditionModel)) return null; } if (await TenantDb.Queryable().AnyAsync(x => x.BusinessId == order.Id && x.BusinessType == BusinessType.OceanShippingExport && x.FeeType == template.FeeType && x.FeeCategoryId == template.FeeCategoryId)) return null; var templateDetails = await TenantDb.Queryable().Where(y => y.TemplateId == template.Id).ToListAsync(); var groups = templateDetails.GroupBy(x => new { x.FeeId, x.Unit }).ToList(); List fees = new(groups.Count); foreach (var g in groups) { FeeCustTemplateDetail? detail = null; int qty = 0; if (string.Equals(g.Key.Unit, "P", StringComparison.OrdinalIgnoreCase)) { detail = g.FirstOrDefault()!; qty = detail.IsCtn ? 0 : 1; } else { var ctn = order.CtnInfo?.Find(x => x.Ctn == g.Key.Unit); if (ctn == null) continue; detail = g.FirstOrDefault(x => x.Unit == ctn.Ctn); qty = ctn.CtnNum; } var fee = new FeeRecord { BusinessId = order.Id, BusinessType = BusinessType.OceanShippingExport, FeeType = template.FeeType, FeeId = g.Key.FeeId, Unit = g.Key.Unit, Remark = template.FeeCategoryName, TemplateId = template.Id, CustomerId = detail.CustomerId, CustomerName = detail.CustomerName, CustomerType = detail.CustomerType, InputMethod = InputMethod.Automatic, Quantity = qty, FeeCode = detail.FeeCode, FeeName = detail.FeeName, UnitPrice = detail.UnitPrice.GetValueOrDefault(), Currency = detail.Currency, ExchangeRate = detail.ExchangeRate, TaxRate = detail.TaxRate.GetValueOrDefault(), AccTaxRate = detail.AccTaxRate.GetValueOrDefault(), Tax = detail.Tax.GetValueOrDefault(), TaxUnitPrice = detail.TaxUnitPrice.GetValueOrDefault(), IsInvoice = detail.IsInvoice, IsAdvancedPay = detail.IsAdvancedPay }; if (fee.ExchangeRate.GetValueOrDefault() == 0 && fee.Currency != localCurrecy) { var er = await exchangeService.Value.GetExchangeRateAsync(new ExchangeRate { CurrencyFrom = detail.Currency, CurrencyTo = localCurrecy, FeeType = template.FeeType }); fee.ExchangeRate = er.Data?.Rate; } if (fee.CustomerId == 0) { switch (fee.CustomerType) { case "controller": fee.CustomerId = order.CustomerId; fee.CustomerName = order.CustomerName; break; case "yard": fee.CustomerId = order.YardId; fee.CustomerName = order.Yard; break; case "custom": fee.CustomerId = order.CustomserId; fee.CustomerName = order.Customser; break; case "contract": fee.CustomerId = order.ContractClientId; fee.CustomerName = order.ContractClientName; break; case "shipagency": fee.CustomerId = order.ShipAgencyId; fee.CustomerName = order.ShipAgency; break; case "shipper": fee.CustomerId = order.ShipperId.GetValueOrDefault(); fee.CustomerName = order.Shipper; break; case "truck": fee.CustomerId = order.TruckerId; fee.CustomerName = order.Trucker; break; case "booking": fee.CustomerId = order.ForwarderId; fee.CustomerName = order.Forwarder; break; case "wareHouse": fee.CustomerId = order.WareHouseId; fee.CustomerName = order.WareHouse; break; case "shippercn": fee.CustomerId = order.ShipperCnId.GetValueOrDefault(); fee.CustomerName = order.ShipperCn; break; case "agent": fee.CustomerId = order.AgentId.GetValueOrDefault(); fee.CustomerName = order.Agent; break; } } fees.Add(fee); } return fees; } /// /// 导入费用模板 /// /// /// public async Task ImportAsync(IEnumerable models) { ArgumentNullException.ThrowIfNull(models, nameof(models)); long userId = long.Parse(User.UserId); var custNames = models.Select(x => x.CustomerName).Distinct(); var custList = await TenantDb.Queryable().Where(x => custNames.Contains(x.Name) && x.Status == 0).Select(x => new { x.Id, x.Name }).ToListAsync(); var feeNames = models.Select(x => x.FeeName).Distinct(); var fees = await TenantDb.Queryable().Where(x => feeNames.Contains(x.Name)).Select(x => new { x.Id, x.Code, x.Name }).ToListAsync(); var carrierCodes = models.Select(x => x.CarrierName).Distinct(); var carriers = await TenantDb.Queryable().Where(x => carrierCodes.Contains(x.Code)).Select(x => new { x.Id, x.Code }).ToListAsync(); var laneCodes = models.Select(x => x.LaneName).Distinct(); var lanes = await TenantDb.Queryable().Where(x => laneCodes.Contains(x.LaneName)).Select(x => new { x.Id, x.LaneName }).ToListAsync(); var ptCodes = models.Select(x => x.PaymentType).Distinct(); var frtList = await TenantDb.Queryable().Where(x => ptCodes.Contains(x.CnName)).Select(x => new { x.CnName, x.FrtName }).ToListAsync(); var sourceCodes = models.Select(x => x.SourceCode).Distinct(); var sources = await TenantDb.Queryable().Where(x => sourceCodes.Contains(x.SourceCode)).Select(x => new { x.Id, x.SourceCode }).ToListAsync(); List list = []; var groups = models.GroupBy(x => new { x.CarrierName, x.POL, x.POD, x.LaneName, x.PaymentType, x.CustomerName, x.SourceCode }).ToList(); foreach (var g in groups) { var first = g.First(); FeeCustTemplate template = new FeeCustTemplate { Name = g.Key.SourceCode + "-" + "导入费用模板" + groups.IndexOf(g), BusinessType = BusinessType.OceanShippingExport, CustomerId = custList.Find(x => x.Name == g.Key.CustomerName)?.Id, CustomerName = g.Key.CustomerName, FeeType = FeeType.Receivable, IsDisabled = first.StatusText != "有效", StartTime = first.StartTime, EndTime = first.EndTime, IsShared = string.IsNullOrEmpty(g.Key.CustomerName), Priority = 1, CarrierId = carriers.Find(x => x.Code == g.Key.CarrierName)?.Id, LaneId = lanes.Find(x => x.LaneName == g.Key.LaneName)?.Id, MBLFrtCode = frtList.Find(x => x.CnName == g.Key.PaymentType)?.FrtName, POLCode = g.Key.POL, PODCode = g.Key.POD, SourceId = sources.Find(x => x.SourceCode == g.Key.SourceCode)?.Id, CreateBy = userId, Note = "系统导入", Details = [] }; list.Add(template); foreach (var item in g) { var detail = new FeeCustTemplateDetail { CustomerId = template.CustomerId.GetValueOrDefault(), CustomerName = template.CustomerName, CustomerType = template.FeeType == FeeType.Receivable ? "controller" : null, Currency = item.Currency, FeeId = (fees.Find(x => x.Name == item.FeeName)?.Id).GetValueOrDefault(), FeeCode = fees.Find(x => x.Name == item.FeeName)?.Code, FeeName = item.FeeName, IsCtn = item.UnitPrice == null, Tax = 0, TaxRate = 0, AccTaxRate = 0, ExchangeRate = item.Currency == FeeCurrency.RMB_CODE ? 1 : null, IsInvoice = true, CreateBy = userId }; if (item.UnitPrice.HasValue) { detail.UnitPrice = detail.TaxUnitPrice = item.UnitPrice.Value; detail.Unit = "P"; template.Details.Add(detail); } if (item.GP20.HasValue) { var copiedDetail = detail.DeepClone(); copiedDetail.Unit = "20GP"; copiedDetail.UnitPrice = copiedDetail.TaxUnitPrice = item.GP20.Value; template.Details.Add(copiedDetail); } if (item.GP40.HasValue) { var copiedDetail = detail.DeepClone(); copiedDetail.Unit = "40GP"; copiedDetail.UnitPrice = copiedDetail.TaxUnitPrice = item.GP40.Value; template.Details.Add(copiedDetail); } if (item.HQ40.HasValue) { var copiedDetail = detail.DeepClone(); copiedDetail.Unit = "40HQ"; copiedDetail.UnitPrice = copiedDetail.TaxUnitPrice = item.HQ40.Value; template.Details.Add(copiedDetail); } } } try { await TenantDb.InsertNav(list).Include(x => x.Details).ExecuteCommandAsync(); return DataResult.Success; } catch (Exception ex) { await ex.LogAsync(Db); return DataResult.Failed(ex.Message); } } /// /// 列表 /// /// /// public async Task>> GetListAsync(PageRequest request) { var whereList = request.GetConditionalModels(Db); return await TenantDb.Queryable().Where(whereList).ToQueryPageAsync(request.PageCondition); } /// /// 详情 /// /// /// public async Task> GetAsync(long id) { var data = await TenantDb.Queryable().Where(x => x.Id == id).FirstAsync(); if (data != null) data.Details = await TenantDb.Queryable().Where(x => x.TemplateId == data.Id).ToListAsync(); return DataResult.Success(data, MultiLanguageConst.DataQuerySuccess); } /// /// 编辑 /// /// /// public async Task EditAsync(FeeCustTemplate model) { if (model.Details.Count > 0) { long userId = long.Parse(User.UserId); DateTime dt = DateTime.Now; foreach (var item in model.Details) { item.TemplateId = model.Id; item.TaxRate ??= 0; item.AccTaxRate ??= 0; item.Tax = Math.Round(item.TaxUnitPrice.GetValueOrDefault() - item.TaxRate.GetValueOrDefault() / 100 * item.TaxUnitPrice.GetValueOrDefault(), 2, MidpointRounding.AwayFromZero); item.UnitPrice = item.TaxUnitPrice - item.Tax.GetValueOrDefault(); if (item.ExchangeRate == null) { if (item.Currency == FeeCurrency.RMB_CODE) { item.ExchangeRate = 1m; } else { var er = await exchangeService.Value.GetExchangeRateAsync(new ExchangeRate { CurrencyFrom = item.Currency, CurrencyTo = FeeCurrency.RMB_CODE, FeeType = model.FeeType }); item.ExchangeRate = er.Data?.Rate; } } if (item.CustomerId == 0 && model.CustomerId.HasValue) item.CustomerId = model.CustomerId.Value; if (string.IsNullOrEmpty(item.CustomerName)) item.CustomerName = model.CustomerName; item.CreateBy = userId; item.CreateTime = dt; } } await TenantDb.Ado.BeginTranAsync(); try { if (model.Id == 0) { await TenantDb.InsertNav(model).Include(x => x.Details).ExecuteCommandAsync(); } else { await TenantDb.Updateable(model).ExecuteCommandAsync(); if (model.Details.Count > 0) await TenantDb.Storageable(model.Details).DefaultAddElseUpdate().ExecuteCommandAsync(); } await TenantDb.Ado.CommitTranAsync(); return DataResult.Successed("提交成功", model.Id, MultiLanguageConst.DataCreateSuccess); } catch (Exception ex) { await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 根据ID批量删除 /// /// /// public async Task DeleteAsync(IdModel model) { bool flag = await TenantDb.DeleteNav(x => model.Ids.Contains(x.Id)) .Include(x => x.Details).ExecuteCommandAsync(); return flag ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } }