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 { /// /// 费用记录实现 /// public class FeeRecordService : FeeServiceBase, IFeeRecordService { const int BATCH_SIZE = 300; readonly IClientFlowInstanceService flowService; readonly IFeeTaskService feeTaskService; readonly IFeeBillTaskService billService; /// /// 初始化 /// /// public FeeRecordService(IServiceProvider serviceProvider) : base(serviceProvider) { flowService = serviceProvider.GetRequiredService(); feeTaskService = serviceProvider.GetRequiredService(); billService = serviceProvider.GetRequiredService(); } /// /// 列表 /// /// /// public async Task>> 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>.Success([]); var data = await TenantDb.Queryable() .Where(x => x.IsOpen || (!x.IsOpen && x.CreateBy == UserId)) .Where(whereList) .Select() .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().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; } /// /// 根据查询条件获取费用数据 /// /// /// public async Task>> GetListAsync(string query) { long UserId = long.Parse(User.UserId); var src = TenantDb.Queryable().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>.Success(data); } /// /// 获取费用统计对象 /// /// 业务ID /// 业务类型 /// public async Task GetFeeStatisticsAsync(long bsId, BusinessType bsType) { var list = await TenantDb.Queryable() .Where(x => x.BusinessId == bsId && x.BusinessType == bsType).ToListAsync(); return new FeeStatistics(list); } /// /// 获取已申请修改的费用记录值 /// /// 费用记录ID /// public async Task> GetModifyValue(long id) { var fm = await TenantDb.Queryable().Where(x => x.FeeRecordId == id) .OrderByDescending(x => x.CreateTime).Take(1).FirstAsync(); return DataResult.Success(fm); } /// /// 检查业务是否已费用锁定 /// /// 业务ID /// 业务类型 /// 锁定范围 /// internal async Task 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().Where( x => x.BusinessId == bid && x.BusinessType == businessType).Select(x => x.IsFeeLocking).FirstAsync(); break; } return isFeeLocking.GetValueOrDefault(); } /// /// 费用提交 /// /// 要提交的费用记录 /// 是否排除总价为零的费用 /// 是否使用事务 /// public async Task SaveAsync(List 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().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().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? fees = null; var feeIds = items.Where(x => x.Id > 0).Select(x => x.Id).ToArray(); //包含修改的项,需要检测费用状态再修改 if (feeIds.Length > 0) { fees = await TenantDb.Queryable().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().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().BulkCopyAsync(createList); } else { await TenantDb.Insertable(createList).ExecuteCommandAsync(); } } if (useTransaction) await TenantDb.Ado.CommitTranAsync(); var list = createList.Union(updateList).Select(x => x.Adapt()); 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)); } } /// /// 费用保存后提交审核 /// /// 要提交的费用记录 /// 是否排除总价为零的费用 /// 是否使用事务 /// public async Task SaveAndSubmitAsync(List 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().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; } /// /// 根据模板ID创建 /// /// 业务ID /// 业务类型 /// 模板ID /// public async Task CreateByTemplateAsync(long bid, BusinessType businessType, params long[] tidArray) { bool hasExists = TenantDb.Queryable().LeftJoin((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().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 records = new List(details.Count); foreach (var item in details) { var record = item.Adapt(); 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)); } /// /// 删除 /// /// 费用记录ID /// public async Task DeleteAsync(params long[] ids) { var model = await TenantDb.Queryable().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().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(x => ids.Contains(x.Id)).ExecuteCommandAsync(); await WriteBackStatusAsync(model.BusinessId, model.BusinessType); return result > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } #region 审核相关 /// /// 发起审批/申请删除工作流 /// /// 审批类型 /// 备注 /// 费用记录ID /// public async Task SubmitForApprovalAsync(TaskBaseTypeEnum auditType, string remark, params long[] ids) { var fees = await TenantDb.Queryable().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 ApplyAuditAsync(List 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 ApplyDeleteAsync(List 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; } /// /// 发起费用修改申请 /// /// 费用修改信息 /// public async Task SubmitForModificationAsync(IEnumerable items) { var idList = items.Select(x => x.FeeRecordId).Distinct().ToList(); var fees = await TenantDb.Queryable().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)); } } /// /// 撤销审批 /// /// 费用记录ID /// public async Task WithdrawAsync(params long[] ids) { var fees = await TenantDb.Queryable().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> 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.FEE_AUDIT, item.Id)); break; case FeeStatus.ApplyModification: item.FeeStatus = FeeStatus.AuditPassed; taskTypes.Add(new Tuple(TaskBaseTypeEnum.FEE_MODIFY_AUDIT, item.Id)); //删除暂存数据 var entity = await TenantDb.Queryable().OrderByDescending( x => x.CreateTime).Select(x => new { x.Id, x.FeeRecordId }).FirstAsync(x => x.FeeRecordId == item.Id); if (entity != null) await TenantDb.Deleteable().Where(x => x.Id == entity.Id).ExecuteCommandAsync(); break; case FeeStatus.ApplyDeletion: item.FeeStatus = FeeStatus.AuditPassed; taskTypes.Add(new Tuple(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)); } } /// /// 整票审核 /// /// 业务ID /// 业务类型 /// 任务类型 /// 是否使用事务 /// public async Task SubmitBillAuditAsync(long bid, BusinessType type, TaskBaseTypeEnum taskType, bool useTransaction = true) { var entity = await TenantDb.Queryable().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; if (useTransaction) await TenantDb.Ado.BeginTranAsync(); try { if (await billService.HasAuthorizedAsync()) { string? bizNo = null; switch (type) { case BusinessType.OceanShippingExport: bizNo = await TenantDb.Queryable().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().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(); } if (useTransaction) await TenantDb.Ado.CommitTranAsync(); return DataResult.Success; } catch (Exception ex) { if (useTransaction) await TenantDb.Ado.RollbackTranAsync(); await ex.LogAsync(Db); return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } } /// /// 撤销整票审批 /// /// 业务ID /// 业务类型 /// 任务类型 /// public async Task WithdrawBillAsync(long bid, BusinessType type, TaskBaseTypeEnum taskType) { var entity = await TenantDb.Queryable().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().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 /// /// 设置发票启用状态 /// /// 是否启用 /// 费用记录ID /// public async Task 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)); } /// /// 设置费用对象 /// /// 费用对象ID /// 客户类别 /// 费用记录ID /// public async Task SetCustomerAsync(long customerId, string customerType, params long[] idArray) { var model = await TenantDb.Queryable().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)); } /// /// 获取费用打印信息 /// /// 数据提供程序 /// 业务ID /// 业务类型 /// 费用记录ID /// public async Task> GetPrintInfoAsync(string providerName, long bsId, BusinessType bsType, params long[] ids) { Type type = Type.GetType(providerName, false); if (type == null) return DataResult.Failed("未能找到数据提供程序:" + providerName); var provider = Fasterflect.ConstructorExtensions.CreateInstance(type) as IReportProvider; if (provider == null) return DataResult.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.Success(data); return DataResult.Failed(context.ErrorResult.Message, context.ErrorResult.MultiCode); } /// /// 回写业务表费用状态 /// /// 业务ID /// 业务类型 /// public async Task WriteBackStatusAsync(long businessId, BusinessType businessType) { var fees = await TenantDb.Queryable().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(); 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 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; } } }