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.
564 lines
24 KiB
C#
564 lines
24 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.Op.Interface.TaskInteraction;
|
|
using DS.WMS.Core.Sys.Entity;
|
|
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 = DateTime.Now.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<SeaExportRes>().ToListAsync();
|
|
if (orders.Count == 0)
|
|
return;
|
|
|
|
List<FeeRecord> feeList = [];
|
|
await TenantDb.Ado.BeginTranAsync();
|
|
try
|
|
{
|
|
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);
|
|
if (fees != null)
|
|
{
|
|
feeList.AddRange(fees);
|
|
|
|
var record = new FeeCustTemplateRecord
|
|
{
|
|
BusinessId = order.CustomerId,
|
|
BusinessType = BusinessType.OceanShippingExport,
|
|
CreateBy = long.Parse(User.UserId),
|
|
CreateTime = dt,
|
|
FeeCategoryId = 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);
|
|
if (fees != null)
|
|
{
|
|
feeList.AddRange(fees);
|
|
|
|
var record = new FeeCustTemplateRecord
|
|
{
|
|
BusinessId = order.CustomerId,
|
|
BusinessType = BusinessType.OceanShippingExport,
|
|
CreateBy = long.Parse(User.UserId),
|
|
CreateTime = dt,
|
|
FeeCategoryId = 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)
|
|
{
|
|
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.MBLFrtCode)
|
|
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 details = await TenantDb.Queryable<FeeCustTemplateDetail>().Where(y => y.TemplateId == template.Id)
|
|
.Select(x => new FeeRecord
|
|
{
|
|
BusinessId = order.Id,
|
|
BusinessType = BusinessType.OceanShippingExport,
|
|
FeeType = template.FeeType,
|
|
FeeId = x.FeeId,
|
|
FeeCode = x.FeeCode,
|
|
FeeName = x.FeeName,
|
|
CustomerId = x.CustomerId,
|
|
CustomerName = x.CustomerName,
|
|
CustomerType = x.CustomerType,
|
|
Unit = x.Unit,
|
|
UnitPrice = SqlFunc.IsNull(x.UnitPrice.Value, 0),
|
|
//Quantity = x.IsCtn ? 1 : 0,
|
|
Currency = x.Currency,
|
|
ExchangeRate = x.ExchangeRate,
|
|
TaxRate = SqlFunc.IsNull(x.TaxRate.Value, 0),
|
|
AccTaxRate = SqlFunc.IsNull(x.AccTaxRate.Value, 0),
|
|
Tax = SqlFunc.IsNull(x.Tax.Value, 0),
|
|
TaxUnitPrice = SqlFunc.IsNull(x.TaxUnitPrice.Value, 0),
|
|
IsInvoice = x.IsInvoice,
|
|
IsAdvancedPay = x.IsAdvancedPay,
|
|
Remark = template.FeeCategoryName,
|
|
TemplateId = x.TemplateId,
|
|
InputMethod = InputMethod.Automatic
|
|
}).ToListAsync();
|
|
|
|
var localCurrecy = await Db.Queryable<SysOrg>().Where(x => x.Id == User.OrgId).Select(x => x.LocalCurrency).FirstAsync();
|
|
foreach (var detail in details)
|
|
{
|
|
if (detail.ExchangeRate.GetValueOrDefault() == 0 && detail.Currency != localCurrecy)
|
|
{
|
|
var er = await exchangeService.Value.GetExchangeRateAsync(new ExchangeRate
|
|
{
|
|
CurrencyFrom = detail.Currency,
|
|
CurrencyTo = localCurrecy,
|
|
FeeType = template.FeeType
|
|
});
|
|
}
|
|
|
|
if (detail.CustomerId == 0)
|
|
{
|
|
switch (detail.CustomerType)
|
|
{
|
|
case "controller":
|
|
detail.CustomerId = order.CustomerId;
|
|
detail.CustomerName = order.CustomerName;
|
|
break;
|
|
|
|
case "yard":
|
|
detail.CustomerId = order.YardId;
|
|
detail.CustomerName = order.Yard;
|
|
break;
|
|
|
|
case "custom":
|
|
detail.CustomerId = order.CustomserId;
|
|
detail.CustomerName = order.Customser;
|
|
break;
|
|
|
|
case "contract":
|
|
detail.CustomerId = order.ContractClientId;
|
|
detail.CustomerName = order.ContractClientName;
|
|
break;
|
|
|
|
case "shipagency":
|
|
detail.CustomerId = order.ShipAgencyId;
|
|
detail.CustomerName = order.ShipAgency;
|
|
break;
|
|
|
|
case "shipper":
|
|
detail.CustomerId = order.ShipperId.GetValueOrDefault();
|
|
detail.CustomerName = order.Shipper;
|
|
break;
|
|
|
|
case "truck":
|
|
detail.CustomerId = order.TruckerId;
|
|
detail.CustomerName = order.Trucker;
|
|
break;
|
|
|
|
case "booking":
|
|
detail.CustomerId = order.ForwarderId;
|
|
detail.CustomerName = order.Forwarder;
|
|
break;
|
|
|
|
case "carrier":
|
|
detail.CustomerId = order.CarrierId;
|
|
detail.CustomerName = order.Carrier;
|
|
break;
|
|
|
|
case "wareHouse":
|
|
detail.CustomerId = order.WareHouseId;
|
|
detail.CustomerName = order.WareHouse;
|
|
break;
|
|
|
|
case "shippercn":
|
|
detail.CustomerId = order.ShipperCnId.GetValueOrDefault();
|
|
detail.CustomerName = order.ShipperCn;
|
|
break;
|
|
|
|
case "agent":
|
|
detail.CustomerId = order.AgentId.GetValueOrDefault();
|
|
detail.CustomerName = order.Agent;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
/// <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));
|
|
}
|
|
}
|
|
}
|