diff --git a/axelor-account/src/main/java/com/axelor/apps/account/exception/AccountExceptionMessage.java b/axelor-account/src/main/java/com/axelor/apps/account/exception/AccountExceptionMessage.java index 8e1a4573ee5..e014b6015ba 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/exception/AccountExceptionMessage.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/exception/AccountExceptionMessage.java @@ -1813,4 +1813,7 @@ private AccountExceptionMessage() {} public static final String SUM_OF_NON_DEDUCTIBLE_TAXES_EXCEEDS_ONE_HUNDRED = /*$$(*/ "The sum of non-deductible taxes should not exceed 100%." /*)*/; + + public static final String MOVE_LINE_WITH_NON_DEDUCTIBLE_TAX_NOT_AUTHORIZED = /*$$(*/ + "Non-deductible tax only authorized when functional origin is purchase. Please remove the non-deductible tax on move line." /*)*/; } diff --git a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckService.java b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckService.java index 00ca6b780cf..40bde3c0ada 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckService.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckService.java @@ -20,8 +20,10 @@ import com.axelor.apps.account.db.Move; import com.axelor.apps.account.db.MoveLine; +import com.axelor.apps.account.db.TaxLine; import com.axelor.apps.base.AxelorException; import java.util.List; +import java.util.Set; public interface MoveLineCheckService { void checkAnalyticByTemplate(MoveLine moveLine) throws AxelorException; @@ -35,4 +37,8 @@ public interface MoveLineCheckService { void checkAnalyticAccount(List moveLineList) throws AxelorException; void checkAnalyticMoveLinesPercentage(MoveLine moveLine) throws AxelorException; + + void nonDeductibleTaxAuthorized(Move move, MoveLine moveLine) throws AxelorException; + + void checkMoveLineTaxes(Set taxLineSet) throws AxelorException; } diff --git a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckServiceImpl.java b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckServiceImpl.java index 4adeab02807..adff22be220 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckServiceImpl.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCheckServiceImpl.java @@ -20,16 +20,22 @@ import com.axelor.apps.account.db.Move; import com.axelor.apps.account.db.MoveLine; +import com.axelor.apps.account.db.TaxLine; +import com.axelor.apps.account.db.repo.MoveRepository; import com.axelor.apps.account.exception.AccountExceptionMessage; import com.axelor.apps.account.service.AccountService; +import com.axelor.apps.account.service.TaxAccountService; import com.axelor.apps.account.service.analytic.AnalyticDistributionTemplateService; import com.axelor.apps.account.service.analytic.AnalyticMoveLineService; import com.axelor.apps.base.AxelorException; import com.axelor.apps.base.db.repo.TraceBackRepository; +import com.axelor.common.ObjectUtils; import com.axelor.i18n.I18n; import com.google.inject.Inject; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.Set; import org.apache.commons.collections.CollectionUtils; public class MoveLineCheckServiceImpl implements MoveLineCheckService { @@ -37,17 +43,20 @@ public class MoveLineCheckServiceImpl implements MoveLineCheckService { protected AnalyticMoveLineService analyticMoveLineService; protected AnalyticDistributionTemplateService analyticDistributionTemplateService; protected MoveLineToolService moveLineToolService; + protected TaxAccountService taxAccountService; @Inject public MoveLineCheckServiceImpl( AccountService accountService, AnalyticMoveLineService analyticMoveLineService, AnalyticDistributionTemplateService analyticDistributionTemplateService, - MoveLineToolService moveLineToolService) { + MoveLineToolService moveLineToolService, + TaxAccountService taxAccountService) { this.accountService = accountService; this.analyticMoveLineService = analyticMoveLineService; this.analyticDistributionTemplateService = analyticDistributionTemplateService; this.moveLineToolService = moveLineToolService; + this.taxAccountService = taxAccountService; } @Override @@ -120,4 +129,19 @@ public void checkAnalyticMoveLinesPercentage(MoveLine moveLine) throws AxelorExc I18n.get(AccountExceptionMessage.INVALID_ANALYTIC_MOVE_LINE)); } } + + public void nonDeductibleTaxAuthorized(Move move, MoveLine moveLine) throws AxelorException { + int technicalType = Optional.of(move.getFunctionalOriginSelect()).orElse(0); + if (technicalType != MoveRepository.FUNCTIONAL_ORIGIN_PURCHASE) { + this.checkMoveLineTaxes(moveLine.getTaxLineSet()); + } + } + + public void checkMoveLineTaxes(Set taxLineSet) throws AxelorException { + if (ObjectUtils.notEmpty(taxLineSet) && taxAccountService.isNonDeductibleTaxesSet(taxLineSet)) { + throw new AxelorException( + TraceBackRepository.CATEGORY_INCONSISTENCY, + I18n.get(AccountExceptionMessage.MOVE_LINE_WITH_NON_DEDUCTIBLE_TAX_NOT_AUTHORIZED)); + } + } } diff --git a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateService.java b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateService.java index 7e513d2f2c9..2dc1fd3d230 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateService.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateService.java @@ -132,7 +132,8 @@ MoveLine createMoveLineForAutoTax( TaxLine taxLine, String accountType, Account newAccount, - boolean percentMoveTemplate) + boolean percentMoveTemplate, + List nonDeductibleTaxList) throws AxelorException; MoveLine createTaxMoveLine( diff --git a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateServiceImpl.java b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateServiceImpl.java index c891ed369e0..0e698787784 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateServiceImpl.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineCreateServiceImpl.java @@ -678,7 +678,8 @@ public MoveLine createMoveLineForAutoTax( TaxLine taxLine, String accountType, Account newAccount, - boolean percentMoveTemplate) + boolean percentMoveTemplate, + List nonDeductibleTaxList) throws AxelorException { BigDecimal debit = moveLine.getDebit(); BigDecimal credit = moveLine.getCredit(); @@ -760,7 +761,7 @@ public MoveLine createMoveLineForAutoTax( move.getJournal(), move.getOrigin(), move.getDescription()))); moveLineToolService.setDecimals(newOrUpdatedMoveLine, move); - BigDecimal taxLineValue = taxLine.getValue(); + BigDecimal taxLineValue = this.computeTaxLineValue(taxLine, nonDeductibleTaxList); if (percentMoveTemplate) { debit = sumMoveLinesByAccountType(move.getMoveLineList(), AccountTypeRepository.TYPE_PAYABLE); @@ -808,6 +809,51 @@ public MoveLine createMoveLineForAutoTax( return newOrUpdatedMoveLine; } + protected BigDecimal computeTaxLineValue(TaxLine taxLine, List nonDeductibleTaxList) { + BigDecimal taxValue = taxLine.getValue(); + if (taxLine.getTax().getIsNonDeductibleTax()) { + taxValue = this.getAdjustedNonDeductibleTaxValue(taxValue, nonDeductibleTaxList); + } else { + taxValue = this.getAdjustedTaxValue(taxValue, nonDeductibleTaxList); + } + + return taxValue; + } + + protected BigDecimal getAdjustedTaxValue( + BigDecimal taxValue, List nonDeductibleTaxList) { + BigDecimal deductibleTaxValue = + nonDeductibleTaxList.stream() + .map(TaxLine::getValue) + .reduce(BigDecimal::multiply) + .orElse(BigDecimal.ZERO) + .divide( + BigDecimal.valueOf(100), AppBaseService.COMPUTATION_SCALING, RoundingMode.HALF_UP); + + return BigDecimal.ONE + .subtract(deductibleTaxValue) + .multiply(taxValue) + .setScale(AppBaseService.COMPUTATION_SCALING, RoundingMode.HALF_UP); + } + + protected BigDecimal getAdjustedNonDeductibleTaxValue( + BigDecimal taxValue, List deductibleTaxList) { + BigDecimal nonDeductibleTaxValue = BigDecimal.ZERO; + + for (TaxLine taxLine : deductibleTaxList) { + nonDeductibleTaxValue = + nonDeductibleTaxValue.add( + taxValue.multiply( + taxLine + .getValue() + .divide( + BigDecimal.valueOf(100), + AppBaseService.COMPUTATION_SCALING, + RoundingMode.HALF_UP))); + } + return nonDeductibleTaxValue.setScale(AppBaseService.COMPUTATION_SCALING, RoundingMode.HALF_UP); + } + protected void createMoveLineRCForAutoTax( Move move, Map map, diff --git a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineTaxServiceImpl.java b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineTaxServiceImpl.java index 3018814483d..9ceec2a44de 100644 --- a/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineTaxServiceImpl.java +++ b/axelor-account/src/main/java/com/axelor/apps/account/service/moveline/MoveLineTaxServiceImpl.java @@ -30,13 +30,13 @@ import com.axelor.apps.account.db.repo.MoveLineRepository; import com.axelor.apps.account.db.repo.MoveRepository; import com.axelor.apps.account.exception.AccountExceptionMessage; +import com.axelor.apps.account.service.TaxAccountService; import com.axelor.apps.account.service.TaxPaymentMoveLineService; import com.axelor.apps.account.util.TaxAccountToolService; import com.axelor.apps.base.AxelorException; import com.axelor.apps.base.db.Partner; import com.axelor.apps.base.db.repo.TraceBackRepository; import com.axelor.apps.base.service.app.AppBaseService; -import com.axelor.apps.base.service.tax.TaxService; import com.axelor.common.ObjectUtils; import com.axelor.i18n.I18n; import com.google.common.collect.Lists; @@ -54,6 +54,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; @RequestScoped @@ -66,7 +67,8 @@ public class MoveLineTaxServiceImpl implements MoveLineTaxService { protected MoveRepository moveRepository; protected TaxAccountToolService taxAccountToolService; protected MoveLineToolService moveLineToolService; - protected TaxService taxService; + protected TaxAccountService taxAccountService; + protected MoveLineCheckService moveLineCheckService; @Inject public MoveLineTaxServiceImpl( @@ -77,7 +79,8 @@ public MoveLineTaxServiceImpl( MoveRepository moveRepository, TaxAccountToolService taxAccountToolService, MoveLineToolService moveLineToolService, - TaxService taxService) { + TaxAccountService taxAccountService, + MoveLineCheckService moveLineCheckService) { this.moveLineRepository = moveLineRepository; this.taxPaymentMoveLineService = taxPaymentMoveLineService; this.appBaseService = appBaseService; @@ -85,7 +88,8 @@ public MoveLineTaxServiceImpl( this.moveRepository = moveRepository; this.taxAccountToolService = taxAccountToolService; this.moveLineToolService = moveLineToolService; - this.taxService = taxService; + this.taxAccountService = taxAccountService; + this.moveLineCheckService = moveLineCheckService; } @Override @@ -113,7 +117,7 @@ public MoveLine generateTaxPaymentMoveLineList( } else if (!moveLineToolService.isMoveLineTaxAccount(invoiceMoveLine) && CollectionUtils.isNotEmpty(invoiceMoveLine.getTaxLineSet()) - && taxService + && taxAccountService .getTotalTaxRateInPercentage(invoiceMoveLine.getTaxLineSet()) .compareTo(BigDecimal.ZERO) == 0) { @@ -327,20 +331,78 @@ public int compare(MoveLine o1, MoveLine o2) { continue; } - if (CollectionUtils.isNotEmpty(taxLineSet)) - for (TaxLine taxLine : taxLineSet) { - if (taxLine != null && taxLine.getValue().signum() != 0) { - String accountType = moveLine.getAccount().getAccountType().getTechnicalTypeSelect(); + taxAccountService.checkTaxLinesNotOnlyNonDeductibleTaxes(taxLineSet); + taxAccountService.checkSumOfNonDeductibleTaxes(taxLineSet); + if (CollectionUtils.isNotEmpty(taxLineSet)) { + List deductibleTaxList = + moveLineList.stream() + .map(MoveLine::getTaxLineSet) + .flatMap(Set::stream) + .filter(it -> !this.isNonDeductibleTax(it)) + .collect(Collectors.toList()); + List nonDeductibleTaxList = + moveLineList.stream() + .map(MoveLine::getTaxLineSet) + .flatMap(Set::stream) + .filter(this::isNonDeductibleTax) + .collect(Collectors.toList()); + + this.computeMoveLineTax( + move, + map, + newMap, + moveLine, + account, + percentMoveTemplate, + nonDeductibleTaxList, + deductibleTaxList); + this.computeMoveLineTax( + move, + map, + newMap, + moveLine, + account, + percentMoveTemplate, + deductibleTaxList, + nonDeductibleTaxList); + } + } - if (this.isGenerateMoveLineForAutoTax(moveLine)) { - moveLineCreateService.createMoveLineForAutoTax( - move, map, newMap, moveLine, taxLine, accountType, account, percentMoveTemplate); - } - } + moveLineList.addAll(newMap.values()); + } + + protected void computeMoveLineTax( + Move move, + Map map, + Map newMap, + MoveLine moveLine, + Account account, + boolean percentMoveTemplate, + List taxLineList, + List otherTaxLineList) + throws AxelorException { + for (TaxLine taxLine : taxLineList) { + if (taxLine != null && taxLine.getValue().signum() != 0) { + String accountType = moveLine.getAccount().getAccountType().getTechnicalTypeSelect(); + if (this.isGenerateMoveLineForAutoTax(moveLine)) { + moveLineCheckService.nonDeductibleTaxAuthorized(move, moveLine); + moveLineCreateService.createMoveLineForAutoTax( + move, + map, + newMap, + moveLine, + taxLine, + accountType, + account, + percentMoveTemplate, + otherTaxLineList); } + } } + } - moveLineList.addAll(newMap.values()); + protected boolean isNonDeductibleTax(TaxLine taxLine) { + return Optional.of(taxLine.getTax().getIsNonDeductibleTax()).orElse(false); } @Override diff --git a/axelor-account/src/main/resources/i18n/messages.csv b/axelor-account/src/main/resources/i18n/messages.csv index efe14fa7a4d..808c4ab3b18 100644 --- a/axelor-account/src/main/resources/i18n/messages.csv +++ b/axelor-account/src/main/resources/i18n/messages.csv @@ -2363,6 +2363,7 @@ "No style",,, "No value",,, "Non deductible tax",,, +"Non-deductible tax only authorized when functional origin is purchase. Please remove the non-deductible tax on move line.",,, "None",,, "None of the payment due dates have been exceeded.",,, "Not implemented for OSM",,, diff --git a/axelor-account/src/main/resources/i18n/messages_en.csv b/axelor-account/src/main/resources/i18n/messages_en.csv index 3aafebe281b..fa8470cb832 100644 --- a/axelor-account/src/main/resources/i18n/messages_en.csv +++ b/axelor-account/src/main/resources/i18n/messages_en.csv @@ -2363,6 +2363,7 @@ "No style",,, "No value",,, "Non deductible tax",,, +"Non-deductible tax only authorized when functional origin is purchase. Please remove the non-deductible tax on move line.",,, "None",,, "None of the payment due dates have been exceeded.",,, "Not implemented for OSM",,, diff --git a/axelor-account/src/main/resources/i18n/messages_fr.csv b/axelor-account/src/main/resources/i18n/messages_fr.csv index c0975a50649..7aa34a5a442 100644 --- a/axelor-account/src/main/resources/i18n/messages_fr.csv +++ b/axelor-account/src/main/resources/i18n/messages_fr.csv @@ -2364,6 +2364,7 @@ e a été payée ou qu'elle est reliée à une session de paiement.", "No style","Aucun style",, "No value","Aucune valeur",, "Non deductible tax","Taxe non déductible",, +"Non-deductible tax only authorized when functional origin is purchase. Please remove the non-deductible tax on move line.","Les taxes non déductibles sont autorisées uniquement pour les origines fonctionnelles Achat. Veuillez retirer la/les taxe(s) non déductible(s) de la ligne d'écriture.",, "None","Aucun",, "None of the payment due dates have been exceeded.","Aucune des échéances de paiement n'a été dépassée",, "Not implemented for OSM","Non implémenté pour OSM",, diff --git a/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MoveLineTaxHRServiceImpl.java b/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MoveLineTaxHRServiceImpl.java index 2ceba5d6d65..b77d055e303 100644 --- a/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MoveLineTaxHRServiceImpl.java +++ b/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MoveLineTaxHRServiceImpl.java @@ -21,13 +21,14 @@ import com.axelor.apps.account.db.MoveLine; import com.axelor.apps.account.db.repo.MoveLineRepository; import com.axelor.apps.account.db.repo.MoveRepository; +import com.axelor.apps.account.service.TaxAccountService; import com.axelor.apps.account.service.TaxPaymentMoveLineService; +import com.axelor.apps.account.service.moveline.MoveLineCheckService; import com.axelor.apps.account.service.moveline.MoveLineCreateService; import com.axelor.apps.account.service.moveline.MoveLineTaxServiceImpl; import com.axelor.apps.account.service.moveline.MoveLineToolService; import com.axelor.apps.account.util.TaxAccountToolService; import com.axelor.apps.base.service.app.AppBaseService; -import com.axelor.apps.base.service.tax.TaxService; import com.google.inject.Inject; public class MoveLineTaxHRServiceImpl extends MoveLineTaxServiceImpl { @@ -41,7 +42,8 @@ public MoveLineTaxHRServiceImpl( MoveRepository moveRepository, TaxAccountToolService taxAccountToolService, MoveLineToolService moveLineToolService, - TaxService taxService) { + TaxAccountService taxAccountService, + MoveLineCheckService moveLineCheckService) { super( moveLineRepository, taxPaymentMoveLineService, @@ -50,7 +52,8 @@ public MoveLineTaxHRServiceImpl( moveRepository, taxAccountToolService, moveLineToolService, - taxService); + taxAccountService, + moveLineCheckService); } @Override