OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | 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
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
93 ilm 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.finance.accounting.model;
15
 
16
import org.openconcerto.erp.config.ComptaPropsConfiguration;
17
import org.openconcerto.sql.model.DBRoot;
18
import org.openconcerto.sql.model.Order;
19
import org.openconcerto.sql.model.SQLRow;
20
import org.openconcerto.sql.model.SQLRowListRSH;
21
import org.openconcerto.sql.model.SQLSelect;
22
import org.openconcerto.sql.model.SQLTable;
23
import org.openconcerto.sql.model.Where;
24
import org.openconcerto.utils.DecimalUtils;
156 ilm 25
import org.openconcerto.utils.Tuple2;
26
import org.openconcerto.utils.Tuple3;
93 ilm 27
 
28
import java.math.BigDecimal;
132 ilm 29
import java.util.ArrayList;
93 ilm 30
import java.util.Arrays;
31
import java.util.Calendar;
32
import java.util.Date;
156 ilm 33
import java.util.HashMap;
93 ilm 34
import java.util.List;
156 ilm 35
import java.util.Map;
93 ilm 36
import java.util.TimeZone;
37
 
38
public class CurrencyConverter {
39
    private String companyCurrencyCode;
40
    private final DBRoot root;
156 ilm 41
    private boolean useCache = false;
93 ilm 42
    private String baseCurrencyCode;
43
 
44
    public CurrencyConverter(DBRoot rootSociete, String companyCurrencyCode, String baseCurrencyCode) {
45
        if (companyCurrencyCode == null || companyCurrencyCode.isEmpty()) {
46
            this.companyCurrencyCode = "EUR";
47
        } else {
48
            this.companyCurrencyCode = companyCurrencyCode.trim().toUpperCase();
49
        }
50
        this.baseCurrencyCode = baseCurrencyCode;
51
        this.root = rootSociete;
52
    }
53
 
54
    public CurrencyConverter() {
142 ilm 55
        this(ComptaPropsConfiguration.getInstanceCompta().getRootSociete(), ComptaPropsConfiguration.getInstanceCompta().getCurrency().getCode(), "EUR");
93 ilm 56
    }
57
 
58
    public String getCompanyCurrencyCode() {
59
        return companyCurrencyCode;
60
    }
61
 
62
    public String getBaseCurrencyCode() {
63
        return baseCurrencyCode;
64
    }
65
 
156 ilm 66
    public void setUseCache(boolean useCache) {
67
        this.useCache = useCache;
68
    }
69
 
93 ilm 70
    /**
71
     * Converter an amount to an other currency
132 ilm 72
     */
93 ilm 73
    public BigDecimal convert(BigDecimal amount, String from, String to) {
74
        return convert(amount, from, to, Calendar.getInstance().getTime());
75
    }
76
 
77
    /**
78
     * Converter an amount to an other currency at a precise date
132 ilm 79
     */
93 ilm 80
    public BigDecimal convert(BigDecimal amount, String from, String to, Date date) {
81
        return convert(amount, from, to, date, false);
82
    }
83
 
156 ilm 84
    Map<Tuple3<String, String, Date>, Tuple2<BigDecimal, BigDecimal>> cacheBiased = new HashMap<Tuple3<String, String, Date>, Tuple2<BigDecimal, BigDecimal>>();
85
    Map<Tuple3<String, String, Date>, Tuple2<BigDecimal, BigDecimal>> cacheNotBiased = new HashMap<Tuple3<String, String, Date>, Tuple2<BigDecimal, BigDecimal>>();
86
 
87
    public void clearCache() {
88
        cacheBiased.clear();
89
        cacheNotBiased.clear();
90
    }
91
 
93 ilm 92
    /**
93
     * Converter an amount to an other currency at a precise date
132 ilm 94
     */
93 ilm 95
    public BigDecimal convert(BigDecimal amount, String from, String to, Date date, boolean useBiased) {
96
 
97
        if (from.equalsIgnoreCase(to)) {
98
            return amount;
99
        }
100
 
156 ilm 101
        if (amount.signum() == 0) {
102
            return BigDecimal.ZERO;
103
        }
93 ilm 104
 
105
        BigDecimal r1 = null;
106
        BigDecimal r2 = null;
132 ilm 107
 
156 ilm 108
        if (useCache) {
109
            Tuple3<String, String, Date> key = Tuple3.create(from, to, date);
110
            if (useBiased && cacheBiased.containsKey(key)) {
111
                Tuple2<BigDecimal, BigDecimal> value = cacheBiased.get(key);
112
                r1 = value.get0();
113
                r2 = value.get1();
114
            } else if (!useBiased && cacheNotBiased.containsKey(key)) {
115
                Tuple2<BigDecimal, BigDecimal> value = cacheNotBiased.get(key);
116
                r1 = value.get0();
117
                r2 = value.get1();
118
            }
132 ilm 119
        }
120
 
156 ilm 121
        if (r1 == null || r2 == null) {
122
            // Clean date
123
            final Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
124
            c.setTimeInMillis(date.getTime());
182 ilm 125
            c.set(Calendar.HOUR_OF_DAY, 23);
126
            c.set(Calendar.MINUTE, 59);
127
            c.set(Calendar.SECOND, 59);
128
            c.set(Calendar.MILLISECOND, 59);
129
 
130
            // Limit pour récupérer le taux de la veille pour le taux réel
131
            if (!useBiased) {
132
                c.add(Calendar.DAY_OF_MONTH, -1);
133
            }
134
 
156 ilm 135
            final Date d = c.getTime();
136
 
137
            // Get conversion info
138
            final List<SQLRow> rowsFrom = getRates(from, d);
139
            final List<SQLRow> rowsTo = getRates(to, d);
140
 
141
            List<SQLRow> rows = new ArrayList<SQLRow>();
142
            rows.addAll(rowsTo);
143
            rows.addAll(rowsFrom);
144
            // Pour le taux réel, on récupére celui de la veille
145
            for (SQLRow sqlRow : rows) {
146
                if (sqlRow.getString("DST").equals(from)) {
147
                    if (useBiased) {
148
                        if (r1 == null) {
149
                            r1 = sqlRow.getBigDecimal("TAUX_COMMERCIAL");
150
                        }
151
                    } else {
152
                        r1 = sqlRow.getBigDecimal("TAUX");
153
                    }
132 ilm 154
                }
156 ilm 155
                if (sqlRow.getString("DST").equals(to)) {
156
                    if (useBiased) {
157
                        if (r2 == null) {
158
                            r2 = sqlRow.getBigDecimal("TAUX_COMMERCIAL");
159
                        }
160
                    } else {
161
                        r2 = sqlRow.getBigDecimal("TAUX");
162
                    }
132 ilm 163
                }
164
            }
156 ilm 165
            if (from.equals(this.baseCurrencyCode)) {
166
                r1 = BigDecimal.ONE;
167
            }
168
            if (to.equals(this.baseCurrencyCode)) {
169
                r2 = BigDecimal.ONE;
170
            }
132 ilm 171
 
156 ilm 172
            if (r1 == null || r2 == null) {
173
                // Récupération des taux par défaut dans DEVISE si aucun taux dans l'historique
174
                List<SQLRow> rowsDevise = new ArrayList<SQLRow>();
175
                if (rowsTo.isEmpty() || rowsFrom.isEmpty()) {
176
                    SQLSelect sel = new SQLSelect();
177
                    sel.addSelectStar(root.findTable("DEVISE"));
178
                    rowsDevise.addAll(SQLRowListRSH.execute(sel));
93 ilm 179
                }
156 ilm 180
                if (r2 == null) {
181
                    for (SQLRow sqlRow : rowsDevise) {
182
                        if (sqlRow.getString("CODE").equalsIgnoreCase(to)) {
183
                            r2 = (useBiased ? sqlRow.getBigDecimal("TAUX_COMMERCIAL") : sqlRow.getBigDecimal("TAUX"));
184
                        }
185
                    }
93 ilm 186
                }
156 ilm 187
                if (r1 == null) {
188
                    for (SQLRow sqlRow : rowsDevise) {
189
                        if (sqlRow.getString("CODE").equalsIgnoreCase(from)) {
190
                            r1 = (useBiased ? sqlRow.getBigDecimal("TAUX_COMMERCIAL") : sqlRow.getBigDecimal("TAUX"));
191
                        }
192
                    }
193
                }
93 ilm 194
            }
156 ilm 195
 
93 ilm 196
        }
197
        if (r1 == null) {
198
            throw new IllegalStateException("No conversion rate for " + from);
199
        }
200
        if (r2 == null) {
201
            throw new IllegalStateException("No conversion rate for " + to);
202
        }
156 ilm 203
        if (useCache) {
204
            Tuple3<String, String, Date> key = Tuple3.create(from, to, date);
205
 
206
            if (useBiased) {
207
                cacheBiased.put(key, Tuple2.create(r1, r2));
208
            } else {
209
                cacheNotBiased.put(key, Tuple2.create(r1, r2));
210
            }
211
        }
182 ilm 212
        if (r1.equals(BigDecimal.ZERO)) {
213
            r1 = BigDecimal.ONE;
214
        }
93 ilm 215
        final BigDecimal result = amount.multiply(r2, DecimalUtils.HIGH_PRECISION).divide(r1, DecimalUtils.HIGH_PRECISION);
216
        return result;
217
    }
132 ilm 218
 
219
    public List<SQLRow> getRates(String currencyCode, final Date d) {
220
        final SQLSelect select = new SQLSelect();
221
        final SQLTable t = this.root.getTable("DEVISE_HISTORIQUE");
222
        select.addAllSelect(t, Arrays.asList("ID", "DATE", "SRC", "DST", "TAUX", "TAUX_COMMERCIAL"));
223
        Where w = new Where(t.getField("SRC"), "=", baseCurrencyCode);
224
        w = w.and(new Where(t.getField("DST"), "=", currencyCode));
225
        w = w.and(new Where(t.getField("DATE"), "<=", d));
226
        select.setWhere(w);
227
        select.addFieldOrder(t.getField("DATE"), Order.desc());
182 ilm 228
        // On ne veut que le plus récent
229
        select.setLimit(1);
132 ilm 230
        final List<SQLRow> rows = SQLRowListRSH.execute(select);
231
        return rows;
232
    }
93 ilm 233
}