report inventory
This commit is contained in:
@@ -11,3 +11,4 @@ part 'dtos/product_analytic_dto.dart';
|
||||
part 'dtos/payment_method_analytic_dto.dart';
|
||||
part 'dtos/profit_loss_analytic_dto.dart';
|
||||
part 'dtos/category_analytic_dto.dart';
|
||||
part 'dtos/inventory_analytic_dto.dart';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -527,3 +527,146 @@ Map<String, dynamic> _$$CategoryAnalyticItemDtoImplToJson(
|
||||
'product_count': instance.productCount,
|
||||
'order_count': instance.orderCount,
|
||||
};
|
||||
|
||||
_$InventoryAnalyticDtoImpl _$$InventoryAnalyticDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$InventoryAnalyticDtoImpl(
|
||||
summary: json['summary'] == null
|
||||
? null
|
||||
: InventoryAnalyticSummaryDto.fromJson(
|
||||
json['summary'] as Map<String, dynamic>,
|
||||
),
|
||||
products: (json['products'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) =>
|
||||
InventoryAnalyticProductItemDto.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList(),
|
||||
ingredients: (json['ingredients'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => InventoryAnalyticIngredientItemDto.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InventoryAnalyticDtoImplToJson(
|
||||
_$InventoryAnalyticDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'summary': instance.summary,
|
||||
'products': instance.products,
|
||||
'ingredients': instance.ingredients,
|
||||
};
|
||||
|
||||
_$InventoryAnalyticSummaryDtoImpl _$$InventoryAnalyticSummaryDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$InventoryAnalyticSummaryDtoImpl(
|
||||
totalProducts: (json['total_products'] as num?)?.toInt(),
|
||||
totalIngredients: (json['total_ingredients'] as num?)?.toInt(),
|
||||
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||
lowStockProducts: (json['low_stock_products'] as num?)?.toInt(),
|
||||
lowStockIngredients: (json['low_stock_ingredients'] as num?)?.toInt(),
|
||||
zeroStockProducts: (json['zero_stock_products'] as num?)?.toInt(),
|
||||
zeroStockIngredients: (json['zero_stock_ingredients'] as num?)?.toInt(),
|
||||
totalSoldProducts: (json['total_sold_products'] as num?)?.toInt(),
|
||||
totalSoldIngredients: (json['total_sold_ingredients'] as num?)?.toInt(),
|
||||
outletId: json['outlet_id'] as String?,
|
||||
outletName: json['outlet_name'] as String?,
|
||||
generatedAt: json['generated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['generated_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InventoryAnalyticSummaryDtoImplToJson(
|
||||
_$InventoryAnalyticSummaryDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'total_products': instance.totalProducts,
|
||||
'total_ingredients': instance.totalIngredients,
|
||||
'total_value': instance.totalValue,
|
||||
'low_stock_products': instance.lowStockProducts,
|
||||
'low_stock_ingredients': instance.lowStockIngredients,
|
||||
'zero_stock_products': instance.zeroStockProducts,
|
||||
'zero_stock_ingredients': instance.zeroStockIngredients,
|
||||
'total_sold_products': instance.totalSoldProducts,
|
||||
'total_sold_ingredients': instance.totalSoldIngredients,
|
||||
'outlet_id': instance.outletId,
|
||||
'outlet_name': instance.outletName,
|
||||
'generated_at': instance.generatedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
_$InventoryAnalyticProductItemDtoImpl
|
||||
_$$InventoryAnalyticProductItemDtoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryAnalyticProductItemDtoImpl(
|
||||
id: json['id'] as String?,
|
||||
productId: json['product_id'] as String?,
|
||||
productName: json['product_name'] as String?,
|
||||
categoryName: json['category_name'] as String?,
|
||||
quantity: (json['quantity'] as num?)?.toInt(),
|
||||
reorderLevel: (json['reorder_level'] as num?)?.toInt(),
|
||||
unitCost: json['unit_cost'] as num?,
|
||||
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||
totalIn: (json['total_in'] as num?)?.toInt(),
|
||||
totalOut: (json['total_out'] as num?)?.toInt(),
|
||||
isLowStock: json['is_low_stock'] as bool?,
|
||||
isZeroStock: json['is_zero_stock'] as bool?,
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InventoryAnalyticProductItemDtoImplToJson(
|
||||
_$InventoryAnalyticProductItemDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'product_id': instance.productId,
|
||||
'product_name': instance.productName,
|
||||
'category_name': instance.categoryName,
|
||||
'quantity': instance.quantity,
|
||||
'reorder_level': instance.reorderLevel,
|
||||
'unit_cost': instance.unitCost,
|
||||
'total_value': instance.totalValue,
|
||||
'total_in': instance.totalIn,
|
||||
'total_out': instance.totalOut,
|
||||
'is_low_stock': instance.isLowStock,
|
||||
'is_zero_stock': instance.isZeroStock,
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
_$InventoryAnalyticIngredientItemDtoImpl
|
||||
_$$InventoryAnalyticIngredientItemDtoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryAnalyticIngredientItemDtoImpl(
|
||||
id: json['id'] as String?,
|
||||
ingredientId: json['ingredient_id'] as String?,
|
||||
ingredientName: json['ingredient_name'] as String?,
|
||||
unitName: json['unit_name'] as String?,
|
||||
quantity: (json['quantity'] as num?)?.toInt(),
|
||||
reorderLevel: (json['reorder_level'] as num?)?.toInt(),
|
||||
unitCost: json['unit_cost'] as num?,
|
||||
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||
totalIn: (json['total_in'] as num?)?.toInt(),
|
||||
totalOut: (json['total_out'] as num?)?.toInt(),
|
||||
isLowStock: json['is_low_stock'] as bool?,
|
||||
isZeroStock: json['is_zero_stock'] as bool?,
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InventoryAnalyticIngredientItemDtoImplToJson(
|
||||
_$InventoryAnalyticIngredientItemDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'ingredient_id': instance.ingredientId,
|
||||
'ingredient_name': instance.ingredientName,
|
||||
'unit_name': instance.unitName,
|
||||
'quantity': instance.quantity,
|
||||
'reorder_level': instance.reorderLevel,
|
||||
'unit_cost': instance.unitCost,
|
||||
'total_value': instance.totalValue,
|
||||
'total_in': instance.totalIn,
|
||||
'total_out': instance.totalOut,
|
||||
'is_low_stock': instance.isLowStock,
|
||||
'is_zero_stock': instance.isZeroStock,
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ import '../../../common/extension/extension.dart';
|
||||
import '../../../common/function/app_function.dart';
|
||||
import '../../../common/url/api_path.dart';
|
||||
import '../../../domain/analytic/analytic.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../outlet/datasources/local_data_provider.dart';
|
||||
import '../analytic_dtos.dart';
|
||||
|
||||
@injectable
|
||||
@@ -192,4 +194,34 @@ class AnalyticRemoteDataProvider {
|
||||
return DC.error(AnalyticFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
|
||||
Future<DC<AnalyticFailure, InventoryAnalyticDto>> fetchInventory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final user = await getIt<OutletLocalDatasource>().currentOutlet();
|
||||
final response = await _apiClient.get(
|
||||
'${ApiPath.analyticInventory}/${user.id}',
|
||||
params: {
|
||||
'date_from': dateFrom.toServerDate(),
|
||||
'date_to': dateTo.toServerDate(),
|
||||
},
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['success'] == false) {
|
||||
return DC.error(AnalyticFailure.unexpectedError());
|
||||
}
|
||||
|
||||
final inventory = InventoryAnalyticDto.fromJson(
|
||||
response.data['data'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
return DC.data(inventory);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchInventory', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(AnalyticFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
part of '../analytic_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class InventoryAnalyticDto with _$InventoryAnalyticDto {
|
||||
const InventoryAnalyticDto._();
|
||||
|
||||
const factory InventoryAnalyticDto({
|
||||
@JsonKey(name: "summary") InventoryAnalyticSummaryDto? summary,
|
||||
@JsonKey(name: "products") List<InventoryAnalyticProductItemDto>? products,
|
||||
@JsonKey(name: "ingredients")
|
||||
List<InventoryAnalyticIngredientItemDto>? ingredients,
|
||||
}) = _InventoryAnalyticDto;
|
||||
|
||||
factory InventoryAnalyticDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryAnalyticDtoFromJson(json);
|
||||
|
||||
// Mapper ke domain (opsional)
|
||||
InventoryAnalytic toDomain() => InventoryAnalytic(
|
||||
summary: summary?.toDomain() ?? InventoryAnalyticSummary.empty(),
|
||||
products: products?.map((e) => e.toDomain()).toList() ?? [],
|
||||
ingredients: ingredients?.map((e) => e.toDomain()).toList() ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class InventoryAnalyticSummaryDto with _$InventoryAnalyticSummaryDto {
|
||||
const InventoryAnalyticSummaryDto._();
|
||||
|
||||
const factory InventoryAnalyticSummaryDto({
|
||||
@JsonKey(name: "total_products") int? totalProducts,
|
||||
@JsonKey(name: "total_ingredients") int? totalIngredients,
|
||||
@JsonKey(name: "total_value") int? totalValue,
|
||||
@JsonKey(name: "low_stock_products") int? lowStockProducts,
|
||||
@JsonKey(name: "low_stock_ingredients") int? lowStockIngredients,
|
||||
@JsonKey(name: "zero_stock_products") int? zeroStockProducts,
|
||||
@JsonKey(name: "zero_stock_ingredients") int? zeroStockIngredients,
|
||||
@JsonKey(name: "total_sold_products") int? totalSoldProducts,
|
||||
@JsonKey(name: "total_sold_ingredients") int? totalSoldIngredients,
|
||||
@JsonKey(name: "outlet_id") String? outletId,
|
||||
@JsonKey(name: "outlet_name") String? outletName,
|
||||
@JsonKey(name: "generated_at") DateTime? generatedAt,
|
||||
}) = _InventoryAnalyticSummaryDto;
|
||||
|
||||
factory InventoryAnalyticSummaryDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryAnalyticSummaryDtoFromJson(json);
|
||||
|
||||
// Optional mapper ke entity
|
||||
InventoryAnalyticSummary toDomain() => InventoryAnalyticSummary(
|
||||
totalProducts: totalProducts ?? 0,
|
||||
totalIngredients: totalIngredients ?? 0,
|
||||
totalValue: totalValue ?? 0,
|
||||
lowStockProducts: lowStockProducts ?? 0,
|
||||
lowStockIngredients: lowStockIngredients ?? 0,
|
||||
zeroStockProducts: zeroStockProducts ?? 0,
|
||||
zeroStockIngredients: zeroStockIngredients ?? 0,
|
||||
totalSoldProducts: totalSoldProducts ?? 0,
|
||||
totalSoldIngredients: totalSoldIngredients ?? 0,
|
||||
outletId: outletId ?? '',
|
||||
outletName: outletName ?? '',
|
||||
generatedAt: generatedAt ?? DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class InventoryAnalyticProductItemDto with _$InventoryAnalyticProductItemDto {
|
||||
const InventoryAnalyticProductItemDto._();
|
||||
|
||||
const factory InventoryAnalyticProductItemDto({
|
||||
@JsonKey(name: "id") String? id,
|
||||
@JsonKey(name: "product_id") String? productId,
|
||||
@JsonKey(name: "product_name") String? productName,
|
||||
@JsonKey(name: "category_name") String? categoryName,
|
||||
@JsonKey(name: "quantity") int? quantity,
|
||||
@JsonKey(name: "reorder_level") int? reorderLevel,
|
||||
@JsonKey(name: "unit_cost") num? unitCost,
|
||||
@JsonKey(name: "total_value") int? totalValue,
|
||||
@JsonKey(name: "total_in") int? totalIn,
|
||||
@JsonKey(name: "total_out") int? totalOut,
|
||||
@JsonKey(name: "is_low_stock") bool? isLowStock,
|
||||
@JsonKey(name: "is_zero_stock") bool? isZeroStock,
|
||||
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||
}) = _InventoryAnalyticProductItemDto;
|
||||
|
||||
factory InventoryAnalyticProductItemDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryAnalyticProductItemDtoFromJson(json);
|
||||
|
||||
// Mapper ke domain (opsional)
|
||||
InventoryAnalyticProductItem toDomain() => InventoryAnalyticProductItem(
|
||||
id: id ?? '',
|
||||
productId: productId ?? '',
|
||||
productName: productName ?? '',
|
||||
categoryName: categoryName ?? '',
|
||||
quantity: quantity ?? 0,
|
||||
reorderLevel: reorderLevel ?? 0,
|
||||
unitCost: unitCost ?? 0,
|
||||
totalValue: totalValue ?? 0,
|
||||
totalIn: totalIn ?? 0,
|
||||
totalOut: totalOut ?? 0,
|
||||
isLowStock: isLowStock ?? false,
|
||||
isZeroStock: isZeroStock ?? false,
|
||||
updatedAt: updatedAt ?? DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class InventoryAnalyticIngredientItemDto
|
||||
with _$InventoryAnalyticIngredientItemDto {
|
||||
const InventoryAnalyticIngredientItemDto._();
|
||||
|
||||
const factory InventoryAnalyticIngredientItemDto({
|
||||
@JsonKey(name: "id") String? id,
|
||||
@JsonKey(name: "ingredient_id") String? ingredientId,
|
||||
@JsonKey(name: "ingredient_name") String? ingredientName,
|
||||
@JsonKey(name: "unit_name") String? unitName,
|
||||
@JsonKey(name: "quantity") int? quantity,
|
||||
@JsonKey(name: "reorder_level") int? reorderLevel,
|
||||
@JsonKey(name: "unit_cost") num? unitCost,
|
||||
@JsonKey(name: "total_value") int? totalValue,
|
||||
@JsonKey(name: "total_in") int? totalIn,
|
||||
@JsonKey(name: "total_out") int? totalOut,
|
||||
@JsonKey(name: "is_low_stock") bool? isLowStock,
|
||||
@JsonKey(name: "is_zero_stock") bool? isZeroStock,
|
||||
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||
}) = _InventoryAnalyticIngredientItemDto;
|
||||
|
||||
factory InventoryAnalyticIngredientItemDto.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$InventoryAnalyticIngredientItemDtoFromJson(json);
|
||||
|
||||
// Mapper ke domain (opsional)
|
||||
InventoryAnalyticIngredientItem toDomain() => InventoryAnalyticIngredientItem(
|
||||
id: id ?? '',
|
||||
ingredientId: ingredientId ?? '',
|
||||
ingredientName: ingredientName ?? '',
|
||||
unitName: unitName ?? '',
|
||||
quantity: quantity ?? 0,
|
||||
reorderLevel: reorderLevel ?? 0,
|
||||
unitCost: unitCost ?? 0,
|
||||
totalValue: totalValue ?? 0,
|
||||
totalIn: totalIn ?? 0,
|
||||
totalOut: totalOut ?? 0,
|
||||
isLowStock: isLowStock ?? false,
|
||||
isZeroStock: isZeroStock ?? false,
|
||||
updatedAt: updatedAt ?? DateTime.now(),
|
||||
);
|
||||
}
|
||||
@@ -157,4 +157,28 @@ class AnalyticRepository implements IAnalyticRepository {
|
||||
return left(const AnalyticFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<AnalyticFailure, InventoryAnalytic>> getInventory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetchInventory(
|
||||
dateFrom: dateFrom,
|
||||
dateTo: dateTo,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final inventory = result.data!.toDomain();
|
||||
|
||||
return right(inventory);
|
||||
} catch (e) {
|
||||
log('getInventoryError', name: _logName, error: e);
|
||||
return left(const AnalyticFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user