OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 94 | Rev 142 | 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
 
140
        String fieldPrice = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "PRIX_ACHAT_DEVISE_F" : "PRIX");
141
        String fieldDate = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "DATE_PRIX" : "DATE");
142
 
93 ilm 143
        // get all costs
144
        final SQLTable costTable = root.getTable(type.name());
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));
150
        sel.addSelect(costTable.getField(fieldDate));
93 ilm 151
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE"), true, productQties.keySet()));
152
        sel.addFieldOrder(costTable.getField("QTE"));
132 ilm 153
        sel.addFieldOrder(costTable.getField(fieldDate));
93 ilm 154
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
155
        @SuppressWarnings("unchecked")
156
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
157
        for (SQLRow sqlRow : l) {
158
            System.out.println(sqlRow.getID() + ":" + sqlRow.getAllValues());
159
        }
160
        final int size = l.size();
161
        for (Long id : productQties.keySet()) {
162
            BigDecimal cost = BigDecimal.ZERO;
163
            final int qty = productQties.get(id);
164
            for (int i = 0; i < size; i++) {
165
                final SQLRow row = l.get(i);
166
                if (row.getInt("ID_ARTICLE") == id.intValue()) {
167
                    // stop when the max qty is found
168
                    if (row.getLong("QTE") > qty) {
169
                        if (cost == null) {
132 ilm 170
                            cost = row.getBigDecimal(fieldPrice);
93 ilm 171
                        }
172
                        break;
173
                    }
132 ilm 174
                    cost = row.getBigDecimal(fieldPrice);
93 ilm 175
 
176
                }
177
            }
178
            if (cost == null) {
179
                cost = BigDecimal.ZERO;
180
            }
181
 
182
            result.put(id, cost);
183
        }
184
        return result;
185
    }
186
 
187
    /**
188
     *
189
     * @param items List de SQLRowAccessor avec ID_ARTICLE, QTE, QTE_UV
190
     * @return Map article qty
191
     */
192
    public List<ProductComponent> getChildWithQtyFrom(final List<ProductComponent> items) {
193
 
194
        return getChildWithQtyFrom(items, new HashSet<Integer>());
195
    }
196
 
197
    private List<ProductComponent> getChildWithQtyFrom(List<ProductComponent> items, Set<Integer> ancestors) {
198
 
199
        if (root.contains("ARTICLE_ELEMENT")) {
200
 
201
            int originalAncestorsSize = ancestors.size();
202
 
203
            List<ProductComponent> result = new ArrayList<ProductComponent>();
204
 
205
            // liste des ids parents
206
            final List<Integer> parentsArticleIDs = new ArrayList<Integer>();
207
 
208
            // Quantité par parents
209
            Map<Integer, ProductComponent> productCompByID = new HashMap<Integer, ProductComponent>();
210
            final Map<Integer, BigDecimal> qtyParent = new HashMap<Integer, BigDecimal>();
211
            for (ProductComponent p : items) {
212
                parentsArticleIDs.add(p.getProduct().getID());
213
                qtyParent.put(p.getProduct().getID(), p.getQty());
214
            }
215
 
216
            // get all childs
217
            final SQLTable costTable = root.getTable("ARTICLE_ELEMENT");
218
 
219
            SQLRowValues rowVals = new SQLRowValues(costTable);
220
 
221
            final SQLRowValues stockRowValues = rowVals.putRowValues("ID_ARTICLE").put("ID", null).put("GESTION_STOCK", null).put("CODE", null).put("NOM", null).putRowValues("ID_STOCK");
222
            stockRowValues.putNulls("QTE_TH", "QTE_RECEPT_ATTENTE", "QTE_REEL", "QTE_LIV_ATTENTE");
223
            rowVals.putRowValues("ID_ARTICLE_PARENT").put("ID", null);
224
            rowVals.put("QTE", null);
225
            rowVals.put("QTE_UNITAIRE", null);
226
 
227
            SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowVals);
228
            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
229
 
230
                @Override
231
                public SQLSelect transformChecked(SQLSelect input) {
232
 
233
                    input.setWhere(new Where(costTable.getField("ID_ARTICLE_PARENT"), parentsArticleIDs));
234
                    return input;
235
                }
236
            });
237
 
238
            List<SQLRowValues> childs = fetcher.fetch();
239
 
