OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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