feat(finance): add cost management backend module
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 成本录入月度汇总实体(按维度 + 分类)。
|
||||
/// </summary>
|
||||
public sealed class FinanceCostEntry : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 统计维度。
|
||||
/// </summary>
|
||||
public FinanceCostDimension Dimension { get; set; } = FinanceCostDimension.Tenant;
|
||||
|
||||
/// <summary>
|
||||
/// 门店标识(租户汇总维度为空)。
|
||||
/// </summary>
|
||||
public long? StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本月份(统一存储为 UTC 每月第一天 00:00:00)。
|
||||
/// </summary>
|
||||
public DateTime CostMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public FinanceCostCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分类总金额。
|
||||
/// </summary>
|
||||
public decimal TotalAmount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 成本录入明细项实体。
|
||||
/// </summary>
|
||||
public sealed class FinanceCostEntryItem : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 关联汇总行标识。
|
||||
/// </summary>
|
||||
public long EntryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 统计维度。
|
||||
/// </summary>
|
||||
public FinanceCostDimension Dimension { get; set; } = FinanceCostDimension.Tenant;
|
||||
|
||||
/// <summary>
|
||||
/// 门店标识(租户汇总维度为空)。
|
||||
/// </summary>
|
||||
public long? StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本月份(统一存储为 UTC 每月第一天 00:00:00)。
|
||||
/// </summary>
|
||||
public DateTime CostMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public FinanceCostCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 明细名称。
|
||||
/// </summary>
|
||||
public string ItemName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 明细金额。
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量(人工类可用)。
|
||||
/// </summary>
|
||||
public decimal? Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单价(人工类可用)。
|
||||
/// </summary>
|
||||
public decimal? UnitPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序值。
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; } = 100;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public enum FinanceCostCategory
|
||||
{
|
||||
/// <summary>
|
||||
/// 食材原料。
|
||||
/// </summary>
|
||||
FoodMaterial = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 人工成本。
|
||||
/// </summary>
|
||||
Labor = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 固定费用。
|
||||
/// </summary>
|
||||
FixedExpense = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 包装耗材。
|
||||
/// </summary>
|
||||
PackagingConsumable = 4
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 成本统计维度。
|
||||
/// </summary>
|
||||
public enum FinanceCostDimension
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户汇总维度。
|
||||
/// </summary>
|
||||
Tenant = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 门店维度。
|
||||
/// </summary>
|
||||
Store = 2
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 成本明细项快照。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostDetailItemSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 明细标识。
|
||||
/// </summary>
|
||||
public long? ItemId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 明细名称。
|
||||
/// </summary>
|
||||
public required string ItemName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 明细金额。
|
||||
/// </summary>
|
||||
public decimal Amount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量(人工类可用)。
|
||||
/// </summary>
|
||||
public decimal? Quantity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 单价(人工类可用)。
|
||||
/// </summary>
|
||||
public decimal? UnitPrice { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序值。
|
||||
/// </summary>
|
||||
public int SortOrder { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类快照。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostCategorySnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 成本分类。
|
||||
/// </summary>
|
||||
public required FinanceCostCategory Category { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 分类总金额。
|
||||
/// </summary>
|
||||
public decimal TotalAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 分类明细。
|
||||
/// </summary>
|
||||
public IReadOnlyList<FinanceCostDetailItemSnapshot> Items { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 成本录入页快照。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostMonthSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 统计维度。
|
||||
/// </summary>
|
||||
public required FinanceCostDimension Dimension { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 门店标识(租户维度为空)。
|
||||
/// </summary>
|
||||
public long? StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本月份。
|
||||
/// </summary>
|
||||
public required DateTime CostMonth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 本月营业额。
|
||||
/// </summary>
|
||||
public decimal MonthRevenue { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 成本分类集合。
|
||||
/// </summary>
|
||||
public IReadOnlyList<FinanceCostCategorySnapshot> Categories { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 月度趋势行。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostTrendSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 月份起始时间(UTC)。
|
||||
/// </summary>
|
||||
public required DateTime MonthStartUtc { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 月度总成本。
|
||||
/// </summary>
|
||||
public decimal TotalCost { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 月度营业额。
|
||||
/// </summary>
|
||||
public decimal Revenue { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 月度成本明细表行。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostMonthlyDetailSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 月份起始时间(UTC)。
|
||||
/// </summary>
|
||||
public required DateTime MonthStartUtc { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 食材成本。
|
||||
/// </summary>
|
||||
public decimal FoodAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 人工成本。
|
||||
/// </summary>
|
||||
public decimal LaborAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 固定费用。
|
||||
/// </summary>
|
||||
public decimal FixedAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 包装耗材。
|
||||
/// </summary>
|
||||
public decimal PackagingAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 月度总成本。
|
||||
/// </summary>
|
||||
public decimal TotalCost { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 月度营业额。
|
||||
/// </summary>
|
||||
public decimal Revenue { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 成本分析快照。
|
||||
/// </summary>
|
||||
public sealed record FinanceCostAnalysisSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// 统计维度。
|
||||
/// </summary>
|
||||
public required FinanceCostDimension Dimension { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 门店标识(租户维度为空)。
|
||||
/// </summary>
|
||||
public long? StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前月份。
|
||||
/// </summary>
|
||||
public required DateTime CostMonth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前月总成本。
|
||||
/// </summary>
|
||||
public decimal CurrentTotalCost { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前月食材成本。
|
||||
/// </summary>
|
||||
public decimal CurrentFoodAmount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前月营业额。
|
||||
/// </summary>
|
||||
public decimal CurrentRevenue { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前月支付成功订单数。
|
||||
/// </summary>
|
||||
public int CurrentPaidOrderCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 环比变化率(%)。
|
||||
/// </summary>
|
||||
public decimal MonthOnMonthChangeRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 分类构成。
|
||||
/// </summary>
|
||||
public IReadOnlyList<FinanceCostCategorySnapshot> CurrentCategories { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 近 N 月趋势。
|
||||
/// </summary>
|
||||
public IReadOnlyList<FinanceCostTrendSnapshot> Trends { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 明细表数据。
|
||||
/// </summary>
|
||||
public IReadOnlyList<FinanceCostMonthlyDetailSnapshot> DetailRows { get; init; } = [];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using TakeoutSaaS.Domain.Finance.Enums;
|
||||
using TakeoutSaaS.Domain.Finance.Models;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Finance.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 成本管理仓储契约。
|
||||
/// </summary>
|
||||
public interface IFinanceCostRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取成本录入页月度快照。
|
||||
/// </summary>
|
||||
Task<FinanceCostMonthSnapshot> GetMonthSnapshotAsync(
|
||||
long tenantId,
|
||||
FinanceCostDimension dimension,
|
||||
long? storeId,
|
||||
DateTime costMonth,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 保存月度成本录入快照。
|
||||
/// </summary>
|
||||
Task SaveMonthSnapshotAsync(
|
||||
long tenantId,
|
||||
FinanceCostDimension dimension,
|
||||
long? storeId,
|
||||
DateTime costMonth,
|
||||
IReadOnlyList<FinanceCostCategorySnapshot> categories,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取成本分析页快照。
|
||||
/// </summary>
|
||||
Task<FinanceCostAnalysisSnapshot> GetAnalysisSnapshotAsync(
|
||||
long tenantId,
|
||||
FinanceCostDimension dimension,
|
||||
long? storeId,
|
||||
DateTime costMonth,
|
||||
int trendMonthCount,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user