240
            if (childs.size() > 0) {
241
 
242
                for (SQLRowValues childRowValues : childs) {
243
 
244
                    final SQLRowAccessor foreignArticleParent = childRowValues.getForeign("ID_ARTICLE_PARENT");
245
                    ProductComponent childComponent = ProductComponent.createFrom(childRowValues);
246
                    // Test pour éviter les boucles dans les boms
247
                    if (!ancestors.contains(childComponent.getProduct().getID())) {
248
                        ancestors.add(foreignArticleParent.getID());
249
                        // parentsArticleIDs.remove(foreignArticleParent.getID());
250
                        // Calcul de la quantité qte_unit * qte * qteMergedParent
251
                        childComponent.setQty(childComponent.getQty().multiply(qtyParent.get(foreignArticleParent.getID()), DecimalUtils.HIGH_PRECISION));
252
 
253
                        // Cumul des valeurs si l'article est présent plusieurs fois dans le bom
254
                        ProductComponent existProduct = productCompByID.get(childComponent.getProduct().getID());
255
                        if (existProduct == null) {
256
                            result.add(childComponent);
257
                            productCompByID.put(childComponent.getProduct().getID(), childComponent);
258
                        } else {
259
                            existProduct.addQty(childComponent.getQty());
260
                        }
261
                    }
262
                }
263
 
264
                // Recherche si un kit est présent parmis les articles
265
                final List<ProductComponent> bomFromChilds = getChildWithQtyFrom(new ArrayList(result), ancestors);
266
                // Merge des valeurs
267
                for (ProductComponent s : bomFromChilds) {
268
 
269
                    ProductComponent existProduct = productCompByID.get(s.getProduct().getID());
270
                    if (existProduct == null) {
271
                        result.add(s);
272
                        productCompByID.put(s.getProduct().getID(), s);
273
                    } else {
274
                        existProduct.addQty(s.getQty());
275
                    }
276
                }
277
            }
278
 
279
            // Ajout des articles présents dans l'ensemble de départ
280
            if (originalAncestorsSize == 0) {
281
                for (ProductComponent p : items) {
282
                    ProductComponent existProduct = productCompByID.get(p.getProduct().getID());
283
                    if (existProduct == null) {
284
                        result.add(p);
285
                        productCompByID.put(p.getProduct().getID(), p);
286
                    } else {
287
                        existProduct.addQty(p.getQty());
288
                    }
289
                }
290
            }
291
 
292
            // On supprime les ancestors (kits) du result
293
            for (Integer anc : ancestors) {
294
                ProductComponent comp = productCompByID.get(anc);
295
                if (comp != null) {
296
                    result.remove(comp);
297
                }
298
            }
299
 
300
            return result;
301
        } else {
302
            return items;
303
        }
304
    }
305
 
306
    public Map<Long, Integer> getBOM(Long productId) {
307
        final Map<Long, Integer> result = new HashMap<Long, Integer>();
308
        // get all costs
309
        final SQLTable costTable = root.getTable("ARTICLE_ELEMENT");
310
        final SQLSelect sel = new SQLSelect();
311
 
312
        sel.addSelect(costTable.getField("ID_ARTICLE"));
313
        sel.addSelect(costTable.getField("QTE"));
314
 
315
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE_PARENT"), "=", productId));
316
        sel.addFieldOrder(costTable.getField("QTE"));
317
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
318
        @SuppressWarnings("unchecked")
319
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
320
        final int size = l.size();
321
        for (int i = 0; i < size; i++) {
322
            final SQLRow row = l.get(i);
323
            final long id = row.getLong("ID_ARTICLE");
324
            Integer qte = result.get(id);
325
            if (qte == null) {
326
                qte = row.getInt("QTE");
327
            } else {
328
                qte = qte + row.getInt("QTE");
329
            }
330
            result.put(id, qte);
331
        }
332
 
333
        return result;
334
    }
335
 
336
    public enum TypePrice {
132 ilm 337
        ARTICLE_PRIX_REVIENT, ARTICLE_PRIX_MIN_VENTE, ARTICLE_PRIX_PUBLIC, ARTICLE_TARIF_FOURNISSEUR
93 ilm 338
    };
339
 
