OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 132 | Rev 156 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
93 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.core.sales.product.model;
15
 
16
import org.openconcerto.sql.model.DBRoot;
17
import org.openconcerto.sql.model.SQLDataSource;
18
import org.openconcerto.sql.model.SQLRow;
19
import org.openconcerto.sql.model.SQLRowAccessor;
20
import org.openconcerto.sql.model.SQLRowListRSH;
21
import org.openconcerto.sql.model.SQLRowValues;
22
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
23
import org.openconcerto.sql.model.SQLSelect;
24
import org.openconcerto.sql.model.SQLTable;
25
import org.openconcerto.sql.model.Where;
26
import org.openconcerto.utils.DecimalUtils;
27
import org.openconcerto.utils.cc.ITransformer;
28
 
29
import java.math.BigDecimal;
132 ilm 30
import java.math.RoundingMode;
93 ilm 31
import java.util.ArrayList;
32
import java.util.Calendar;
33
import java.util.Collection;
34
import java.util.Date;
35
import java.util.HashMap;
36
import java.util.HashSet;
37
import java.util.List;
38
import java.util.Map;
39
import java.util.Set;
40
 
41
public class ProductHelper {
42
 
43
    private DBRoot root;
44
 
45
    public ProductHelper(DBRoot root) {
46
        this.root = root;
47
    }
48
 
132 ilm 49
    public interface PriceField {
50
    };
51
 
52
    public enum SupplierPriceField implements PriceField {
53
        PRIX_ACHAT, COEF_TRANSPORT_PORT, COEF_TAXE_D, COEF_TRANSPORT_SIEGE, COEF_FRAIS_MOULE, COEF_FRAIS_INDIRECTS, COEF_PRIX_MINI
54
    };
55
 
56
    public BigDecimal getEnumPrice(final SQLRowAccessor r, PriceField field) {
57
        final PriceField[] values = field.getClass().getEnumConstants();
58
        BigDecimal result = r.getBigDecimal(values[0].toString());
59
        if (result == null) {
60
            return null;
61
        }
62
 
63
        for (int i = 1; i < values.length; i++) {
64
 
65
            BigDecimal m0 = r.getBigDecimal(values[i].toString());
66
            if (m0 != null && m0.floatValue() > 0) {
67
                result = result.divide(m0, 2, RoundingMode.HALF_UP);
68
            }
69
            if (values[i] == field) {
70
                break;
71
            }
72
        }
73
        return result;
74
    }
75
 
93 ilm 76
    public BigDecimal getUnitCostForQuantity(SQLRowAccessor rArticle, int qty) {
77
 
78
        Collection<? extends SQLRowAccessor> l = rArticle.getReferentRows(rArticle.getTable().getTable("ARTICLE_PRIX_REVIENT"));
79
        BigDecimal result = null;
80
 
81
        for (SQLRowAccessor row : l) {
82
 
83
            if (row.getLong("QTE") > qty) {
84
                break;
85
            }
86
            result = row.getBigDecimal("PRIX");
87
        }
88
        if (result == null) {
89
            // Can occur during editing
90
            result = BigDecimal.ZERO;
91
        }
92
        return result;
93
    }
94
 
95
    @SuppressWarnings("unchecked")
96
    public List<String> getRequiredProperties(int categoryId) {
97
        final SQLTable table = root.getTable("FAMILLE_CARACTERISTIQUE");
98
        final SQLSelect sel = new SQLSelect();
99
        sel.addSelect(table.getField("NOM"));
100
        sel.setWhere(table.getField("ID_FAMILLE_ARTICLE"), "=", categoryId);
101
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
102
        return (List<String>) src.executeCol(sel.asString());
103
    }
104
 
105
    /**
106
     * Get the minimum quantity used to provide a cost for a product
107
     *
108
     * @return -1 if no quantity are provided
132 ilm 109
     */
93 ilm 110
    public int getMinQuantityForCostCalculation(int productId) {
111
        final SQLTable costTable = root.getTable("ARTICLE_PRIX_REVIENT");
112
        final SQLSelect sel = new SQLSelect();
113
        sel.addSelect(costTable.getKey());
114
        sel.addSelect(costTable.getField("ID_ARTICLE"));
115
        sel.addSelect(costTable.getField("QTE"));
116
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE"), "=", productId));
117
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
118
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
119
        if (l.isEmpty()) {
120
            return -1;
121
        }
122
        int min = Integer.MAX_VALUE;
123
        for (SQLRow sqlRow : l) {
124
            int n = sqlRow.getInt("QTE");
125
            if (n < min) {
126
                min = n;
127
            }
128
        }
129
        return min;
130
    }
131
 
132
    /**
133
     * Get the cost for products and quantities
134
     *
135
     * @return for each product ID the unit cost
132 ilm 136
     */
93 ilm 137
    public Map<Long, BigDecimal> getUnitCost(Map<Long, Integer> productQties, TypePrice type) {
138
        final Map<Long, BigDecimal> result = new HashMap<Long, BigDecimal>();
132 ilm 139
 
142 ilm 140
        String fieldPrice = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR || type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP ? "PRIX_ACHAT_DEVISE_F" : "PRIX");
141
        String fieldDate = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR || type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP ? "DATE_PRIX" : "DATE");
