OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.erp.core.sales.order.element;

import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.core.common.component.TransfertBaseSQLComponent;
import org.openconcerto.erp.core.common.component.TransfertGroupSQLComponent;
import org.openconcerto.erp.core.common.element.ComptaSQLConfElement;
import org.openconcerto.erp.core.sales.account.VenteFactureSituationSQLComponent;
import org.openconcerto.erp.core.sales.account.VenteFactureSoldeSQLComponent;
import org.openconcerto.erp.core.sales.order.component.CommandeClientSQLComponent;
import org.openconcerto.erp.core.sales.order.report.CommandeClientXmlSheet;
import org.openconcerto.erp.core.sales.order.ui.EtatCommandeClient;
import org.openconcerto.erp.core.sales.order.ui.ReliquatCommandeTableModel;
import org.openconcerto.erp.core.sales.product.element.UniteVenteArticleSQLElement;
import org.openconcerto.erp.core.sales.product.model.ProductComponent;
import org.openconcerto.erp.core.sales.product.model.ProductHelper;
import org.openconcerto.erp.core.sales.shipment.component.BonDeLivraisonSQLComponent;
import org.openconcerto.erp.core.supplychain.stock.element.MouvementStockSQLElement;
import org.openconcerto.erp.core.supplychain.stock.element.StockSQLElement;
import org.openconcerto.erp.model.MouseSheetXmlListeListener;
import org.openconcerto.erp.preferences.GestionCommercialeGlobalPreferencePanel;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementLink.LinkType;
import org.openconcerto.sql.element.SQLElementLinksSetup;
import org.openconcerto.sql.element.TreesOfSQLRows;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLInjector;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSelectJoin;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.preferences.SQLPreferences;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.view.EditFrame;
import org.openconcerto.sql.view.EditPanel;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.sql.view.EditPanelListener;
import org.openconcerto.sql.view.list.IListe;
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelSource;
import org.openconcerto.ui.PanelFrame;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.cc.ITransformer;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;

import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;

public class CommandeClientSQLElement extends ComptaSQLConfElement {

    public CommandeClientSQLElement() {
        super("COMMANDE_CLIENT", "une commande client", "commandes clients");

        SQLPreferences prefs = new SQLPreferences(getTable().getDBRoot());
        if (prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.ORDER_PACKAGING_MANAGEMENT, true)) {

            for (final EtatCommandeClient etat : EtatCommandeClient.values()) {

                PredicateRowAction action = new PredicateRowAction(new AbstractAction(etat.getTranslation()) {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        changeStateOfRows(IListe.get(e).getSelectedRows(), etat);
                    }
                }, false);
                action.setPredicate(IListeEvent.getNonEmptySelectionPredicate());
                action.setPath(Arrays.asList("Etat", "Etat", "Etat"));
                getRowActions().add(action);
            }

