feat(finance): implement invoice and business report backend modules
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表快照实体。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportSnapshot : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期类型。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportPeriodType PeriodType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期开始时间(UTC,含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodStartAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期结束时间(UTC,不含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodEndAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成状态。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportStatus Status { get; set; } = FinanceBusinessReportStatus.Queued;
|
||||
|
||||
/// <summary>
|
||||
/// 营业额。
|
||||
/// </summary>
|
||||
public decimal RevenueAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单数。
|
||||
/// </summary>
|
||||
public int OrderCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客单价。
|
||||
/// </summary>
|
||||
public decimal AverageOrderValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款率(0-1)。
|
||||
/// </summary>
|
||||
public decimal RefundRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本总额。
|
||||
/// </summary>
|
||||
public decimal CostTotalAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 净利润。
|
||||
/// </summary>
|
||||
public decimal NetProfitAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 利润率(0-1)。
|
||||
/// </summary>
|
||||
public decimal ProfitRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// KPI 比较快照 JSON(同比/环比)。
|
||||
/// </summary>
|
||||
public string KpiComparisonJson { get; set; } = "[]";
|
||||
|
||||
/// <summary>
|
||||
/// 收入明细快照 JSON(按渠道)。
|
||||
/// </summary>
|
||||
public string IncomeBreakdownJson { get; set; } = "[]";
|
||||
|
||||
/// <summary>
|
||||
/// 成本明细快照 JSON(按类别)。
|
||||
/// </summary>
|
||||
public string CostBreakdownJson { get; set; } = "[]";
|
||||
|
||||
/// <summary>
|
||||
/// 生成开始时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? StartedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成完成时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? FinishedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近一次失败信息。
|
||||
/// </summary>
|
||||
public string? LastError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 重试次数。
|
||||
/// </summary>
|
||||
public int RetryCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 调度任务 ID。
|
||||
/// </summary>
|
||||
public string? HangfireJobId { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 成本日覆盖实体。
|
||||
/// </summary>
|
||||
public sealed class FinanceCostDailyOverride : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 业务日期(UTC 日期)。
|
||||
/// </summary>
|
||||
public DateTime BusinessDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public FinanceCostCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 覆盖金额。
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注。
|
||||
/// </summary>
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 成本配置实体(类别级规则)。
|
||||
/// </summary>
|
||||
public sealed class FinanceCostProfile : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public FinanceCostCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 计算模式。
|
||||
/// </summary>
|
||||
public FinanceCostCalcMode CalcMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 比例值(0-1,Ratio 模式使用)。
|
||||
/// </summary>
|
||||
public decimal Ratio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 固定日金额(FixedDaily 模式使用)。
|
||||
/// </summary>
|
||||
public decimal FixedDailyAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生效开始日期(UTC 日期)。
|
||||
/// </summary>
|
||||
public DateTime EffectiveFrom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生效结束日期(UTC 日期,含,null 表示长期)。
|
||||
/// </summary>
|
||||
public DateTime? EffectiveTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用。
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 排序值。
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; } = 100;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表周期类型。
|
||||
/// </summary>
|
||||
public enum FinanceBusinessReportPeriodType
|
||||
{
|
||||
/// <summary>
|
||||
/// 日报。
|
||||
/// </summary>
|
||||
Daily = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 周报。
|
||||
/// </summary>
|
||||
Weekly = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 月报。
|
||||
/// </summary>
|
||||
Monthly = 3
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表快照状态。
|
||||
/// </summary>
|
||||
public enum FinanceBusinessReportStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 已排队。
|
||||
/// </summary>
|
||||
Queued = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 生成中。
|
||||
/// </summary>
|
||||
Running = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 已生成。
|
||||
/// </summary>
|
||||
Succeeded = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 生成失败。
|
||||
/// </summary>
|
||||
Failed = 4
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 成本计算模式。
|
||||
/// </summary>
|
||||
public enum FinanceCostCalcMode
|
||||
{
|
||||
/// <summary>
|
||||
/// 按营业额比例计算。
|
||||
/// </summary>
|
||||
Ratio = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 按固定日金额计算。
|
||||
/// </summary>
|
||||
FixedDaily = 2
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表 KPI 快照项。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportKpiSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 指标键。
|
||||
/// </summary>
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 指标名称。
|
||||
/// </summary>
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 指标值。
|
||||
/// </summary>
|
||||
public decimal Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 同比变化率(百分数,如 3.5 表示 +3.5%)。
|
||||
/// </summary>
|
||||
public decimal YoyChangeRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 环比变化率(百分数,如 2.1 表示 +2.1%)。
|
||||
/// </summary>
|
||||
public decimal MomChangeRate { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表明细行快照。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportBreakdownSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 明细键。
|
||||
/// </summary>
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 明细名称。
|
||||
/// </summary>
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 金额。
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 占比(0-1)。
|
||||
/// </summary>
|
||||
public decimal Ratio { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表列表行快照。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportListItemSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 报表 ID。
|
||||
/// </summary>
|
||||
public long ReportId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期类型。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportPeriodType PeriodType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期开始时间(UTC,含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodStartAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期结束时间(UTC,不含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodEndAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 营业额。
|
||||
/// </summary>
|
||||
public decimal RevenueAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单数。
|
||||
/// </summary>
|
||||
public int OrderCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客单价。
|
||||
/// </summary>
|
||||
public decimal AverageOrderValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款率(0-1)。
|
||||
/// </summary>
|
||||
public decimal RefundRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本总额。
|
||||
/// </summary>
|
||||
public decimal CostTotalAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 净利润。
|
||||
/// </summary>
|
||||
public decimal NetProfitAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 利润率(0-1)。
|
||||
/// </summary>
|
||||
public decimal ProfitRate { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表分页快照。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportPageSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 列表。
|
||||
/// </summary>
|
||||
public List<FinanceBusinessReportListItemSnapshot> Items { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 总数。
|
||||
/// </summary>
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表详情快照。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportDetailSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 报表 ID。
|
||||
/// </summary>
|
||||
public long ReportId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期类型。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportPeriodType PeriodType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期开始时间(UTC,含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodStartAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 周期结束时间(UTC,不含)。
|
||||
/// </summary>
|
||||
public DateTime PeriodEndAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public FinanceBusinessReportStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 营业额。
|
||||
/// </summary>
|
||||
public decimal RevenueAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单数。
|
||||
/// </summary>
|
||||
public int OrderCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客单价。
|
||||
/// </summary>
|
||||
public decimal AverageOrderValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款率(0-1)。
|
||||
/// </summary>
|
||||
public decimal RefundRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本总额。
|
||||
/// </summary>
|
||||
public decimal CostTotalAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 净利润。
|
||||
/// </summary>
|
||||
public decimal NetProfitAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 利润率(0-1)。
|
||||
/// </summary>
|
||||
public decimal ProfitRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关键指标快照列表。
|
||||
/// </summary>
|
||||
public List<FinanceBusinessReportKpiSnapshot> Kpis { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 收入明细(按渠道)。
|
||||
/// </summary>
|
||||
public List<FinanceBusinessReportBreakdownSnapshot> IncomeBreakdowns { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 成本明细(按类别)。
|
||||
/// </summary>
|
||||
public List<FinanceBusinessReportBreakdownSnapshot> CostBreakdowns { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 待处理报表任务快照。
|
||||
/// </summary>
|
||||
public sealed class FinanceBusinessReportPendingSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 快照 ID。
|
||||
/// </summary>
|
||||
public long SnapshotId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long TenantId { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Domain.Finance.Models;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表仓储契约。
|
||||
/// </summary>
|
||||
public interface IFinanceBusinessReportRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 确保门店存在默认成本配置。
|
||||
/// </summary>
|
||||
Task EnsureDefaultCostProfilesAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 为指定分页周期补齐快照并排队。
|
||||
/// </summary>
|
||||
Task QueueSnapshotsForPageAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
FinanceBusinessReportPeriodType periodType,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 查询经营报表分页结果。
|
||||
/// </summary>
|
||||
Task<FinanceBusinessReportPageSnapshot> SearchPageAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
FinanceBusinessReportPeriodType periodType,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 查询经营报表详情。
|
||||
/// </summary>
|
||||
Task<FinanceBusinessReportDetailSnapshot?> GetDetailAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
long reportId,
|
||||
bool allowRealtimeBuild,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 查询批量导出详情集合。
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<FinanceBusinessReportDetailSnapshot>> ListBatchDetailsAsync(
|
||||
long tenantId,
|
||||
long storeId,
|
||||
FinanceBusinessReportPeriodType periodType,
|
||||
int page,
|
||||
int pageSize,
|
||||
bool allowRealtimeBuild,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 拉取待处理任务。
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<FinanceBusinessReportPendingSnapshot>> GetPendingSnapshotsAsync(
|
||||
int take,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 执行报表快照生成。
|
||||
/// </summary>
|
||||
Task GenerateSnapshotAsync(
|
||||
long snapshotId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using TakeoutSaaS.Domain.Finance.Models;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 经营报表导出服务契约。
|
||||
/// </summary>
|
||||
public interface IFinanceBusinessReportExportService
|
||||
{
|
||||
/// <summary>
|
||||
/// 导出单条报表 PDF。
|
||||
/// </summary>
|
||||
Task<byte[]> ExportSinglePdfAsync(
|
||||
FinanceBusinessReportDetailSnapshot detail,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 导出单条报表 Excel。
|
||||
/// </summary>
|
||||
Task<byte[]> ExportSingleExcelAsync(
|
||||
FinanceBusinessReportDetailSnapshot detail,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 导出批量报表 PDF。
|
||||
/// </summary>
|
||||
Task<byte[]> ExportBatchPdfAsync(
|
||||
IReadOnlyList<FinanceBusinessReportDetailSnapshot> details,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 导出批量报表 Excel。
|
||||
/// </summary>
|
||||
Task<byte[]> ExportBatchExcelAsync(
|
||||
IReadOnlyList<FinanceBusinessReportDetailSnapshot> details,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Tenants.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 租户发票记录。
|
||||
/// </summary>
|
||||
public sealed class TenantInvoiceRecord : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 发票号码。
|
||||
/// </summary>
|
||||
public string InvoiceNo { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 申请人。
|
||||
/// </summary>
|
||||
public string ApplicantName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 开票抬头(公司名)。
|
||||
/// </summary>
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 纳税人识别号快照。
|
||||
/// </summary>
|
||||
public string? TaxpayerNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发票类型。
|
||||
/// </summary>
|
||||
public TenantInvoiceType InvoiceType { get; set; } = TenantInvoiceType.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// 开票金额。
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联订单号。
|
||||
/// </summary>
|
||||
public string OrderNo { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 接收邮箱。
|
||||
/// </summary>
|
||||
public string? ContactEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系电话。
|
||||
/// </summary>
|
||||
public string? ContactPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 申请备注。
|
||||
/// </summary>
|
||||
public string? ApplyRemark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发票状态。
|
||||
/// </summary>
|
||||
public TenantInvoiceStatus Status { get; set; } = TenantInvoiceStatus.Pending;
|
||||
|
||||
/// <summary>
|
||||
/// 申请时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime AppliedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 开票时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? IssuedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开票人 ID。
|
||||
/// </summary>
|
||||
public long? IssuedByUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开票备注。
|
||||
/// </summary>
|
||||
public string? IssueRemark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 作废时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? VoidedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 作废人 ID。
|
||||
/// </summary>
|
||||
public long? VoidedByUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 作废原因。
|
||||
/// </summary>
|
||||
public string? VoidReason { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Tenants.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 租户发票开票基础设置。
|
||||
/// </summary>
|
||||
public sealed class TenantInvoiceSetting : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 企业名称。
|
||||
/// </summary>
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 纳税人识别号。
|
||||
/// </summary>
|
||||
public string TaxpayerNumber { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 注册地址。
|
||||
/// </summary>
|
||||
public string? RegisteredAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 注册电话。
|
||||
/// </summary>
|
||||
public string? RegisteredPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开户银行。
|
||||
/// </summary>
|
||||
public string? BankName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 银行账号。
|
||||
/// </summary>
|
||||
public string? BankAccount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用电子普通发票。
|
||||
/// </summary>
|
||||
public bool EnableElectronicNormalInvoice { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用电子专用发票。
|
||||
/// </summary>
|
||||
public bool EnableElectronicSpecialInvoice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用自动开票。
|
||||
/// </summary>
|
||||
public bool EnableAutoIssue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自动开票单张最大金额。
|
||||
/// </summary>
|
||||
public decimal AutoIssueMaxAmount { get; set; } = 10_000m;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Domain.Tenants.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 租户发票状态。
|
||||
/// </summary>
|
||||
public enum TenantInvoiceStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 待开票。
|
||||
/// </summary>
|
||||
Pending = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 已开票。
|
||||
/// </summary>
|
||||
Issued = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 已作废。
|
||||
/// </summary>
|
||||
Voided = 3
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TakeoutSaaS.Domain.Tenants.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 租户发票类型。
|
||||
/// </summary>
|
||||
public enum TenantInvoiceType
|
||||
{
|
||||
/// <summary>
|
||||
/// 电子普通发票。
|
||||
/// </summary>
|
||||
Normal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 电子专用发票。
|
||||
/// </summary>
|
||||
Special = 2
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 租户发票仓储契约。
|
||||
/// </summary>
|
||||
public interface ITenantInvoiceRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 查询租户发票设置。
|
||||
/// </summary>
|
||||
Task<TenantInvoiceSetting?> GetSettingAsync(long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增发票设置。
|
||||
/// </summary>
|
||||
Task AddSettingAsync(TenantInvoiceSetting entity, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新发票设置。
|
||||
/// </summary>
|
||||
Task UpdateSettingAsync(TenantInvoiceSetting entity, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询发票记录。
|
||||
/// </summary>
|
||||
Task<(IReadOnlyList<TenantInvoiceRecord> Items, int TotalCount)> SearchRecordsAsync(
|
||||
long tenantId,
|
||||
DateTime? startUtc,
|
||||
DateTime? endUtc,
|
||||
TenantInvoiceStatus? status,
|
||||
TenantInvoiceType? invoiceType,
|
||||
string? keyword,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取发票页统计。
|
||||
/// </summary>
|
||||
Task<TenantInvoiceRecordStatsSnapshot> GetStatsAsync(
|
||||
long tenantId,
|
||||
DateTime nowUtc,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据标识查询发票记录。
|
||||
/// </summary>
|
||||
Task<TenantInvoiceRecord?> FindRecordByIdAsync(
|
||||
long tenantId,
|
||||
long recordId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 判断租户下发票号码是否已存在。
|
||||
/// </summary>
|
||||
Task<bool> ExistsInvoiceNoAsync(
|
||||
long tenantId,
|
||||
string invoiceNo,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增发票记录。
|
||||
/// </summary>
|
||||
Task AddRecordAsync(TenantInvoiceRecord entity, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新发票记录。
|
||||
/// </summary>
|
||||
Task UpdateRecordAsync(TenantInvoiceRecord entity, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发票页面统计快照。
|
||||
/// </summary>
|
||||
public sealed record TenantInvoiceRecordStatsSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 本月已开票金额。
|
||||
/// </summary>
|
||||
public decimal CurrentMonthIssuedAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 本月已开票张数。
|
||||
/// </summary>
|
||||
public int CurrentMonthIssuedCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 待开票张数。
|
||||
/// </summary>
|
||||
public int PendingCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 已作废张数。
|
||||
/// </summary>
|
||||
public int VoidedCount { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user