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, }) = _ProfitSharingCategory; factory ProfitSharingCategory.empty() => const ProfitSharingCategory( parentCategoryId: '', parentCategoryName: '', totalRevenue: 0, totalQuantity: 0, categoryCount: 0, productCount: 0, orderCount: 0, totalStandardHpp: 0, totalFifoHpp: 0, totalMovingAverageHpp: 0, ); /// 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; } @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 limitOwner, 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, limitOwner: 0, limitTeam: 0, ); bool get hasRevenue => revenue > 0; 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; }