132 ilm 142
 
93 ilm 143
        // get all costs
142 ilm 144
        final SQLTable costTable = root.getTable(type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP ? "ARTICLE_TARIF_FOURNISSEUR" : type.name());
93 ilm 145
        final SQLSelect sel = new SQLSelect();
146
        sel.addSelect(costTable.getKey());
147
        sel.addSelect(costTable.getField("ID_ARTICLE"));
148
        sel.addSelect(costTable.getField("QTE"));
132 ilm 149
        sel.addSelect(costTable.getField(fieldPrice));
142 ilm 150
        if (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP) {
151
            for (SupplierPriceField f : SupplierPriceField.values()) {
152
                sel.addSelect(costTable.getField(f.name()));
153
            }
154
        }
132 ilm 155
        sel.addSelect(costTable.getField(fieldDate));
93 ilm 156
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE"), true, productQties.keySet()));
157
        sel.addFieldOrder(costTable.getField("QTE"));
132 ilm 158
        sel.addFieldOrder(costTable.getField(fieldDate));
93 ilm 159
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
160
        @SuppressWarnings("unchecked")
161
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
162
        for (SQLRow sqlRow : l) {
163
            System.out.println(sqlRow.getID() + ":" + sqlRow.getAllValues());
164
        }
165
        final int size = l.size();
142 ilm 166
        if (size == 0 && type == TypePrice.ARTICLE_PRIX_REVIENT) {
167
            return getUnitCost(productQties, TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP);
168
        } else {
169
            for (Long id : productQties.keySet()) {
170
                BigDecimal cost = BigDecimal.ZERO;
171
                final int qty = productQties.get(id);
172
                for (int i = 0; i < size; i++) {
173
                    final SQLRow row = l.get(i);
174
                    if (row.getInt("ID_ARTICLE") == id.intValue()) {
175
                        // stop when the max qty is found
176
                        if (row.getLong("QTE") > qty) {
177
                            if (cost == null) {
178
                                if (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP) {
179
                                    cost = getEnumPrice(row, SupplierPriceField.COEF_TRANSPORT_SIEGE);
180
                                } else {
181
                                    cost = row.getBigDecimal(fieldPrice);
182
                                }
183
                            }
184
                            break;
185
                        }
186
                        if (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR_DDP) {
187
                            cost = getEnumPrice(row, SupplierPriceField.COEF_TRANSPORT_SIEGE);
188
                        } else {
132 ilm 189
                            cost = row.getBigDecimal(fieldPrice);
93 ilm 190
                        }
142 ilm 191
 
93 ilm 192
                    }
142 ilm 193
                }
194
                if (cost == null) {
195
                    cost = BigDecimal.ZERO;
196
                }
93 ilm 197
 
142 ilm 198
                result.put(id, cost);
93 ilm 199
            }
142 ilm 200
            return result;
93 ilm 201
        }