            PredicateRowAction actionTransfertBL = new PredicateRowAction(new AbstractAction("Transfert automatique vers BL") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    TransfertCommandeAutoUtils transfert = new TransfertCommandeAutoUtils(getTable());
                    List<SQLRowValues> selectedRows = IListe.get(e).getSelectedRows();
                    Set<String> clientBloque = checkClient(selectedRows);
                    if (clientBloque.isEmpty()) {
                        transfert.transfertMultiBL(IListe.get(e).getSelectedRows());
                    } else if (clientBloque.size() == 1) {
                        JOptionPane.showMessageDialog(null, "Impossible de faire le transfert car le client " + clientBloque.iterator().next() + " est bloqué!");
                    } else {
                        JOptionPane.showMessageDialog(null, "Impossible de faire le transfert car les clients " + CollectionUtils.join(clientBloque, ",") + " sont bloqués!");
                    }

                }
            }, false);
            actionTransfertBL.setPredicate(IListeEvent.getNonEmptySelectionPredicate());
            getRowActions().add(actionTransfertBL);

            PredicateRowAction actionStock = new PredicateRowAction(new AbstractAction("Vérification des stocks") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    new Thread("Check Commande To Ship") {
                        public void run() {
                            try {
                                checkCommandeToShip();
                            } catch (Exception e) {
                                ExceptionHandler.handle("Erreur pendant la vérification du statut des commandes", e);
                            }
                        }
                    }.start();
                }

            }, false);
            actionStock.setPredicate(IListeEvent.getNonEmptySelectionPredicate());
            getRowActions().add(actionStock);

            PredicateRowAction actionFacture = new PredicateRowAction(new AbstractAction("Transfert automatique en facture") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    TransfertCommandeAutoUtils transfert = new TransfertCommandeAutoUtils(getTable());
                    List<SQLRowValues> selectedRows = IListe.get(e).getSelectedRows();
                    Set<String> clientBloque = checkClient(selectedRows);
                    if (clientBloque.isEmpty()) {
                        transfert.transfertFacture(IListe.get(e).getSelectedRows());
                    } else if (clientBloque.size() == 1) {
                        JOptionPane.showMessageDialog(null, "Impossible de faire le transfert car le client " + clientBloque.iterator().next() + " est bloqué!");
                    } else {
                        JOptionPane.showMessageDialog(null, "Impossible de faire le transfert car les clients " + CollectionUtils.join(clientBloque, ",") + " sont bloqués!");
                    }

                }
            }, false);
            actionFacture.setPredicate(IListeEvent.getNonEmptySelectionPredicate());
            getRowActions().add(actionFacture);

        }

        if (prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.ACOMPTE_DEVIS, false)) {
            PredicateRowAction actionClient = new PredicateRowAction(new AbstractAction("Saisir un acompte") {
                EditFrame edit;

                public void actionPerformed(ActionEvent e) {
                    final SQLElement eltEncaisser = Configuration.getInstance().getDirectory()
                            .getElement(((ComptaPropsConfiguration) Configuration.getInstance()).getRootSociete().getTable("ENCAISSER_MONTANT"));

                    if (this.edit == null) {
                        this.edit = new EditFrame(eltEncaisser, EditMode.CREATION);
                    }
                    final SQLRowAccessor selRow = IListe.get(e).getSelectedRow();
                    SQLRowValues rowVals = new SQLRowValues(eltEncaisser.getTable());
                    rowVals.put("ACOMPTE", true);
                    rowVals.put("NOM", "Acompte commande " + selRow.getString("NUMERO"));
                    rowVals.put("ID_CLIENT", selRow.getForeignID("ID_CLIENT"));
                    rowVals.put("ID_COMMANDE_CLIENT", selRow.getID());
                    SQLRowValues rowValsElt = new SQLRowValues(eltEncaisser.getTable().getTable("ENCAISSER_MONTANT_ELEMENT"));
                    rowValsElt.put("MONTANT_A_REGLER", selRow.getLong("T_TTC"));
                    rowValsElt.put("DATE", selRow.getObject("DATE"));
                    rowValsElt.put("ID_ENCAISSER_MONTANT", rowVals);
                    this.edit.getSQLComponent().select(rowVals);
                    this.edit.setVisible(true);
                    this.edit.addEditPanelListener(new EditPanelListener() {

                        @Override
                        public void modified() {
                        }

                        @Override
                        public void inserted(int id) {
                            // Put id devis and refresh devis.t_acompte
                            SQLRow rowE = eltEncaisser.getTable().getRow(id);
                            String up = "UPDATE " + selRow.getTable().getSQLName().quote() + " set \"T_ACOMPTE\"=(SELECT COALESCE(SUM(\"MONTANT\"),0) from " + rowE.getTable().getSQLName().quote()
                                    + " where \"ARCHIVE\"=0 and \"ID_COMMANDE_CLIENT\"=" + selRow.getID() + ") where \"ID\"=" + selRow.getID();
                            eltEncaisser.getTable().getDBSystemRoot().getDataSource().execute(up);
                        }

                        @Override
                        public void deleted() {
                        }

                        @Override
                        public void cancelled() {
                        }
                    });
                }
            }, false);
            actionClient.setPredicate(IListeEvent.getSingleSelectionPredicate());
            getRowActions().add(actionClient);
        }

        final List<RowAction> allowedActions = new ArrayList<RowAction>();
        // Transfert vers facture
        PredicateRowAction bonAction = new PredicateRowAction(new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                transfertBonLivraisonClient(IListe.get(e).getSelectedRows());
            }
        }, true, "sales.order.create.deliverynote");

        // Transfert vers facture
        RowAction factureAction = new RowAction(new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                transfertFactureClient(IListe.get(e).getSelectedRows());
            }
        }, true, "sales.order.create.invoice") {

            @Override
            public boolean enabledFor(List<SQLRowValues> selection) {
                if (selection.isEmpty()) {
                    return false;
                } else if (selection.size() > 1) {
                    return true;
                } else {
                    BigDecimal d = getAvancement(selection.get(0));
                    return d.signum() == 0;
                }
            }
        };

        // Transfert vers facture intermédiaire
        RowAction acompteAction = new RowAction(new AbstractAction("Créer une facture intermédiaire") {
            public void actionPerformed(ActionEvent e) {
                transfertAcompteClient(IListe.get(e).getSelectedRows());
            }
        }, false, "sales.order.create.account") {
            BigDecimal cent = BigDecimal.ONE.movePointRight(2);

            @Override
            public boolean enabledFor(List<SQLRowValues> selection) {
                if (selection.isEmpty() || selection.size() > 1) {
                    return false;
                } else {
                    BigDecimal d = getAvancement(selection.get(0));
                    return NumberUtils.compare(d, cent) != 0;
                }
            }
        };

        // Transfert vers facture solde
        RowAction soldeAction = new RowAction(new AbstractAction("Facturer le solde") {
            public void actionPerformed(ActionEvent e) {
                transfertSoldeClient(IListe.get(e).getSelectedRows());
            }
        }, false, "sales.order.create.account.solde") {
            BigDecimal cent = BigDecimal.ONE.movePointRight(2);

            @Override
            public boolean enabledFor(List<SQLRowValues> selection) {
                if (selection.isEmpty() || selection.size() > 1) {
                    return false;
                } else {
                    BigDecimal d = getAvancement(selection.get(0));
                    return NumberUtils.compare(d, cent) != 0 && NumberUtils.compare(d, BigDecimal.ZERO) != 0;
                }
            }
        };

        // Transfert vers commande
        PredicateRowAction cmdAction = new PredicateRowAction(new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                final int selectedId = IListe.get(e).getSelectedId();
                ComptaPropsConfiguration.getInstanceCompta().getNonInteractiveSQLExecutor().execute(new Runnable() {

                    @Override
                    public void run() {
                        final CommandeClientSQLElement elt = (CommandeClientSQLElement) Configuration.getInstance().getDirectory().getElement("COMMANDE_CLIENT");
                        elt.transfertCommande(selectedId, true);

                    }
                });

            }

        }, false, "sales.order.create.supplier.order");

        cmdAction.setPredicate(IListeEvent.getSingleSelectionPredicate());

        bonAction.setPredicate(IListeEvent.getSingleSelectionPredicate());

        // Reliquat
        PredicateRowAction reliquatAction = new PredicateRowAction(new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                JPanel panelReliquat = new JPanel(new GridBagLayout());
                GridBagConstraints c = new GridBagConstraints();
                final ReliquatCommandeTableModel dm = new ReliquatCommandeTableModel(IListe.get(e).getSelectedRowAccessor());
                if (dm.getRowCount() > 0) {
                    JTable table = new JTable(dm);
                    JScrollPane comp = new JScrollPane(table);
                    c.weightx = 1;
                    c.weighty = 1;
                    c.fill = GridBagConstraints.BOTH;
                    panelReliquat.add(comp, c);
                    PanelFrame frame = new PanelFrame(panelReliquat, "Reliquat commande");
                    frame.pack();
                    frame.setVisible(true);
                } else {
                    JOptionPane.showMessageDialog(null, "Aucun reliquat restant");
                }
            }

        }, false, "sales.order.reliquat.show");

        reliquatAction.setPredicate(IListeEvent.getSingleSelectionPredicate());

        MouseSheetXmlListeListener mouseSheetXmlListeListener = new MouseSheetXmlListeListener(this, CommandeClientXmlSheet.class);
        mouseSheetXmlListeListener.setGenerateHeader(true);
        mouseSheetXmlListeListener.setShowHeader(true);

        // Dupliquer
        RowAction cloneAction = getCloneAction();
        allowedActions.add(cloneAction);

        allowedActions.add(bonAction);
        allowedActions.add(factureAction);
        allowedActions.add(acompteAction);
        allowedActions.add(soldeAction);
        allowedActions.add(cmdAction);
        allowedActions.add(reliquatAction);
        allowedActions.addAll(mouseSheetXmlListeListener.getRowActions());
        getRowActions().addAll(allowedActions);
    }

    private boolean chiffrageEditableInUI = true;

    public void setChiffrageEditableInUI(boolean chiffrageEditableInUI) {
        this.chiffrageEditableInUI = chiffrageEditableInUI;
    }

    public boolean isChiffrageEditableInUI() {
        return this.chiffrageEditableInUI;
    }

    @Override
    protected void setupLinks(SQLElementLinksSetup links) {
        super.setupLinks(links);
        if (getTable().contains("ID_ADRESSE")) {
            links.get("ID_ADRESSE").setType(LinkType.ASSOCIATION);
        }
        if (getTable().contains("ID_ADRESSE_FACT")) {
            links.get("ID_ADRESSE_FACT").setType(LinkType.ASSOCIATION);
        }
        if (getTable().contains("ID_ADRESSE_LIVRAISON")) {
            links.get("ID_ADRESSE_LIVRAISON").setType(LinkType.ASSOCIATION);
        }
    }

    public SQLRow getNextCommandeToPrepare() {
        final SQLTable tableCmd = getTable();
        SQLSelect sel = new SQLSelect();
        sel.addSelect(tableCmd.getKey());
        sel.addSelect(tableCmd.getField("NUMERO"));
        sel.addSelect(tableCmd.getField("DATE"));
        sel.addSelect(tableCmd.getField("T_HT"));
        sel.addSelect(tableCmd.getField("T_TVA"));
        sel.addSelect(tableCmd.getField("T_TTC"));
        sel.addSelect(tableCmd.getField("PORT_HT"));
        sel.addSelect(tableCmd.getField("REMISE_HT"));
        sel.addSelect(tableCmd.getField("ID_TAXE_PORT"));
        sel.addSelect(tableCmd.getField("ID_CLIENT"));
        Where w = new Where(tableCmd.getField("ETAT_COMMANDE"), "=", EtatCommandeClient.A_PREPARER.getId());
        sel.setWhere(w);
        sel.clearOrder();
        sel.addFieldOrder(sel.getAlias(tableCmd.getField("DATE")));
        sel.addFieldOrder(sel.getAlias(tableCmd.getField("T_HT")));

        List<SQLRow> result = SQLRowListRSH.execute(sel);
        if (result == null || result.size() == 0) {
            return null;
        } else {
            return result.get(0);
        }
    }

    public int getNbCommandeAPreparer() {
        final SQLTable tableCmd = getTable();
        SQLSelect sel = new SQLSelect();
        sel.addSelect(tableCmd.getKey(), "COUNT");
        Where w = new Where(tableCmd.getField("ETAT_COMMANDE"), "=", EtatCommandeClient.A_PREPARER.getId());
        sel.setWhere(w);

        Object r = getTable().getDBSystemRoot().getDataSource().executeScalar(sel.asString());
        int nb = 0;
        if (r != null) {
            nb = ((Number) r).intValue();
        }
        return nb;
    }

    public void checkCommandeToShip() throws Exception {
        assert !SwingUtilities.isEventDispatchThread();

        SQLUtils.executeAtomic(getTable().getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, IOException>() {
            @Override
            public Object handle(final SQLDataSource ds) throws SQLException, IOException {
                final SQLTable tableCmd = getTable();

                SQLRowValues rowVals = new SQLRowValues(tableCmd);
                rowVals.put(tableCmd.getKey().getName(), null);
                rowVals.put("NUMERO", null);

                final SQLTable tableCmdElt = tableCmd.getTable("COMMANDE_CLIENT_ELEMENT");
                SQLRowValues rowValsElt = new SQLRowValues(tableCmdElt);
                rowValsElt.put("QTE", null);
                rowValsElt.put("ID_DEPOT_STOCK", null);
                rowValsElt.put("QTE_UNITAIRE", null);
                rowValsElt.put("ID_COMMANDE_CLIENT", rowVals);

                SQLRowValues rowValsArt = new SQLRowValues(tableCmd.getTable("ARTICLE"));
                SQLRowValues rowValsStock = new SQLRowValues(tableCmd.getTable("STOCK"));
                rowValsStock.putNulls("QTE_REEL", "QTE_TH");
                rowValsStock.put("ID_DEPOT_STOCK", null);
                rowValsStock.put("ID_ARTICLE", rowValsArt);
                rowValsArt.put("ID_DEPOT_STOCK", null);
                rowValsElt.put("ID_ARTICLE", rowValsArt);

                SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowVals);
                fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {

                    @Override
                    public SQLSelect transformChecked(SQLSelect input) {
                        Where w = new Where(input.getAlias(tableCmd).getField("ETAT_COMMANDE"), "=", EtatCommandeClient.A_PREPARER.getId());
                        w = w.or(new Where(input.getAlias(tableCmd).getField("ETAT_COMMANDE"), "=", EtatCommandeClient.RUPTURE.getId()));
                        input.setWhere(w);
                        // ORDER BY ETAT COMMANDE et T_HT pour mettre en priorité les ruptures de
                        // stock et les commandes les plus chers
                        input.clearOrder();
                        input.addFieldOrder(input.getAlias(tableCmd.getField("ETAT_COMMANDE")));
                        input.addFieldOrder(input.getAlias(tableCmd.getField("T_HT")));
                        System.err.println(input.asString());
                        return input;
                    }
                });

                List<SQLRowValues> result = fetcher.fetch();
                List<Integer> cmdStockOK = new ArrayList<Integer>();
                List<Integer> cmdNoStock = new ArrayList<Integer>();

                // Stock utilisé par les commandes à préparer
                StockCommande stockGlobalUsed = new StockCommande();
                for (int i = result.size() - 1; i >= 0; i--) {
                    SQLRowValues sqlRowValues = result.get(i);
                    boolean inStock = true;

                    // Stock utilisé par la commande actuelle
                    StockCommande stockCmd = new StockCommande();
                    for (SQLRowValues item : sqlRowValues.getReferentRows(tableCmdElt)) {

                        if (item.getObject("ID_ARTICLE") != null && !item.isForeignEmpty("ID_ARTICLE")) {
                            final int foreignID = item.getForeignID("ID_ARTICLE");

                            // Stock = stock actuel dans la base - stock utilisé par les commandes
                            // déja testées -
                            // stock utilisé par la commande en cours (si 2 fois le meme article)
                            BigDecimal stock = BigDecimal.ZERO;
                            SQLRowAccessor rowStock = StockSQLElement.getStockFetched(item);
                            if (rowStock != null) {
                                stock = new BigDecimal(rowStock.getFloat("QTE_REEL"));
                            }
                            stock = stock.subtract(stockCmd.getQty(foreignID)).subtract(stockGlobalUsed.getQty(foreignID));

                            BigDecimal needQty = item.getBigDecimal("QTE_UNITAIRE").multiply(new BigDecimal(item.getInt("QTE")), DecimalUtils.HIGH_PRECISION);

                            stockCmd.addQty(foreignID, needQty);

                            inStock = CompareUtils.compare(stock, needQty) >= 0;

                            if (!inStock) {
                                break;
                            }
                        }
                    }

                    if (inStock) {
                        Map<Integer, BigDecimal> m = stockCmd.getMap();
                        for (Integer id : m.keySet()) {
                            stockGlobalUsed.addQty(id, m.get(id));
                        }

                        cmdStockOK.add(sqlRowValues.getID());
                    } else {
                        cmdNoStock.add(sqlRowValues.getID());
                    }
                }

                List<String> reqs = new ArrayList<String>(2);

                if (cmdStockOK.size() > 0) {
                    UpdateBuilder builderStockOK = new UpdateBuilder(tableCmd);
                    builderStockOK.setObject("ETAT_COMMANDE", EtatCommandeClient.A_PREPARER.getId());
                    builderStockOK.setWhere(new Where(getTable().getKey(), cmdStockOK));
                    reqs.add(builderStockOK.asString());
                }

                if (cmdNoStock.size() > 0) {
                    UpdateBuilder builderNoStock = new UpdateBuilder(tableCmd);
                    builderNoStock.setObject("ETAT_COMMANDE", EtatCommandeClient.RUPTURE.getId());
                    builderNoStock.setWhere(new Where(getTable().getKey(), cmdNoStock));
                    reqs.add(builderNoStock.asString());
                }

                if (reqs.size() > 0) {
                    List<? extends ResultSetHandler> handlers = new ArrayList<ResultSetHandler>(reqs.size());
                    for (String s : reqs) {
                        handlers.add(null);
                    }
                    SQLUtils.executeMultiple(tableCmd.getDBSystemRoot(), reqs, handlers);
                    tableCmd.fireTableModified(-1);
                }
                return null;
            }
        });

    }

    private void changeStateOfRows(List<SQLRowValues> l, EtatCommandeClient etat) {

        List<Integer> ids = new ArrayList<Integer>(l.size());
        for (SQLRowValues sqlRowValues : l) {
            ids.add(sqlRowValues.getID());
        }

        UpdateBuilder builder = new UpdateBuilder(getTable());
        builder.setObject("ETAT_COMMANDE", etat.getId());
        builder.setWhere(new Where(getTable().getKey(), ids));

        getTable().getDBSystemRoot().getDataSource().execute(builder.asString());
        getTable().fireTableModified(-1);

        if (etat == EtatCommandeClient.ANNULEE) {

            // Suppression des stocks si commande annulée
            SQLElement eltMvtStock = getDirectory().getElement("MOUVEMENT_STOCK");
            SQLSelect sel = new SQLSelect();
            sel.addSelect(eltMvtStock.getTable().getField("ID"));
            Where w = new Where(eltMvtStock.getTable().getField("IDSOURCE"), ids);
            Where w2 = new Where(eltMvtStock.getTable().getField("SOURCE"), "=", getTable().getName());
            sel.setWhere(w.and(w2));

            try {
                @SuppressWarnings("rawtypes")
                List res = (List) eltMvtStock.getTable().getBase().getDataSource().execute(sel.asString(), new ArrayListHandler());
                if (res != null) {
                    for (int i = 0; i < res.size(); i++) {
                        Object[] tmp = (Object[]) res.get(i);
                        eltMvtStock.archive(((Number) tmp[0]).intValue());
                    }
                }
            } catch (SQLException e) {
                ExceptionHandler.handle("Erreur lors de la suppression des mouvements de stocks", e);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openconcerto.devis.BaseSQLElement#getComboFields()
     */
    protected List<String> getComboFields() {
        final List<String> l = new ArrayList<String>();
        l.add("NUMERO");
        return l;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openconcerto.devis.BaseSQLElement#getListFields()
     */
    protected List<String> getListFields() {
        final List<String> l = new ArrayList<String>();
        l.add("NUMERO");
        l.add("DATE");
        if (getTable().contains("DATE_LIVRAISON_PREV")) {
            l.add("DATE_LIVRAISON_PREV");
        }
        l.add("ID_CLIENT");
        l.add("ID_COMMERCIAL");
        l.add("T_HT");
        l.add("T_TTC");
        SQLPreferences prefs = SQLPreferences.getMemCached(getTable().getDBRoot());
        if (prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.ACOMPTE_DEVIS, false)) {
            l.add("T_ACOMPTE");
        }
        if (getTable().getFieldsName().contains("ACOMPTE_COMMANDE")) {
            l.add("ACOMPTE_COMMANDE");
        }
        l.add("NOM");
        l.add("INFOS");
        if (prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.ORDER_PACKAGING_MANAGEMENT, true)) {
            l.add("NUMERO_EXPEDITION");
            l.add("ETAT_COMMANDE");
        }
        return l;
    }

    @Override
    public Set<String> getReadOnlyFields() {
        final Set<String> s = new HashSet<String>();
        s.add("ID_DEVIS");
        if (getTable().contains("T_ACOMPTE")) {
            s.add("T_ACOMPTE");
        }
        return s;
    }

    @Override
    protected void archive(TreesOfSQLRows trees, boolean cutLinks) throws SQLException {

        for (SQLRow row : trees.getRows()) {

            // Mise à jour des stocks
            SQLElement eltMvtStock = Configuration.getInstance().getDirectory().getElement("MOUVEMENT_STOCK");
            SQLSelect sel = new SQLSelect();
            sel.addSelect(eltMvtStock.getTable().getField("ID"));
            Where w = new Where(eltMvtStock.getTable().getField("IDSOURCE"), "=", row.getID());
            Where w2 = new Where(eltMvtStock.getTable().getField("SOURCE"), "=", getTable().getName());
            sel.setWhere(w.and(w2));

            @SuppressWarnings("rawtypes")
            List l = (List) eltMvtStock.getTable().getBase().getDataSource().execute(sel.asString(), new ArrayListHandler());
            if (l != null) {
                for (int i = 0; i < l.size(); i++) {
                    Object[] tmp = (Object[]) l.get(i);
                    eltMvtStock.archive(((Number) tmp[0]).intValue());
                }
            }
        }
        super.archive(trees, cutLinks);
    }

    @Override
    protected synchronized void _initTableSource(final SQLTableModelSource source) {
        super._initTableSource(source);
        // TODO: refaire un renderer pour les commandes transférées en BL
        // final CommandeClientRenderer rend = CommandeClientRenderer.getInstance();
        // final SQLTableModelColumn col = source.getColumn(getTable().getField("T_HT"));
        // col.setColumnInstaller(new IClosure<TableColumn>() {
        // @Override
        // public void executeChecked(TableColumn input) {
        // input.setCellRenderer(rend);
        // }
        // });
        source.init();
        SQLPreferences prefs = SQLPreferences.getMemCached(getTable().getDBRoot());
        if (prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.ORDER_PACKAGING_MANAGEMENT, true)) {

            SQLTableModelColumn col = source.getColumn(getTable().getField("ETAT_COMMANDE"));
            if (col != null) {
                col.setRenderer(new DefaultTableCellRenderer() {

                    @Override
                    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

                        JLabel comp = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                        if (value != null) {
                            final EtatCommandeClient fromID = EtatCommandeClient.fromID((Integer) value);
                            if (fromID != null) {
                                comp.setText(fromID.getTranslation());
                            } else {
                                comp.setText("");
                            }
                        }
                        return comp;
                    }
                });

            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openconcerto.devis.SQLElement#getComponent()
     */
    public SQLComponent createComponent() {
        return new CommandeClientSQLComponent();
    }

    /**
     * Transfert d'une commande en commande fournisseur
     * 
     * @param commandeID
     */
    public void transfertCommande(int commandeID, boolean useCommandeEnCours) {

        SQLElement elt = Configuration.getInstance().getDirectory().getElement("COMMANDE_CLIENT_ELEMENT");
        SQLRow rowCmd = getTable().getRow(commandeID);
        List<SQLRow> rows = rowCmd.getReferentRows(elt.getTable());
        transfertEltToCommandeF(rows);
    }

    public void transfertEltToCommandeF(List<? extends SQLRowAccessor> rowsItems) {
        ComptaPropsConfiguration.getInstanceCompta().getNonInteractiveSQLExecutor().execute(new Runnable() {

            @Override
            public void run() {
                ProductHelper helper = new ProductHelper(getTable().getDBRoot());
                List<ProductComponent> productComp = new ArrayList<>();
                helper.fillProductComponent(rowsItems, productComp, 1, 0, 1);

                List<ProductComponent> leafItems = helper.getChildWithQtyFrom(productComp);

                final ListMap<SQLRow, SQLRowValues> map = new ListMap<SQLRow, SQLRowValues>();
                final Set<Integer> stockChecked = new HashSet<Integer>();
                for (ProductComponent comp : leafItems) {
                    SQLRowAccessor rowArticleFind = comp.getProduct();

                    SQLRow row = rowArticleFind.asRow();
                    SQLRowAccessor rowStock = comp.getStock();
                    int value = 0;
                    if (row.getBoolean("GESTION_STOCK") && !stockChecked.contains(rowStock.getID())) {
                        stockChecked.add(rowStock.getID());

                        value = -Math.round(rowStock.getFloat("QTE_TH") - rowStock.getFloat("QTE_MIN"));
                    } else if (!row.getBoolean("GESTION_STOCK")) {
                        value = comp.getQty().intValue();
                    }
                    if (value > 0) {

                        SQLInjector inj = SQLInjector.getInjector(row.getTable(), row.getTable().getTable("COMMANDE_ELEMENT"));
                        SQLRowValues rowValsElt = new SQLRowValues(inj.createRowValuesFrom(row));

                        int qte = 1;
                        BigDecimal qteUV = BigDecimal.ONE;

                        if (row.getObject("ID_UNITE_VENTE") != null && row.getForeignID("ID_UNITE_VENTE") != UniteVenteArticleSQLElement.A_LA_PIECE) {
                            qteUV = comp.getQty();
                        } else {
                            qte = comp.getQty().setScale(0, RoundingMode.HALF_UP).intValue();
                        }

                        rowValsElt.put("QTE", qte);
                        rowValsElt.put("QTE_UNITAIRE", qteUV);

                        rowValsElt.put("T_POIDS", rowValsElt.getLong("POIDS") * rowValsElt.getInt("QTE"));
                        rowValsElt.put("T_PA_HT", ((BigDecimal) rowValsElt.getObject("PA_HT")).multiply(new BigDecimal(rowValsElt.getInt("QTE")), DecimalUtils.HIGH_PRECISION));
                        rowValsElt.put("T_PA_TTC", ((BigDecimal) rowValsElt.getObject("T_PA_HT")).multiply(new BigDecimal((rowValsElt.getForeign("ID_TAXE").getFloat("TAUX") / 100.0 + 1.0)),
                                DecimalUtils.HIGH_PRECISION));
                        final SQLRow asRow = rowArticleFind.getForeign("ID_FOURNISSEUR").asRow();
                        final SQLRowAccessor rowDeviseF = asRow.getNonEmptyForeign("ID_DEVISE");
                        if (rowDeviseF != null) {
                            rowValsElt.put("ID_DEVISE", rowDeviseF.getID());
                        }
                        map.add(rowArticleFind.getForeign("ID_FOURNISSEUR").asRow(), rowValsElt);
                    }

                }
                MouvementStockSQLElement.createCommandeF(map, null, "");
            }
        });
    }

    /**
     * Transfert en BL
     * 
     * @param row
     */
    public void transfertBonLivraisonClient(final List<SQLRowValues> rows) {
        BonDeLivraisonSQLComponent comp = (BonDeLivraisonSQLComponent) TransfertBaseSQLComponent.openTransfertFrame(rows, "BON_DE_LIVRAISON").getSQLComponent();
        final SQLTable tableElt = comp.getElement().getTable().getTable("BON_DE_LIVRAISON_ELEMENT");
        SQLRowValues rowVals = new SQLRowValues(tableElt);
        rowVals.put("QTE_UNITAIRE", null);
        rowVals.put("QTE", null);
        rowVals.put("QTE_LIVREE", null);
        rowVals.put("ID_ARTICLE", null);
        rowVals.put("PV_HT", null);
        rowVals.put("ID_COMMANDE_CLIENT_ELEMENT", null);

        SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowVals);
        fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {

            @Override
            public SQLSelect transformChecked(SQLSelect input) {
                List<Integer> ids = new ArrayList<Integer>(rows.size());
                for (SQLRowValues sqlRowValues : rows) {
                    ids.add(sqlRowValues.getID());
                }
                SQLSelectJoin joinBR = input.addJoin("RIGHT", tableElt.getTable("BON_DE_LIVRAISON_ELEMENT").getField("ID_BON_DE_LIVRAISON"));
                SQLSelectJoin joinTR = input.addBackwardJoin("RIGHT", tableElt.getTable("TR_COMMANDE_CLIENT").getField("ID_BON_DE_LIVRAISON"), joinBR.getJoinedTable().getAlias());
                joinTR.setWhere(new Where(joinTR.getJoinedTable().getField("ID_COMMANDE_CLIENT"), ids));
                System.err.println(input.asString());
                return input;
            }
        });
        comp.loadQuantity(fetcher.fetch(), "COMMANDE_CLIENT_ELEMENT");
        comp.removeZeroQtyLines();
    }

    /**
     * Transfert en Facture
     * 
     * @param row
     */
    public EditFrame transfertFactureClient(List<SQLRowValues> rows) {
        return TransfertBaseSQLComponent.openTransfertFrame(rows, "SAISIE_VENTE_FACTURE");

    }

    /**
     * Transfert en Facture
     * 
     * @param row
     */
    public EditFrame transfertAcompteClient(List<SQLRowValues> rows) {
        return TransfertGroupSQLComponent.openTransfertFrame(rows, "SAISIE_VENTE_FACTURE", VenteFactureSituationSQLComponent.ID);
    }

    /**
     * Transfert en Facture
     * 
     * @param row
     */
    public EditFrame transfertSoldeClient(List<SQLRowValues> rows) {
        return TransfertGroupSQLComponent.openTransfertFrame(rows, "SAISIE_VENTE_FACTURE", VenteFactureSoldeSQLComponent.ID);
    }

    public BigDecimal getAvancement(SQLRowAccessor r) {
        Collection<? extends SQLRowAccessor> rows = r.getReferentRows(r.getTable().getTable("TR_COMMANDE_CLIENT"));
        long totalFact = 0;
        long total = r.getLong("T_HT");
        for (SQLRowAccessor row : rows) {
            if (!row.isForeignEmpty("ID_SAISIE_VENTE_FACTURE")) {
                SQLRowAccessor rowFact = row.getForeign("ID_SAISIE_VENTE_FACTURE");
                Long l = rowFact.getLong("T_HT");
                totalFact += l;
            }
        }
        if (total > 0) {
            return new BigDecimal(totalFact).divide(new BigDecimal(total), DecimalUtils.HIGH_PRECISION).movePointRight(2).setScale(2, RoundingMode.HALF_UP);
        } else {
            return BigDecimal.ONE.movePointRight(2);
        }
    }

    public RowAction getCloneAction() {
        return new RowAction(new AbstractAction() {

            public void actionPerformed(ActionEvent e) {
                SQLRowAccessor selectedRow = IListe.get(e).getSelectedRow();

                EditFrame editFrame = new EditFrame(CommandeClientSQLElement.this, EditPanel.CREATION);

                ((CommandeClientSQLComponent) editFrame.getSQLComponent()).loadCommandeExistant(selectedRow.getID());
                editFrame.setVisible(true);
            }
        }, true, "sales.quote.clone") {
            @Override
            public boolean enabledFor(java.util.List<org.openconcerto.sql.model.SQLRowValues> selection) {
                return (selection != null && selection.size() == 1);
            }
        };
    }

    @Override
    protected String createCode() {
        return "sales.order";

        // r32617 | ludo | 2021-12-20 11:42:44 CET
        //
        // blocage des intéractions
        // ----------------------------------------------------------------------------
    }

    private final List<SQLTableModifiedListener> listenerCmdInserted = new ArrayList<>();

    public void addInsertedCmdListener(SQLTableModifiedListener l) {
        this.listenerCmdInserted.add(l);
    }

    public void removeInsertedCmdListener(SQLTableModifiedListener l) {
        this.listenerCmdInserted.remove(l);
    }

    public void fireInsertedCmdListener(SQLRow row) {
        for (SQLTableModifiedListener sqlTableModifiedListener : this.listenerCmdInserted) {
            sqlTableModifiedListener.tableModified(new SQLTableEvent(row, Mode.ROW_ADDED, null));
        }
    }

    private Set<String> checkClient(List<? extends SQLRowAccessor> rows) {

        Set<String> clientBloque = new HashSet<>();
        for (SQLRowAccessor sqlRowAccessor : rows) {
            SQLRowAccessor client = sqlRowAccessor.getForeign("ID_CLIENT").fetchNewRow();
            if (client.getBoolean("BLOQUE_LIVRAISON") || client.getBoolean("BLOQUE")) {
                clientBloque.add(client.getString("CODE") + " " + client.getString("NOM"));
            }
        }
        return clientBloque;
    }

}