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.

492 lines
20 KiB
C#

using DS.Module.Core;
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.Fee.Entity;
using DS.WMS.Core.Fee.Method;
using DS.WMS.Core.Invoice.Dto;
using DS.WMS.Core.Invoice.Dtos;
using DS.WMS.Core.Invoice.Interface;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.Sys.Interface;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
namespace DS.WMS.Core.Invoice.Method
{
/// <summary>
/// 发票服务基类
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class InvoiceService<TEntity> : FeeServiceBase, IInvoiceService<TEntity>
where TEntity : Entity.Invoice, new()
{
protected readonly Lazy<ICommonService> CommonService;
/// <summary>
/// 初始化
/// </summary>
/// <param name="provider"></param>
public InvoiceService(IServiceProvider provider) : base(provider)
{
CommonService = new Lazy<ICommonService>(provider.GetRequiredService<ICommonService>());
}
/// <summary>
/// 获取分页列表
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResult<List<InvoiceDto>>> GetListAsync(PageRequest<string> request)
{
var query = TenantDb.Queryable<Entity.Invoice>()
.Select(i => new InvoiceDto
{
Id = i.Id,
InvoiceNO = i.InvoiceNO,
BillNO = i.BillNO,
InvoiceDate = i.InvoiceDate,
CustomerId = i.CustomerId,
CustomerName = i.CustomerName,
Type = i.Type,
Category = i.Category,
InvoiceHeader = i.InvoiceHeader,
TaxID = i.TaxID,
CustomerAddTel = i.CustomerAddTel,
CustomerBank = i.CustomerBank,
AutualCustomerName = i.AutualCustomerName,
Currency = i.Currency,
ReceiptCurrency = i.ReceiptCurrency,
ApplyAmount = i.ApplyAmount,
InvoiceAmount = i.InvoiceAmount,
OperatorId = i.OperatorId,
IsLocked = i.IsLocked,
LockUserId = i.LockUserId,
LockTime = i.LockTime,
TaxRate = i.TaxRate,
OrgId = i.OrgId,
SaleDeptId = i.SaleDeptId,
IsCancelled = i.IsCancelled,
CancelUserId = i.CancelUserId,
CancelTime = i.CancelTime,
CreateTime = i.CreateTime,
CreateBy = i.CreateBy,
InvoiceApplicationList = SqlFunc.Subqueryable<ApplicationDetail>().InnerJoin<InvoiceApplication>((d, a) =>
d.ApplicationId == i.Id && d.Category == DetailCategory.InvoiceIssuance && d.RefId == a.Id)
.GroupBy((d, a) => a.ApplicationNO).ToList((d, a) => a.ApplicationNO)
});
if (!request.OtherQueryCondition.IsNullOrEmpty())
{
query = query.Where(i => i.InvoiceApplicationList.Contains(request.OtherQueryCondition));
}
var whereList = request.GetConditionalModels(Db);
var result = await query.Where(whereList).ToQueryPageAsync(request.PageCondition);
if (result.Data.Count > 0)
{
var userIds = result.Data.Select(x => x.CreateBy)
.Union(result.Data.Where(x => x.LockUserId.HasValue).Select(x => x.LockUserId.Value))
.Union(result.Data.Where(x => x.OperatorId.HasValue).Select(x => x.OperatorId.Value))
.Distinct();
var users = await Db.Queryable<SysUser>().Where(x => userIds.Contains(x.Id)).Select(x => new { x.Id, x.UserName }).ToListAsync();
var orgIds = result.Data.Where(x => x.SaleDeptId.HasValue).Select(x => x.SaleDeptId.Value)
.Union(result.Data.Where(x => x.OrgId.HasValue).Select(x => x.OrgId.Value))
.Distinct();
var orgs = await Db.Queryable<SysOrg>().Where(x => orgIds.Contains(x.Id)).Select(x => new { x.Id, x.OrgName }).ToListAsync();
foreach (var item in result.Data)
{
item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName;
item.LockUserName = users.Find(x => x.Id == item.LockUserId)?.UserName;
item.OperatorName = users.Find(x => x.Id == item.OperatorId)?.UserName;
item.SaleDeptName = orgs.Find(x => x.Id == item.SaleDeptId)?.OrgName;
item.OrgName = orgs.Find(x => x.Id == item.OrgId)?.OrgName;
}
}
return result;
}
/// <summary>
/// 提交发票开票
/// </summary>
/// <param name="request">请求参数</param>
/// <returns></returns>
public async Task<DataResult<TEntity>> SaveAsync(InvoiceRequest<TEntity> request)
{
var invoice = request.Invoice;
if (invoice.InvoiceDate == default)
invoice.InvoiceDate = DateTime.Now;
if (invoice.Currency.IsNullOrEmpty())
invoice.Currency = RMB_CODE;
TEntity? dbValue = default;
if (request.Invoice.Id > 0)
{
dbValue = await TenantDb.Queryable<TEntity>().Select(x => new TEntity
{
Id = x.Id,
IsLocked = x.IsLocked,
Type = x.Type,
}).FirstAsync(x => x.Id == request.Invoice.Id);
if (dbValue != null && dbValue.IsLocked)
return DataResult<TEntity>.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsLocked));
}
var result = EnsureSettlement(request.Invoice, dbValue);
if (!result.Succeeded)
return DataResult<TEntity>.Failed(result.Message, result.MultiCode);
if (request.Details?.Count > 0)
{
if (invoice.Id == 0 && invoice.CustomerId == 0)
{
var first = request.Details[0];
invoice.CustomerId = first.CustomerId.GetValueOrDefault();
invoice.CustomerName = first.CustomerName;
}
invoice.Details = request.Details.Select(x => new ApplicationDetail
{
ApplicationId = x.ApplicationId,
DetailId = x.Id == 0 ? null : x.Id,
RecordId = x.RecordId,
CustomerName = x.CustomerName ?? invoice.CustomerName,
FeeId = x.FeeId,
FeeName = x.FeeName,
FeeType = x.FeeType,
ApplyAmount = x.Amount,
Currency = x.Currency,
ExchangeRate = x.ExchangeRate,
OriginalAmount = x.OriginalAmount,
OriginalCurrency = x.OriginalCurrency ?? (invoice.Currency.IsNullOrEmpty() ? x.Currency : invoice.Currency),
}).ToList();
//金额禁止为0
if (invoice.Details.Any(x => x.ApplyAmount == 0 || x.OriginalAmount == 0))
return DataResult<TEntity>.FailedWithDesc(nameof(MultiLanguageConst.AmountCannotBeZero));
if (invoice.Details.Any(x => x.OriginalCurrency.IsNullOrEmpty()))
return DataResult<TEntity>.FailedWithDesc(nameof(MultiLanguageConst.OriginalCurrencyCanNotNull));
if (invoice.Details.Any(x => x.Currency != invoice.Currency && x.ExchangeRate == null))
return DataResult<TEntity>.FailedWithDesc(nameof(MultiLanguageConst.NeedExchangeRate));
result = await PreSaveAsync(invoice);
if (!result.Succeeded)
return DataResult<TEntity>.Failed(result.Message, result.MultiCode);
invoice.InvoiceAmount = invoice.Details.Sum(x => x.ApplyAmount);
invoice.AmountUppercase = new Money(invoice.InvoiceAmount).ToString();
}
await TenantDb.Ado.BeginTranAsync();
try
{
//关联导航属性插入
if (invoice.Id == 0)
{
//创建时需要生成申请单编号
var sequence = CommonService.Value.GetSequenceNext<TEntity>();
if (!sequence.Succeeded)
{
return DataResult<TEntity>.Failed(sequence.Message, MultiLanguageConst.SequenceSetNotExist);
}
invoice.BillNO = sequence.Data;
await TenantDb.InsertNav(invoice).Include(x => x.Details).ExecuteCommandAsync();
}
else
{
if (invoice.Details.Count > 0)
await TenantDb.Insertable(invoice.Details).ExecuteCommandAsync();
await TenantDb.Updateable(invoice).IgnoreColumns(x => new
{
x.BillNO,
x.IsLocked,
x.CreateBy,
x.CreateTime,
x.Deleted,
x.DeleteBy,
x.DeleteTime
}).ExecuteCommandAsync();
}
await OnSaveAsync(invoice);
if (invoice.Details?.Count > 0)
{
//更新费用记录的已开票金额
var fees = invoice.Details.Select(x => new FeeRecord
{
Id = x.RecordId,
InvoiceAmount = x.OriginalAmount
}).ToList();
await TenantDb.Updateable(fees)
.PublicSetColumns(x => x.InvoiceAmount, "+")
.UpdateColumns(x => new { x.InvoiceAmount })
.ExecuteCommandAsync();
}
await TenantDb.Ado.CommitTranAsync();
return DataResult<TEntity>.Success(await PostSaveAsync(invoice));
}
catch (Exception ex)
{
await TenantDb.Ado.RollbackTranAsync();
await ex.LogAsync(Db);
return DataResult<TEntity>.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed));
}
}
/// <summary>
/// 用于发票的状态检查
/// </summary>
/// <param name="invoice">提交的发票</param>
/// <param name="dbValue">数据库值新增时为null</param>
/// <returns></returns>
protected virtual DataResult EnsureSettlement(TEntity invoice, TEntity? dbValue)
{
return DataResult.Success;
}
/// <summary>
/// 在保存前调用
/// </summary>
/// <param name="invoice">发票</param>
/// <returns></returns>
protected virtual Task<DataResult> PreSaveAsync(TEntity invoice)
{
return Task.FromResult(DataResult.Success);
}
/// <summary>
/// 在保存时调用
/// </summary>
/// <param name="invoice">要保存的发票</param>
/// <returns></returns>
protected virtual Task OnSaveAsync(TEntity invoice)
{
return Task.CompletedTask;
}
/// <summary>
/// 在保存完成后调用
/// </summary>
/// <param name="invoice">发票</param>
protected virtual Task<TEntity> PostSaveAsync(TEntity invoice)
{
return Task.FromResult(invoice);
}
/// <summary>
/// 删除发票
/// </summary>
/// <param name="ids">发票ID</param>
/// <returns></returns>
public async Task<DataResult> DeleteAsync(params long[] ids)
{
var apps = await TenantDb.Queryable<TEntity>().Where(x => ids.Contains(x.Id)).Select(x => new TEntity
{
Id = x.Id,
IsLocked = x.IsLocked
}).ToListAsync();
var details = await TenantDb.Queryable<ApplicationDetail>().Where(x => ids.Contains(x.ApplicationId)).Select(
x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId,
RecordId = x.RecordId,
OriginalAmount = x.OriginalAmount
}).ToListAsync();
foreach (var app in apps)
app.Details = details.FindAll(x => x.ApplicationId == app.Id); await TenantDb.Ado.BeginTranAsync();
var result = PreDelete(apps);
if (!result.Succeeded)
return result;
try
{
await OnDeleteDetailAsync(apps);
await TenantDb.DeleteNav<TEntity>(x => ids.Contains(x.Id)).Include(x => x.Details).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> DeleteDetailAsync(params long[] ids)
{
var details = await TenantDb.Queryable<ApplicationDetail>().Where(x => ids.Contains(x.Id)).Select(
x => new ApplicationDetail
{
Id = x.Id,
ApplicationId = x.ApplicationId,
RecordId = x.RecordId,
OriginalAmount = x.OriginalAmount
}).ToListAsync();
var appIds = details.Select(x => x.ApplicationId).Distinct().ToList();
var apps = await TenantDb.Queryable<TEntity>().Where(x => appIds.Contains(x.Id)).Select(x => new TEntity
{
Id = x.Id,
IsLocked = x.IsLocked
}).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 TenantDb.Ado.BeginTranAsync();
try
{
await OnDeleteDetailAsync(apps);
await TenantDb.Deleteable(details).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="invoices">发票</param>
/// <returns></returns>
protected virtual DataResult PreDelete(List<TEntity> invoices)
{
if (invoices.Any(x => x.IsLocked))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.InvoiceIsLocked));
return DataResult.Success;
}
/// <summary>
/// 在执行删除发票或其明细时调用
/// </summary>
/// <param name="invoices">发票及其明细</param>
/// <returns></returns>
protected virtual async Task OnDeleteDetailAsync(List<TEntity> invoices)
{
//删除明细需要同时变更发票明细
var appIds = invoices.Select(x => x.Id).ToList();
var excludeIds = invoices.SelectMany(x => x.Details).Select(x => x.Id).ToList();
var details = await TenantDb.Queryable<ApplicationDetail>().Where(x => appIds.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);
}
await TenantDb.Deleteable<InvoiceDetail>().Where(x => appIds.Contains(x.ApplicationId)).ExecuteCommandAsync();
await CreateInvoiceDetailsAsync(invoices);
var invDetails = invoices.SelectMany(x => x.InvoiceDetails).ToList();
if (invDetails.Count > 0)
await TenantDb.Insertable(invDetails).ExecuteCommandAsync();
await TenantDb.Updateable(invoices).UpdateColumns(x => new
{
x.ApplyAmount,
x.AmountUppercase
}).ExecuteCommandAsync();
//还原费用表的已开票金额
var fees = invoices.SelectMany(x => x.Details).Select(x => new FeeRecord { Id = x.RecordId, InvoiceAmount = x.OriginalAmount }).ToList();
await TenantDb.Updateable(fees)
.PublicSetColumns(it => it.InvoiceAmount, "-").UpdateColumns(x => new { x.InvoiceAmount })
.ExecuteCommandAsync();
}
/// <summary>
/// 生成发票明细
/// </summary>
/// <param name="invoices"></param>
/// <returns></returns>
protected async Task CreateInvoiceDetailsAsync(IEnumerable<TEntity> invoices)
{
foreach (var invoice in invoices)
{
invoice.ApplyAmount = invoice.Details.Sum(x => x.ApplyAmount);
invoice.AmountUppercase = new Money(invoice.ApplyAmount).ToString();
if (invoice.Details.Count > 0 && (invoice.InvoiceDetails == null || invoice.InvoiceDetails.Count == 0))
{
var feeIds = invoice.Details.Select(x => x.RecordId).ToList();
var list = await TenantDb.Queryable<FeeRecord>().InnerJoin<FeeCode>((f, c) => f.FeeId == c.Id)
.GroupBy((f, c) => f.Id)
.Select((f, c) => new
{
FeeId = c.Id,
c.GoodName
}).ToListAsync();
invoice.InvoiceDetails = list.Select(x => new InvoiceDetail
{
ApplicationId = invoice.Id,
Name = x.GoodName,
TaxRate = invoice.TaxRate,
TaxUnitPrice = invoice.Details.FindAll(y => y.FeeId == x.FeeId).Sum(x => x.ApplyAmount),
Category = DetailCategory.InvoiceIssuance
}).ToList();
}
if (invoice.InvoiceDetails?.Count > 0)
{
foreach (var detail in invoice.InvoiceDetails)
{
detail.TaxAmount = detail.TaxUnitPrice * detail.TaxRate;
detail.UnitPrice = detail.TaxUnitPrice - detail.TaxAmount;
}
}
}
}
public Task<DataResult> SetLockAsync(bool isLocked, params long[] ids)
{
throw new NotImplementedException();
}
}
}