202
    }
203
 
204
    /**
205
     *
206
     * @param items List de SQLRowAccessor avec ID_ARTICLE, QTE, QTE_UV
207
     * @return Map article qty
208
     */
209
    public List<ProductComponent> getChildWithQtyFrom(final List<ProductComponent> items) {
210
 
211
        return getChildWithQtyFrom(items, new HashSet<Integer>());
212
    }
213
 
214
    private List<ProductComponent> getChildWithQtyFrom(List<ProductComponent> items, Set<Integer> ancestors) {
215
 
216
        if (root.contains("ARTICLE_ELEMENT")) {
217
 
218
            int originalAncestorsSize = ancestors.size();
219
 
220
            List<ProductComponent> result = new ArrayList<ProductComponent>();
221
 
222
            // liste des ids parents
223
            final List<Integer> parentsArticleIDs = new ArrayList<Integer>();
224
 
225
            // Quantité par parents
226
            Map<Integer, ProductComponent> productCompByID = new HashMap<Integer, ProductComponent>();
227
            final Map<Integer, BigDecimal> qtyParent = new HashMap<Integer, BigDecimal>();
228
            for (ProductComponent p : items) {
229
                parentsArticleIDs.add(p.getProduct().getID());
142 ilm 230
                BigDecimal qty = BigDecimal.ZERO;
231
                if (qtyParent.get(p.getProduct().getID()) != null) {
232
                    qty = qtyParent.get(p.getProduct().getID());
233
                }
234
                qtyParent.put(p.getProduct().getID(), qty.add(p.getQty()));
93 ilm 235
            }
236
 
237
            // get all childs
238
            final SQLTable costTable = root.getTable("ARTICLE_ELEMENT");
239
 
240
            SQLRowValues rowVals = new SQLRowValues(costTable);
241
 
242
            final SQLRowValues stockRowValues = rowVals.putRowValues("ID_ARTICLE").put("ID", null).put("GESTION_STOCK", null).put("CODE", null).put("NOM", null).putRowValues("ID_STOCK");
243
            stockRowValues.putNulls("QTE_TH", "QTE_RECEPT_ATTENTE", "QTE_REEL", "QTE_LIV_ATTENTE");
244
            rowVals.putRowValues("ID_ARTICLE_PARENT").put("ID", null);
245
            rowVals.put("QTE", null);
246
            rowVals.put("QTE_UNITAIRE", null);
247
 
248
            SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowVals);
249
            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
250
 
251
                @Override
252
                public SQLSelect transformChecked(SQLSelect input) {
253
 
254
                    input.setWhere(new Where(costTable.getField("ID_ARTICLE_PARENT"), parentsArticleIDs));
255
                    return input;
256
                }
257
            });
258
 
259
            List<SQLRowValues> childs = fetcher.fetch();
260
 
261
            if (childs.size() > 0) {
262
 
263
                for (SQLRowValues childRowValues : childs) {
264
                    final SQLRowAccessor foreignArticleParent = childRowValues.getForeign("ID_ARTICLE_PARENT");
265
 
142 ilm 266
                    if (!childRowValues.isForeignEmpty("ID_ARTICLE") && childRowValues.getForeign("ID_ARTICLE") != null) {
267
                        ProductComponent childComponent = ProductComponent.createFrom(childRowValues);
268
                        // Test pour éviter les boucles dans les boms
269
                        if (!ancestors.contains(childComponent.getProduct().getID())) {
270
                            ancestors.add(foreignArticleParent.getID());
271
                            // parentsArticleIDs.remove(foreignArticleParent.getID());
272
                            // Calcul de la quantité qte_unit * qte * qteMergedParent
273
                            childComponent.setQty(childComponent.getQty().multiply(qtyParent.get(foreignArticleParent.getID()), DecimalUtils.HIGH_PRECISION));
274
 
275
                            // Cumul des valeurs si l'article est présent plusieurs fois dans le bom
276
                            ProductComponent existProduct = productCompByID.get(childComponent.getProduct().getID());
277
                            if (existProduct == null) {
278
                                result.add(childComponent);
279
                                productCompByID.put(childComponent.getProduct().getID(), childComponent);
280
                            } else {
281
                                existProduct.addQty(childComponent.getQty());
282
                            }
93 ilm 283
                        }
284
                    }
285
                }
286
 
287
                // Recherche si un kit est présent parmis les articles
288
                final List<ProductComponent> bomFromChilds = getChildWithQtyFrom(new ArrayList(result), ancestors);
289
                // Merge des valeurs
290
                for (ProductComponent s : bomFromChilds) {
291
 
292
                    ProductComponent existProduct = productCompByID.get(s.getProduct().getID());
293
                    if (existProduct == null) {
294
                        result.add(s);
295
                        productCompByID.put(s.getProduct().getID(), s);
296
                    } else {
297
                        existProduct.addQty(s.getQty());
298
                    }
299
                }
300
            }
