/** * Axelor Business Solutions * * Copyright (C) 2016 Axelor (<http://axelor.com>). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.axelor.apps.purchase.service; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.axelor.apps.account.db.TaxLine; import com.axelor.apps.base.db.IAdministration; import com.axelor.apps.base.db.IPriceListLine; import com.axelor.apps.base.db.Partner; import com.axelor.apps.base.db.PriceList; import com.axelor.apps.base.db.PriceListLine; import com.axelor.apps.base.db.Product; import com.axelor.apps.base.db.SupplierCatalog; import com.axelor.apps.base.db.Unit; import com.axelor.apps.base.db.repo.GeneralRepository; import com.axelor.apps.base.db.repo.SupplierCatalogRepository; import com.axelor.apps.base.service.CurrencyService; import com.axelor.apps.base.service.PriceListService; import com.axelor.apps.base.service.ProductService; import com.axelor.apps.base.service.administration.GeneralService; import com.axelor.apps.base.service.tax.AccountManagementService; import com.axelor.apps.purchase.db.PurchaseOrder; import com.axelor.apps.purchase.db.PurchaseOrderLine; import com.axelor.exception.AxelorException; import com.axelor.inject.Beans; import com.google.inject.Inject; public class PurchaseOrderLineServiceImpl implements PurchaseOrderLineService { private static final Logger LOG = LoggerFactory.getLogger(PurchaseOrderLineServiceImpl.class); @Inject protected CurrencyService currencyService; @Inject protected AccountManagementService accountManagementService; @Inject protected PriceListService priceListService; @Inject protected GeneralService generalService; @Inject protected ProductService productService; private int sequence = 0; /** * Calculer le montant HT d'une ligne de commande. * * @param quantity * Quantité. * @param price * Le prix. * * @return * Le montant HT de la ligne. */ public static BigDecimal computeAmount(BigDecimal quantity, BigDecimal price) { BigDecimal amount = quantity.multiply(price).setScale(IAdministration.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_EVEN); LOG.debug("Calcul du montant HT avec une quantité de {} pour {} : {}", new Object[] { quantity, price, amount }); return amount; } @Override public BigDecimal getUnitPrice(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine, TaxLine taxLine) throws AxelorException { Product product = purchaseOrderLine.getProduct(); BigDecimal price = this.convertUnitPrice(product, taxLine, product.getPurchasePrice(), purchaseOrder); return currencyService.getAmountCurrencyConverted( product.getPurchaseCurrency(), purchaseOrder.getCurrency(), price, purchaseOrder.getOrderDate()) .setScale(generalService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_UP); } @Override public BigDecimal getMinSalePrice(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine) throws AxelorException { Product product = purchaseOrderLine.getProduct(); TaxLine saleTaxLine = accountManagementService.getTaxLine( purchaseOrder.getOrderDate(), purchaseOrderLine.getProduct(), purchaseOrder.getCompany(), purchaseOrder.getSupplierPartner().getFiscalPosition(), false); BigDecimal price = this.convertUnitPrice(product, saleTaxLine, product.getSalePrice(), purchaseOrder); return currencyService.getAmountCurrencyConverted( product.getSaleCurrency(), purchaseOrder.getCurrency(), price, purchaseOrder.getOrderDate()) .setScale(generalService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_UP); } @Override public BigDecimal getSalePrice(PurchaseOrder purchaseOrder, Product product, BigDecimal price) throws AxelorException { TaxLine saleTaxLine = accountManagementService.getTaxLine( purchaseOrder.getOrderDate(), product, purchaseOrder.getCompany(), purchaseOrder.getSupplierPartner().getFiscalPosition(), false); price = this.convertUnitPrice(product, saleTaxLine, price, purchaseOrder); price = price.multiply(product.getManagPriceCoef()); return currencyService.getAmountCurrencyConverted( product.getSaleCurrency(), purchaseOrder.getCurrency(), price, purchaseOrder.getOrderDate()) .setScale(generalService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_UP); } @Override public TaxLine getTaxLine(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine) throws AxelorException { return accountManagementService.getTaxLine( purchaseOrder.getOrderDate(), purchaseOrderLine.getProduct(), purchaseOrder.getCompany(), purchaseOrder.getSupplierPartner().getFiscalPosition(), true); } @Override public BigDecimal computePurchaseOrderLine(PurchaseOrderLine purchaseOrderLine) { return purchaseOrderLine.getExTaxTotal(); } @Override public BigDecimal getCompanyExTaxTotal(BigDecimal exTaxTotal, PurchaseOrder purchaseOrder) throws AxelorException { return currencyService.getAmountCurrencyConverted( purchaseOrder.getCurrency(), purchaseOrder.getCompany().getCurrency(), exTaxTotal, purchaseOrder.getOrderDate()) .setScale(IAdministration.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_UP); } @Override public PriceListLine getPriceListLine(PurchaseOrderLine purchaseOrderLine, PriceList priceList) { return priceListService.getPriceListLine(purchaseOrderLine.getProduct(), purchaseOrderLine.getQty(), priceList); } @Override public BigDecimal computeDiscount(PurchaseOrderLine purchaseOrderLine) { return priceListService.computeDiscount(purchaseOrderLine.getPrice(), purchaseOrderLine.getDiscountTypeSelect(),purchaseOrderLine.getDiscountAmount()); } @Override public PurchaseOrderLine createPurchaseOrderLine(PurchaseOrder purchaseOrder, Product product, String productName, String description, BigDecimal qty, Unit unit) throws AxelorException { PurchaseOrderLine purchaseOrderLine = new PurchaseOrderLine(); purchaseOrderLine.setPurchaseOrder(purchaseOrder); purchaseOrderLine.setEstimatedDelivDate(purchaseOrder.getDeliveryDate()); purchaseOrderLine.setDescription(description); purchaseOrderLine.setIsOrdered(false); purchaseOrderLine.setQty(qty); purchaseOrderLine.setSequence(sequence); sequence++; purchaseOrderLine.setUnit(unit); purchaseOrderLine.setProductName(productName); if(product == null) { return purchaseOrderLine; } purchaseOrderLine.setProduct(product); if(productName == null) { purchaseOrderLine.setProductName(product.getName()); } TaxLine taxLine = this.getTaxLine(purchaseOrder, purchaseOrderLine); purchaseOrderLine.setTaxLine(taxLine); BigDecimal price = this.getUnitPrice(purchaseOrder, purchaseOrderLine, taxLine); Map<String, Object> discounts = this.getDiscount(purchaseOrder, purchaseOrderLine, price); if(discounts != null){ purchaseOrderLine.setDiscountAmount((BigDecimal) discounts.get("discountAmount")); purchaseOrderLine.setDiscountTypeSelect((Integer) discounts.get("discountTypeSelect")); if(discounts.get("price") != null) { price = (BigDecimal) discounts.get("price"); } } purchaseOrderLine.setPrice(price); purchaseOrderLine.setPriceDiscounted(this.computeDiscount(purchaseOrderLine)); BigDecimal exTaxTotal, inTaxTotal, companyExTaxTotal, companyInTaxTotal; if(!purchaseOrder.getInAti()){ exTaxTotal = PurchaseOrderLineServiceImpl.computeAmount(purchaseOrderLine.getQty(), this.computeDiscount(purchaseOrderLine)); inTaxTotal = exTaxTotal.add(exTaxTotal.multiply(purchaseOrderLine.getTaxLine().getValue())); companyExTaxTotal = this.getCompanyExTaxTotal(exTaxTotal, purchaseOrder); companyInTaxTotal = companyExTaxTotal.add(companyExTaxTotal.multiply(purchaseOrderLine.getTaxLine().getValue())); } else{ inTaxTotal = PurchaseOrderLineServiceImpl.computeAmount(purchaseOrderLine.getQty(), this.computeDiscount(purchaseOrderLine)); exTaxTotal = inTaxTotal.divide(purchaseOrderLine.getTaxLine().getValue().add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP); companyInTaxTotal = this.getCompanyExTaxTotal(inTaxTotal, purchaseOrder); companyExTaxTotal = companyInTaxTotal.divide(purchaseOrderLine.getTaxLine().getValue().add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP); } purchaseOrderLine.setExTaxTotal(exTaxTotal); purchaseOrderLine.setCompanyExTaxTotal(companyExTaxTotal); purchaseOrderLine.setCompanyInTaxTotal(companyInTaxTotal); purchaseOrderLine.setInTaxTotal(inTaxTotal); return purchaseOrderLine; } @Override public BigDecimal getQty(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine) { SupplierCatalog supplierCatalog = this.getSupplierCatalog(purchaseOrder,purchaseOrderLine); if(supplierCatalog != null) { return supplierCatalog.getMinQty(); } return BigDecimal.ONE; } @Override public SupplierCatalog getSupplierCatalog(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine) { Product product = purchaseOrderLine.getProduct(); SupplierCatalog supplierCatalog = this.getSupplierCatalog(product, purchaseOrder.getSupplierPartner()); // If there is no catalog for supplier, then we don't take the default catalog. // if(supplierCatalog == null) { // // supplierCatalog = this.getSupplierCatalog(product, product.getDefaultSupplierPartner()); // } return supplierCatalog; } @Override public SupplierCatalog getSupplierCatalog(Product product, Partner supplierPartner) { if(product.getSupplierCatalogList() != null) { for(SupplierCatalog supplierCatalog : product.getSupplierCatalogList()) { if(supplierCatalog.getSupplierPartner().equals(supplierPartner)) { return supplierCatalog; } } } return null; } @Override public BigDecimal convertUnitPrice(Product product, TaxLine taxLine, BigDecimal price, PurchaseOrder purchaseOrder) { if(taxLine == null) { return price; } if(product.getInAti() && !purchaseOrder.getInAti()){ price = price.divide(taxLine.getValue().add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP); } else if(!product.getInAti() && purchaseOrder.getInAti()){ price = price.add(price.multiply(taxLine.getValue())); } return price; } public Map<String,Object> getDiscount(PurchaseOrder purchaseOrder, PurchaseOrderLine purchaseOrderLine, BigDecimal price) { PriceList priceList = purchaseOrder.getPriceList(); BigDecimal discountAmount = BigDecimal.ZERO; int computeMethodDiscountSelect = generalService.getGeneral().getComputeMethodDiscountSelect(); Map<String, Object> discounts = null; if(priceList != null) { int discountTypeSelect = 0; PriceListLine priceListLine = this.getPriceListLine(purchaseOrderLine, priceList); if(priceListLine != null) { discountTypeSelect = priceListLine.getTypeSelect(); } discounts = priceListService.getDiscounts(priceList, priceListLine, price); discountAmount = (BigDecimal) discounts.get("discountAmount"); if((computeMethodDiscountSelect == GeneralRepository.INCLUDE_DISCOUNT_REPLACE_ONLY && discountTypeSelect == IPriceListLine.TYPE_REPLACE) || computeMethodDiscountSelect == GeneralRepository.INCLUDE_DISCOUNT) { discounts.put("price", priceListService.computeDiscount(price, (int) discounts.get("discountTypeSelect"), discountAmount)); } } if(discountAmount.compareTo(BigDecimal.ZERO) == 0) { List<SupplierCatalog> supplierCatalogList = purchaseOrderLine.getProduct().getSupplierCatalogList(); if(supplierCatalogList != null && !supplierCatalogList.isEmpty()){ SupplierCatalog supplierCatalog = Beans.get(SupplierCatalogRepository.class).all().filter("self.product = ?1 AND self.minQty <= ?2 AND self.supplierPartner = ?3 ORDER BY self.minQty DESC",purchaseOrderLine.getProduct(),purchaseOrderLine.getQty(),purchaseOrder.getSupplierPartner()).fetchOne(); if(supplierCatalog != null) { discounts = productService.getDiscountsFromCatalog(supplierCatalog,price); if(computeMethodDiscountSelect != GeneralRepository.DISCOUNT_SEPARATE){ discounts.put("price", priceListService.computeDiscount(price, (int) discounts.get("discountTypeSelect"), (BigDecimal) discounts.get("discountAmount"))); } } } } return discounts; } @Override public int getDiscountTypeSelect(PurchaseOrderLine purchaseOrderLine, PurchaseOrder purchaseOrder){ PriceList priceList = purchaseOrder.getPriceList(); if(priceList != null) { PriceListLine priceListLine = this.getPriceListLine(purchaseOrderLine, priceList); return priceListLine.getTypeSelect(); } return 0; } public Unit getPurchaseUnit(PurchaseOrderLine purchaseOrderLine) { Unit unit = purchaseOrderLine.getProduct().getPurchasesUnit(); if(unit == null){ unit = purchaseOrderLine.getProduct().getUnit(); } return unit; } public boolean unitPriceShouldBeUpdate(PurchaseOrder purchaseOrder, Product product) { if(product != null && product.getInAti() != purchaseOrder.getInAti()) { return true; } return false; } }