Files
ERP-APP-INVENTORY/lib/controllers/home_controller.dart
BACHIR SOULDI 5a483da01d Add full app source
Adds the Flutter inventory app source, cleaned up for a shared repo:
generic package name, no hardcoded ERP endpoints (moved to gitignored
local config), no dead auth code, no debug logging of session data.
2026-07-16 14:37:28 +01:00

263 lines
7.1 KiB
Dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:inventory_app/constants.dart';
import 'package:inventory_app/models/famille_produit.dart';
import 'package:inventory_app/models/depot.dart';
import 'package:inventory_app/models/inventory_line.dart';
import 'package:inventory_app/models/product.dart';
import 'package:inventory_app/models/tracking_number.dart';
import 'package:inventory_app/service/axelor_client.dart';
import 'package:inventory_app/views/login_view.dart';
enum InventoryLineState { newLine, existingLine }
class HomeController extends GetxController {
final outputController = TextEditingController();
final labelController = TextEditingController();
final qtyController = TextEditingController(text: "1");
final unitController = TextEditingController();
final serialNumberTxt = TextEditingController();
var product = Rxn<Product>();
var selectedOption = 'comptage1'.obs;
var depotList = <Depot>[].obs;
var selectedDepot = Rxn<Depot>();
var trackingNumbers = <TrackingNumber>[].obs;
var selectedTrackingNumber = Rxn<TrackingNumber>();
var isLoading = false.obs;
var imageUrl = ''.obs;
var familleProduits = <FamilleProduit>[].obs;
var selectedFamilleProduit = Rxn<FamilleProduit>();
var sousFamilleProduits = <FamilleProduit>[].obs;
var selectedSousFamilleProduit = Rxn<FamilleProduit>();
var zoneCode = ''.obs;
var selectedState = 'Moyen'.obs;
late AxelorClient client;
@override
void onInit() {
super.onInit();
_initClient();
}
@override
void onReady() {
super.onReady();
_handleArgs();
}
Future<void> _initClient() async {
isLoading(true);
client = await AxelorClient.create();
depotList.value = (await client.fetchLocations()) ?? [];
isLoading(false);
}
Future<void> fetchLocations() async {
isLoading(true);
client = await AxelorClient.create();
depotList.value = (await client.fetchLocations()) ?? [];
isLoading(false);
}
Future<void> _loadProduct(String code) async {
try {
isLoading(true);
Product? p = await client.fetchProductByCode(code);
if (p == null) {
Get.snackbar('Erreur', 'Produit introuvable', backgroundColor: Colors.red);
return;
}
product.value = p;
labelController.text = p.name ?? '';
unitController.text = p.unit?.name ?? '';
imageUrl.value = p.imageUrl ?? '';
trackingNumbers.value =
await client.fetchTrackingNumberByProduct(p.id!) ?? [];
Get.snackbar(
'Produit trouvé',
'Produit: ${p.name}',
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
);
} catch (e) {
Get.snackbar(
'Erreur',
'Impossible de charger le produit',
backgroundColor: Colors.red,
);
} finally {
isLoading(false);
}
}
Future<void> _handleArgs() async {
final args = Get.arguments;
if (args is String) {
outputController.text = args;
await _loadProduct(args);
}
}
void handleScannedCode(String code) async {
outputController.text = code;
trackingNumbers.clear();
selectedTrackingNumber.value = null;
await _loadProduct(code);
}
void handleProductScan(String code) => handleScannedCode(code);
void handleLocationScan(String name) async {
var finalStr = "";
if (name.isNotEmpty) {
finalStr = name.split(":")[0].trim();
}
zoneCode.value = finalStr;
final locationJson = await client.fetchStockLocationByName(name);
if (locationJson == null) {
Get.snackbar(
'Erreur',
'Zone introuvable (Veuillez rafraichir la page et ressayer)',
backgroundColor: Colors.red,
);
return;
}
final depot = Depot.fromJson(locationJson);
selectedDepot.value = depot;
Get.snackbar('Zone trouvée', 'Nom: ${depot.name}', backgroundColor: Colors.blue);
}
Future<void> save() async {
if (outputController.text.isEmpty ||
qtyController.text.isEmpty ||
product.value == null) {
Get.snackbar('Erreur', 'Produit ou quantité invalide', backgroundColor: Colors.red);
return;
}
final now = DateTime.now().toIso8601String();
final qty = double.tryParse(qtyController.text) ?? 1;
final user = await client.fetchUserProfile();
final selected = selectedOption.value;
double first = 0, second = 0, third = 0;
String? firstDate, secondDate, thirdDate;
Map<String, dynamic>? firstUser, secondUser, thirdUser;
switch (selected) {
case "comptage1":
first = qty;
firstDate = now;
firstUser = user;
break;
case "comptage2":
second = qty;
secondDate = now;
secondUser = user;
break;
case "comptage3":
third = qty;
thirdDate = now;
thirdUser = user;
break;
}
final line = InventoryLine(
inventoryId: kInventoryId,
productId: product.value!.id!,
productName: product.value!.name ?? '',
currentQty: qty,
realQty: qty,
unitId: product.value!.unit?.id ?? 2,
description: selectedState.value,
observation: serialNumberTxt.text,
ticketId: "TICKET-${DateTime.now().millisecondsSinceEpoch}",
rack: "A1",
trackingNumberId: selectedTrackingNumber.value?.id,
countingTypeSelect: 1,
stockLocationId: selectedDepot.value?.id ?? 4,
firstCounting: first,
secondCounting: second,
thirdCounting: third,
firstCountingDate: firstDate,
secondCountingDate: secondDate,
thirdCountingDate: thirdDate,
firstCountingByUser: firstUser,
secondCountingByUser: secondUser,
thirdCountingByUser: thirdUser,
);
await client.saveInventoryLine(inventoryLine: line);
_resetForm();
Get.snackbar('Succès', 'Ligne enregistrée avec succès', backgroundColor: Colors.green);
}
void _resetForm() {
outputController.clear();
labelController.clear();
product.value = null;
trackingNumbers.clear();
selectedTrackingNumber.value = null;
serialNumberTxt.text = "";
}
Future<Uint8List?> fetchImageBytes(int imageId) => client.fetchImageBytes(imageId);
Future<void> fetchFamilleProduits() async {
try {
isLoading(true);
final list = await client.getFamilleProduit();
familleProduits.value = list ?? [];
} catch (e) {
familleProduits.clear();
} finally {
isLoading(false);
}
}
Future<void> fetchSousFamilleProduits(int parentId) async {
try {
isLoading(true);
final list = await client.getSousFamilleProduit(parentId);
sousFamilleProduits.value = list ?? [];
} catch (e) {
sousFamilleProduits.clear();
} finally {
isLoading(false);
}
}
void onFamilleSelected(FamilleProduit? famille) async {
selectedFamilleProduit.value = famille;
selectedSousFamilleProduit.value = null;
if (famille?.id != null) {
await fetchSousFamilleProduits(famille!.id!);
} else {
sousFamilleProduits.clear();
}
}
void logout() async {
if (await client.logout()) Get.off(LoginView());
}
}