feat: inventory report

This commit is contained in:
efrilm
2025-08-15 01:12:04 +07:00
parent f37814fec8
commit 34c0ad5411
9 changed files with 1871 additions and 1 deletions
@@ -7,6 +7,7 @@ import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/dashboard_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/inventory_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
@@ -219,4 +220,38 @@ class AnalyticRemoteDatasource {
return left('Unexpected error occurred');
}
}
Future<Either<String, InventoryAnalyticResponseModel>> getInventory({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/inventory/report/details/${authData.user?.outletId}',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(InventoryAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
}
@@ -0,0 +1,290 @@
class InventoryAnalyticResponseModel {
final bool success;
final InventoryAnalyticData? data;
final dynamic errors;
InventoryAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
// From JSON
factory InventoryAnalyticResponseModel.fromJson(Map<String, dynamic> json) {
return InventoryAnalyticResponseModel(
success: json['success'],
data: json['data'] != null
? InventoryAnalyticData.fromMap(json['data'])
: null,
errors: json['errors'],
);
}
// To JSON
Map<String, dynamic> toJson() {
return {
'success': success,
'data': data?.toMap(),
'errors': errors,
};
}
// From Map
factory InventoryAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return InventoryAnalyticResponseModel(
success: map['success'],
data: map['data'] != null
? InventoryAnalyticData.fromMap(map['data'])
: null,
errors: map['errors'],
);
}
// To Map
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data?.toMap(),
'errors': errors,
};
}
}
class InventoryAnalyticData {
final InventorySummary summary;
final List<InventoryProductItem> products;
final List<InventoryIngredientItem> ingredients;
InventoryAnalyticData({
required this.summary,
required this.products,
required this.ingredients,
});
factory InventoryAnalyticData.fromMap(Map<String, dynamic> map) {
return InventoryAnalyticData(
summary: InventorySummary.fromMap(map['summary']),
products: map['products'] == null
? []
: List<InventoryProductItem>.from(
map['products']?.map((x) => InventoryProductItem.fromMap(x)) ??
[],
),
ingredients: map['ingredients'] == null
? []
: List<InventoryIngredientItem>.from(
map['ingredients']
?.map((x) => InventoryIngredientItem.fromMap(x)) ??
[],
),
);
}
Map<String, dynamic> toMap() {
return {
'summary': summary.toMap(),
'products': products.map((x) => x.toMap()).toList(),
'ingredients': ingredients.map((x) => x.toMap()).toList(),
};
}
}
class InventorySummary {
final int totalProducts;
final int totalIngredients;
final int totalValue;
final int lowStockProducts;
final int lowStockIngredients;
final int zeroStockProducts;
final int zeroStockIngredients;
final int totalSoldProducts;
final int totalSoldIngredients;
final String outletId;
final String outletName;
final DateTime generatedAt;
InventorySummary({
required this.totalProducts,
required this.totalIngredients,
required this.totalValue,
required this.lowStockProducts,
required this.lowStockIngredients,
required this.zeroStockProducts,
required this.zeroStockIngredients,
required this.totalSoldProducts,
required this.totalSoldIngredients,
required this.outletId,
required this.outletName,
required this.generatedAt,
});
factory InventorySummary.fromMap(Map<String, dynamic> map) {
return InventorySummary(
totalProducts: map['total_products'] ?? 0,
totalIngredients: map['total_ingredients'] ?? 0,
totalValue: map['total_value'] ?? 0,
lowStockProducts: map['low_stock_products'] ?? 0,
lowStockIngredients: map['low_stock_ingredients'] ?? 0,
zeroStockProducts: map['zero_stock_products'] ?? 0,
zeroStockIngredients: map['zero_stock_ingredients'] ?? 0,
totalSoldProducts: map['total_sold_products'] ?? 0,
totalSoldIngredients: map['total_sold_ingredients'] ?? 0,
outletId: map['outlet_id'],
outletName: map['outlet_name'],
generatedAt: DateTime.parse(map['generated_at']),
);
}
Map<String, dynamic> toMap() {
return {
'total_products': totalProducts,
'total_ingredients': totalIngredients,
'total_value': totalValue,
'low_stock_products': lowStockProducts,
'low_stock_ingredients': lowStockIngredients,
'zero_stock_products': zeroStockProducts,
'zero_stock_ingredients': zeroStockIngredients,
'total_sold_products': totalSoldProducts,
'total_sold_ingredients': totalSoldIngredients,
'outlet_id': outletId,
'outlet_name': outletName,
'generated_at': generatedAt.toIso8601String(),
};
}
}
class InventoryProductItem {
final String id;
final String productId;
final String productName;
final String categoryName;
final int quantity;
final int reorderLevel;
final int unitCost;
final int totalValue;
final int totalIn;
final int totalOut;
final bool isLowStock;
final bool isZeroStock;
final DateTime updatedAt;
InventoryProductItem({
required this.id,
required this.productId,
required this.productName,
required this.categoryName,
required this.quantity,
required this.reorderLevel,
required this.unitCost,
required this.totalValue,
required this.totalIn,
required this.totalOut,
required this.isLowStock,
required this.isZeroStock,
required this.updatedAt,
});
factory InventoryProductItem.fromMap(Map<String, dynamic> map) {
return InventoryProductItem(
id: map['id'],
productId: map['product_id'],
productName: map['product_name'],
categoryName: map['category_name'],
quantity: map['quantity'] ?? 0,
reorderLevel: map['reorder_level'] ?? 0,
unitCost: map['unit_cost'] ?? 0,
totalValue: map['total_value'] ?? 0,
totalIn: map['total_in'] ?? 0,
totalOut: map['total_out'] ?? 0,
isLowStock: map['is_low_stock'] ?? false,
isZeroStock: map['is_zero_stock'] ?? false,
updatedAt: DateTime.parse(map['updated_at']),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'product_id': productId,
'product_name': productName,
'category_name': categoryName,
'quantity': quantity,
'reorder_level': reorderLevel,
'unit_cost': unitCost,
'total_value': totalValue,
'total_in': totalIn,
'total_out': totalOut,
'is_low_stock': isLowStock,
'is_zero_stock': isZeroStock,
'updated_at': updatedAt.toIso8601String(),
};
}
}
class InventoryIngredientItem {
final String id;
final String ingredientId;
final String ingredientName;
final String unitName;
final int quantity;
final int reorderLevel;
final int unitCost;
final int totalValue;
final int totalIn;
final int totalOut;
final bool isLowStock;
final bool isZeroStock;
final DateTime updatedAt;
InventoryIngredientItem({
required this.id,
required this.ingredientId,
required this.ingredientName,
required this.unitName,
required this.quantity,
required this.reorderLevel,
required this.unitCost,
required this.totalValue,
required this.totalIn,
required this.totalOut,
required this.isLowStock,
required this.isZeroStock,
required this.updatedAt,
});
factory InventoryIngredientItem.fromMap(Map<String, dynamic> map) {
return InventoryIngredientItem(
id: map['id'],
ingredientId: map['ingredient_id'],
ingredientName: map['ingredient_name'],
unitName: map['unit_name'],
quantity: map['quantity'] ?? 0,
reorderLevel: map['reorder_level'] ?? 0,
unitCost: map['unit_cost'] ?? 0,
totalValue: map['total_value'] ?? 0,
totalIn: map['total_in'] ?? 0,
totalOut: map['total_out'] ?? 0,
isLowStock: map['is_low_stock'] ?? false,
isZeroStock: map['is_zero_stock'] ?? false,
updatedAt: DateTime.parse(map['updated_at']),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'ingredient_id': ingredientId,
'ingredient_name': ingredientName,
'unit_name': unitName,
'quantity': quantity,
'reorder_level': reorderLevel,
'unit_cost': unitCost,
'total_value': totalValue,
'total_in': totalIn,
'total_out': totalOut,
'is_low_stock': isLowStock,
'is_zero_stock': isZeroStock,
'updated_at': updatedAt.toIso8601String(),
};
}
}