You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

595 lines
25 KiB
C#

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
{
/// <summary>
/// 往来单位费用模板
/// </summary>
public class FeeCustTemplateService : FeeServiceBase, IFeeCustTemplateService
{
Lazy<IActionManagerService> actionService;
Lazy<IFeeRecordService> feeService;
Lazy<IFeeCurrencyExchangeService> exchangeService;
/// <summary>
/// 初始化
/// </summary>
/// <param name="serviceProvider"></param>
public FeeCustTemplateService(IServiceProvider serviceProvider) : base(serviceProvider)
{
actionService = new Lazy<IActionManagerService>(serviceProvider.GetRequiredService<IActionManagerService>());
feeService = new Lazy<IFeeRecordService>(serviceProvider.GetRequiredService<IFeeRecordService>());
exchangeService = new Lazy<IFeeCurrencyExchangeService>(serviceProvider.GetRequiredService<IFeeCurrencyExchangeService>());
}
/// <summary>
/// 根据开船日生成费用
/// </summary>
/// <param name="etd">开船日</param>
/// <returns></returns>
public async Task GenerateFeesAsync(DateTime etd)
{
DateTime dt = DateTime.Now;
var list = await TenantDb.Queryable<FeeCustTemplate>()
.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<SeaExport>().Where(x => SqlFunc.DateIsSame(x.ETD, etd) &&
SqlFunc.Subqueryable<FeeCustTemplateRecord>().Where(y => y.BusinessId == x.Id && y.BusinessType == BusinessType.OceanShippingExport && tids.Contains(y.TemplateId)).NotAny())
.Select(x => new SeaExportRes
{
CtnInfo = SqlFunc.Subqueryable<OpCtn>().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<FeeRecord> feeList = [];
await TenantDb.Ado.BeginTranAsync();
try
{
var localCurrecy = await Db.Queryable<SysOrg>().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<List<FeeRecord>?> 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<ConditionContent>(template.Condition);
if (!actionService.Value.IsMatch(order, conditionModel))
return null;
}
if (await TenantDb.Queryable<FeeCustTemplateRecord>().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<FeeCustTemplateDetail>().Where(y => y.TemplateId == template.Id).ToListAsync();
var groups = templateDetails.GroupBy(x => new
{
x.FeeId,
x.Unit
}).ToList();
List<FeeRecord> 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;
}
/// <summary>
/// 导入费用模板
/// </summary>
/// <param name="models"></param>
/// <returns></returns>
public async Task<DataResult> ImportAsync(IEnumerable<FeeCustTemplateModel> 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<InfoClient>().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<FeeCode>().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<CodeCarrier>().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<CodeLanes>().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<CodeFrt>().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<CodeSource>().Where(x => sourceCodes.Contains(x.SourceCode)).Select(x => new
{
x.Id,
x.SourceCode
}).ToListAsync();
List<FeeCustTemplate> 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);
}
}
/// <summary>
/// 列表
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResult<List<FeeCustTemplate>>> GetListAsync(PageRequest request)
{
var whereList = request.GetConditionalModels(Db);
return await TenantDb.Queryable<FeeCustTemplate>().Where(whereList).ToQueryPageAsync(request.PageCondition);
}
/// <summary>
/// 详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<DataResult<FeeCustTemplate>> GetAsync(long id)
{
var data = await TenantDb.Queryable<FeeCustTemplate>().Where(x => x.Id == id).FirstAsync();
if (data != null)
data.Details = await TenantDb.Queryable<FeeCustTemplateDetail>().Where(x => x.TemplateId == data.Id).ToListAsync();
return DataResult<FeeCustTemplate>.Success(data, MultiLanguageConst.DataQuerySuccess);
}
/// <summary>
/// 编辑
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<DataResult> 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));
}
}
/// <summary>
/// 根据ID批量删除
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<DataResult> DeleteAsync(IdModel model)
{
bool flag = await TenantDb.DeleteNav<FeeCustTemplate>(x => model.Ids.Contains(x.Id))
.Include(x => x.Details).ExecuteCommandAsync();
return flag ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
}