340
    public BigDecimal getBomPriceForQuantity(int qty, Collection<? extends SQLRowAccessor> rowValuesProductItems, TypePrice type) {
341
        final Map<Long, Integer> productQties = new HashMap<Long, Integer>();
342
        int count = rowValuesProductItems.size();
343
        for (SQLRowAccessor v : rowValuesProductItems) {
344
            if (v.getObject("ID_ARTICLE") != null) {
345
                System.out.println("id:" + v.getObject("ID_ARTICLE"));
346
                int id = v.getForeignID("ID_ARTICLE");
347
                int qte = v.getInt("QTE") * qty;
348
                Integer qteForId = productQties.get(Long.valueOf(id));
349
                if (qteForId == null) {
350
                    productQties.put(Long.valueOf(id), qte);
351
                } else {
352
                    productQties.put(Long.valueOf(id), qte + qteForId);
353
                }
354
            }
355
        }
356
        Map<Long, BigDecimal> costs = getUnitCost(productQties, type);
357
        BigDecimal cost = null;
358
        for (SQLRowAccessor v : rowValuesProductItems) {
359
            if (v.getObject("ID_ARTICLE") != null) {
360
                int id = v.getForeignID("ID_ARTICLE");
361
                int qte = v.getInt("QTE");
362
                final BigDecimal unitCost = costs.get(Long.valueOf(id));
94 ilm 363
                BigDecimal lineCost = unitCost.multiply(BigDecimal.valueOf(qte)).multiply(v.getBigDecimal("QTE_UNITAIRE"));
93 ilm 364
                if (cost == null) {
365
                    cost = BigDecimal.ZERO;
366
                }
367
                cost = cost.add(lineCost);
368
            }
369
        }
370
        return cost;
371
 
372
    }
373
 
374
    public BigDecimal getUnitCost(int id, int qty, TypePrice type) {
375
        Map<Long, Integer> productQties = new HashMap<Long, Integer>();
376
        productQties.put(Long.valueOf(id), Integer.valueOf(qty));
377
        final Map<Long, BigDecimal> unitCost = getUnitCost(productQties, type);
378
        System.out.println(">" + unitCost);
379
        return unitCost.get(Long.valueOf(id));
380
    }
381
 
382
    public Date getUnitCostDate(int id, int qty, TypePrice type) {
383
        Map<Long, Integer> productQties = new HashMap<Long, Integer>();
384
        productQties.put(Long.valueOf(id), Integer.valueOf(qty));
385
        final Map<Long, Date> unitCost = getUnitCostDate(productQties, type);
386
        System.out.println(">" + unitCost);
387
        return unitCost.get(Long.valueOf(id));
388
    }
389
 
390
    private Map<Long, Date> getUnitCostDate(Map<Long, Integer> productQties, TypePrice type) {
391
        final Map<Long, Date> result = new HashMap<Long, Date>();
132 ilm 392
 
393
        String fieldPrice = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "PRIX_ACHAT_DEVISE_F" : "PRIX");
394
        String fieldDate = (type == TypePrice.ARTICLE_TARIF_FOURNISSEUR ? "DATE_PRIX" : "DATE");
395
 
93 ilm 396
        // get all costs
397
        final SQLTable costTable = root.getTable(type.name());
398
        final SQLSelect sel = new SQLSelect();
399
        sel.addSelect(costTable.getKey());
400
        sel.addSelect(costTable.getField("ID_ARTICLE"));
401
        sel.addSelect(costTable.getField("QTE"));
132 ilm 402
        sel.addSelect(costTable.getField(fieldPrice));
403
        sel.addSelect(costTable.getField(fieldDate));
93 ilm 404
        sel.setWhere(new Where(costTable.getField("ID_ARTICLE"), true, productQties.keySet()));
405
        sel.addFieldOrder(costTable.getField("QTE"));
132 ilm 406
        sel.addFieldOrder(costTable.getField(fieldDate));
93 ilm 407
        final SQLDataSource src = root.getDBSystemRoot().getDataSource();
408
        @SuppressWarnings("unchecked")
409
        final List<SQLRow> l = (List<SQLRow>) src.execute(sel.asString(), SQLRowListRSH.createFromSelect(sel));
410
        for (SQLRow sqlRow : l) {
411
            System.out.println(sqlRow.getID() + ":" + sqlRow.getAllValues());
412
        }
413
        final int size = l.size();
414
        for (Long id : productQties.keySet()) {
415
            Calendar cost = null;
416
            final int qty = productQties.get(id);
417
            for (int i = 0; i < size; i++) {
418
                final SQLRow row = l.get(i);
419
                if (row.getInt("ID_ARTICLE") == id.intValue()) {
420
                    // stop when the max qty is found
421
                    if (row.getLong("QTE") > qty) {
422
                        if (cost == null) {
423
                            cost = row.getDate("DATE");
424
                        }
425
                        break;
426
                    }
427
                    cost = row.getDate("DATE");
428
 
429
                }
430
            }
431
            if (cost != null)
432
                result.put(id, cost.getTime());
433
            else
434
                result.put(id, new Date());
435
        }
436
        return result;
437
    }
438
}