301
 
302
            // Ajout des articles présents dans l'ensemble de départ
303
            if (originalAncestorsSize == 0) {
304
                for (ProductComponent p : items) {
305
                    ProductComponent existProduct = productCompByID.get(p.getProduct().getID());
306
                    if (existProduct == null) {
307
                        result.add(p);
308
                        productCompByID.put(p.getProduct().getID(), p);
309
                    } else {
310
                        existProduct.addQty(p.getQty());
311
                    }
312
                }
313
            }
314
 
315
            // On supprime les ancestors (kits) du result
316
            for (Integer anc : ancestors) {
317
                ProductComponent comp = productCompByID.get(anc);
318
                if (comp != null) {
319
                    result.remove(comp);
320
                }
321
            }
322
 
323
            return result;
324
        } else {
325
            return items;
326
        }
327
    }
328
 
329
    public Map<Long, Integer> getBOM(Long productId) {
330
        final Map<Long, Integer> result = new HashMap<Long, Integer>();
331
        // get all costs
332
        final SQLTable costTable = root.getTable("ARTICLE_ELEMENT");
333
        final SQLSelect sel = new SQLSelect();
334
 
335
        sel.addSelect(costTable.getField("ID_ARTICLE"));
336
        sel.addSelect(costTable.getField("QTE"));
337
 
338
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE_PARENT"), "=", productId));
339
        sel.addFieldOrder(costTable.getField("QTE"));
340
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
341
        @SuppressWarnings("unchecked")
342
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
343
        final int size = l.size();
344
        for (int i = 0; i < size; i++) {
345
            final SQLRow row = l.get(i);
346
            final long id = row.getLong("ID_ARTICLE");
347
            Integer qte = result.get(id);
348
            if (qte == null) {
349
                qte = row.getInt("QTE");
350
            } else {
351
                qte = qte + row.getInt("QTE");
352
            }
353
            result.put(id, qte);
354
        }
355
 
356
        return result;
357
    }
358
 
359
    public enum TypePrice {
142 ilm 360
        ARTICLE_PRIX_REVIENT, ARTICLE_PRIX_MIN_VENTE, ARTICLE_PRIX_PUBLIC, ARTICLE_TARIF_FOURNISSEUR, ARTICLE_TARIF_FOURNISSEUR_DDP
93 ilm 361
    };
362
 
