feat: profit sharing
Build & Deploy iOS to TestFlight / build-and-deploy (push) Canceled after 0s

This commit is contained in:
efrilm
2026-08-21 22:31:54 +07:00
parent f9bfb69254
commit 2e4c77888e
48 changed files with 12553 additions and 162 deletions
+1
View File
@@ -13,4 +13,5 @@ part 'entities/product_analytic_entity.dart';
part 'entities/payment_method_analytic_entity.dart';
part 'entities/purchasing_analytic_entity.dart';
part 'entities/exclusive_summary_entity.dart';
part 'entities/profit_sharing_entity.dart';
part 'failures/analytic_failure.dart';
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,219 @@
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,
}) = _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<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: [],
);
}
@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<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;
}
@@ -57,4 +57,18 @@ abstract class IAnalyticRepository {
required DateTime dateTo,
String? outletId,
});
Future<Either<AnalyticFailure, ProfitSharing>> getProfitSharing({
required DateTime dateFrom,
required DateTime dateTo,
String? outletId,
String groupBy = 'day',
});
Future<Either<AnalyticFailure, ProfitSharingDetail>> getProfitSharingDetail({
required String parentCategoryId,
required DateTime dateFrom,
required DateTime dateTo,
String? outletId,
});
}