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.

1266 lines
52 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Text;
using DS.Module.Core;
using DS.Module.Core.Extensions;
using DS.WMS.Core.Fee.Dtos;
using DS.WMS.Core.Fee.Entity;
using DS.WMS.Core.Fee.Interface;
using DS.WMS.Core.Flow.Dtos;
using DS.WMS.Core.Flow.Entity;
using DS.WMS.Core.Flow.Interface;
using DS.WMS.Core.Info.Entity;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.TaskInteraction.Dtos;
using DS.WMS.Core.TaskInteraction.Interface;
using Mapster;
using Masuit.Tools.Systems;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
namespace DS.WMS.Core.Fee.Method
{
/// <summary>
/// 费用记录实现
/// </summary>
public class FeeRecordService : FeeServiceBase, IFeeRecordService
{
const int BATCH_SIZE = 300;
readonly IClientFlowInstanceService flowService;
readonly IFeeTaskService feeTaskService;
readonly IFeeBillTaskService billService;
/// <summary>
/// 初始化
/// </summary>
/// <param name="serviceProvider"></param>
public FeeRecordService(IServiceProvider serviceProvider) : base(serviceProvider)
{
flowService = serviceProvider.GetRequiredService<IClientFlowInstanceService>();
feeTaskService = serviceProvider.GetRequiredService<IFeeTaskService>();
billService = serviceProvider.GetRequiredService<IFeeBillTaskService>();
}
/// <summary>
/// 列表
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResult<List<FeeRecordRes>>> GetListAsync(PageRequest request)
{
long UserId = long.Parse(User.UserId);
//序列化查询条件
var whereList = request.GetConditionalModels(Db);
//传递业务查询条件检测
string? bsId = null;
foreach (var item in whereList)
{
if (item is ConditionalModel conditionModel && string.Equals(conditionModel.FieldName, nameof(FeeRecord.BusinessId), StringComparison.OrdinalIgnoreCase))
{
bsId = conditionModel.FieldValue;
break;
}
}
//未指定业务ID返回空
if (string.IsNullOrEmpty(bsId))
return DataResult<List<FeeRecordRes>>.Success([]);
var data = await TenantDb.Queryable<FeeRecord>()
.Where(x => x.IsOpen || (!x.IsOpen && x.CreateBy == UserId))
.Where(whereList)
.Select<FeeRecordRes>()
.ToQueryPageAsync(request.PageCondition);
//关联用户名称
var UserIds = data.Data.Where(x => x.UpdateBy.HasValue).Select(x => x.UpdateBy.Value)
.Union(data.Data.Where(x => x.SubmitBy.HasValue).Select(x => x.SubmitBy.Value))
.Union(data.Data.Select(x => x.CreateBy)).Distinct();
var Users = await Db.Queryable<SysUser>().Where(x => UserIds.Contains(x.Id)).Select(x => new { x.Id, x.UserName }).ToListAsync();
foreach (var item in data.Data)
{
item.CreateByName = Users.Find(x => x.Id == item.CreateBy)?.UserName;
if (item.UpdateBy.HasValue)
item.UpdateByName = Users.Find(x => x.Id == item.UpdateBy.Value)?.UserName;
if (item.SubmitBy.HasValue)
item.SubmitByName = Users.Find(x => x.Id == item.SubmitBy.Value)?.UserName;
}
return data;
}
/// <summary>
/// 根据查询条件获取费用数据
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public async Task<DataResult<List<FeeRecord>>> GetListAsync(string query)
{
long UserId = long.Parse(User.UserId);
var src = TenantDb.Queryable<FeeRecord>().Where(x => x.IsOpen || (!x.IsOpen && x.CreateBy == UserId));
if (!query.IsNullOrEmpty())
{
//序列化查询条件
var whereList = Db.Utilities.JsonToConditionalModels(query);
src = src.Where(whereList);
}
var data = await src.ToListAsync();
return DataResult<List<FeeRecord>>.Success(data);
}
/// <summary>
/// 获取费用统计对象
/// </summary>
/// <param name="bsId">业务ID</param>
/// <param name="bsType">业务类型</param>
/// <returns></returns>
public async Task<FeeStatistics> GetFeeStatisticsAsync(long bsId, BusinessType bsType)
{
var list = await TenantDb.Queryable<FeeRecord>()
.Where(x => x.BusinessId == bsId && x.BusinessType == bsType).ToListAsync();
return new FeeStatistics(list);
}
/// <summary>
/// 获取已申请修改的费用记录值
/// </summary>
/// <param name="id">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult<FeeModification>> GetModifyValue(long id)
{
var fm = await TenantDb.Queryable<FeeModification>().Where(x => x.FeeRecordId == id)
.OrderByDescending(x => x.CreateTime).Take(1).FirstAsync();
return DataResult<FeeModification>.Success(fm);
}
/// <summary>
/// 检查业务是否已费用锁定
/// </summary>
/// <param name="bid">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="type">锁定范围</param>
/// <returns></returns>
internal async Task<bool> IsFeeLockedAsync(long bid, BusinessType businessType, FeeType type = FeeType.All)
{
bool? isFeeLocking = null;
switch (type)
{
case FeeType.Receivable:
break;
case FeeType.Payable:
break;
case FeeType.All:
isFeeLocking = await TenantDb.Queryable<BusinessFeeStatus>().Where(
x => x.BusinessId == bid && x.BusinessType == businessType).Select(x => x.IsFeeLocking).FirstAsync();
break;
}
return isFeeLocking.GetValueOrDefault();
}
/// <summary>
/// 费用提交
/// </summary>
/// <param name="items">要提交的费用记录</param>
/// <param name="excludeZeroFee">是否排除总价为零的费用</param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> SaveAsync(List<FeeRecord> items, bool excludeZeroFee = false, bool useTransaction = true)
{
ArgumentNullException.ThrowIfNull(items, nameof(items));
if (items.GroupBy(x => new { x.BusinessId, x.BusinessType }).Count() > 1)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeBusinessIdOnlyOne));
var first = items.Select(x => new { x.BusinessType, x.BusinessId }).FirstOrDefault();
if (await IsFeeLockedAsync(first.BusinessId, first.BusinessType))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeLocked));
var order = await TenantDb.Queryable<SeaExport>().Where(x => x.Id == first.BusinessId).Select(x => new
{
x.TEU,
x.KGS, //毛重
x.PKGS, //件数
x.CBM //尺码
}).FirstAsync();
if (order == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
//获取订单箱型箱量
string bsNo = first.BusinessId.ToString();
var ctns = await TenantDb.Queryable<OpCtn>().Where(y => y.BSNO == bsNo).Select(x => new
{
x.CtnCode,
x.CtnNum
}).ToListAsync();
foreach (var item in items)
{
if (item.Quantity == 0)
{
//逐个判定处理标准
switch (item.Unit)
{
case "HOUR": //小时
goto case "P";
case "GE": //个
goto case "P";
case "DAY": //天
goto case "P";
//case "XX": //箱型
// goto default;
case "JJZL": //计价重量
goto case "Z";
case "ZJ": //总价
goto case "P";
case "JZ": //净重
item.Quantity = 0;
break;
case "TEU": //TEU
item.Quantity = order.TEU;
break;
case "JF": //计费吨
item.Quantity = order.KGS.GetValueOrDefault() / 1000 > order.CBM.GetValueOrDefault() ? order.KGS.GetValueOrDefault() : order.CBM.GetValueOrDefault();
break;
case "J": //件数
item.Quantity = order.PKGS.GetValueOrDefault();
break;
case "C": //尺码
item.Quantity = order.CBM.GetValueOrDefault();
break;
case "Z": //重量
item.Quantity = order.KGS.GetValueOrDefault();
break;
case "Bill": //单票
item.Quantity = 1;
break;
case "P": //单票
item.Quantity = 1;
break;
default: //查找箱型标准
var ctn = ctns.Find(x => x.CtnCode == item.Unit);
item.Quantity = ctn == null ? 0 : ctn.CtnNum.GetValueOrDefault();
break;
}
}
//计算税费
item.SetTax();
}
if (excludeZeroFee) //过滤掉数量为0的费用
items = items.FindAll(x => x.Amount != 0);
DateTime dtNow = DateTime.Now;
if (useTransaction)
await TenantDb.Ado.BeginTranAsync();
try
{
List<FeeRecord>? fees = null;
var feeIds = items.Where(x => x.Id > 0).Select(x => x.Id).ToArray();
//包含修改的项,需要检测费用状态再修改
if (feeIds.Length > 0)
{
fees = await TenantDb.Queryable<FeeRecord>().Where(x => feeIds.Contains(x.Id)).Select(x => new FeeRecord
{
Id = x.Id,
FeeName = x.FeeName,
FeeStatus = x.FeeStatus
}).ToListAsync();
StringBuilder sb = new StringBuilder();
foreach (var fe in fees)
{
if (fe.FeeStatus != FeeStatus.Entering && fe.FeeStatus != FeeStatus.RejectSubmission)
{
sb.AppendFormat(MultiLanguageConst.GetDescription(MultiLanguageConst.FeeRecordStatus), fe.FeeName);
continue;
}
}
if (sb.Length > 0)
return DataResult.Failed(sb.ToString(), MultiLanguageConst.Operation_Failed);
}
var localCurrency = await Db.Queryable<SysOrg>().Where(x => x.Id == User.OrgId).Select(x => x.LocalCurrency).FirstAsync();
foreach (var item in items)
{
if (string.IsNullOrEmpty(item.LocalCurrency))
{
//默认本位币为机构本位币或CNY
if (item.Id == 0)
item.LocalCurrency = localCurrency ?? FeeCurrency.RMB_CODE;
else
item.LocalCurrency = fees?.Find(x => x.Id == item.Id)?.LocalCurrency;
}
if (item.LocalCurrency == item.Currency)
item.ExchangeRate = 1m;
}
//若计价货币单位不等于本位币则尝试获取最新汇率
await FetchExchangeRateAsync(items);
var updateList = items.Where(x => x.Id > 0).ToList();
if (updateList.Count > 0)
await TenantDb.Updateable(updateList).IgnoreColumns(x => new
{
x.FeeStatus,
x.BusinessId,
x.BusinessType,
x.CreateBy,
x.CreateTime,
x.DeleteBy,
x.Deleted,
x.DeleteTime,
x.SubmitDate,
x.SubmitBy,
x.AuditBy,
x.AuditOperator,
x.AuditDate
}).ExecuteCommandAsync();
var createList = items.Where(x => x.Id == 0).ToList();
if (createList.Count > 0)
{
if (createList.Count >= BATCH_SIZE)
{
await TenantDb.Fastest<FeeRecord>().BulkCopyAsync(createList);
}
else
{
await TenantDb.Insertable(createList).ExecuteCommandAsync();
}
}
if (useTransaction)
await TenantDb.Ado.CommitTranAsync();
var list = createList.Union(updateList).Select(x => x.Adapt<FeeRecordRes>());
var result = DataResult.Success;
result.Data = list.Select(x => x.Id);
await WriteBackStatusAsync(first.BusinessId, first.BusinessType);
return result;
}
catch (Exception ex)
{
if (useTransaction)
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
/// <summary>
/// 费用保存后提交审核
/// </summary>
/// <param name="items">要提交的费用记录</param>
/// <param name="excludeZeroFee">是否排除总价为零的费用</param>
/// <param name="useTransaction">是否使用事务</param>
/// <returns></returns>
public async Task<DataResult> SaveAndSubmitAsync(List<FeeRecord> items, bool excludeZeroFee = false, bool useTransaction = true)
{
var result = await SaveAsync(items, excludeZeroFee, useTransaction);
if (!result.Succeeded)
return result;
var ids = items.Select(x => x.Id).ToArray();
ids = await TenantDb.Queryable<FeeRecord>().Where(x => ids.Contains(x.Id) && (x.FeeStatus == FeeStatus.Entering || x.FeeStatus == FeeStatus.RejectSubmission || x.FeeStatus == FeeStatus.RejectApplication)).Select(x => x.Id).ToArrayAsync();
if (ids.Length > 0)
result = await SubmitForApprovalAsync(TaskBaseTypeEnum.FEE_AUDIT, string.Empty, ids);
return result;
}
/// <summary>
/// 根据模板ID创建
/// </summary>
/// <param name="bid">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="tidArray">模板ID</param>
/// <returns></returns>
public async Task<DataResult> CreateByTemplateAsync(long bid, BusinessType businessType, params long[] tidArray)
{
bool hasExists = TenantDb.Queryable<FeeRecord>().LeftJoin<FeeTemplateDetail>((x, y) =>
x.FeeId == y.FeeId && x.FeeType == y.FeeType).Any(
(x, y) => x.BusinessId == bid && x.BusinessType == businessType && tidArray.Contains(y.TemplateId));
if (hasExists)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordExist));
var details = await TenantDb.Queryable<FeeTemplateDetail>().Where(x => tidArray.Contains(x.TemplateId) && !x.Deleted).Select(x => new
{
x.FeeType,
x.FeeId,
x.FeeCode,
x.FeeName,
x.FeeFrt,
x.FeeGroup,
x.CustomerName,
x.CustomerType,
x.CustomerId,
x.Unit,
x.UnitPrice,
x.Currency,
x.ExchangeRate,
x.Tax,
x.TaxRate,
x.AccTaxRate,
x.IsAdvancedPay,
x.IsInvoice,
x.SaleOrgId,
x.Note
}).ToListAsync();
List<FeeRecord> records = new List<FeeRecord>(details.Count);
foreach (var item in details)
{
var record = item.Adapt<FeeRecord>();
record.BusinessId = bid;
record.SubmitDate = DateTime.Now;
records.Add(record);
}
//若计价货币单位不等于默认货币则尝试获取最新汇率
await FetchExchangeRateAsync(records);
int result = await TenantDb.Insertable(records).ExecuteCommandAsync();
await WriteBackStatusAsync(bid, businessType);
return result > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
/// <summary>
/// 删除
/// </summary>
/// <param name="ids">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult> DeleteAsync(params long[] ids)
{
var model = await TenantDb.Queryable<FeeRecord>().Where(x => ids.Contains(x.Id)).Select(x => new { x.BusinessId, x.BusinessType }).FirstAsync();
if (await IsFeeLockedAsync(model.BusinessId, model.BusinessType))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeLocked));
if (await TenantDb.Queryable<FeeRecord>().AnyAsync(x => ids.Contains(x.Id) && (x.FeeStatus != FeeStatus.Entering && x.FeeStatus != FeeStatus.RejectSubmission)))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordDelete));
int result = await TenantDb.Deleteable<FeeRecord>(x => ids.Contains(x.Id)).ExecuteCommandAsync();
await WriteBackStatusAsync(model.BusinessId, model.BusinessType);
return result > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
#region 审核相关
/// <summary>
/// 发起审批/申请删除工作流
/// </summary>
/// <param name="auditType">审批类型</param>
/// <param name="remark">备注</param>
/// <param name="ids">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult> SubmitForApprovalAsync(TaskBaseTypeEnum auditType, string remark, params long[] ids)
{
var fees = await TenantDb.Queryable<FeeRecord>().Where(x => ids.Contains(x.Id)).Select(
x => new FeeRecord
{
Id = x.Id,
FeeName = x.FeeName,
FeeStatus = x.FeeStatus,
BusinessId = x.BusinessId,
BusinessType = x.BusinessType
}).ToListAsync();
if (fees.IsNullOrEmpty())
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordNotExist));
var bid = fees[0].BusinessId;
var bType = fees[0].BusinessType;
//业务状态检测
if (await IsFeeLockedAsync(bid, bType))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeLocked));
if (await flowService.IsRunning(auditType, null, ids))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordIsAuditing));
if (fees.Any(x => x.FeeStatus == FeeStatus.PartialSettlement || x.FeeStatus == FeeStatus.SettlementCompleted))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordIsSettled));
DataResult result = DataResult.Success;
await TenantDb.Ado.BeginTranAsync();
try
{
if (auditType == TaskBaseTypeEnum.FEE_AUDIT)
{
result = await ApplyAuditAsync(fees);
}
else if (auditType == TaskBaseTypeEnum.FEE_DELETE_AUDIT)
{
result = await ApplyDeleteAsync(fees, remark);
}
if (!result.Succeeded)
return result;
await TenantDb.Updateable(fees).UpdateColumns(x => new
{
x.Id,
x.FeeStatus,
x.SubmitBy,
x.SubmitDate
}).ExecuteCommandAsync();
await TenantDb.Ado.CommitTranAsync();
return DataResult.Success;
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
//录入->提交审核
async Task<DataResult> ApplyAuditAsync(List<FeeRecord> fees)
{
StringBuilder sb = new StringBuilder();
foreach (var fe in fees)
{
if (fe.FeeStatus != FeeStatus.Entering && fe.FeeStatus != FeeStatus.RejectSubmission)
{
sb.AppendFormat(MultiLanguageConst.FeeRecordStatus, fe.FeeName);
continue;
}
}
if (sb.Length > 0)
return DataResult.Failed(sb.ToString(), MultiLanguageConst.Operation_Failed);
DataResult result;
DateTime dtNow = DateTime.Now;
if (await feeTaskService.HasAuthorizedAsync())
{
var requests = fees.Select(x => new TaskCreationRequest
{
BusinessId = x.Id,
TaskTypeName = TaskBaseTypeEnum.FEE_AUDIT.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.FEE_AUDIT.GetDescription()}】{x.FeeName}"
});
foreach (var request in requests)
{
result = await feeTaskService.CreateTaskAsync(request, false);
if (!result.Succeeded)
return result;
var fee = fees.Find(x => x.Id == request.BusinessId);
//变更状态为提交审核
fee.FeeStatus = FeeStatus.AuditSubmitted;
fee.SubmitBy = long.Parse(User.UserId);
fee.SubmitDate = dtNow;
}
return DataResult.Success;
}
var template = await FindTemplateAsync(TaskBaseTypeEnum.FEE_AUDIT);
if (template == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
foreach (var item in fees)
{
result = flowService.CreateFlowInstance(new CreateFlowInstanceReq
{
BusinessId = item.Id,
BusinessType = item.BusinessType,
TemplateId = template.Id
});
if (result.Succeeded)
{
var instance = result.Data as FlowInstance;
flowService.StartFlowInstance(instance.Id.ToString());
//变更状态为提交审核
item.FeeStatus = FeeStatus.AuditSubmitted;
item.SubmitBy = long.Parse(User.UserId);
item.SubmitDate = dtNow;
}
}
return DataResult.Success;
}
//审核通过->申请删除
async Task<DataResult> ApplyDeleteAsync(List<FeeRecord> fees, string reason)
{
StringBuilder sb = new StringBuilder();
foreach (var fe in fees)
{
if (fe.FeeStatus != FeeStatus.AuditPassed && fe.FeeStatus != FeeStatus.RejectApplication)
{
sb.AppendFormat(MultiLanguageConst.FeeRecordStatus, fe.FeeName);
continue;
}
}
if (sb.Length > 0)
return DataResult.Failed(sb.ToString(), MultiLanguageConst.Operation_Failed);
DataResult result;
DateTime dtNow = DateTime.Now;
if (await feeTaskService.HasAuthorizedAsync())
{
var requests = fees.Select(x => new TaskCreationRequest
{
BusinessId = x.Id,
TaskTypeName = TaskBaseTypeEnum.FEE_AUDIT.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.FEE_AUDIT.GetDescription()}】{x.FeeName}"
});
foreach (var request in requests)
{
result = await feeTaskService.CreateTaskAsync(request, false);
if (!result.Succeeded)
return result;
var fee = fees.Find(x => x.Id == request.BusinessId);
//变更状态为申请删除
fee.FeeStatus = FeeStatus.ApplyDeletion;
fee.SubmitBy = long.Parse(User.UserId);
fee.SubmitDate = dtNow;
fee.Reason = reason;
}
return DataResult.Success;
}
var template = await FindTemplateAsync(TaskBaseTypeEnum.FEE_DELETE_AUDIT);
if (template == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
foreach (var item in fees)
{
result = flowService.CreateFlowInstance(new CreateFlowInstanceReq
{
BusinessId = item.Id,
BusinessType = item.BusinessType,
TemplateId = template.Id
});
if (result.Succeeded)
{
var instance = result.Data as FlowInstance;
flowService.StartFlowInstance(instance.Id.ToString());
//变更状态为申请删除
item.FeeStatus = FeeStatus.ApplyDeletion;
item.SubmitBy = long.Parse(User.UserId);
item.SubmitDate = dtNow;
item.Reason = reason;
}
}
return DataResult.Success;
}
/// <summary>
/// 发起费用修改申请
/// </summary>
/// <param name="items">费用修改信息</param>
/// <returns></returns>
public async Task<DataResult> SubmitForModificationAsync(IEnumerable<FeeModification> items)
{
var idList = items.Select(x => x.FeeRecordId).Distinct().ToList();
var fees = await TenantDb.Queryable<FeeRecord>().Where(x => idList.Contains(x.Id)).Select(x => new FeeRecord
{
Id = x.Id,
FeeName = x.FeeName,
FeeStatus = x.FeeStatus
}).ToListAsync();
if (fees.Count == 0)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordNotExist));
StringBuilder sb = new StringBuilder();
foreach (var fe in fees)
{
if (fe.FeeStatus != FeeStatus.AuditPassed && fe.FeeStatus != FeeStatus.RejectApplication)
{
sb.AppendFormat(MultiLanguageConst.FeeRecordStatus, fe.FeeName);
continue;
}
}
if (sb.Length > 0)
return DataResult.Failed(sb.ToString(), MultiLanguageConst.Operation_Failed);
DataResult result;
DateTime dtNow = DateTime.Now;
await TenantDb.Ado.BeginTranAsync();
try
{
if (await feeTaskService.HasAuthorizedAsync())
{
var requests = fees.Select(x => new TaskCreationRequest
{
BusinessId = x.Id,
TaskTypeName = TaskBaseTypeEnum.FEE_AUDIT.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.FEE_AUDIT.GetDescription()}】{x.FeeName}"
});
foreach (var request in requests)
{
result = await feeTaskService.CreateTaskAsync(request, false);
if (!result.Succeeded)
return result;
var fee = fees.Find(x => x.Id == request.BusinessId);
//变更状态为申请修改
fee.FeeStatus = FeeStatus.ApplyModification;
fee.Reason = items.FirstOrDefault(x => x.FeeRecordId == fee.Id)?.Reason;
}
}
else
{
var template = await FindTemplateAsync(TaskBaseTypeEnum.FEE_MODIFY_AUDIT);
if (template == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
foreach (var fee in fees)
{
result = flowService.CreateFlowInstance(new CreateFlowInstanceReq
{
BusinessId = fee.Id,
BusinessType = fee.BusinessType,
TemplateId = template.Id
});
if (result.Succeeded)
{
var instance = result.Data as FlowInstance;
flowService.StartFlowInstance(instance.Id.ToString());
//变更状态为申请修改
fee.FeeStatus = FeeStatus.ApplyModification;
fee.Reason = items.FirstOrDefault(x => x.FeeRecordId == fee.Id)?.Reason;
}
}
}
var list = items.ToList();
await TenantDb.Insertable(list).ExecuteCommandAsync();
await TenantDb.Updateable(fees).UpdateColumns(x => new { x.FeeStatus }).ExecuteCommandAsync();
await TenantDb.Ado.CommitTranAsync();
return DataResult.Success;
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
/// <summary>
/// 撤销审批
/// </summary>
/// <param name="ids">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult> WithdrawAsync(params long[] ids)
{
var fees = await TenantDb.Queryable<FeeRecord>().Where(x => ids.Contains(x.Id)).Select(
x => new FeeRecord
{
Id = x.Id,
BusinessId = x.BusinessId,
BusinessType = x.BusinessType,
FeeName = x.FeeName,
FeeStatus = x.FeeStatus
}).ToListAsync();
if (fees.Count == 0)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
DataResult result;
List<Tuple<TaskBaseTypeEnum, long>> taskTypes = [];
DateTime dtNow = DateTime.Now;
await TenantDb.Ado.BeginTranAsync();
try
{
foreach (var item in fees)
{
switch (item.FeeStatus)
{
case FeeStatus.AuditSubmitted:
item.FeeStatus = FeeStatus.Entering;
taskTypes.Add(new Tuple<TaskBaseTypeEnum, long>(TaskBaseTypeEnum.FEE_AUDIT, item.Id));
break;
case FeeStatus.ApplyModification:
item.FeeStatus = FeeStatus.AuditPassed;
taskTypes.Add(new Tuple<TaskBaseTypeEnum, long>(TaskBaseTypeEnum.FEE_MODIFY_AUDIT, item.Id));
//删除暂存数据
var entity = await TenantDb.Queryable<FeeModification>().OrderByDescending(
x => x.CreateTime).Select(x => new { x.Id, x.FeeRecordId }).FirstAsync(x => x.FeeRecordId == item.Id);
if (entity != null)
await TenantDb.Deleteable<FeeModification>().Where(x => x.Id == entity.Id).ExecuteCommandAsync();
break;
case FeeStatus.ApplyDeletion:
item.FeeStatus = FeeStatus.AuditPassed;
taskTypes.Add(new Tuple<TaskBaseTypeEnum, long>(TaskBaseTypeEnum.FEE_DELETE_AUDIT, item.Id));
break;
}
item.SubmitBy = null;
item.SubmitDate = null;
}
var groups = taskTypes.GroupBy(x => x.Item1);
if (await feeTaskService.HasAuthorizedAsync())
{
foreach (var group in groups)
{
foreach (var item in group)
{
result = await feeTaskService.WithdrawAsync(new TaskRequest
{
BusinessId = item.Item2,
TaskTypeName = item.Item1.ToString(),
ExtraData = fees.Find(x => x.Id == item.Item2)
}, false);
if (!result.Succeeded)
return result;
}
}
}
else
{
//未在审批状态中
if (!await flowService.Exists(ids: ids))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.NotInAudit));
foreach (var g in groups)
{
result = await flowService.WithdrawAsync(g.Key, g.Select(x => x.Item2).ToArray());
if (!result.Succeeded)
return result;
}
}
await TenantDb.Updateable(fees).UpdateColumns(x => new { x.FeeStatus, x.SubmitBy, x.SubmitDate }).ExecuteCommandAsync();
await TenantDb.Ado.CommitTranAsync();
return DataResult.Success;
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
/// <summary>
/// 整票审核
/// </summary>
/// <param name="bid">业务ID</param>
/// <param name="type">业务类型</param>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
public async Task<DataResult> SubmitBillAuditAsync(long bid, BusinessType type, TaskBaseTypeEnum taskType)
{
var entity = await TenantDb.Queryable<BusinessFeeStatus>().Where(x => x.BusinessId == bid && x.BusinessType == type)
.Select(x => new BusinessFeeStatus
{
Id = x.Id,
IsFeeLocking = x.IsFeeLocking,
BillAuditStatus = x.BillAuditStatus,
ARFeeStatus = x.ARFeeStatus,
APFeeStatus = x.APFeeStatus
}).FirstAsync();
if (entity == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.BusinessNotFound));
if (entity.IsFeeLocking.GetValueOrDefault())
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeLocked));
if (taskType == TaskBaseTypeEnum.BILL_RECV_AUDIT)
{
if (entity.BillAuditStatus != BillAuditStatus.Pending && entity.BillAuditStatus != BillAuditStatus.RecvRejected)
return DataResult.Failed(string.Format(
MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BillFeeStatusError)), entity.BillAuditStatus.GetDescription()));
}
else if (taskType == TaskBaseTypeEnum.BILL_PAY_AUDIT)
{
if (entity.BillAuditStatus != BillAuditStatus.RecvPassed && entity.BillAuditStatus != BillAuditStatus.PayRejected)
return DataResult.Failed(string.Format(
MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BillFeeStatusError)), entity.BillAuditStatus.GetDescription()));
}
else
{
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskNotSupported));
}
if (await flowService.IsRunning(taskType, type, bid))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeRecordIsAuditing));
DataResult result;
await TenantDb.Ado.BeginTranAsync();
try
{
if (await billService.HasAuthorizedAsync())
{
string? bizNo = null;
switch (type)
{
case BusinessType.OceanShippingExport:
bizNo = await TenantDb.Queryable<SeaExport>().Where(x => x.Id == bid).Select(x => x.CustomerNo).FirstAsync();
break;
}
result = await billService.CreateTaskAsync(new TaskCreationRequest
{
BusinessId = bid,
BusinessType = type,
TaskTypeName = taskType.ToString(),
TaskTitle = $"【{taskType.GetDescription()}】【{type.GetDescription()}】{bizNo}"
}, false);
}
else
{
var template = await FindTemplateAsync(taskType);
if (template == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TemplateNotFound));
result = flowService.CreateFlowInstance(new CreateFlowInstanceReq
{
BusinessId = entity.Id,
BusinessType = entity.BusinessType,
TemplateId = template.Id
});
var instance = result.Data as FlowInstance;
flowService.StartFlowInstance(instance.Id.ToString());
}
if (!result.Succeeded)
return result;
//变更状态为提交审核
if (taskType == TaskBaseTypeEnum.BILL_RECV_AUDIT)
{
entity.BillAuditStatus = BillAuditStatus.RecvSubmitted;
entity.ARFeeStatus = BillFeeStatus.AuditSubmitted;
entity.APFeeStatus = BillFeeStatus.AuditSubmitted;
await TenantDb.Updateable(entity).UpdateColumns(x => new
{
x.BillAuditStatus,
x.ARFeeStatus,
x.APFeeStatus
}).ExecuteCommandAsync();
//修改关联费用状态为提交审核
await TenantDb.Updateable<FeeRecord>().Where(x => x.BusinessId == bid && x.BusinessType == type &&
(x.FeeStatus == FeeStatus.Entering || x.FeeStatus == FeeStatus.Withdraw || x.FeeStatus == FeeStatus.RejectSubmission))
.SetColumns(x => x.FeeStatus == FeeStatus.AuditSubmitted).ExecuteCommandAsync();
}
else if (taskType == TaskBaseTypeEnum.BILL_PAY_AUDIT)
{
entity.BillAuditStatus = BillAuditStatus.PaySubmitted;
await TenantDb.Updateable(entity).UpdateColumns(x => new
{
x.BillAuditStatus
}).ExecuteCommandAsync();
}
await TenantDb.Ado.CommitTranAsync();
return DataResult.Success;
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
/// <summary>
/// 撤销整票审批
/// </summary>
/// <param name="bid">业务ID</param>
/// <param name="type">业务类型</param>
/// <param name="taskType">任务类型</param>
/// <returns></returns>
public async Task<DataResult> WithdrawBillAsync(long bid, BusinessType type, TaskBaseTypeEnum taskType)
{
var entity = await TenantDb.Queryable<BusinessFeeStatus>().Where(x => x.BusinessId == bid && x.BusinessType == type)
.Select(x => new BusinessFeeStatus
{
Id = x.Id,
IsFeeLocking = x.IsFeeLocking,
BillAuditStatus = x.BillAuditStatus,
ARFeeStatus = x.ARFeeStatus,
APFeeStatus = x.APFeeStatus
}).FirstAsync();
if (entity == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.BusinessNotFound));
if (entity.IsFeeLocking.GetValueOrDefault())
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.FeeLocked));
if (taskType == TaskBaseTypeEnum.BILL_RECV_AUDIT)
{
if (entity.BillAuditStatus != BillAuditStatus.RecvSubmitted)
return DataResult.Failed(string.Format(
MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BusinessStatusError)), entity.BillAuditStatus.GetDescription()));
}
else if (taskType == TaskBaseTypeEnum.BILL_PAY_AUDIT)
{
return DataResult.Failed(string.Format(
MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BusinessStatusError)), entity.BillAuditStatus.GetDescription()));
}
else
{
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.TaskNotSupported));
}
await TenantDb.Ado.BeginTranAsync();
try
{
DataResult result = DataResult.Success;
if (await billService.HasAuthorizedAsync())
{
await billService.WithdrawAsync(new TaskRequest
{
BusinessId = bid,
BusinessType = type,
TaskTypeName = taskType.ToString()
}, false);
}
else
{
//未在审批状态中
if (!await flowService.Exists(type: taskType, businessType: type, ids: [bid]))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.NotInAudit));
result = await flowService.WithdrawAsync(taskType, [bid], type);
if (!result.Succeeded)
return result;
}
entity.BillAuditStatus = BillAuditStatus.Pending;
entity.ARFeeStatus = BillFeeStatus.Entering;
await TenantDb.Updateable(entity).UpdateColumns(x => new
{
x.BillAuditStatus,
x.ARFeeStatus
}).ExecuteCommandAsync();
//修改关联费用状态为录入状态
await TenantDb.Updateable<FeeRecord>().Where(x => x.BusinessId == bid && x.BusinessType == type && x.FeeStatus == FeeStatus.AuditSubmitted)
.SetColumns(x => x.FeeStatus == FeeStatus.Entering).ExecuteCommandAsync();
await TenantDb.Ado.CommitTranAsync();
return result;
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
#endregion
/// <summary>
/// 设置发票启用状态
/// </summary>
/// <param name="enabled">是否启用</param>
/// <param name="idArray">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult> SetInvoiceEnabledAsync(bool enabled, params long[] idArray)
{
var list = idArray.Select(x => new FeeRecord { Id = x, IsInvoice = enabled }).ToList();
int rows = await TenantDb.Updateable(list).UpdateColumns(x => new { x.IsInvoice }).ExecuteCommandAsync();
return rows > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
/// <summary>
/// 设置费用对象
/// </summary>
/// <param name="customerId">费用对象ID</param>
/// <param name="customerType">客户类别</param>
/// <param name="idArray">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult> SetCustomerAsync(long customerId, string customerType, params long[] idArray)
{
var model = await TenantDb.Queryable<InfoClient>().Where(x => x.Id == customerId).Select(x => new
{
Id = customerId,
x.CodeName,
x.Name
}).FirstAsync();
if (model == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
var list = idArray.Select(x => new FeeRecord
{
Id = x,
CustomerId = customerId,
CustomerCode = model.CodeName,
CustomerName = model.Name,
CustomerType = customerType
}).ToList();
int rows = await TenantDb.Updateable(list).UpdateColumns(x => new { x.CustomerId, x.CustomerCode, x.CustomerName, x.CustomerType }).ExecuteCommandAsync();
return rows > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
/// <summary>
/// 获取费用打印信息
/// </summary>
/// <param name="providerName">数据提供程序</param>
/// <param name="bsId">业务ID</param>
/// <param name="bsType">业务类型</param>
/// <param name="ids">费用记录ID</param>
/// <returns></returns>
public async Task<DataResult<dynamic>> GetPrintInfoAsync(string providerName, long bsId, BusinessType bsType, params long[] ids)
{
Type type = Type.GetType(providerName, false);
if (type == null)
return DataResult<dynamic>.Failed("未能找到数据提供程序:" + providerName);
var provider = Fasterflect.ConstructorExtensions.CreateInstance(type) as IReportProvider;
if (provider == null)
return DataResult<dynamic>.Failed("未能找到数据提供程序:" + providerName);
var context = new ReportContext
{
BusinessId = bsId,
BusinessType = bsType,
Ids = ids,
Db = Db,
TenantDb = TenantDb,
User = User,
ServiceProvider = ServiceProvider
};
var data = await provider.GetDataAsync(context);
if (context.ErrorResult == null)
return DataResult<dynamic>.Success(data);
return DataResult<dynamic>.Failed(context.ErrorResult.Message, context.ErrorResult.MultiCode);
}
/// <summary>
/// 回写业务表费用状态
/// </summary>
/// <param name="businessId">业务ID</param>
/// <param name="businessType">业务类型</param>
/// <returns></returns>
public async Task WriteBackStatusAsync(long businessId, BusinessType businessType)
{
var fees = await TenantDb.Queryable<FeeRecord>().Where(x => x.BusinessId == businessId)
.Select(x => new FeeRecord
{
FeeType = x.FeeType,
FeeStatus = x.FeeStatus
}).ToListAsync();
if (fees.IsNullOrEmpty())
return;
var arFeeStatus = DetermineStatus(fees.FindAll(x => x.FeeType == FeeType.Receivable));
var apFeeStatus = DetermineStatus(fees.FindAll(x => x.FeeType == FeeType.Payable));
if (arFeeStatus != null || apFeeStatus != null)
{
var upt = TenantDb.Updateable<BusinessFeeStatus>();
if (arFeeStatus != null)
{
upt = upt.SetColumns(x => x.ARFeeStatus == arFeeStatus);
}
if (apFeeStatus != null)
{
upt = upt.SetColumns(x => x.APFeeStatus == apFeeStatus);
}
try
{
await upt.Where(x => x.BusinessType == businessType && x.BusinessId == businessId).ExecuteCommandAsync();
}
catch (Exception ex)
{
await ex.LogAsync(Db);
}
}
}
//业务表费用状态判定
static BillFeeStatus? DetermineStatus(List<FeeRecord> fees)
{
BillFeeStatus? billFeeStatus = null;
if (fees.Count == 0)
{
billFeeStatus = BillFeeStatus.NotEntered;
}
//全状态
else if (fees.All(x => x.FeeStatus == FeeStatus.Entering))
{
billFeeStatus = BillFeeStatus.Entering;
}
else if (fees.All(x => x.FeeStatus == FeeStatus.AuditSubmitted))
{
billFeeStatus = BillFeeStatus.AuditSubmitted;
}
else if (fees.All(x => x.FeeStatus == FeeStatus.AuditPassed))
{
billFeeStatus = BillFeeStatus.AuditPassed;
}
else if (fees.All(x => x.FeeStatus == FeeStatus.RejectSubmission))
{
billFeeStatus = BillFeeStatus.RejectSubmission;
}
else if (fees.All(x => x.FeeStatus == FeeStatus.PartialSettlement))
{
billFeeStatus = BillFeeStatus.PartialSettlement;
}
else if (fees.All(x => x.FeeStatus == FeeStatus.SettlementCompleted))
{
billFeeStatus = BillFeeStatus.SettlementCompleted;
}
//部分状态
else if (fees.Any(x => x.FeeStatus == FeeStatus.Entering))
{
billFeeStatus = BillFeeStatus.PartialEntering;
}
else if (fees.Any(x => x.FeeStatus == FeeStatus.AuditSubmitted))
{
billFeeStatus = BillFeeStatus.PartialSubmitted;
}
else if (fees.Any(x => x.FeeStatus == FeeStatus.AuditPassed))
{
billFeeStatus = BillFeeStatus.PartialAudited;
}
else if (fees.Any(x => x.FeeStatus == FeeStatus.PartialSettlement || x.FeeStatus == FeeStatus.SettlementCompleted))
{
billFeeStatus = BillFeeStatus.PartialSettlement;
}
return billFeeStatus;
}
}
}