248 lines
7.8 KiB
Dart
248 lines
7.8 KiB
Dart
part of '../analytic.dart';
|
||
|
||
/// Bagi hasil (profit sharing) berdasarkan omzet per parent category.
|
||
@freezed
|
||
class ProfitSharing with _$ProfitSharing {
|
||
const factory ProfitSharing({
|
||
required String organizationId,
|
||
required String outletId,
|
||
required String outletName,
|
||
required DateTime dateFrom,
|
||
required DateTime dateTo,
|
||
required List<ProfitSharingCategory> categories,
|
||
required ProfitSharingBudget budget,
|
||
}) = _ProfitSharing;
|
||
|
||
factory ProfitSharing.empty() => ProfitSharing(
|
||
organizationId: '',
|
||
outletId: '',
|
||
outletName: '',
|
||
dateFrom: DateTime.fromMillisecondsSinceEpoch(0),
|
||
dateTo: DateTime.fromMillisecondsSinceEpoch(0),
|
||
categories: [],
|
||
budget: ProfitSharingBudget.empty(),
|
||
);
|
||
}
|
||
|
||
@freezed
|
||
class ProfitSharingCategory with _$ProfitSharingCategory {
|
||
const ProfitSharingCategory._();
|
||
|
||
const factory ProfitSharingCategory({
|
||
required String parentCategoryId,
|
||
required String parentCategoryName,
|
||
required int totalRevenue,
|
||
required int totalQuantity,
|
||
required int categoryCount,
|
||
required int productCount,
|
||
required int orderCount,
|
||
required int totalStandardHpp,
|
||
required int totalFifoHpp,
|
||
required int totalMovingAverageHpp,
|
||
@Default(0) double ownerFeePercent,
|
||
@Default(0) int sdl,
|
||
@Default(ProfitSharingTopProduct()) ProfitSharingTopProduct topProduct,
|
||
}) = _ProfitSharingCategory;
|
||
|
||
factory ProfitSharingCategory.empty() => const ProfitSharingCategory(
|
||
parentCategoryId: '',
|
||
parentCategoryName: '',
|
||
totalRevenue: 0,
|
||
totalQuantity: 0,
|
||
categoryCount: 0,
|
||
productCount: 0,
|
||
orderCount: 0,
|
||
totalStandardHpp: 0,
|
||
totalFifoHpp: 0,
|
||
totalMovingAverageHpp: 0,
|
||
);
|
||
|
||
bool get hasTopProduct => topProduct.productId.isNotEmpty;
|
||
|
||
/// Porsi tim = omzet − SDL. Response tidak membawa porsi tim per kategori,
|
||
/// jadi dihitung dari nominalnya (docs/migrasi-profit-sharing.md).
|
||
int get teamShare => totalRevenue - sdl;
|
||
|
||
/// % porsi tim terhadap omzet, yaitu 100 − fee owner.
|
||
double get teamPercent => 100 - ownerFeePercent;
|
||
|
||
/// Laba kotor memakai HPP standar (acuan yang dipakai di laporan laba rugi).
|
||
int get grossProfit => totalRevenue - totalStandardHpp;
|
||
|
||
double get grossMargin =>
|
||
totalRevenue == 0 ? 0 : (grossProfit / totalRevenue) * 100;
|
||
|
||
/// % HPP standar terhadap omzet.
|
||
double get standardHppPercentage =>
|
||
totalRevenue == 0 ? 0 : (totalStandardHpp / totalRevenue) * 100;
|
||
|
||
/// % HPP riil (FIFO) terhadap omzet — dipakai untuk status kesehatan margin.
|
||
double get realHppPercentage =>
|
||
totalRevenue == 0 ? 0 : (totalFifoHpp / totalRevenue) * 100;
|
||
}
|
||
|
||
/// Produk dengan omzet tertinggi di satu kategori induk.
|
||
@freezed
|
||
class ProfitSharingTopProduct with _$ProfitSharingTopProduct {
|
||
const factory ProfitSharingTopProduct({
|
||
@Default('') String productId,
|
||
@Default('') String productName,
|
||
@Default(0) int quantitySold,
|
||
@Default(0) int revenue,
|
||
}) = _ProfitSharingTopProduct;
|
||
}
|
||
|
||
@freezed
|
||
class ProfitSharingBudget with _$ProfitSharingBudget {
|
||
const factory ProfitSharingBudget({
|
||
required ProfitSharingPercentage percentages,
|
||
required DateTime cutOffFrom,
|
||
required DateTime cutOffTo,
|
||
required ProfitSharingPeriod total,
|
||
required List<ProfitSharingPeriod> weekly,
|
||
required List<ProfitSharingPeriod> monthly,
|
||
}) = _ProfitSharingBudget;
|
||
|
||
factory ProfitSharingBudget.empty() => ProfitSharingBudget(
|
||
percentages: ProfitSharingPercentage.empty(),
|
||
cutOffFrom: DateTime.fromMillisecondsSinceEpoch(0),
|
||
cutOffTo: DateTime.fromMillisecondsSinceEpoch(0),
|
||
total: ProfitSharingPeriod.empty(),
|
||
weekly: [],
|
||
monthly: [],
|
||
);
|
||
}
|
||
|
||
/// Porsi bawaan bagi hasil: owner (SDL) dan tim, dengan tim = 100 − owner.
|
||
@freezed
|
||
class ProfitSharingPercentage with _$ProfitSharingPercentage {
|
||
const factory ProfitSharingPercentage({
|
||
required double owner,
|
||
required double team,
|
||
}) = _ProfitSharingPercentage;
|
||
|
||
factory ProfitSharingPercentage.empty() =>
|
||
const ProfitSharingPercentage(owner: 0, team: 0);
|
||
}
|
||
|
||
/// Satu baris periode bagi hasil. Dipakai untuk total, mingguan, dan bulanan.
|
||
/// [month] dan [weekCount] hanya terisi untuk data bulanan.
|
||
@freezed
|
||
class ProfitSharingPeriod with _$ProfitSharingPeriod {
|
||
const ProfitSharingPeriod._();
|
||
|
||
const factory ProfitSharingPeriod({
|
||
required DateTime periodStart,
|
||
required DateTime periodEnd,
|
||
required int revenue,
|
||
required int orderCount,
|
||
required int sdl,
|
||
required int limitTeam,
|
||
@Default('') String month,
|
||
@Default(0) int weekCount,
|
||
}) = _ProfitSharingPeriod;
|
||
|
||
factory ProfitSharingPeriod.empty() => ProfitSharingPeriod(
|
||
periodStart: DateTime.fromMillisecondsSinceEpoch(0),
|
||
periodEnd: DateTime.fromMillisecondsSinceEpoch(0),
|
||
revenue: 0,
|
||
orderCount: 0,
|
||
sdl: 0,
|
||
limitTeam: 0,
|
||
);
|
||
|
||
bool get hasRevenue => revenue > 0;
|
||
|
||
/// % SDL (bagian owner) terhadap omzet. Fee owner berbeda tiap kategori
|
||
/// induk, jadi porsinya dihitung dari nominal, bukan dari persentase tetap.
|
||
double get sdlPercentage => revenue == 0 ? 0 : (sdl / revenue) * 100;
|
||
|
||
/// % porsi tim terhadap omzet, dihitung dari nominal dengan alasan yang sama.
|
||
double get teamPercentage => revenue == 0 ? 0 : (limitTeam / revenue) * 100;
|
||
|
||
int get averageOrderValue => orderCount == 0 ? 0 : revenue ~/ orderCount;
|
||
}
|
||
|
||
/// Rincian satu kategori induk: ringkasan, sub kategori beserta produknya,
|
||
/// dan bagi hasil khusus kategori tersebut.
|
||
@freezed
|
||
class ProfitSharingDetail with _$ProfitSharingDetail {
|
||
const factory ProfitSharingDetail({
|
||
required String organizationId,
|
||
required String outletId,
|
||
required String outletName,
|
||
required DateTime dateFrom,
|
||
required DateTime dateTo,
|
||
required String parentCategoryId,
|
||
required String parentCategoryName,
|
||
required ProfitSharingCategory summary,
|
||
required List<ProfitSharingSubCategory> categories,
|
||
required ProfitSharingBudget budget,
|
||
}) = _ProfitSharingDetail;
|
||
|
||
factory ProfitSharingDetail.empty() => ProfitSharingDetail(
|
||
organizationId: '',
|
||
outletId: '',
|
||
outletName: '',
|
||
dateFrom: DateTime.fromMillisecondsSinceEpoch(0),
|
||
dateTo: DateTime.fromMillisecondsSinceEpoch(0),
|
||
parentCategoryId: '',
|
||
parentCategoryName: '',
|
||
summary: ProfitSharingCategory.empty(),
|
||
categories: [],
|
||
budget: ProfitSharingBudget.empty(),
|
||
);
|
||
}
|
||
|
||
@freezed
|
||
class ProfitSharingSubCategory with _$ProfitSharingSubCategory {
|
||
const ProfitSharingSubCategory._();
|
||
|
||
const factory ProfitSharingSubCategory({
|
||
required String categoryId,
|
||
required String categoryName,
|
||
required int totalRevenue,
|
||
required int totalQuantity,
|
||
required int productCount,
|
||
required int orderCount,
|
||
required int totalStandardHpp,
|
||
required int totalFifoHpp,
|
||
required int totalMovingAverageHpp,
|
||
required List<ProfitSharingProduct> products,
|
||
}) = _ProfitSharingSubCategory;
|
||
|
||
double get standardHppPercentage =>
|
||
totalRevenue == 0 ? 0 : (totalStandardHpp / totalRevenue) * 100;
|
||
|
||
double get realHppPercentage =>
|
||
totalRevenue == 0 ? 0 : (totalFifoHpp / totalRevenue) * 100;
|
||
}
|
||
|
||
@freezed
|
||
class ProfitSharingProduct with _$ProfitSharingProduct {
|
||
const ProfitSharingProduct._();
|
||
|
||
const factory ProfitSharingProduct({
|
||
required String productId,
|
||
required String productName,
|
||
required String productSku,
|
||
required int productPrice,
|
||
required int quantitySold,
|
||
required int revenue,
|
||
required double averagePrice,
|
||
required int orderCount,
|
||
required double standardHppPerUnit,
|
||
required int standardHppTotal,
|
||
required double fifoHppPerUnit,
|
||
required int fifoHppTotal,
|
||
required double movingAverageHppPerUnit,
|
||
required int movingAverageHppTotal,
|
||
}) = _ProfitSharingProduct;
|
||
|
||
double get standardHppPercentage =>
|
||
revenue == 0 ? 0 : (standardHppTotal / revenue) * 100;
|
||
|
||
double get realHppPercentage =>
|
||
revenue == 0 ? 0 : (fifoHppTotal / revenue) * 100;
|
||
}
|