using DS.Module.Core;
using DS.Module.Core.Data;
using DS.Module.Core.Enums;
using DS.Module.Core.Extensions;
using DS.WMS.Core.Application.Dtos;
using DS.WMS.Core.Application.Entity;
using DS.WMS.Core.Application.Method;
using DS.WMS.Core.Code.Entity;
using DS.WMS.Core.Fee.Entity;
using DS.WMS.Core.Info.Entity;
using DS.WMS.Core.Invoice.Dtos;
using DS.WMS.Core.Invoice.Interface;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.Sys.Interface;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
namespace DS.WMS.Core.Invoice.Method
{
///
/// 开票服务基类
///
///
public class InvoiceService : ApplicationServiceBase, IInvoiceService
where TEntity : Entity.Invoice, new()
{
readonly Lazy CommonService;
///
/// 初始化
///
///
public InvoiceService(IServiceProvider provider) : base(provider)
{
CommonService = new Lazy(provider.GetRequiredService());
TenantDb.QueryFilter.Clear();
}
///
/// 获取分页列表
///
///
///
public async Task> GetListAsync(PageRequest request)
{
var query = TenantDb.Queryable()
.LeftJoin((i, s) => i.SaleDeptId == s.Id, "shippingweb8_dev.sys_org")
.LeftJoin((i, s, u1) => i.LockUserId == s.Id, "shippingweb8_dev.sys_user")
.LeftJoin((i, s, u1, u2) => i.OperatorId == s.Id, "shippingweb8_dev.sys_user")
.WhereIF(!string.IsNullOrEmpty(request.OtherQueryCondition?.Number), i => i.BillNO.Contains(request.OtherQueryCondition.Number) || i.InvoiceNO.Contains(request.OtherQueryCondition.Number) ||
SqlFunc.Subqueryable().InnerJoin((d, f) => d.RecordId == f.Id && f.BusinessType == BusinessType.OceanShippingExport)
.InnerJoin((d, f, s) => f.BusinessId == s.Id).Where((d, f, s) =>
s.CustomerNo.Contains(request.OtherQueryCondition.Number) || s.BookingNo.Contains(request.OtherQueryCondition.Number) ||
s.MBLNO.Contains(request.OtherQueryCondition.Number) || s.CustomerNum.Contains(request.OtherQueryCondition.Number)).Any())
.Select((i, s, u1, u2) => new InvoiceDto
{
CreateByName = i.CreateUserName,
SaleDeptName = s.OrgName,
LockUserName = u1.UserName,
OperatorName = u2.UserName,
InvoiceApplicationList = SqlFunc.Subqueryable().LeftJoin((d, a) =>
d.ApplicationId == i.Id && d.RefId == a.Id)
//.WhereIF(request.OtherQueryCondition != null && !string.IsNullOrEmpty(request.OtherQueryCondition.Number),
// (d, a) => a.ApplicationNO.Contains(request.OtherQueryCondition.Number))
.GroupBy((d, a) => a.ApplicationNO).ToList((d, a) => a.ApplicationNO),
ApplyAmountRMB = SqlFunc.Subqueryable().LeftJoin((d1, d2) =>
d1.Id == d2.DetailId).Where((d1, d2) => d2.ApplicationId == i.Id && d1.Currency == FeeCurrency.RMB_CODE)
.Sum((d1, d2) => d1.ApplyAmount),
ApplyAmountUSD = SqlFunc.Subqueryable().LeftJoin((d1, d2) =>
d1.Id == d2.DetailId).Where((d1, d2) => d2.ApplicationId == i.Id && d1.Currency == FeeCurrency.USD_CODE)
.Sum((d1, d2) => d1.ApplyAmount)
}, true).MergeTable();
var whereList = request.GetConditionalModels(Db);
var result = await query.Where(whereList).ToQueryPageAsync(request.PageCondition);
if (result.Data?.Count > 0)
{
}
InvoiceList list = new() { List = result.Data };
var invResult = DataResult.Success(list, result.MultiCode);
invResult.Count = result.Count;
return invResult;
}
///
/// 获取发票详情
///
/// 发票ID
///
public async Task> GetAsync(long id)
{
var invoice = await TenantDb.Queryable()
.LeftJoin((i, ri) => i.RedId == ri.Id)
.LeftJoin((i, ri, s) => i.SaleDeptId == s.Id, "shippingweb8_dev.sys_org")
.LeftJoin((i, ri, s, u1) => i.LockUserId == s.Id, "shippingweb8_dev.sys_user")
.LeftJoin((i, ri, s, u1, u2) => i.OperatorId == s.Id, "shippingweb8_dev.sys_user")
.Select((i, ri, s, u1, u2) => new InvoiceDto
{
RedNO = ri.InvoiceNO,
CreateByName = i.CreateUserName,
SaleDeptName = s.OrgName,
LockUserName = u1.UserName,
OperatorName = u2.UserName,
ApplyAmountRMB = SqlFunc.Subqueryable().LeftJoin((d1, d2) =>
d1.Id == d2.DetailId).Where((d1, d2) => d2.ApplicationId == i.Id && d1.Currency == FeeCurrency.RMB_CODE)
.Sum((d1, d2) => d1.ApplyAmount),
ApplyAmountUSD = SqlFunc.Subqueryable().LeftJoin((d1, d2) =>
d1.Id == d2.DetailId).Where((d1, d2) => d2.ApplicationId == i.Id && d1.Currency == FeeCurrency.USD_CODE)
.Sum((d1, d2) => d1.ApplyAmount)
}, true).FirstAsync(i => i.Id == id);
if (invoice != null)
{
if (!string.IsNullOrEmpty(invoice.PushMode))
{
invoice.PushModeValues = invoice.PushMode.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(x => (PushMode)int.Parse(x)).ToArray();
}
invoice.Details = await CreateApplicationDetailQuery((d, f, s) => d.ApplicationId == id && d.Category == DetailCategory.InvoiceIssuance)
.Select(x => new ApplicationDetailDto
{
Id = x.Id,
ApplicationId = x.ApplicationId,
RecordId = x.RecordId,
DetailId = x.DetailId,
RefId = x.RefId,
FeeName = x.FeeName,
FeeType = x.FeeType,
ApplyAmount = x.ApplyAmount,
ExchangeRate = x.ExchangeRate,
Currency = x.Currency,
OriginalAmount = x.OriginalAmount,
OriginalCurrency = x.OriginalCurrency,
OriginalRate = x.OriginalRate,
CustomerNo = x.CustomerNo,
MBLNO = x.MBLNO,
ClientName = x.ClientName,
ETD = x.ETD,
SaleName = x.SaleName,
SourceName = x.SourceName,
LoadPort = x.LoadPort,
Vessel = x.Vessel,
Voyage = x.Voyage
}).ToListAsync();
invoice.Summary = invoice.Details.GroupBy(x => new { x.FeeType, x.Currency }).Select(x => new SummaryItem
{
FeeType = x.Key.FeeType,
Currency = x.Key.Currency,
Amount = x.Sum(y => y.ApplyAmount)
}).ToList();
if (invoice.Mode == InvoiceMode.Applcation)
{
var ids = invoice.Details.Where(x => x.RefId.HasValue).Select(x => x.RefId).Distinct();
invoice.Applications = await TenantDb.Queryable().Where(x => ids.Contains(x.Id))
.Select(x => new InvoiceApplicationDto
{
Id = x.Id,
ApplicationNO = x.ApplicationNO,
Status = x.Status,
Currency = x.Currency,
InvoiceRemark = x.InvoiceRemark,
CreateBy = x.CreateBy,
CreateByName = x.CreateUserName,
ApplyAmountRMB = SqlFunc.Subqueryable().Where(y => x.Id == y.ApplicationId && y.Currency == FeeCurrency.RMB_CODE).Sum(y => y.ApplyAmount),
ApplyAmountUSD = SqlFunc.Subqueryable().Where(y => x.Id == y.ApplicationId && y.Currency == FeeCurrency.USD_CODE).Sum(y => y.ApplyAmount)
}).ToListAsync();
}
invoice.InvoiceDetails = await TenantDb.Queryable().Where(
x => x.ApplicationId == id && x.Category == DetailCategory.InvoiceIssuance).ToListAsync();
}
return DataResult.Success(invoice);
}
///
/// 根据业务编号及类型获取费用记录
///
/// 业务ID与业务类型
///
public virtual Task> GetFeesAsync(DetailInquiry inquiry)
{
return Task.FromResult(DataResult.Success(new InvoiceApplicaitonBiz([])));
}
#pragma warning disable CS4014
///
/// 提交
///
/// 请求参数
///
public async Task> SaveAsync(InvoiceRequest request)
{
var invoice = request.Invoice;
if (invoice.Currency.IsNullOrEmpty())
invoice.Currency = FeeCurrency.RMB_CODE;
TEntity? dbValue = default;
if (request.Invoice.Id > 0)
{
dbValue = await TenantDb.Queryable().Select(x => new TEntity
{
Id = x.Id,
InvoiceNO = x.InvoiceNO,
IsLocked = x.IsLocked,
IsSettled = x.IsSettled,
Mode = x.Mode,
}).FirstAsync(x => x.Id == request.Invoice.Id);
}
var result = EnsureSettlement(request.Invoice, dbValue);
if (!result.Succeeded)
return DataResult.Failed(result.Message, result.MultiCode);
if (invoice.InvoiceDate == default)
invoice.InvoiceDate = DateTime.Now;
invoice.OperatorId ??= long.Parse(User.UserId);
if (invoice.PushModeValues.Length > 0)
invoice.PushMode = string.Join(",", invoice.PushModeValues.Select(x => (int)x));
string? invRemark = null;
//按申请开票
if (request.Invoice.Mode == InvoiceMode.Applcation && request.Applications?.Count > 0)
{
var ids = request.Applications.Select(x => x.ApplicationId);
var details = await TenantDb.Queryable()
.InnerJoin((x, y) => x.ApplicationId == y.Id)
.LeftJoin((x, y, z) => y.CustomerId == z.ClientId && z.Currency == invoice.Currency && z.IsInvoiceDefault == true)
.Where((x, y, z) => ids.Contains(x.ApplicationId) && x.Category == DetailCategory.InvoiceApplication)
.Select((x, y, z) => new
{
x.Id,
x.ApplicationId,
x.RecordId,
x.CustomerName,
x.FeeId,
x.FeeType,
x.FeeName,
x.ApplyAmount,
x.ExchangeRate,
x.OriginalAmount,
x.Currency,
x.OriginalCurrency,
x.ProcessedAmount,
x.OriginalProcessedAmount,
y.CustomerId,
y.TaxRate,
y.TaxID, //税号
y.InvoiceHeader, //抬头
y.CustomerAddTel,
y.SaleDeptId,
y.PushMode,
y.CellPhoneNO,
y.Email,
y.InvoiceRemark,
z.BankAccountNo,
z.BankName
}).ToListAsync();
invRemark = string.Join(" | ", details.Where(x => !string.IsNullOrEmpty(x.InvoiceRemark)).Select(x => x.InvoiceRemark));
//税率不一致
if (details.GroupBy(x => x.TaxRate).Select(x => x.Key).Count() > 1)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InconsistentTaxRates));
invoice.Details ??= new List(details.Count);
foreach (var item in details)
{
if (invoice.Id == 0)
{
invoice.CustomerId = item.CustomerId;
invoice.CustomerName = item.CustomerName;
invoice.TaxRate = item.TaxRate;
invoice.InvoiceHeader = item.InvoiceHeader;
invoice.CustomerTaxID = item.TaxID;
invoice.CustomerAddressTel = item.CustomerAddTel;
invoice.CustomerAccount = item.BankAccountNo;
invoice.CustomerBankName = item.BankName;
invoice.SaleDeptId = item.SaleDeptId;
invoice.PushMode = item.PushMode;
invoice.CellPhoneNO = item.CellPhoneNO;
invoice.Email = item.Email;
}
else if (invoice.CustomerId != item.CustomerId) //校验开票单位是否一致
{
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceCustomerOnlyOne));
}
else if (invoice.TaxRate != item.TaxRate) //校验税率是否一致
{
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InconsistentTaxRates));
}
//转换为费用明细
var detail = new ApplicationDetail
{
ApplicationId = invoice.Id,
RefId = item.ApplicationId,
DetailId = item.Id,
RecordId = item.RecordId,
Category = DetailCategory.InvoiceIssuance,
CustomerName = item.CustomerName,
FeeId = item.FeeId,
FeeName = item.FeeName,
FeeType = item.FeeType,
Currency = item.Currency,
OriginalCurrency = item.OriginalCurrency,
ApplyAmount = item.ApplyAmount - item.ProcessedAmount,
OriginalAmount = item.OriginalAmount - item.OriginalProcessedAmount,
ExchangeRate = item.ExchangeRate
};
var app = request.Applications.Find(x => x.ApplicationId == item.ApplicationId);
if (app != null) //设置汇率
{
var er = app.ExchangeRates?.FirstOrDefault(x => x.Currency == detail.OriginalCurrency);
if (er != null)
detail.ExchangeRate = er.ExchangeRate;
}
invoice.Details.Add(detail);
}
DataResult result2;
//执行开票金额分配
foreach (var app in request.Applications)
{
var details2 = invoice.Details.Where(x => x.RefId == app.ApplicationId).OrderBy(x => x.ApplyAmount).ToList();
if (app.AmountRMB.HasValue)
{
result2 = AssignAmount(details2.FindAll(x => x.Currency == FeeCurrency.RMB_CODE), app.AmountRMB.Value);
if (!result2.Succeeded)
return DataResult.Failed(result2.Message, result2.MultiCode);
}
if (app.AmountUSD.HasValue)
{
result2 = AssignAmount(details2.FindAll(x => x.Currency == FeeCurrency.USD_CODE), app.AmountUSD.Value);
if (!result2.Succeeded)
return DataResult.Failed(result2.Message, result2.MultiCode);
}
if (app.AmountOther.HasValue)
{
result2 = AssignAmount(details2.FindAll(x => x.Currency != FeeCurrency.RMB_CODE && x.Currency != FeeCurrency.USD_CODE), app.AmountOther.Value);
if (!result2.Succeeded)
return DataResult.Failed(result2.Message, result2.MultiCode);
}
}
invoice.Details.RemoveAll(x => !x.Assigned);
}
//自由开票
else if (request.Invoice.Mode == InvoiceMode.Free)
{
if (request.FreeInvoice?.Items.Count > 0)
{
var result2 = await GetFeesAsync(request.FreeInvoice);
if (result2.Data != null)
{
request.Details ??= [];
foreach (var item in result2.Data.Items)
{
var dto = item.Adapt();
if (dto.Currency != invoice.Currency)
{
var biz = request.FreeInvoice.Items.Find(x => x.Id == dto.BusinessId && x.BusinessType == dto.BusinessType && x.CustomerId == dto.CustomerId);
var er = biz?.ExchangeRates?.FirstOrDefault(x => x.Currency == dto.Currency);
dto.ExchangeRate = er == null ? 1 : er.ExchangeRate;
}
request.Details.Add(dto);
}
}
}
if (request.Details?.Count > 0)
{
if (invoice.Id == 0 && invoice.CustomerId == 0)
{
var first = request.Details[0];
invoice.CustomerId = first.CustomerId;
invoice.CustomerName = first.CustomerName;
}
var ids = request.Details.Select(x => x.RecordId).Distinct();
var fees = await TenantDb.Queryable().Where(x => ids.Contains(x.Id)).Select(x => new { x.Id, x.TaxRate }).ToListAsync();
//税率不一致
if (fees.GroupBy(x => x.TaxRate).Select(x => x.Key).Count() > 1 || (invoice.Id > 0 && invoice.TaxRate != fees[0].TaxRate))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InconsistentTaxRates));
//将请求明细转换为数据库的费用明细
invoice.Details = request.Details.Select(x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId == 0 ? x.ApplicationId : invoice.Id,
RefId = x.RefId,
DetailId = x.Id == 0 ? null : x.Id,
RecordId = x.RecordId,
Category = DetailCategory.InvoiceIssuance,
CustomerName = x.CustomerName ?? invoice.CustomerName,
FeeId = x.FeeId,
FeeName = x.FeeName,
FeeType = x.FeeType,
Currency = x.Currency,
ApplyAmount = x.ApplyAmount,
ExchangeRate = x.ExchangeRate,
OriginalAmount = x.OriginalAmount,
OriginalCurrency = x.OriginalCurrency ?? (invoice.Currency.IsNullOrEmpty() ? x.Currency : invoice.Currency),
}).ToList();
}
}
if (invoice.Id == 0)
{
if (request.Invoice.Mode == InvoiceMode.Free)
{
//补充购方信息
invoice.CustomerTaxID = await TenantDb.Queryable().Where(x => x.Id == invoice.CustomerId).Select(x => x.TaxNo).FirstAsync();
var header = await TenantDb.Queryable().Where(x => x.RelativeId == invoice.CustomerId)
.OrderByDescending(x => x.Id).FirstAsync();
if (header != null)
{
invoice.InvoiceHeader = header.Header;
invoice.CustomerAddressTel = header.AddressTel;
}
var clientBank = await TenantDb.Queryable().Where(x => x.ClientId == invoice.CustomerId && x.Currency == invoice.Currency)
.OrderByDescending(x => x.IsInvoiceDefault).Select(x => new
{
x.Account,
x.BankName
}).FirstAsync();
if (clientBank != null)
{
invoice.CustomerAccount = clientBank.Account;
invoice.CustomerBankName = clientBank.BankName;
}
}
//补充销方信息
var orgId = invoice.SaleDeptId.GetValueOrDefault() == 0 ? User.OrgId : invoice.SaleDeptId;
var org = await Db.Queryable().LeftJoin((t, a) => t.Id == a.OrgId).Where((t, a) => t.Id == orgId).Select((t, a) => new { t.OrgFullName, t.LicenseCode }).FirstAsync();
if (org != null)
{
invoice.OrgName = org.OrgFullName;
invoice.TaxID = org.LicenseCode;
}
var orgBank = await Db.Queryable().Where(x => x.LinkId == User.OrgId && x.Currency == invoice.Currency).OrderByDescending(x => x.IsDefault).Select(x => new
{
x.BankAccountNo,
x.BankName
}).FirstAsync();
if (orgBank != null)
{
invoice.BankName = orgBank.BankName;
invoice.Account = orgBank.BankAccountNo;
}
if (!string.IsNullOrEmpty(invRemark))
invoice.InvoiceRemark += Environment.NewLine + invRemark;
}
//筛选出新增的费用明细
invoice.Details = invoice.Details.FindAll(x => x.Id == 0);
if (invoice.Details.Count > 0)
{
//金额禁止为0
if (invoice.Details.Any(x => x.ApplyAmount == 0 || x.OriginalAmount == 0))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.AmountCannotBeZero));
if (invoice.Details.Any(x => x.OriginalCurrency.IsNullOrEmpty()))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.OriginalCurrencyCanNotNull));
if (invoice.Details.Any(x => x.Currency != invoice.Currency && x.ExchangeRate == null))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.NeedExchangeRate));
result = await PreSaveAsync(invoice);
if (!result.Succeeded)
return DataResult.Failed(result.Message, result.MultiCode);
}
await TenantDb.Ado.BeginTranAsync();
try
{
BuildOption buildOption;
if (invoice.Id == 0)//新增
{
buildOption = BuildOption.Create;
//创建时需要生成业务编号
var sequence = CommonService.Value.GetSequenceNext();
if (!sequence.Succeeded)
return DataResult.Failed(sequence.Message, MultiLanguageConst.SequenceSetNotExist);
invoice.BillNO = sequence.Data;
invoice.Type = InvoiceType.Blue;
//关联导航属性插入
await TenantDb.InsertNav(invoice).Include(x => x.Details).ExecuteCommandAsync();
}
else//编辑
{
buildOption = BuildOption.Update;
await TenantDb.Updateable(invoice).UpdateColumns(x => new
{
x.InvoiceDate,
x.ReceiptCurrency,
x.AutualCustomerName,
x.OperatorId,
x.SaleDeptId,
x.Category,
x.CategoryCode,
x.IsOverseasInvoice,
x.Payee,
x.Checker,
x.PushMode,
x.CellPhoneNO,
x.Email,
x.InvoiceRemark,
x.Note
}).ExecuteCommandAsync();
if (invoice.Details?.Count > 0)
await TenantDb.Insertable(invoice.Details).ExecuteCommandAsync();
if (invoice.InvoiceDetails?.Count > 0)
await TenantDb.Storageable(invoice.InvoiceDetails).DefaultAddElseUpdate().ExecuteCommandAsync();
}
if (invoice.Details?.Count > 0)
{
//更新费用记录的已开票金额
var fees = invoice.Details.Select(x => new FeeRecord
{
Id = x.RecordId,
InvoiceAmount = x.OriginalAmount,
OrderInvSettlementAmount = x.OriginalAmount,
}).ToList();
var updateable = TenantDb.Updateable(fees).PublicSetColumns(x => x.InvoiceAmount, "+");
if (invoice.Mode == InvoiceMode.Applcation)
updateable = updateable.PublicSetColumns(x => x.OrderInvSettlementAmount, "+")
.UpdateColumns(x => new { x.OrderInvSettlementAmount });
await updateable.UpdateColumns(x => new { x.InvoiceAmount }).ExecuteCommandAsync();
//生成发票明细
await BuildInvoiceDetailAsync(invoice, buildOption);
if (invoice.InvoiceDetails?.Count > 0)
await TenantDb.Storageable(invoice.InvoiceDetails).DefaultAddElseUpdate().ExecuteCommandAsync();
//重新计算发票总金额
await RefreshInvoiceAsync([invoice]);
}
await OnSaveAsync(invoice);
await TenantDb.Ado.CommitTranAsync();
PostSaveAsync(invoice);
return DataResult.Success(invoice);
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
internal static DataResult AssignAmount(List details, decimal invoiceAmount, string currency = FeeCurrency.RMB_CODE)
{
if (details.Count == 0)
return DataResult.Success;
if (invoiceAmount == 0)
return DataResult.Failed("开票金额不能为零");
var totalAmount = details.Sum(x => x.ApplyAmount);
if (Math.Abs(invoiceAmount) > totalAmount)
return DataResult.Failed("申请开票金额不能大于剩余发票金额");
decimal rest = invoiceAmount;
decimal currentAmount = default;
foreach (var detail in details)
{
if (rest <= 0)
break;
if (totalAmount != invoiceAmount) //非全额开
{
if (rest >= detail.ApplyAmount)
{
rest -= detail.ApplyAmount;
}
else
{
detail.ApplyAmount = rest;
rest = 0;
}
}
currentAmount = detail.ApplyAmount;
if (detail.Currency == currency)
{
if (detail.Currency == detail.OriginalCurrency)
{
detail.OriginalAmount = detail.ApplyAmount;
}
else
{
detail.OriginalAmount = detail.ApplyAmount / detail.ExchangeRate.Value;
}
detail.ExchangeRate = 1;
}
else //开票币别与费用币别不一致
{
detail.ExchangeRate ??= 1;
detail.ApplyAmount *= detail.ExchangeRate.Value;
if (detail.OriginalCurrency == detail.Currency)
{
detail.OriginalAmount = currentAmount;
}
else
{
detail.OriginalAmount = currentAmount * detail.ExchangeRate.Value;
}
detail.Currency = currency;
}
detail.Assigned = true;
}
return DataResult.Success;
}
///
/// 生成发票明细
///
/// 发票
/// 生成类型
///
protected async Task BuildInvoiceDetailAsync(TEntity invoice, BuildOption option)
{
if (invoice.Details == null || invoice.Details.Count == 0)
return;
invoice.InvoiceDetails ??= [];
var currencies = invoice.Details.Select(x => x.OriginalCurrency).Distinct();
var codeList = await TenantDb.Queryable()
.Where(ci => currencies.Contains(ci.DefaultCurrency) && ci.IsDefault)
.Select(ci => new
{
ci.Id,
ci.Name,
ci.DefaultCurrency,
ci.TaxRate,
ci.Specification,
ci.Unit
}).ToListAsync();
foreach (var detail in invoice.Details)
{
var code = codeList.Find(x => x.DefaultCurrency == detail.OriginalCurrency);
if (code == null || string.IsNullOrEmpty(code.Name))
continue;
if (option == BuildOption.Create && codeList.IndexOf(code) == 0)//取第一条发票代码税率
invoice.TaxRate = code.TaxRate;
var invDetail = invoice.InvoiceDetails.Find(x => x.CodeId == code.Id);
if (invDetail == null)
{
invDetail = new InvoiceDetail
{
ApplicationId = invoice.Id,
CodeId = code.Id,
Name = code.Name,
Currency = FeeCurrency.RMB_CODE,
Quantity = 1,
TaxUnitPrice = detail.ApplyAmount,
TaxRate = code.TaxRate,
Specification = code.Specification,
Unit = code.Unit,
Category = DetailCategory.InvoiceIssuance
};
invDetail.Amount = invDetail.TaxUnitPrice;
invoice.InvoiceDetails.Add(invDetail);
}
else
{
invDetail.Amount += detail.ApplyAmount;
invDetail.UnitPrice = invDetail.TaxUnitPrice = invDetail.Amount;
}
}
foreach (var item in invoice.InvoiceDetails)
{
item.TaxAmount = item.Amount * (invoice.TaxRate / 100);
item.UnitPrice = item.TaxUnitPrice - item.TaxAmount;
item.Amount = item.TaxUnitPrice * item.Quantity;
}
}
///
/// 重新计算发票的各项金额数据
///
/// 发票
///
protected async Task RefreshInvoiceAsync(List invoices)
{
var ids = invoices.Select(x => x.Id);
var details = await TenantDb.Queryable().Where(x => ids.Contains(x.ApplicationId))
.Select(x => new { x.ApplicationId, x.Currency, x.ApplyAmount, x.OriginalAmount }).ToListAsync();
var invDetails = await TenantDb.Queryable().Where(x => ids.Contains(x.ApplicationId))
.Select(x => new { x.ApplicationId, x.Amount }).ToListAsync();
foreach (var invoice in invoices)
{
var currDetails = details.FindAll(x => x.ApplicationId == invoice.Id);
invoice.OriginalAmount = currDetails.Sum(x => x.OriginalAmount);
invoice.OtherInvoiceAmount = currDetails.FindAll(x => x.Currency != FeeCurrency.RMB_CODE).Sum(x => x.OriginalAmount);
invoice.InvoiceAmount = invDetails.FindAll(x => x.ApplicationId == invoice.Id).Sum(x => x.Amount);
}
return await TenantDb.Updateable(invoices).UpdateColumns(x => new
{
x.OriginalAmount,
x.OtherInvoiceAmount,
x.InvoiceAmount
}).ExecuteCommandAsync();
}
///
/// 用于发票的状态检查
///
/// 提交的发票
/// 数据库值,新增时为null
///
protected virtual DataResult EnsureSettlement(TEntity invoice, TEntity? dbValue)
{
if (dbValue != null)
{
if (dbValue.IsLocked)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsLocked));
if (!string.IsNullOrEmpty(dbValue.InvoiceNO))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsIssued));
if (dbValue.IsSettled)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsSettled));
}
return DataResult.Success;
}
///
/// 在保存前调用
///
/// 发票
///
protected virtual Task PreSaveAsync(TEntity invoice) => Task.FromResult(DataResult.Success);
///
/// 在保存时调用
///
/// 要保存的发票
///
protected virtual Task OnSaveAsync(TEntity invoice) => Task.CompletedTask;
///
/// 在保存完成后调用
///
/// 发票
protected virtual Task PostSaveAsync(TEntity invoice) => Task.CompletedTask;
///
/// 删除发票
///
/// 发票ID
///
public async Task DeleteAsync(params long[] ids)
{
await TenantDb.Ado.BeginTranAsync();
try
{
var apps = await TenantDb.Queryable().Where(x => ids.Contains(x.Id)).Select(x => new TEntity
{
Id = x.Id,
Mode = x.Mode,
IsLocked = x.IsLocked
}).ToListAsync();
if (apps.Count == 0)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
var details = await TenantDb.Queryable().Where(x => ids.Contains(x.ApplicationId)).Select(
x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId,
DetailId = x.DetailId,
RefId = x.RefId,
RecordId = x.RecordId,
ApplyAmount = x.ApplyAmount,
OriginalAmount = x.OriginalAmount
}).ToListAsync();
foreach (var app in apps)
app.Details = details.FindAll(x => x.ApplicationId == app.Id);
var result = PreDelete(apps);
if (!result.Succeeded)
return result;
await OnDeleteDetailAsync(apps, DeleteOption.Entire);
await TenantDb.DeleteNav(x => ids.Contains(x.Id)).Include(x => x.Details).ExecuteCommandAsync();
await TenantDb.Ado.CommitTranAsync();
PostDeleteAsync(apps, DeleteOption.Entire);
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 DeleteDetailAsync(params long[] ids)
{
await TenantDb.Ado.BeginTranAsync();
try
{
var details = await TenantDb.Queryable().Where(x => ids.Contains(x.Id)).Select(
x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId,
DetailId = x.DetailId,
RefId = x.RefId,
RecordId = x.RecordId,
ApplyAmount = x.ApplyAmount,
OriginalAmount = x.OriginalAmount
}).ToListAsync();
if (details.Count == 0)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
var invIds = details.Select(x => x.ApplicationId).Distinct().ToList();
var invoices = await TenantDb.Queryable().Where(x => invIds.Contains(x.Id)).Select(x => new TEntity
{
Id = x.Id,
Mode = x.Mode,
IsLocked = x.IsLocked
}).ToListAsync();
foreach (var app in invoices)
app.Details = details.FindAll(x => x.ApplicationId == app.Id);
var result = PreDelete(invoices);
if (!result.Succeeded)
return result;
await OnDeleteDetailAsync(invoices, DeleteOption.DetailOnly);
await TenantDb.Deleteable(details).ExecuteCommandAsync();
//重新计算发票总金额
await RefreshInvoiceAsync(invoices);
await TenantDb.Ado.CommitTranAsync();
PostDeleteAsync(invoices, DeleteOption.Entire);
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 DeleteInvoiceDetailAsync(params long[] ids)
{
await TenantDb.Ado.BeginTranAsync();
try
{
var invDetails = await TenantDb.Queryable().Where(x => ids.Contains(x.Id))
.Select(x => new { x.ApplicationId, x.Amount }).ToListAsync();
var groups = invDetails.GroupBy(x => x.ApplicationId);
foreach (var g in groups)
{
var list = g.Select(x => new TEntity { Id = g.Key, InvoiceAmount = g.Sum(x => x.Amount) }).ToList();
//更新发票主表的开票金额
await TenantDb.Updateable(list).PublicSetColumns(x => x.InvoiceAmount, "-")
.UpdateColumns(x => new { x.InvoiceAmount }).ExecuteCommandAsync();
}
await TenantDb.Deleteable().Where(x => ids.Contains(x.Id)).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));
}
}
///
/// 在删除发票或其明细之前调用,用于检查状态
///
/// 发票
///
protected virtual DataResult PreDelete(List invoices)
{
if (invoices.Any(x => x.IsLocked))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsLocked));
if (invoices.Any(x => !string.IsNullOrEmpty(x.InvoiceNO)))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsIssued));
//var ids = invoices.Select(x => x.Id);
//if (TenantDb.Queryable().Any(x => ids.Contains(x.RefId.Value) && x.Category == DetailCategory.InvoiceSettlement))
// return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsSettled));
if (invoices.Any(x => x.IsSettled))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsSettled));
return DataResult.Success;
}
///
/// 在执行删除发票或其明细时调用
///
/// 发票及其明细
/// 发票删除选项
///
protected virtual async Task OnDeleteDetailAsync(List invoices, DeleteOption deleteOption)
{
var ids = invoices.Select(x => x.Id);
if (deleteOption == DeleteOption.DetailOnly)
{
var excludeIds = invoices.SelectMany(x => x.Details).Select(x => x.Id);
var details = await TenantDb.Queryable().Where(x => ids.Contains(x.ApplicationId) && !excludeIds.Contains(x.Id))
.Select(x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId,
RecordId = x.RecordId,
FeeId = x.FeeId,
ApplyAmount = x.ApplyAmount
}).ToListAsync();
foreach (var item in invoices)
{
//重新设置申请明细与总金额
item.Details = details.FindAll(x => x.ApplicationId == item.Id);
//item.InvoiceAmount = item.InvoiceDetails.Sum(x => x.Amount);
}
}
else if (deleteOption == DeleteOption.Entire)
{
//删除发票主表则同时删除对应发票明细
await TenantDb.Deleteable().Where(x => ids.Contains(x.ApplicationId)).ExecuteCommandAsync();
}
foreach (var item in invoices)
{
//还原费用表的已开票金额
var fees = item.Details?.Select(x => new FeeRecord { Id = x.RecordId, InvoiceAmount = x.OriginalAmount }).ToList();
var updateable = TenantDb.Updateable(fees).PublicSetColumns(x => x.InvoiceAmount, "-");
if (item.Mode == InvoiceMode.Applcation)
updateable = updateable.PublicSetColumns(x => x.OrderInvSettlementAmount, "-")
.UpdateColumns(x => new { x.OrderInvSettlementAmount });
await updateable.UpdateColumns(x => new { x.InvoiceAmount }).ExecuteCommandAsync();
}
}
///
/// 在删除完成后调用
///
/// 发票及其明细
/// 发票删除选项
protected virtual Task PostDeleteAsync(List invoices, DeleteOption deleteOption) => Task.CompletedTask;
///
/// 设置发票的锁定状态
///
/// 是否锁定
/// 发票ID
///
public async Task SetLockAsync(bool isLocked, params long[] ids)
{
var dt = DateTime.Now;
var userId = long.Parse(User.UserId);
var list = ids.Select(x => new TEntity
{
Id = x,
IsLocked = isLocked,
LockTime = isLocked ? dt : null,
LockUserId = isLocked ? userId : null
}).ToList();
int rows = await TenantDb.Updateable(list).UpdateColumns(x => new { x.IsLocked, x.LockTime, x.LockUserId }).ExecuteCommandAsync();
return rows > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
///
/// 设置发票的作废状态
///
/// 是否锁定
/// 发票ID
///
public async Task SetCancelAsync(bool isCancelled, params long[] ids)
{
var dt = DateTime.Now;
var userId = long.Parse(User.UserId);
var list = ids.Select(x => new TEntity
{
Id = x,
IsCancelled = isCancelled,
CancelTime = isCancelled ? dt : null,
CancelUserId = isCancelled ? userId : null
}).ToList();
int rows = await TenantDb.Updateable(list)
.UpdateColumns(x => new { x.IsCancelled, x.CancelTime, x.CancelUserId }).ExecuteCommandAsync();
return rows > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
}