363
    public BigDecimal getBomPriceForQuantity(int qty, Collection<? extends SQLRowAccessor> rowValuesProductItems, TypePrice type) {
364
        final Map<Long, Integer> productQties = new HashMap<Long, Integer>();
365
        int count = rowValuesProductItems.size();
366
        for (SQLRowAccessor v : rowValuesProductItems) {
367
            if (v.getObject("ID_ARTICLE") != null) {
368
                System.out.println("id:" + v.getObject("ID_ARTICLE"));
369
                int id = v.getForeignID("ID_ARTICLE");
370
                int qte = v.getInt("QTE") * qty;
371
                Integer qteForId = productQties.get(Long.valueOf(id));
372
                if (qteForId == null) {
373
                    productQties.put(Long.valueOf(id), qte);
374
                } else {
375
                    productQties.put(Long.valueOf(id), qte + qteForId);
376
                }
377
            }
378
        }
379
        Map<Long, BigDecimal> costs = getUnitCost(productQties, type);
380
        BigDecimal cost = null;
381
        for (SQLRowAccessor v : rowValuesProductItems) {
382
            if (v.getObject("ID_ARTICLE") != null) {
383
                int id = v.getForeignID("ID_ARTICLE");
384
                int qte = v.getInt("QTE");
385
                final BigDecimal unitCost = costs.get(Long.valueOf(id));
94 ilm 386
                BigDecimal lineCost = unitCost.multiply(BigDecimal.valueOf(qte)).multiply(v.getBigDecimal("QTE_UNITAIRE"));
93 ilm 387
                if (cost == null) {
388
                    cost = BigDecimal.ZERO;
389
                }
390
                cost = cost.add(lineCost);
391
            }
392
        }
393
        return cost;
394
 
395
    }
396
 
397
    public BigDecimal getUnitCost(int id, int qty, TypePrice type) {
398
        Map<Long, Integer> productQties = new HashMap<Long, Integer>();
399
        productQties.put(Long.valueOf(id), Integer.valueOf(qty));
400
        final Map<Long, BigDecimal> unitCost = getUnitCost(productQties, type);
401
        System.out.println(">" + unitCost);
402
        return unitCost.get(Long.valueOf(id));
403
    }
404
 
405
    public Date getUnitCostDate(int id, int qty, TypePrice type) {
406
        Map<Long, Integer> productQties = new HashMap<Long, Integer>();
407
        productQties.put(Long.valueOf(id), Integer.valueOf(qty));
408
        final Map<Long, Date> unitCost = getUnitCostDate(productQties, type);
409
        System.out.println(">" + unitCost);
410
        return unitCost.get(Long.valueOf(id));
411
    }
412
 
413
    private Map<Long, Date> getUnitCostDate(Map<Long, Integer> productQties, TypePrice type) {
414
        final Map<Long, Date> result = new HashMap<Long, Date>();
132 ilm 415
 
416
        String fieldPrice = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "PRIX_ACHAT_DEVISE_F" : "PRIX");
417
        String fieldDate = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "DATE_PRIX" : "DATE");
418
 
93 ilm 419
        // get all costs
420
        final SQLTable costTable = root.getTable(type.name());
421
        final SQLSelect sel = new SQLSelect();
422
        sel.addSelect(costTable.getKey());
423
        sel.addSelect(costTable.getField("ID_ARTICLE"));
424
        sel.addSelect(costTable.getField("QTE"));
132 ilm 425
        sel.addSelect(costTable.getField(fieldPrice));
426
        sel.addSelect(costTable.getField(fieldDate));
93 ilm 427
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE"), true, productQties.keySet()));
428
        sel.addFieldOrder(costTable.getField("QTE"));
132 ilm 429
        sel.addFieldOrder(costTable.getField(fieldDate));
93 ilm 430
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
431
        @SuppressWarnings("unchecked")
432
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
433
        for (SQLRow sqlRow : l) {
434
            System.out.println(sqlRow.getID() + ":" + sqlRow.getAllValues());
435
        }
436
        final int size = l.size();
437
        for (Long id : productQties.keySet()) {
438
            Calendar cost = null;
439
            final int qty = productQties.get(id);
440
            for (int i = 0; i < size; i++) {
441
                final SQLRow row = l.get(i);
442
                if (row.getInt("ID_ARTICLE") == id.intValue()) {
443
                    // stop when the max qty is found
444
                    if (row.getLong("QTE") > qty) {
445
                        if (cost == null) {
446
                            cost = row.getDate("DATE");
447
                        }
448
                        break;
449
                    }
450
                    cost = row.getDate("DATE");
451
 
452
                }
453
            }
454
            if (cost != null)
455
                result.put(id, cost.getTime());
456
            else
457
                result.put(id, new Date());
458
        }
459
        return result;
460
    }
461
}