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 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; /// 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 weekly, required List monthly, }) = _ProfitSharingBudget; factory ProfitSharingBudget.empty() => ProfitSharingBudget( percentages: ProfitSharingPercentage.empty(), cutOffFrom: DateTime.fromMillisecondsSinceEpoch(0), cutOffTo: DateTime.fromMillisecondsSinceEpoch(0), total: ProfitSharingPeriod.empty(), weekly: [], monthly: [], ); } @freezed class ProfitSharingPercentage with _$ProfitSharingPercentage { const factory ProfitSharingPercentage({ required double purchase, required double owner, required double team, }) = _ProfitSharingPercentage; factory ProfitSharingPercentage.empty() => const ProfitSharingPercentage(purchase: 0, 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 limitPurchase, 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, limitPurchase: 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; 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 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 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; }