/* jBilling - The Enterprise Open Source Billing System Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde This file is part of jbilling. jbilling is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jbilling is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with jbilling. If not, see <http://www.gnu.org/licenses/>. */ package com.sapienter.jbilling.server.item; import com.sapienter.jbilling.common.Constants; import com.sapienter.jbilling.common.JNDILookup; import com.sapienter.jbilling.common.SessionInternalError; import com.sapienter.jbilling.server.user.EntityBL; import com.sapienter.jbilling.server.user.db.CompanyDAS; import com.sapienter.jbilling.server.user.db.CompanyDTO; import com.sapienter.jbilling.server.util.db.CurrencyDAS; import com.sapienter.jbilling.server.util.db.CurrencyDTO; import com.sapienter.jbilling.server.util.db.CurrencyExchangeDAS; import com.sapienter.jbilling.server.util.db.CurrencyExchangeDTO; import org.apache.log4j.Logger; import javax.naming.NamingException; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; /** * @author Emil */ public class CurrencyBL { private static final Logger LOG = Logger.getLogger(CurrencyBL.class); private static final Integer SYSTEM_RATE_ENTITY_ID = 0; private CurrencyDAS currencyDas = null; private CurrencyExchangeDAS exchangeDas = null; private CurrencyDTO currency = null; public CurrencyBL() { init(); } public CurrencyBL(Integer currencyId) { init(); set(currencyId); } public CurrencyBL(CurrencyDAS currencyDas, CurrencyExchangeDAS exchangeDas) { this.currencyDas = currencyDas; this.exchangeDas = exchangeDas; } private void init() { currencyDas = new CurrencyDAS(); exchangeDas = new CurrencyExchangeDAS(); } public void set(Integer id) { currency = currencyDas.find(id); } public CurrencyDTO getEntity() { return currency; } public Integer create(CurrencyDTO dto, Integer entityId) { if (dto != null) { /* Simplify currency creation; Set exchange rates from transient CurrencyDTO#getRate() and CurrencyDTO#getSysRate() if no currency exchanges have been mapped. */ if (dto.getCurrencyExchanges().isEmpty()) { // set optional exchange rate if (dto.getRate() != null) { CurrencyExchangeDTO exchangeRate = new CurrencyExchangeDTO(); exchangeRate.setEntityId(entityId); exchangeRate.setCurrency(dto); exchangeRate.setRate(dto.getRateAsDecimal()); exchangeRate.setCreateDatetime(new Date()); dto.getCurrencyExchanges().add(exchangeRate); } // set system rate CurrencyExchangeDTO sysRate = new CurrencyExchangeDTO(); sysRate.setEntityId(SYSTEM_RATE_ENTITY_ID); sysRate.setCurrency(dto); sysRate.setRate(dto.getSysRate() != null ? dto.getSysRate() : BigDecimal.ONE); sysRate.setCreateDatetime(new Date()); dto.getCurrencyExchanges().add(sysRate); } this.currency = currencyDas.save(dto); // add active currencies to the company map if (dto.getInUse()) { CompanyDTO company = new CompanyDAS().find(entityId); company.getCurrencies().add(this.currency); } return this.currency.getId(); } LOG.error("Cannot save a null CurrencyDTO!"); return null; } public void update(CurrencyDTO dto, Integer entityId) { if (currency != null) { currency.setSymbol(dto.getSymbol()); currency.setCode(dto.getCode()); currency.setCountryCode(dto.getCountryCode()); currency.getCurrencyExchanges().clear(); // set optional exchange rate if (dto.getRate() != null) { CurrencyExchangeDTO exchangeRate = new CurrencyExchangeDTO(); exchangeRate.setEntityId(entityId); exchangeRate.setCurrency(currency); exchangeRate.setRate(dto.getRateAsDecimal()); exchangeRate.setCreateDatetime(new Date()); currency.getCurrencyExchanges().add(exchangeRate); } // set system rate CurrencyExchangeDTO sysRate = new CurrencyExchangeDTO(); sysRate.setEntityId(SYSTEM_RATE_ENTITY_ID); sysRate.setCurrency(currency); sysRate.setRate(dto.getSysRate() != null ? dto.getSysRate() : BigDecimal.ONE); sysRate.setCreateDatetime(new Date()); currency.getCurrencyExchanges().add(sysRate); // add active currencies to the company map CompanyDTO company = new CompanyDAS().find(entityId); if (dto.getInUse()) { company.getCurrencies().add(currency); } else { company.getCurrencies().remove(currency); } } else { LOG.error("Cannot update, CurrencyDTO not found or not set!"); } } public Integer getEntityCurrency(Integer entityId) { CompanyDTO entity = new CompanyDAS().find(entityId); return entity.getCurrencyId(); } public void setEntityCurrency(Integer entityId, Integer currencyId) { CompanyDTO entity = new CompanyDAS().find(entityId); entity.setCurrency(new CurrencyDAS().find(currencyId)); } @SuppressWarnings("unchecked") public CurrencyDTO[] getCurrencies(Integer languageId, Integer entityId) throws NamingException, SQLException { CurrencyDTO[] currencies = getSymbols(); for (CurrencyDTO currency : currencies) { set(currency.getId()); currency.setName(this.currency.getDescription(languageId)); // find system rate if (currency.getId() == 1) { currency.setSysRate(new BigDecimal("1.0")); } else { currency.setSysRate(exchangeDas.findExchange(0, currency.getId()).getRate()); } // find entity specific rate CurrencyExchangeDTO exchange = exchangeDas.findExchange(entityId, currency.getId()); if (exchange != null) currency.setRate(exchange.getRate().toString()); // set in-use flag currency.setInUse(entityHasCurrency(entityId, currency.getId())); } return currencies; } public void setCurrencies(Integer entityId, CurrencyDTO[] currencies) throws NamingException, ParseException { EntityBL entity = new EntityBL(entityId); // start by wiping out the existing data for this entity entity.getEntity().getCurrencies().clear(); for (Iterator it = exchangeDas.findByEntity(entityId).iterator(); it.hasNext(); ) { CurrencyExchangeDTO exchange = (CurrencyExchangeDTO) it.next(); exchangeDas.delete(exchange); } for (int f = 0; f < currencies.length; f++) { if (currencies[f].getInUse()) { set(currencies[f].getId()); entity.getEntity().getCurrencies().add(new CurrencyDAS().find(currency.getId())); if (currencies[f].getRate() != null) { CurrencyExchangeDTO exchange = new CurrencyExchangeDTO(); exchange.setCreateDatetime(Calendar.getInstance().getTime()); exchange.setCurrency(new CurrencyDAS().find(currencies[f].getId())); exchange.setEntityId(entityId); exchange.setRate(currencies[f].getRateAsDecimal()); exchangeDas.save(exchange); } } } } public CurrencyDTO[] getSymbols() throws NamingException, SQLException { List<CurrencyDTO> currencies = new CurrencyDAS().findAll(); return currencies.toArray(new CurrencyDTO[currencies.size()]); } /** * Ok, this is cheating, but heck is easy and fast. * @param entityId * @param currencyId * @return * @throws SQLException * @throws NamingException */ private boolean entityHasCurrency(Integer entityId, Integer currencyId) throws SQLException, NamingException { boolean retValue = false; JNDILookup jndi = JNDILookup.getFactory(); Connection conn = jndi.lookUpDataSource().getConnection(); PreparedStatement stmt = conn.prepareStatement( "select 1 " + " from currency_entity_map " + " where currency_id = ? " + " and entity_id = ?"); stmt.setInt(1, currencyId); stmt.setInt(2, entityId); ResultSet result = stmt.executeQuery(); if (result.next()) { retValue = true; } result.close(); stmt.close(); conn.close(); return retValue; } /* Currency conversion */ public BigDecimal convert(Integer fromCurrencyId, Integer toCurrencyId, BigDecimal amount, Integer entityId) throws SessionInternalError { LOG.debug("Converting " + fromCurrencyId + " to " + toCurrencyId + " am " + amount + " en " + entityId); if (fromCurrencyId.equals(toCurrencyId)) { return amount; // mmm.. no conversion needed } // make the conversions return convertPivotToCurrency(toCurrencyId, convertToPivot(fromCurrencyId, amount, entityId), entityId); } public BigDecimal convertToPivot(Integer currencyId, BigDecimal amount, Integer entityId) throws SessionInternalError { if (currencyId.equals(1)) { return amount; // this is already in the pivot } // make the conversion itself CurrencyExchangeDTO exchange = findExchange(entityId, currencyId); return amount.divide(exchange.getRate(), Constants.BIGDECIMAL_SCALE, Constants.BIGDECIMAL_ROUND); } public BigDecimal convertPivotToCurrency(Integer currencyId, BigDecimal amount, Integer entityId) throws SessionInternalError { if (currencyId.equals(1)) { return amount; // this is already in the pivot } if ( amount.compareTo(BigDecimal.ZERO) == 0) { return BigDecimal.ZERO; } CurrencyExchangeDTO exchange = findExchange(entityId, currencyId); // make the conversion itself return amount.multiply(exchange.getRate()); } public CurrencyExchangeDTO findExchange(Integer entityId, Integer currencyId) throws SessionInternalError { CurrencyExchangeDTO exchange = exchangeDas.findExchange(entityId, currencyId); if (exchange == null) { // this entity doesn't have this exchange defined // 0 is the default, don't try to use null, it won't work exchange = exchangeDas.findExchange(0, currencyId); if (exchange == null) { throw new SessionInternalError("Currency " + currencyId + " doesn't have a defualt exchange"); } } return exchange; } }