# 🚀 Exemple d'Implémentation : Lazy Loading pour Factures

## 📋 Objectif
Transformer la liste des factures pour utiliser le lazy loading avec pagination côté serveur.

---

## 📁 Fichiers à Créer/Modifier

### 1. Créer FactureLazyDataModel.java

**Chemin** : `src/main/java/dev/lions/btpxpress/view/model/FactureLazyDataModel.java`

```java
package dev.lions.btpxpress.view.model;

import dev.lions.btpxpress.service.FactureService;
import dev.lions.btpxpress.view.FactureView.Facture;
import org.primefaces.model.FilterMeta;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortMeta;
import org.primefaces.model.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

public class FactureLazyDataModel extends LazyDataModel<Facture> implements Serializable {
    
    private static final Logger LOG = LoggerFactory.getLogger(FactureLazyDataModel.class);
    private final FactureService factureService;
    
    public FactureLazyDataModel(FactureService factureService) {
        this.factureService = factureService;
    }
    
    @Override
    public int count(Map<String, FilterMeta> filterBy) {
        try {
            Map<String, Object> filterParams = buildFilterParams(filterBy);
            int count = factureService.countFactures(filterParams);
            LOG.debug("Count factures with filters: {}", count);
            return count;
        } catch (Exception e) {
            LOG.error("Erreur lors du comptage des factures", e);
            return 0;
        }
    }
    
    @Override
    public List<Facture> load(int first, int pageSize, 
                              Map<String, SortMeta> sortBy,
                              Map<String, FilterMeta> filterBy) {
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("offset", first);
            params.put("limit", pageSize);
            
            // Ajouter les paramètres de tri
            if (sortBy != null && !sortBy.isEmpty()) {
                SortMeta sortMeta = sortBy.values().iterator().next();
                params.put("sortField", sortMeta.getField());
                params.put("sortOrder", sortMeta.getOrder() == SortOrder.ASCENDING ? "ASC" : "DESC");
            }
            
            // Ajouter les paramètres de filtrage
            params.putAll(buildFilterParams(filterBy));
            
            LOG.debug("Loading factures with params: {}", params);
            List<Facture> factures = factureService.getFacturesLazy(params);
            LOG.debug("Loaded {} factures", factures.size());
            
            return factures;
        } catch (Exception e) {
            LOG.error("Erreur lors du chargement des factures", e);
            return Collections.emptyList();
        }
    }
    
    @Override
    public String getRowKey(Facture facture) {
        return facture.getId() != null ? facture.getId().toString() : null;
    }
    
    @Override
    public Facture getRowData(String rowKey) {
        // Cette méthode est appelée pour récupérer une facture par son ID
        // Pour l'instant, on retourne null car on ne garde pas de cache local
        return null;
    }
    
    private Map<String, Object> buildFilterParams(Map<String, FilterMeta> filterBy) {
        Map<String, Object> params = new HashMap<>();
        
        if (filterBy != null) {
            filterBy.forEach((field, filterMeta) -> {
                Object filterValue = filterMeta.getFilterValue();
                if (filterValue != null && !filterValue.toString().trim().isEmpty()) {
                    params.put("filter_" + field, filterValue);
                }
            });
        }
        
        return params;
    }
}
```

---

### 2. Modifier FactureService.java

**Ajouter les méthodes pour le lazy loading** :

