report product

This commit is contained in:
efrilm
2025-11-03 19:34:46 +07:00
parent 3bb6bd653e
commit 14c3c69ad6
19 changed files with 2557 additions and 2 deletions
+1
View File
@@ -7,5 +7,6 @@ part 'analytic.freezed.dart';
part 'entities/dashboard_entity.dart';
part 'entities/sales_entity.dart';
part 'entities/product_analytic_entity.dart';
part 'failures/analytic_failure.dart';
part 'repositories/i_analytic_repository.dart';
+744
View File
@@ -2349,6 +2349,750 @@ abstract class _SalesAnalyticItem implements SalesAnalyticItem {
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$ProductAnalytic {
String get organizationId => throw _privateConstructorUsedError;
String get outletId => throw _privateConstructorUsedError;
DateTime get dateFrom => throw _privateConstructorUsedError;
DateTime get dateTo => throw _privateConstructorUsedError;
List<ProductAnalyticItem> get data => throw _privateConstructorUsedError;
/// Create a copy of ProductAnalytic
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ProductAnalyticCopyWith<ProductAnalytic> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ProductAnalyticCopyWith<$Res> {
factory $ProductAnalyticCopyWith(
ProductAnalytic value,
$Res Function(ProductAnalytic) then,
) = _$ProductAnalyticCopyWithImpl<$Res, ProductAnalytic>;
@useResult
$Res call({
String organizationId,
String outletId,
DateTime dateFrom,
DateTime dateTo,
List<ProductAnalyticItem> data,
});
}
/// @nodoc
class _$ProductAnalyticCopyWithImpl<$Res, $Val extends ProductAnalytic>
implements $ProductAnalyticCopyWith<$Res> {
_$ProductAnalyticCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ProductAnalytic
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? organizationId = null,
Object? outletId = null,
Object? dateFrom = null,
Object? dateTo = null,
Object? data = null,
}) {
return _then(
_value.copyWith(
organizationId: null == organizationId
? _value.organizationId
: organizationId // ignore: cast_nullable_to_non_nullable
as String,
outletId: null == outletId
? _value.outletId
: outletId // ignore: cast_nullable_to_non_nullable
as String,
dateFrom: null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
dateTo: null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
data: null == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as List<ProductAnalyticItem>,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$ProductAnalyticImplCopyWith<$Res>
implements $ProductAnalyticCopyWith<$Res> {
factory _$$ProductAnalyticImplCopyWith(
_$ProductAnalyticImpl value,
$Res Function(_$ProductAnalyticImpl) then,
) = __$$ProductAnalyticImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
String organizationId,
String outletId,
DateTime dateFrom,
DateTime dateTo,
List<ProductAnalyticItem> data,
});
}
/// @nodoc
class __$$ProductAnalyticImplCopyWithImpl<$Res>
extends _$ProductAnalyticCopyWithImpl<$Res, _$ProductAnalyticImpl>
implements _$$ProductAnalyticImplCopyWith<$Res> {
__$$ProductAnalyticImplCopyWithImpl(
_$ProductAnalyticImpl _value,
$Res Function(_$ProductAnalyticImpl) _then,
) : super(_value, _then);
/// Create a copy of ProductAnalytic
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? organizationId = null,
Object? outletId = null,
Object? dateFrom = null,
Object? dateTo = null,
Object? data = null,
}) {
return _then(
_$ProductAnalyticImpl(
organizationId: null == organizationId
? _value.organizationId
: organizationId // ignore: cast_nullable_to_non_nullable
as String,
outletId: null == outletId
? _value.outletId
: outletId // ignore: cast_nullable_to_non_nullable
as String,
dateFrom: null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
dateTo: null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
data: null == data
? _value._data
: data // ignore: cast_nullable_to_non_nullable
as List<ProductAnalyticItem>,
),
);
}
}
/// @nodoc
class _$ProductAnalyticImpl extends _ProductAnalytic {
const _$ProductAnalyticImpl({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required final List<ProductAnalyticItem> data,
}) : _data = data,
super._();
@override
final String organizationId;
@override
final String outletId;
@override
final DateTime dateFrom;
@override
final DateTime dateTo;
final List<ProductAnalyticItem> _data;
@override
List<ProductAnalyticItem> get data {
if (_data is EqualUnmodifiableListView) return _data;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_data);
}
@override
String toString() {
return 'ProductAnalytic(organizationId: $organizationId, outletId: $outletId, dateFrom: $dateFrom, dateTo: $dateTo, data: $data)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ProductAnalyticImpl &&
(identical(other.organizationId, organizationId) ||
other.organizationId == organizationId) &&
(identical(other.outletId, outletId) ||
other.outletId == outletId) &&
(identical(other.dateFrom, dateFrom) ||
other.dateFrom == dateFrom) &&
(identical(other.dateTo, dateTo) || other.dateTo == dateTo) &&
const DeepCollectionEquality().equals(other._data, _data));
}
@override
int get hashCode => Object.hash(
runtimeType,
organizationId,
outletId,
dateFrom,
dateTo,
const DeepCollectionEquality().hash(_data),
);
/// Create a copy of ProductAnalytic
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ProductAnalyticImplCopyWith<_$ProductAnalyticImpl> get copyWith =>
__$$ProductAnalyticImplCopyWithImpl<_$ProductAnalyticImpl>(
this,
_$identity,
);
}
abstract class _ProductAnalytic extends ProductAnalytic {
const factory _ProductAnalytic({
required final String organizationId,
required final String outletId,
required final DateTime dateFrom,
required final DateTime dateTo,
required final List<ProductAnalyticItem> data,
}) = _$ProductAnalyticImpl;
const _ProductAnalytic._() : super._();
@override
String get organizationId;
@override
String get outletId;
@override
DateTime get dateFrom;
@override
DateTime get dateTo;
@override
List<ProductAnalyticItem> get data;
/// Create a copy of ProductAnalytic
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ProductAnalyticImplCopyWith<_$ProductAnalyticImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$ProductAnalyticItem {
String get productId => throw _privateConstructorUsedError;
String get productName => throw _privateConstructorUsedError;
String get categoryId => throw _privateConstructorUsedError;
String get categoryName => throw _privateConstructorUsedError;
int get quantitySold => throw _privateConstructorUsedError;
int get revenue => throw _privateConstructorUsedError;
double get averagePrice => throw _privateConstructorUsedError;
int get orderCount => throw _privateConstructorUsedError;
/// Create a copy of ProductAnalyticItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ProductAnalyticItemCopyWith<ProductAnalyticItem> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ProductAnalyticItemCopyWith<$Res> {
factory $ProductAnalyticItemCopyWith(
ProductAnalyticItem value,
$Res Function(ProductAnalyticItem) then,
) = _$ProductAnalyticItemCopyWithImpl<$Res, ProductAnalyticItem>;
@useResult
$Res call({
String productId,
String productName,
String categoryId,
String categoryName,
int quantitySold,
int revenue,
double averagePrice,
int orderCount,
});
}
/// @nodoc
class _$ProductAnalyticItemCopyWithImpl<$Res, $Val extends ProductAnalyticItem>
implements $ProductAnalyticItemCopyWith<$Res> {
_$ProductAnalyticItemCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ProductAnalyticItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? productId = null,
Object? productName = null,
Object? categoryId = null,
Object? categoryName = null,
Object? quantitySold = null,
Object? revenue = null,
Object? averagePrice = null,
Object? orderCount = null,
}) {
return _then(
_value.copyWith(
productId: null == productId
? _value.productId
: productId // ignore: cast_nullable_to_non_nullable
as String,
productName: null == productName
? _value.productName
: productName // ignore: cast_nullable_to_non_nullable
as String,
categoryId: null == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String,
categoryName: null == categoryName
? _value.categoryName
: categoryName // ignore: cast_nullable_to_non_nullable
as String,
quantitySold: null == quantitySold
? _value.quantitySold
: quantitySold // ignore: cast_nullable_to_non_nullable
as int,
revenue: null == revenue
? _value.revenue
: revenue // ignore: cast_nullable_to_non_nullable
as int,
averagePrice: null == averagePrice
? _value.averagePrice
: averagePrice // ignore: cast_nullable_to_non_nullable
as double,
orderCount: null == orderCount
? _value.orderCount
: orderCount // ignore: cast_nullable_to_non_nullable
as int,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$ProductAnalyticItemImplCopyWith<$Res>
implements $ProductAnalyticItemCopyWith<$Res> {
factory _$$ProductAnalyticItemImplCopyWith(
_$ProductAnalyticItemImpl value,
$Res Function(_$ProductAnalyticItemImpl) then,
) = __$$ProductAnalyticItemImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
String productId,
String productName,
String categoryId,
String categoryName,
int quantitySold,
int revenue,
double averagePrice,
int orderCount,
});
}
/// @nodoc
class __$$ProductAnalyticItemImplCopyWithImpl<$Res>
extends _$ProductAnalyticItemCopyWithImpl<$Res, _$ProductAnalyticItemImpl>
implements _$$ProductAnalyticItemImplCopyWith<$Res> {
__$$ProductAnalyticItemImplCopyWithImpl(
_$ProductAnalyticItemImpl _value,
$Res Function(_$ProductAnalyticItemImpl) _then,
) : super(_value, _then);
/// Create a copy of ProductAnalyticItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? productId = null,
Object? productName = null,
Object? categoryId = null,
Object? categoryName = null,
Object? quantitySold = null,
Object? revenue = null,
Object? averagePrice = null,
Object? orderCount = null,
}) {
return _then(
_$ProductAnalyticItemImpl(
productId: null == productId
? _value.productId
: productId // ignore: cast_nullable_to_non_nullable
as String,
productName: null == productName
? _value.productName
: productName // ignore: cast_nullable_to_non_nullable
as String,
categoryId: null == categoryId
? _value.categoryId
: categoryId // ignore: cast_nullable_to_non_nullable
as String,
categoryName: null == categoryName
? _value.categoryName
: categoryName // ignore: cast_nullable_to_non_nullable
as String,
quantitySold: null == quantitySold
? _value.quantitySold
: quantitySold // ignore: cast_nullable_to_non_nullable
as int,
revenue: null == revenue
? _value.revenue
: revenue // ignore: cast_nullable_to_non_nullable
as int,
averagePrice: null == averagePrice
? _value.averagePrice
: averagePrice // ignore: cast_nullable_to_non_nullable
as double,
orderCount: null == orderCount
? _value.orderCount
: orderCount // ignore: cast_nullable_to_non_nullable
as int,
),
);
}
}
/// @nodoc
class _$ProductAnalyticItemImpl implements _ProductAnalyticItem {
const _$ProductAnalyticItemImpl({
required this.productId,
required this.productName,
required this.categoryId,
required this.categoryName,
required this.quantitySold,
required this.revenue,
required this.averagePrice,
required this.orderCount,
});
@override
final String productId;
@override
final String productName;
@override
final String categoryId;
@override
final String categoryName;
@override
final int quantitySold;
@override
final int revenue;
@override
final double averagePrice;
@override
final int orderCount;
@override
String toString() {
return 'ProductAnalyticItem(productId: $productId, productName: $productName, categoryId: $categoryId, categoryName: $categoryName, quantitySold: $quantitySold, revenue: $revenue, averagePrice: $averagePrice, orderCount: $orderCount)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ProductAnalyticItemImpl &&
(identical(other.productId, productId) ||
other.productId == productId) &&
(identical(other.productName, productName) ||
other.productName == productName) &&
(identical(other.categoryId, categoryId) ||
other.categoryId == categoryId) &&
(identical(other.categoryName, categoryName) ||
other.categoryName == categoryName) &&
(identical(other.quantitySold, quantitySold) ||
other.quantitySold == quantitySold) &&
(identical(other.revenue, revenue) || other.revenue == revenue) &&
(identical(other.averagePrice, averagePrice) ||
other.averagePrice == averagePrice) &&
(identical(other.orderCount, orderCount) ||
other.orderCount == orderCount));
}
@override
int get hashCode => Object.hash(
runtimeType,
productId,
productName,
categoryId,
categoryName,
quantitySold,
revenue,
averagePrice,
orderCount,
);
/// Create a copy of ProductAnalyticItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ProductAnalyticItemImplCopyWith<_$ProductAnalyticItemImpl> get copyWith =>
__$$ProductAnalyticItemImplCopyWithImpl<_$ProductAnalyticItemImpl>(
this,
_$identity,
);
}
abstract class _ProductAnalyticItem implements ProductAnalyticItem {
const factory _ProductAnalyticItem({
required final String productId,
required final String productName,
required final String categoryId,
required final String categoryName,
required final int quantitySold,
required final int revenue,
required final double averagePrice,
required final int orderCount,
}) = _$ProductAnalyticItemImpl;
@override
String get productId;
@override
String get productName;
@override
String get categoryId;
@override
String get categoryName;
@override
int get quantitySold;
@override
int get revenue;
@override
double get averagePrice;
@override
int get orderCount;
/// Create a copy of ProductAnalyticItem
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ProductAnalyticItemImplCopyWith<_$ProductAnalyticItemImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$ProductAnalyticCategoryItem {
String get categoryName => throw _privateConstructorUsedError;
int get productCount => throw _privateConstructorUsedError;
int get totalRevenue => throw _privateConstructorUsedError;
/// Create a copy of ProductAnalyticCategoryItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ProductAnalyticCategoryItemCopyWith<ProductAnalyticCategoryItem>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ProductAnalyticCategoryItemCopyWith<$Res> {
factory $ProductAnalyticCategoryItemCopyWith(
ProductAnalyticCategoryItem value,
$Res Function(ProductAnalyticCategoryItem) then,
) =
_$ProductAnalyticCategoryItemCopyWithImpl<
$Res,
ProductAnalyticCategoryItem
>;
@useResult
$Res call({String categoryName, int productCount, int totalRevenue});
}
/// @nodoc
class _$ProductAnalyticCategoryItemCopyWithImpl<
$Res,
$Val extends ProductAnalyticCategoryItem
>
implements $ProductAnalyticCategoryItemCopyWith<$Res> {
_$ProductAnalyticCategoryItemCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ProductAnalyticCategoryItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categoryName = null,
Object? productCount = null,
Object? totalRevenue = null,
}) {
return _then(
_value.copyWith(
categoryName: null == categoryName
? _value.categoryName
: categoryName // ignore: cast_nullable_to_non_nullable
as String,
productCount: null == productCount
? _value.productCount
: productCount // ignore: cast_nullable_to_non_nullable
as int,
totalRevenue: null == totalRevenue
? _value.totalRevenue
: totalRevenue // ignore: cast_nullable_to_non_nullable
as int,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$ProductAnalyticCategoryItemImplCopyWith<$Res>
implements $ProductAnalyticCategoryItemCopyWith<$Res> {
factory _$$ProductAnalyticCategoryItemImplCopyWith(
_$ProductAnalyticCategoryItemImpl value,
$Res Function(_$ProductAnalyticCategoryItemImpl) then,
) = __$$ProductAnalyticCategoryItemImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String categoryName, int productCount, int totalRevenue});
}
/// @nodoc
class __$$ProductAnalyticCategoryItemImplCopyWithImpl<$Res>
extends
_$ProductAnalyticCategoryItemCopyWithImpl<
$Res,
_$ProductAnalyticCategoryItemImpl
>
implements _$$ProductAnalyticCategoryItemImplCopyWith<$Res> {
__$$ProductAnalyticCategoryItemImplCopyWithImpl(
_$ProductAnalyticCategoryItemImpl _value,
$Res Function(_$ProductAnalyticCategoryItemImpl) _then,
) : super(_value, _then);
/// Create a copy of ProductAnalyticCategoryItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? categoryName = null,
Object? productCount = null,
Object? totalRevenue = null,
}) {
return _then(
_$ProductAnalyticCategoryItemImpl(
categoryName: null == categoryName
? _value.categoryName
: categoryName // ignore: cast_nullable_to_non_nullable
as String,
productCount: null == productCount
? _value.productCount
: productCount // ignore: cast_nullable_to_non_nullable
as int,
totalRevenue: null == totalRevenue
? _value.totalRevenue
: totalRevenue // ignore: cast_nullable_to_non_nullable
as int,
),
);
}
}
/// @nodoc
class _$ProductAnalyticCategoryItemImpl
implements _ProductAnalyticCategoryItem {
const _$ProductAnalyticCategoryItemImpl({
required this.categoryName,
required this.productCount,
required this.totalRevenue,
});
@override
final String categoryName;
@override
final int productCount;
@override
final int totalRevenue;
@override
String toString() {
return 'ProductAnalyticCategoryItem(categoryName: $categoryName, productCount: $productCount, totalRevenue: $totalRevenue)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ProductAnalyticCategoryItemImpl &&
(identical(other.categoryName, categoryName) ||
other.categoryName == categoryName) &&
(identical(other.productCount, productCount) ||
other.productCount == productCount) &&
(identical(other.totalRevenue, totalRevenue) ||
other.totalRevenue == totalRevenue));
}
@override
int get hashCode =>
Object.hash(runtimeType, categoryName, productCount, totalRevenue);
/// Create a copy of ProductAnalyticCategoryItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ProductAnalyticCategoryItemImplCopyWith<_$ProductAnalyticCategoryItemImpl>
get copyWith =>
__$$ProductAnalyticCategoryItemImplCopyWithImpl<
_$ProductAnalyticCategoryItemImpl
>(this, _$identity);
}
abstract class _ProductAnalyticCategoryItem
implements ProductAnalyticCategoryItem {
const factory _ProductAnalyticCategoryItem({
required final String categoryName,
required final int productCount,
required final int totalRevenue,
}) = _$ProductAnalyticCategoryItemImpl;
@override
String get categoryName;
@override
int get productCount;
@override
int get totalRevenue;
/// Create a copy of ProductAnalyticCategoryItem
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ProductAnalyticCategoryItemImplCopyWith<_$ProductAnalyticCategoryItemImpl>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$AnalyticFailure {
@optionalTypeArgs
@@ -0,0 +1,97 @@
part of '../analytic.dart';
@freezed
class ProductAnalytic with _$ProductAnalytic {
const ProductAnalytic._();
const factory ProductAnalytic({
required String organizationId,
required String outletId,
required DateTime dateFrom,
required DateTime dateTo,
required List<ProductAnalyticItem> data,
}) = _ProductAnalytic;
factory ProductAnalytic.empty() => ProductAnalytic(
organizationId: '',
outletId: '',
dateFrom: DateTime.now(),
dateTo: DateTime.now(),
data: const [],
);
ProductAnalyticItem get bestProduct {
if (data.isEmpty) {
return ProductAnalyticItem.empty();
}
return data.first;
}
List<ProductAnalyticCategoryItem> get categories {
final Map<String, ProductAnalyticCategoryItem> categoryMap = {};
for (final product in data) {
final key = product.categoryName;
if (categoryMap.containsKey(key)) {
final existing = categoryMap[key]!;
categoryMap[key] = existing.copyWith(
productCount: existing.productCount + 1,
totalRevenue: existing.totalRevenue + product.revenue,
);
} else {
categoryMap[key] = ProductAnalyticCategoryItem(
categoryName: product.categoryName,
productCount: 1,
totalRevenue: product.revenue,
);
}
}
final categorySummary = categoryMap.values.toList()
..sort((a, b) => b.totalRevenue.compareTo(a.totalRevenue));
return categorySummary;
}
}
@freezed
class ProductAnalyticItem with _$ProductAnalyticItem {
const factory ProductAnalyticItem({
required String productId,
required String productName,
required String categoryId,
required String categoryName,
required int quantitySold,
required int revenue,
required double averagePrice,
required int orderCount,
}) = _ProductAnalyticItem;
factory ProductAnalyticItem.empty() => const ProductAnalyticItem(
productId: '',
productName: '',
categoryId: '',
categoryName: '',
quantitySold: 0,
revenue: 0,
averagePrice: 0.0,
orderCount: 0,
);
}
@freezed
class ProductAnalyticCategoryItem with _$ProductAnalyticCategoryItem {
const factory ProductAnalyticCategoryItem({
required String categoryName,
required int productCount,
required int totalRevenue,
}) = _ProductAnalyticCategoryItem;
factory ProductAnalyticCategoryItem.empty() => ProductAnalyticCategoryItem(
categoryName: '',
productCount: 0,
totalRevenue: 0,
);
}
@@ -9,4 +9,8 @@ abstract class IAnalyticRepository {
required DateTime dateFrom,
required DateTime dateTo,
});
Future<Either<AnalyticFailure, ProductAnalytic>> getProducts({
required DateTime dateFrom,
required DateTime dateTo,
});
}