feat: exclusive summary
Build & Deploy iOS to TestFlight / build-and-deploy (push) Has been cancelled

This commit is contained in:
efrilm
2026-06-22 14:44:03 +07:00
parent 0b70194d8e
commit e236d811ce
26 changed files with 6510 additions and 161 deletions
@@ -13,3 +13,4 @@ part 'dto/dashboard_analytic_dto.dart';
part 'dto/product_analytic_dto.dart';
part 'dto/payment_method_analytic_dto.dart';
part 'dto/purchasing_analytic_dto.dart';
part 'dto/exclusive_summary_dto.dart';
File diff suppressed because it is too large Load Diff
@@ -789,3 +789,185 @@ Map<String, dynamic> _$$PurchasingVendorDataDtoImplToJson(
'ingredient_count': instance.ingredientCount,
'quantity': instance.quantity,
};
_$ExclusiveSummaryDtoImpl _$$ExclusiveSummaryDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummaryDtoImpl(
organizationId: json['organization_id'] as String?,
outletId: json['outlet_id'] as String?,
period: json['period'] == null
? null
: ExclusiveSummaryPeriodDto.fromJson(
json['period'] as Map<String, dynamic>,
),
summary: json['summary'] == null
? null
: ExclusiveSummarySummaryDto.fromJson(
json['summary'] as Map<String, dynamic>,
),
reimburse: json['reimburse'] == null
? null
: ExclusiveSummaryReimburseDto.fromJson(
json['reimburse'] as Map<String, dynamic>,
),
hppBreakdown: (json['hpp_breakdown'] as List<dynamic>?)
?.map(
(e) => ExclusiveSummaryBreakdownDto.fromJson(e as Map<String, dynamic>),
)
.toList(),
operationalExpenseBreakdown:
(json['operational_expense_breakdown'] as List<dynamic>?)
?.map(
(e) => ExclusiveSummaryBreakdownDto.fromJson(
e as Map<String, dynamic>,
),
)
.toList(),
dailySummary: (json['daily_summary'] as List<dynamic>?)
?.map((e) => ExclusiveSummaryDailyDto.fromJson(e as Map<String, dynamic>))
.toList(),
dailyTransactions: (json['daily_transactions'] as List<dynamic>?)
?.map(
(e) =>
ExclusiveSummaryTransactionDto.fromJson(e as Map<String, dynamic>),
)
.toList(),
);
Map<String, dynamic> _$$ExclusiveSummaryDtoImplToJson(
_$ExclusiveSummaryDtoImpl instance,
) => <String, dynamic>{
'organization_id': instance.organizationId,
'outlet_id': instance.outletId,
'period': instance.period,
'summary': instance.summary,
'reimburse': instance.reimburse,
'hpp_breakdown': instance.hppBreakdown,
'operational_expense_breakdown': instance.operationalExpenseBreakdown,
'daily_summary': instance.dailySummary,
'daily_transactions': instance.dailyTransactions,
};
_$ExclusiveSummaryPeriodDtoImpl _$$ExclusiveSummaryPeriodDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummaryPeriodDtoImpl(
dateFrom: json['date_from'] == null
? null
: DateTime.parse(json['date_from'] as String),
dateTo: json['date_to'] == null
? null
: DateTime.parse(json['date_to'] as String),
);
Map<String, dynamic> _$$ExclusiveSummaryPeriodDtoImplToJson(
_$ExclusiveSummaryPeriodDtoImpl instance,
) => <String, dynamic>{
'date_from': instance.dateFrom?.toIso8601String(),
'date_to': instance.dateTo?.toIso8601String(),
};
_$ExclusiveSummarySummaryDtoImpl _$$ExclusiveSummarySummaryDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummarySummaryDtoImpl(
sales: json['sales'] as num?,
hpp: json['hpp'] as num?,
grossProfit: json['gross_profit'] as num?,
salaryTotal: json['salary_total'] as num?,
salaryDw: json['salary_dw'] as num?,
salaryStaff: json['salary_staff'] as num?,
salaryOther: json['salary_other'] as num?,
otherOperationalExpenses: json['other_operational_expenses'] as num?,
operationalExpensesTotal: json['operational_expenses_total'] as num?,
totalCost: json['total_cost'] as num?,
netProfit: json['net_profit'] as num?,
);
Map<String, dynamic> _$$ExclusiveSummarySummaryDtoImplToJson(
_$ExclusiveSummarySummaryDtoImpl instance,
) => <String, dynamic>{
'sales': instance.sales,
'hpp': instance.hpp,
'gross_profit': instance.grossProfit,
'salary_total': instance.salaryTotal,
'salary_dw': instance.salaryDw,
'salary_staff': instance.salaryStaff,
'salary_other': instance.salaryOther,
'other_operational_expenses': instance.otherOperationalExpenses,
'operational_expenses_total': instance.operationalExpensesTotal,
'total_cost': instance.totalCost,
'net_profit': instance.netProfit,
};
_$ExclusiveSummaryReimburseDtoImpl _$$ExclusiveSummaryReimburseDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummaryReimburseDtoImpl(
totalCost: json['total_cost'] as num?,
excludedSalaryStaff: json['excluded_salary_staff'] as num?,
totalReimburse: json['total_reimburse'] as num?,
);
Map<String, dynamic> _$$ExclusiveSummaryReimburseDtoImplToJson(
_$ExclusiveSummaryReimburseDtoImpl instance,
) => <String, dynamic>{
'total_cost': instance.totalCost,
'excluded_salary_staff': instance.excludedSalaryStaff,
'total_reimburse': instance.totalReimburse,
};
_$ExclusiveSummaryBreakdownDtoImpl _$$ExclusiveSummaryBreakdownDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummaryBreakdownDtoImpl(
categoryCode: json['category_code'] as String?,
categoryName: json['category_name'] as String?,
amount: json['amount'] as num?,
percentage: json['percentage'] as num?,
);
Map<String, dynamic> _$$ExclusiveSummaryBreakdownDtoImplToJson(
_$ExclusiveSummaryBreakdownDtoImpl instance,
) => <String, dynamic>{
'category_code': instance.categoryCode,
'category_name': instance.categoryName,
'amount': instance.amount,
'percentage': instance.percentage,
};
_$ExclusiveSummaryDailyDtoImpl _$$ExclusiveSummaryDailyDtoImplFromJson(
Map<String, dynamic> json,
) => _$ExclusiveSummaryDailyDtoImpl(
date: json['date'] == null ? null : DateTime.parse(json['date'] as String),
transactionCount: json['transaction_count'] as num?,
totalCost: json['total_cost'] as num?,
);
Map<String, dynamic> _$$ExclusiveSummaryDailyDtoImplToJson(
_$ExclusiveSummaryDailyDtoImpl instance,
) => <String, dynamic>{
'date': instance.date?.toIso8601String(),
'transaction_count': instance.transactionCount,
'total_cost': instance.totalCost,
};
_$ExclusiveSummaryTransactionDtoImpl
_$$ExclusiveSummaryTransactionDtoImplFromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryTransactionDtoImpl(
date: json['date'] == null
? null
: DateTime.parse(json['date'] as String),
categoryCode: json['category_code'] as String?,
categoryName: json['category_name'] as String?,
description: json['description'] as String?,
amount: json['amount'] as num?,
source: json['source'] as String?,
);
Map<String, dynamic> _$$ExclusiveSummaryTransactionDtoImplToJson(
_$ExclusiveSummaryTransactionDtoImpl instance,
) => <String, dynamic>{
'date': instance.date?.toIso8601String(),
'category_code': instance.categoryCode,
'category_name': instance.categoryName,
'description': instance.description,
'amount': instance.amount,
'source': instance.source,
};
@@ -264,4 +264,35 @@ class AnalyticRemoteDataProvider {
return DC.error(AnalyticFailure.serverError(e));
}
}
Future<DC<AnalyticFailure, ExclusiveSummaryDto>> fetchExclusiveSummary({
required DateTime dateFrom,
required DateTime dateTo,
String? outletId,
}) async {
try {
final Map<String, dynamic> params = {
'date_from': dateFrom.toServerDate,
'date_to': dateTo.toServerDate,
};
if (outletId != null) params['outlet_id'] = outletId;
final response = await _apiClient.get(
ApiPath.exclusiveSummaryAnalytic,
params: params,
headers: getAuthorizationHeader(),
);
if (response.data['data'] == null) {
return DC.error(AnalyticFailure.empty());
}
final dto = ExclusiveSummaryDto.fromJson(response.data['data']);
return DC.data(dto);
} on ApiFailure catch (e, s) {
log('fetchExclusiveSummaryError', name: _logName, error: e, stackTrace: s);
return DC.error(AnalyticFailure.serverError(e));
}
}
}
@@ -0,0 +1,181 @@
part of '../analytic_dtos.dart';
@freezed
class ExclusiveSummaryDto with _$ExclusiveSummaryDto {
const ExclusiveSummaryDto._();
const factory ExclusiveSummaryDto({
@JsonKey(name: 'organization_id') String? organizationId,
@JsonKey(name: 'outlet_id') String? outletId,
@JsonKey(name: 'period') ExclusiveSummaryPeriodDto? period,
@JsonKey(name: 'summary') ExclusiveSummarySummaryDto? summary,
@JsonKey(name: 'reimburse') ExclusiveSummaryReimburseDto? reimburse,
@JsonKey(name: 'hpp_breakdown')
List<ExclusiveSummaryBreakdownDto>? hppBreakdown,
@JsonKey(name: 'operational_expense_breakdown')
List<ExclusiveSummaryBreakdownDto>? operationalExpenseBreakdown,
@JsonKey(name: 'daily_summary')
List<ExclusiveSummaryDailyDto>? dailySummary,
@JsonKey(name: 'daily_transactions')
List<ExclusiveSummaryTransactionDto>? dailyTransactions,
}) = _ExclusiveSummaryDto;
factory ExclusiveSummaryDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryDtoFromJson(json);
ExclusiveSummary toDomain() => ExclusiveSummary(
organizationId: organizationId ?? '',
outletId: outletId ?? '',
period: period?.toDomain() ?? ExclusiveSummaryPeriod.empty(),
summary: summary?.toDomain() ?? ExclusiveSummarySummary.empty(),
reimburse: reimburse?.toDomain() ?? ExclusiveSummaryReimburse.empty(),
hppBreakdown: hppBreakdown?.map((e) => e.toDomain()).toList() ?? [],
operationalExpenseBreakdown:
operationalExpenseBreakdown?.map((e) => e.toDomain()).toList() ?? [],
dailySummary: dailySummary?.map((e) => e.toDomain()).toList() ?? [],
dailyTransactions:
dailyTransactions?.map((e) => e.toDomain()).toList() ?? [],
);
}
@freezed
class ExclusiveSummaryPeriodDto with _$ExclusiveSummaryPeriodDto {
const ExclusiveSummaryPeriodDto._();
const factory ExclusiveSummaryPeriodDto({
@JsonKey(name: 'date_from') DateTime? dateFrom,
@JsonKey(name: 'date_to') DateTime? dateTo,
}) = _ExclusiveSummaryPeriodDto;
factory ExclusiveSummaryPeriodDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryPeriodDtoFromJson(json);
ExclusiveSummaryPeriod toDomain() => ExclusiveSummaryPeriod(
dateFrom: dateFrom ?? DateTime.fromMillisecondsSinceEpoch(0),
dateTo: dateTo ?? DateTime.fromMillisecondsSinceEpoch(0),
);
}
@freezed
class ExclusiveSummarySummaryDto with _$ExclusiveSummarySummaryDto {
const ExclusiveSummarySummaryDto._();
const factory ExclusiveSummarySummaryDto({
@JsonKey(name: 'sales') num? sales,
@JsonKey(name: 'hpp') num? hpp,
@JsonKey(name: 'gross_profit') num? grossProfit,
@JsonKey(name: 'salary_total') num? salaryTotal,
@JsonKey(name: 'salary_dw') num? salaryDw,
@JsonKey(name: 'salary_staff') num? salaryStaff,
@JsonKey(name: 'salary_other') num? salaryOther,
@JsonKey(name: 'other_operational_expenses') num? otherOperationalExpenses,
@JsonKey(name: 'operational_expenses_total') num? operationalExpensesTotal,
@JsonKey(name: 'total_cost') num? totalCost,
@JsonKey(name: 'net_profit') num? netProfit,
}) = _ExclusiveSummarySummaryDto;
factory ExclusiveSummarySummaryDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummarySummaryDtoFromJson(json);
ExclusiveSummarySummary toDomain() => ExclusiveSummarySummary(
sales: sales?.toInt() ?? 0,
hpp: hpp?.toInt() ?? 0,
grossProfit: grossProfit?.toInt() ?? 0,
salaryTotal: salaryTotal?.toInt() ?? 0,
salaryDw: salaryDw?.toInt() ?? 0,
salaryStaff: salaryStaff?.toInt() ?? 0,
salaryOther: salaryOther?.toInt() ?? 0,
otherOperationalExpenses: otherOperationalExpenses?.toInt() ?? 0,
operationalExpensesTotal: operationalExpensesTotal?.toInt() ?? 0,
totalCost: totalCost?.toInt() ?? 0,
netProfit: netProfit?.toInt() ?? 0,
);
}
@freezed
class ExclusiveSummaryReimburseDto with _$ExclusiveSummaryReimburseDto {
const ExclusiveSummaryReimburseDto._();
const factory ExclusiveSummaryReimburseDto({
@JsonKey(name: 'total_cost') num? totalCost,
@JsonKey(name: 'excluded_salary_staff') num? excludedSalaryStaff,
@JsonKey(name: 'total_reimburse') num? totalReimburse,
}) = _ExclusiveSummaryReimburseDto;
factory ExclusiveSummaryReimburseDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryReimburseDtoFromJson(json);
ExclusiveSummaryReimburse toDomain() => ExclusiveSummaryReimburse(
totalCost: totalCost?.toInt() ?? 0,
excludedSalaryStaff: excludedSalaryStaff?.toInt() ?? 0,
totalReimburse: totalReimburse?.toInt() ?? 0,
);
}
@freezed
class ExclusiveSummaryBreakdownDto with _$ExclusiveSummaryBreakdownDto {
const ExclusiveSummaryBreakdownDto._();
const factory ExclusiveSummaryBreakdownDto({
@JsonKey(name: 'category_code') String? categoryCode,
@JsonKey(name: 'category_name') String? categoryName,
@JsonKey(name: 'amount') num? amount,
@JsonKey(name: 'percentage') num? percentage,
}) = _ExclusiveSummaryBreakdownDto;
factory ExclusiveSummaryBreakdownDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryBreakdownDtoFromJson(json);
ExclusiveSummaryBreakdown toDomain() => ExclusiveSummaryBreakdown(
categoryCode: categoryCode ?? '',
categoryName: categoryName ?? '',
amount: amount?.toInt() ?? 0,
percentage: percentage?.toDouble() ?? 0,
);
}
@freezed
class ExclusiveSummaryDailyDto with _$ExclusiveSummaryDailyDto {
const ExclusiveSummaryDailyDto._();
const factory ExclusiveSummaryDailyDto({
@JsonKey(name: 'date') DateTime? date,
@JsonKey(name: 'transaction_count') num? transactionCount,
@JsonKey(name: 'total_cost') num? totalCost,
}) = _ExclusiveSummaryDailyDto;
factory ExclusiveSummaryDailyDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryDailyDtoFromJson(json);
ExclusiveSummaryDaily toDomain() => ExclusiveSummaryDaily(
date: date ?? DateTime.fromMillisecondsSinceEpoch(0),
transactionCount: transactionCount?.toInt() ?? 0,
totalCost: totalCost?.toInt() ?? 0,
);
}
@freezed
class ExclusiveSummaryTransactionDto with _$ExclusiveSummaryTransactionDto {
const ExclusiveSummaryTransactionDto._();
const factory ExclusiveSummaryTransactionDto({
@JsonKey(name: 'date') DateTime? date,
@JsonKey(name: 'category_code') String? categoryCode,
@JsonKey(name: 'category_name') String? categoryName,
@JsonKey(name: 'description') String? description,
@JsonKey(name: 'amount') num? amount,
@JsonKey(name: 'source') String? source,
}) = _ExclusiveSummaryTransactionDto;
factory ExclusiveSummaryTransactionDto.fromJson(Map<String, dynamic> json) =>
_$ExclusiveSummaryTransactionDtoFromJson(json);
ExclusiveSummaryTransaction toDomain() => ExclusiveSummaryTransaction(
date: date ?? DateTime.fromMillisecondsSinceEpoch(0),
categoryCode: categoryCode ?? '',
categoryName: categoryName ?? '',
description: description ?? '',
amount: amount?.toInt() ?? 0,
source: source ?? '',
);
}
@@ -199,4 +199,25 @@ class AnalyticRepository implements IAnalyticRepository {
return left(const AnalyticFailure.unexpectedError());
}
}
@override
Future<Either<AnalyticFailure, ExclusiveSummary>> getExclusiveSummary({
required DateTime dateFrom,
required DateTime dateTo,
String? outletId,
}) async {
try {
final result = await _dataProvider.fetchExclusiveSummary(
dateFrom: dateFrom,
dateTo: dateTo,
outletId: _resolveOutletId(outletId),
);
if (result.hasError) return left(result.error!);
return right(result.data!.toDomain());
} catch (e, s) {
log('getExclusiveSummaryError', name: _logName, error: e, stackTrace: s);
return left(const AnalyticFailure.unexpectedError());
}
}
}