Dépôt officiel du code source de l'ERP OpenConcerto
Rev 174 | 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.supplychain.stock.element;
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.core.common.element.ComptaSQLConfElement;
import org.openconcerto.erp.core.sales.product.element.ReferenceArticleSQLElement;
import org.openconcerto.erp.core.sales.product.element.UniteVenteArticleSQLElement;
import org.openconcerto.erp.core.sales.product.model.ProductComponent;
import org.openconcerto.erp.core.supplychain.order.component.CommandeSQLComponent;
import org.openconcerto.erp.core.supplychain.order.ui.CommandeItemTable;
import org.openconcerto.erp.core.supplychain.stock.element.StockItem.TypeStockMouvement;
import org.openconcerto.erp.core.supplychain.supplier.component.MouvementStockSQLComponent;
import org.openconcerto.erp.preferences.GestionArticleGlobalPreferencePanel;
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.TreesOfSQLRows;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLBase;
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.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.preferences.SQLPreferences;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.users.UserManager;
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.list.RowValuesTableModel;
import org.openconcerto.ui.FrameUtil;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.ListMap;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.dbutils.ResultSetHandler;
public class MouvementStockSQLElement extends ComptaSQLConfElement {
public MouvementStockSQLElement() {
super("MOUVEMENT_STOCK", "un mouvement de stock", "mouvements de stock");
}
@Override
public ListMap<String, String> getShowAs() {
ListMap<String, String> map = new ListMap<String, String>();
map.putCollection("ID_STOCK", "ID_DEPOT_STOCK");
return map;
}
protected List<String> getListFields() {
final List<String> l = new ArrayList<String>();
l.add("ID_STOCK");
l.add("DATE");
l.add("NOM");
l.add("ID_ARTICLE");
l.add("QTE");
l.add("REEL");
return l;
}
protected List<String> getComboFields() {
final List<String> l = new ArrayList<String>();
l.add("NOM");
l.add("QTE");
return l;
}
/*
* (non-Javadoc)
*
* @see org.openconcerto.devis.SQLElement#getComponent()
*/
public SQLComponent createComponent() {
return new MouvementStockSQLComponent(this);
}
@Override
protected void archive(TreesOfSQLRows trees, boolean cutLinks) throws SQLException {
super.archive(trees, cutLinks);
updateStock(trees.getRows(), true);
}
/**
* Mise à jour des stocks ajoute la quantité si archive est à false
*
* @param id mouvement stock
* @param archive
* @throws SQLException
*/
public ListMap<SQLRow, SQLRowValues> updateStock(Collection<SQLRow> rowsMvt, boolean archive) throws SQLException {
// FIXME: if (SwingUtilities.isEventDispatchThread()) {
// throw new IllegalStateException("This method must be called outside of EDT");
// }
// Stock Reel : inc/dec QTE_REEL, inc/dec QTE_LIV_ATTENTE/inc/dec
// QTE_RECEPT_ATTENTE
// Stock Th : inc/dec QTE_TH, inc/dec QTE_LIV_ATTENTE/inc/dec
// QTE_RECEPT_ATTENTE
final ListMap<SQLRow, SQLRowValues> map = new ListMap<>();
if (rowsMvt == null || rowsMvt.isEmpty()) {
return map;
}
final DBRoot root = rowsMvt.iterator().next().getTable().getDBRoot();
final SQLTable tableCommandeElement = root.getTable("COMMANDE_ELEMENT");
final SQLTable tableStock = root.getTable("STOCK");
for (SQLRow rowMvtStock : rowsMvt) {
boolean retour = rowMvtStock.getString("SOURCE") == null || rowMvtStock.getString("SOURCE").startsWith("AVOIR_CLIENT");
// Mise à jour des stocks
final SQLRow rowArticle = rowMvtStock.getForeignRow("ID_ARTICLE");
SQLRow rowStock = rowMvtStock.getForeignRow(("ID_STOCK"));
if (rowStock == null || rowStock.isUndefined()) {
rowStock = rowArticle.getForeign("ID_STOCK");
}
if (rowMvtStock.getBoolean("REEL")) {
float qte = rowStock.getFloat("QTE_REEL");
float qteMvt = rowMvtStock.getFloat("QTE");
SQLRowValues rowVals = new SQLRowValues(tableStock);
float qteNvlle;
float qteNvlleEnAttenteRecept = rowStock.getFloat("QTE_RECEPT_ATTENTE");
float qteNvlleEnAttenteExp = rowStock.getFloat("QTE_LIV_ATTENTE");
if (archive) {
qteNvlle = qte - qteMvt;
if (!retour) {
// Réception
if (qteMvt > 0) {
qteNvlleEnAttenteRecept += qteMvt;
} else {
// Livraison
qteNvlleEnAttenteExp -= qteMvt;
}
}
} else {
qteNvlle = qte + qteMvt;
if (!retour) {
// Réception
if (qteMvt > 0) {
qteNvlleEnAttenteRecept -= qteMvt;
} else {
// Livraison
qteNvlleEnAttenteExp += qteMvt;
}
}
}
rowVals.put("QTE_REEL", qteNvlle);
rowVals.put("QTE_RECEPT_ATTENTE", qteNvlleEnAttenteRecept);
rowVals.put("QTE_LIV_ATTENTE", qteNvlleEnAttenteExp);
try {
rowVals.update(rowStock.getID());
SQLPreferences prefs = new SQLPreferences(root);
boolean gestionStockMin = prefs.getBoolean(GestionArticleGlobalPreferencePanel.WARNING_STOCK_MIN, true);
if (!archive && gestionStockMin && rowStock.getObject("QTE_MIN") != null && qteNvlle < rowStock.getFloat("QTE_MIN")) {
// final float qteShow = qteNvlle;
SQLInjector inj = SQLInjector.getInjector(rowArticle.getTable(), tableCommandeElement);
SQLRowValues rowValsElt = new SQLRowValues(inj.createRowValuesFrom(rowArticle));
rowValsElt.put("ID_STYLE", 2);
final SQLRow unite = rowArticle.getForeign("ID_UNITE_VENTE");
final float qteElt = rowStock.getFloat("QTE_MIN") - qteNvlle;
if (unite.isUndefined() || unite.getBoolean("A_LA_PIECE")) {
rowValsElt.put("QTE", Math.round(qteElt));
rowValsElt.put("QTE_UNITAIRE", BigDecimal.ONE);
} else {
rowValsElt.put("QTE", 1);
rowValsElt.put("QTE_UNITAIRE", new BigDecimal(qteElt));
}
rowValsElt.put("ID_TAXE", rowValsElt.getObject("ID_TAXE"));
rowValsElt.put("T_POIDS", rowValsElt.getLong("POIDS") * qteElt);
rowValsElt.put("T_PA_HT", rowValsElt.getLong("PA_HT") * qteElt);
rowValsElt.put("T_PA_TTC", rowValsElt.getLong("T_PA_HT") * (rowValsElt.getForeign("ID_TAXE").getFloat("TAUX") / 100.0 + 1.0));
map.add(rowArticle.getForeignRow("ID_FOURNISSEUR"), rowValsElt);
}
} catch (SQLException e) {
throw new SQLException("Erreur lors de la mise à jour du stock pour l'article " + rowArticle.getString("CODE"), e);
}
} else {
float qte = rowStock.getFloat("QTE_TH");
float qteMvt = rowMvtStock.getFloat("QTE");
SQLRowValues rowVals = new SQLRowValues(tableStock);
float qteNvlle;
float qteNvlleEnAttenteRecept = rowStock.getFloat("QTE_RECEPT_ATTENTE");
float qteNvlleEnAttenteExp = rowStock.getFloat("QTE_LIV_ATTENTE");
if (archive) {
qteNvlle = qte - qteMvt;
if (!retour) {
// CommandeF
if (qteMvt > 0) {
qteNvlleEnAttenteRecept -= qteMvt;
} else {
// CommanceC
qteNvlleEnAttenteExp += qteMvt;
}
}
} else {
qteNvlle = qte + qteMvt;
if (!retour) {
// CommandeF
if (qteMvt > 0) {
qteNvlleEnAttenteRecept += qteMvt;
} else {
// CommanceC
qteNvlleEnAttenteExp -= qteMvt;
}
}
}
rowVals.put("QTE_TH", qteNvlle);
rowVals.put("QTE_RECEPT_ATTENTE", qteNvlleEnAttenteRecept);
rowVals.put("QTE_LIV_ATTENTE", qteNvlleEnAttenteExp);
try {
rowVals.update(rowStock.getID());
} catch (SQLException e) {
throw new SQLException("Erreur lors de la mise à jour du stock pour l'article " + rowArticle.getString("CODE"), e);
}
}
}
return map;
}
public static void createCommandeF(final ListMap<SQLRow, SQLRowValues> col, final SQLRow rowDevise) {
createCommandeF(col, rowDevise, "");
}
public static void createCommandeF(final ListMap<SQLRow, SQLRowValues> col, final SQLRow rowDevise, final String ref) {
if (SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("This method must be called outside of EDT");
}
if (col.size() > 0) {
final SQLElement commande = Configuration.getInstance().getDirectory().getElement("COMMANDE");
Boolean useCommandeEnCours = SQLPreferences.getMemCached(commande.getTable().getDBRoot()).getBoolean(GestionCommercialeGlobalPreferencePanel.COMMANDE_FOURNISSEUR_EN_COURS, false);
for (final Entry<SQLRow, List<SQLRowValues>> e : col.entrySet()) {
final SQLRow fournisseur = e.getKey();
// On regarde si il existe une commande en cours existante
final SQLSelect sel = new SQLSelect();
sel.addSelectStar(commande.getTable());
Where w = new Where(commande.getTable().getField("EN_COURS"), "=", Boolean.TRUE);
w = w.and(new Where(commande.getTable().getField("ID_FOURNISSEUR"), "=", fournisseur.getID()));
sel.setWhere(w);
final List<SQLRow> rowsCmd = !useCommandeEnCours ? null
: (List<SQLRow>) Configuration.getInstance().getBase().getDataSource().execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SQLRow commandeExistante = null;
if (rowsCmd != null && rowsCmd.size() > 0) {
commandeExistante = rowsCmd.get(0);
}
EditFrame frame;
CommandeSQLComponent cmp;
if (commandeExistante != null) {
frame = new EditFrame(commande, EditMode.MODIFICATION);
cmp = (CommandeSQLComponent) frame.getSQLComponent();
cmp.select(commandeExistante);
} else {
frame = new EditFrame(commande);
cmp = (CommandeSQLComponent) frame.getSQLComponent();
final SQLRowValues rowVals = new SQLRowValues(commande.getTable());
final SQLElement eltComm = Configuration.getInstance().getDirectory().getElement("COMMERCIAL");
int idUser = UserManager.getInstance().getCurrentUser().getId();
SQLRow rowsComm = SQLBackgroundTableCache.getInstance().getCacheForTable(eltComm.getTable()).getFirstRowContains(idUser, eltComm.getTable().getField("ID_USER_COMMON"));
if (rowsComm != null) {
rowVals.put("ID_COMMERCIAL", rowsComm.getID());
}
if (fournisseur != null && !fournisseur.isUndefined()) {
rowVals.put("ID_FOURNISSEUR", fournisseur.getID());
}
if (rowDevise != null) {
rowVals.put("ID_DEVISE", rowDevise.getID());
}
if (commande.getTable().contains("ID_ADRESSE")) {
rowVals.put("ID_ADRESSE", null);
}
rowVals.put("NOM", ref);
cmp.select(rowVals);
cmp.getRowValuesTable().getRowValuesTableModel().clearRows();
}
CommandeItemTable itemTable = cmp.getRowValuesTablePanel();
final RowValuesTableModel model = cmp.getRowValuesTable().getRowValuesTableModel();
for (SQLRowValues rowValsElt : e.getValue()) {
SQLRowValues rowValsMatch = null;
int index = 0;
for (int i = 0; i < model.getRowCount(); i++) {
final SQLRowValues rowValsCmdElt = model.getRowValuesAt(i);
Number lineArticleNumber = rowValsCmdElt.contains("ID_ARTICLE") ? rowValsCmdElt.getNonEmptyForeignIDNumber("ID_ARTICLE") : null;
Number lineToAddArticleNumber = rowValsElt.contains("ID_ARTICLE") ? rowValsElt.getNonEmptyForeignIDNumber("ID_ARTICLE") : null;
if (lineToAddArticleNumber == null || lineArticleNumber == null || lineArticleNumber.equals(lineToAddArticleNumber)) {
if (ReferenceArticleSQLElement.isReferenceEquals(rowValsCmdElt, rowValsElt)) {
rowValsMatch = rowValsCmdElt;
index = i;
break;
}
}
}
if (rowValsMatch != null) {
int qte = rowValsMatch.getInt("QTE");
BigDecimal qteUV = rowValsMatch.getBigDecimal("QTE_UNITAIRE");
if (rowValsMatch.getObject("ID_UNITE_VENTE") != null && rowValsMatch.getForeignID("ID_UNITE_VENTE") != UniteVenteArticleSQLElement.A_LA_PIECE) {
qteUV = qteUV.multiply(new BigDecimal(qte));
int qteElt = rowValsElt.getInt("QTE");
BigDecimal qteUVElt = rowValsElt.getBigDecimal("QTE_UNITAIRE");
qteUV = qteUV.add(qteUVElt.multiply(new BigDecimal(qteElt)));
qte = 1;
} else {
qte += rowValsElt.getInt("QTE");
}
model.putValue(qte, index, "QTE");
model.putValue(qteUV, index, "QTE_UNITAIRE");
} else {
model.addRow(rowValsElt);
if (rowValsElt.getObject("ID_ARTICLE") != null && !rowValsElt.isForeignEmpty("ID_ARTICLE")) {
Object o = itemTable.tarifCompletion(rowValsElt.getForeign("ID_ARTICLE").asRow(), "PRIX_METRIQUE_HA_1");
if (o != null) {
model.putValue(o, model.getRowCount() - 1, "PRIX_METRIQUE_HA_1");
}
}
}
}
frame.pack();
FrameUtil.show(frame);
}
});
}
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(null, "Aucune commande à passer", "Commande fournisseur", JOptionPane.INFORMATION_MESSAGE);
}
});
}
}
@Override
protected void _initListRequest(ListSQLRequest req) {
super._initListRequest(req);
req.addToGraphToFetch("SOURCE", "IDSOURCE");
}
public static final void showSource(final int id) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("This method must be called from EDT");
}
if (id != 1) {
final SQLBase base = ((ComptaPropsConfiguration) Configuration.getInstance()).getSQLBaseSociete();
final SQLTable tableMvt = base.getTable("MOUVEMENT_STOCK");
final String stringTableSource = tableMvt.getRow(id).getString("SOURCE");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final EditFrame f;
// Si une source est associée on l'affiche en readonly
if (stringTableSource.trim().length() != 0 && tableMvt.getRow(id).getInt("IDSOURCE") != 1) {
f = new EditFrame(Configuration.getInstance().getDirectory().getElement(stringTableSource), EditPanel.READONLY);
f.selectionId(tableMvt.getRow(id).getInt("IDSOURCE"));
} else {
// Sinon on affiche le mouvement de stock
f = new EditFrame(Configuration.getInstance().getDirectory().getElement(tableMvt), EditPanel.READONLY);
f.selectionId(id);
}
f.pack();
FrameUtil.show(f);
}
});
} else {
System.err.println("Aucun mouvement associé, impossible de modifier ou d'accéder à la source de cette ecriture!");
}
}
public void transfertStock(BigDecimal qteReel, final Date dateValue, final SQLRow selectedRowArticle, final SQLRow selectedRowDepotDepart, final SQLRow selectedRowDepotArrivee,
final String labelTrStock) {
final boolean usePrice = getTable().contains("PRICE");
try {
SQLUtils.executeAtomic(selectedRowDepotDepart.getTable().getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
@Override
public Object handle(SQLDataSource ds) throws SQLException {
List<StockItem> stockItems = new ArrayList<StockItem>();
List<String> multipleRequestsHundred = new ArrayList<String>(100);
{
// DEPART
final SQLRowAccessor rowStockDepart = ProductComponent.findOrCreateStock(selectedRowArticle, selectedRowDepotDepart);
StockItem item = new StockItem(selectedRowArticle, rowStockDepart);
if (!item.isStockInit()) {
SQLRowValues rowVals = new SQLRowValues(getTable().getTable("STOCK"));
rowVals.put("ID_ARTICLE", selectedRowArticle.getID());
rowVals.put("ID_DEPOT_STOCK", selectedRowDepotDepart.getID());
rowVals.commit();
selectedRowArticle.fetchValues();
item = new StockItem(selectedRowArticle, rowStockDepart);
}
stockItems.add(item);
double diff = -qteReel.doubleValue();
item.updateQty(diff, TypeStockMouvement.REEL);
multipleRequestsHundred.add(getMvtRequest(dateValue, BigDecimal.ZERO, diff, item, getLabel(labelTrStock, selectedRowDepotDepart, selectedRowDepotArrivee), true, usePrice));
item.updateQty(diff, TypeStockMouvement.THEORIQUE);
multipleRequestsHundred.add(getMvtRequest(dateValue, BigDecimal.ZERO, diff, item, getLabel(labelTrStock, selectedRowDepotDepart, selectedRowDepotArrivee), false, usePrice));
multipleRequestsHundred.add(item.getUpdateRequest());
}
// ARRIVEE
{
final SQLRowAccessor rowStockArrivee = ProductComponent.findOrCreateStock(selectedRowArticle, selectedRowDepotArrivee);
StockItem item = new StockItem(selectedRowArticle, rowStockArrivee);
if (!item.isStockInit()) {
SQLRowValues rowVals = new SQLRowValues(getTable().getTable("STOCK"));
rowVals.put("ID_ARTICLE", selectedRowArticle.getID());
rowVals.put("ID_DEPOT_STOCK", selectedRowDepotArrivee.getID());
rowVals.commit();
selectedRowArticle.fetchValues();
item = new StockItem(selectedRowArticle, rowStockArrivee);
}
stockItems.add(item);
double diff = qteReel.doubleValue();
item.updateQty(diff, TypeStockMouvement.REEL);
multipleRequestsHundred.add(getMvtRequest(dateValue, BigDecimal.ZERO, diff, item, getLabel(labelTrStock, selectedRowDepotDepart, selectedRowDepotArrivee), true, usePrice));
item.updateQty(diff, TypeStockMouvement.THEORIQUE);
multipleRequestsHundred.add(getMvtRequest(dateValue, BigDecimal.ZERO, diff, item, getLabel(labelTrStock, selectedRowDepotDepart, selectedRowDepotArrivee), false, usePrice));
multipleRequestsHundred.add(item.getUpdateRequest());
}
final int size = multipleRequestsHundred.size();
List<? extends ResultSetHandler> handlers = new ArrayList<ResultSetHandler>(size);
for (int i = 0; i < size; i++) {
handlers.add(null);
}
SQLUtils.executeMultiple(selectedRowArticle.getTable().getDBSystemRoot(), multipleRequestsHundred, handlers);
final DBRoot root = getTable().getDBRoot();
if (root.contains("ARTICLE_ELEMENT")) {
// Mise à jour des stocks des nomenclatures
ComposedItemStockUpdater comp = new ComposedItemStockUpdater(root, stockItems);
comp.update();
}
return null;
}
});
} catch (SQLException e1) {
ExceptionHandler.handle("Stock update error", e1);
}
}
private String getLabel(String label, SQLRowAccessor fromDepot, SQLRowAccessor toDepot) {
return label + " de " + fromDepot.getString("NOM") + " vers " + toDepot.getString("NOM");
}
private String getMvtRequest(Date time, BigDecimal prc, double qteFinal, StockItem item, String label, boolean reel, boolean usePrice) {
String mvtStockTableQuoted = getTable().getSQLName().quote();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String mvtStockQuery = "INSERT INTO " + mvtStockTableQuoted + " (\"QTE\",\"DATE\",\"ID_ARTICLE\",\"ID_STOCK\",\"NOM\",\"REEL\",\"ORDRE\"";
if (usePrice && prc != null) {
mvtStockQuery += ",\"PRICE\"";
}
mvtStockQuery += ") VALUES(" + qteFinal + ",'" + dateFormat.format(time) + "'," + item.getArticle().getID() + "," + item.stock.getID() + ",'" + label + "'," + reel
+ ", (SELECT (MAX(\"ORDRE\")+1) FROM " + mvtStockTableQuoted + ")";
if (usePrice && prc != null) {
mvtStockQuery += "," + prc.setScale(6, RoundingMode.HALF_UP).toString();
}
mvtStockQuery += ")";
return mvtStockQuery;
}
@Override
protected String createCode() {
return createCodeOfPackage() + ".transaction";
}
}