```java
package dev.lions.btpxpress.service;

import dev.lions.btpxpress.view.FactureView.Facture;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@ApplicationScoped
public class FactureService {
    
    private static final Logger LOG = LoggerFactory.getLogger(FactureService.class);
    
    @Inject
    @RestClient
    BtpXpressApiClient apiClient;
    
    /**
     * Récupère les factures avec pagination et filtres
     */
    public List<Facture> getFacturesLazy(Map<String, Object> params) {
        try {
            int offset = (int) params.getOrDefault("offset", 0);
            int limit = (int) params.getOrDefault("limit", 10);
            String sortField = (String) params.get("sortField");
            String sortOrder = (String) params.get("sortOrder");
            
            // Extraire les filtres
            String filtreNumero = (String) params.get("filter_numero");
            String filtreClient = (String) params.get("filter_client");
            String filtreStatut = (String) params.get("filter_statut");
            
            // Appel API (à adapter selon votre backend)
            List<Map<String, Object>> facturesData = apiClient.getFacturesLazy(
                offset, limit, sortField, sortOrder,
                filtreNumero, filtreClient, filtreStatut
            );
            
            // Mapper vers les objets Facture
            return facturesData.stream()
                .map(this::mapToFacture)
                .collect(Collectors.toList());
                
        } catch (Exception e) {
            LOG.error("Erreur lors de la récupération lazy des factures", e);
            return Collections.emptyList();
        }
    }
    
    /**
     * Compte le nombre total de factures avec filtres
     */
    public int countFactures(Map<String, Object> params) {
        try {
            String filtreNumero = (String) params.get("filter_numero");
            String filtreClient = (String) params.get("filter_client");
            String filtreStatut = (String) params.get("filter_statut");
            
            return apiClient.countFactures(filtreNumero, filtreClient, filtreStatut);
            
        } catch (Exception e) {
            LOG.error("Erreur lors du comptage des factures", e);
            return 0;
        }
    }
    
    private Facture mapToFacture(Map<String, Object> data) {
        Facture f = new Facture();
        f.setId(data.get("id") != null ? Long.valueOf(data.get("id").toString()) : null);
        f.setNumero((String) data.get("numero"));
        f.setObjet((String) data.get("objet"));
        
        // Mapping du client
        Object clientObj = data.get("client");
        if (clientObj instanceof Map) {
            Map<String, Object> clientData = (Map<String, Object>) clientObj;
            String entreprise = (String) clientData.get("entreprise");
            String nom = (String) clientData.get("nom");
            String prenom = (String) clientData.get("prenom");
            f.setClient(entreprise != null && !entreprise.trim().isEmpty() ?
                entreprise : (prenom != null ? prenom + " " : "") + (nom != null ? nom : ""));
        } else if (clientObj instanceof String) {
            f.setClient((String) clientObj);
        }
        
        // Mapping des dates
        if (data.get("dateEmission") != null) {
            f.setDateEmission(LocalDate.parse(data.get("dateEmission").toString()));
        }
        if (data.get("dateEcheance") != null) {
            f.setDateEcheance(LocalDate.parse(data.get("dateEcheance").toString()));
        }
        
        // Mapping des montants
        f.setStatut((String) data.get("statut"));
        f.setMontantHT(data.get("montantHT") != null ? Double.valueOf(data.get("montantHT").toString()) : 0.0);
        f.setMontantTTC(data.get("montantTTC") != null ? Double.valueOf(data.get("montantTTC").toString()) : 0.0);
        f.setMontantPaye(data.get("montantPaye") != null ? Double.valueOf(data.get("montantPaye").toString()) : 0.0);
        
        return f;
    }
}
```

---

### 3. Modifier BtpXpressApiClient.java

**Ajouter les endpoints pour le lazy loading** :

```java
package dev.lions.btpxpress.service;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import java.util.List;
import java.util.Map;

@Path("/api")
@RegisterRestClient(configKey = "btpxpress-api")
public interface BtpXpressApiClient {
    
    // ... méthodes existantes ...
    
    @GET
    @Path("/factures/lazy")
    @Produces(MediaType.APPLICATION_JSON)
    List<Map<String, Object>> getFacturesLazy(
        @QueryParam("offset") int offset,
        @QueryParam("limit") int limit,
        @QueryParam("sortField") String sortField,
        @QueryParam("sortOrder") String sortOrder,
        @QueryParam("filter_numero") String filtreNumero,
        @QueryParam("filter_client") String filtreClient,
        @QueryParam("filter_statut") String filtreStatut
    );
    
    @GET
    @Path("/factures/count")
    @Produces(MediaType.APPLICATION_JSON)
    int countFactures(
        @QueryParam("filter_numero") String filtreNumero,
        @QueryParam("filter_client") String filtreClient,
        @QueryParam("filter_statut") String filtreStatut
    );
}
```

---

## ✅ Prochaines Étapes

1. Créer les fichiers ci-dessus
2. Tester avec des données de test
3. Vérifier les logs pour le debugging
4. Adapter le backend si nécessaire
5. Répliquer pour les autres modules (Devis, Clients, etc.)

