From 6881c439b23dbc73afbe88f8b0f62721606aecbf Mon Sep 17 00:00:00 2001 From: BACHIR SOULDI Date: Tue, 17 Feb 2026 15:13:17 +0100 Subject: [PATCH] feat: Enhance Supply Chain Module with Analytic Move Line Features - Added support for analytic move lines in InvoiceServiceSupplychainImpl. - Implemented methods to check for missing analytic move lines in PurchaseOrderController and PurchaseRequestController. - Introduced budget distribution line generation in PurchaseOrderController. - Updated SaleOrderController to generate analytic move lines. - Enhanced StockMoveController with budget distribution line generation. - Modified StockMoveLineController to include analytic account and axis handling. - Updated domain models to include references to analytic accounts and axes in Partner, StockLocation, and StockMoveLine. - Created unit tests for StockMoveLineServiceSupplychainImpl to ensure proper functionality of new features. - Added MockQuery class for testing purposes. --- .../AccountManagementServiceAccountImpl.java | 56 ++ .../service/invoice/InvoiceServiceImpl.java | 83 ++- .../workflow/ventilate/VentilateState.java | 16 +- .../account/service/move/MoveLineService.java | 153 +++-- .../PaymentVoucherConfirmService.java | 131 +++-- .../PaymentVoucherSequenceService.java | 12 +- .../axelor/apps/account/util/MoveLineKey.java | 34 ++ .../web/AccountManagementController.java | 21 + .../account/web/CashInventoryController.java | 64 ++- .../apps/account/web/InvoiceController.java | 48 +- .../account/web/PaymentVoucherController.java | 21 +- .../resources/domains/AnalyticAccount.xml | 2 + .../resources/domains/AnalyticMoveLine.xml | 2 + .../src/main/resources/domains/AppBudget.xml | 5 + .../src/main/resources/domains/Budget.xml | 5 +- .../main/resources/domains/FamilleProduit.xml | 32 ++ .../src/main/resources/domains/Invoice.xml | 4 + .../web/CashInventoryControllerTest.java | 118 ++-- .../base/db/repo/ProductBaseRepository.java | 2 +- .../main/resources/domains/FamilleProduit.xml | 7 +- .../service/InvoiceServiceProjectImpl.java | 7 +- .../ManufOrderStockMoveService.java | 35 +- .../production/web/ManufOrderController.java | 36 +- .../PurchaseOrderManagementRepository.java | 2 +- .../service/PurchaseOrderLineService.java | 2 +- .../service/PurchaseOrderLineServiceImpl.java | 17 +- .../PurchaseRequestPrintServiceImpl.java | 7 +- .../purchase/web/PurchaseOrderController.java | 2 +- .../main/resources/domains/PurchaseOrder.xml | 6 + .../resources/domains/PurchaseRequest.xml | 7 + .../StockLocationLineStockRepository.java | 66 +++ .../StockMoveLineLocationStockRepository.java | 57 ++ .../service/StockLocationLineServiceImpl.java | 329 +++++++---- .../service/StockMoveLineServiceImpl.java | 55 +- .../apps/stock/service/StockMoveService.java | 7 +- .../stock/service/StockMoveServiceImpl.java | 346 ++++++++---- .../service/StockMoveToolServiceImpl.java | 18 +- .../apps/stock/web/StockMoveController.java | 95 +++- .../main/resources/domains/StockLocation.xml | 1 + .../resources/domains/StockLocationLine.xml | 2 + .../main/resources/domains/StockMoveLine.xml | 45 +- ...OrderSupplierLineManagementRepository.java | 42 +- .../PurchaseOrderSupplierLineService.java | 12 +- .../service/PurchaseOrderSupplierService.java | 74 ++- .../web/PurchaseOrderController.java | 6 +- .../PurchaseOrderSupplierLineController.java | 21 +- .../axelor-supplychain/build.gradle | 2 + .../service/AccountingCutOffServiceImpl.java | 37 +- .../service/AnalyticMoveLineServiceImpl.java | 15 + .../service/BudgetSupplychainService.java | 24 +- .../service/ImportationFolderServiceImpl.java | 49 +- ...rchaseOrderLineServiceSupplychainImpl.java | 116 ++-- .../PurchaseOrderServiceSupplychainImpl.java | 80 ++- .../PurchaseOrderStockServiceImpl.java | 52 +- ...PurchaseRequestServiceSupplychainImpl.java | 57 +- .../service/ReservedQtyServiceImpl.java | 8 +- .../SaleOrderServiceSupplychainImpl.java | 37 ++ .../service/StockConfigService.java | 25 +- .../StockMoveLineServiceSupplychainImpl.java | 525 +++++++++++++++++- .../StockMoveServiceSupplychainImpl.java | 118 ++++ .../InvoiceServiceSupplychainImpl.java | 7 +- .../web/PurchaseOrderController.java | 24 + .../web/PurchaseRequestController.java | 25 + .../supplychain/web/SaleOrderController.java | 8 + .../supplychain/web/StockMoveController.java | 120 ++-- .../web/StockMoveLineController.java | 56 +- .../web/SupplychainBatchController.java | 55 +- .../resources/domains/BudgetDistribution.xml | 1 + .../src/main/resources/domains/Partner.xml | 2 + .../main/resources/domains/StockLocation.xml | 4 + .../main/resources/domains/StockMoveLine.xml | 2 + .../apps/supplychain/TestStockMove.java | 39 +- .../apps/supplychain/service/MockQuery.java | 28 + ...ockMoveLineServiceSupplychainImplTest.java | 276 +++++++++ 74 files changed, 2975 insertions(+), 930 deletions(-) create mode 100644 modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/util/MoveLineKey.java create mode 100644 modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/AccountManagementController.java create mode 100644 modules/axelor-open-suite/axelor-account/src/main/resources/domains/FamilleProduit.xml create mode 100644 modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockMoveLineLocationStockRepository.java create mode 100644 modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AnalyticMoveLineServiceImpl.java create mode 100644 modules/axelor-open-suite/axelor-supplychain/src/test/java/com/axelor/apps/supplychain/service/MockQuery.java create mode 100644 modules/axelor-open-suite/axelor-supplychain/src/test/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImplTest.java diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/AccountManagementServiceAccountImpl.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/AccountManagementServiceAccountImpl.java index 6e336ff..c783a93 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/AccountManagementServiceAccountImpl.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/AccountManagementServiceAccountImpl.java @@ -22,8 +22,10 @@ import com.axelor.apps.account.db.AccountManagement; import com.axelor.apps.account.db.AnalyticDistributionTemplate; import com.axelor.apps.account.db.FiscalPosition; import com.axelor.apps.account.db.FixedAssetCategory; +import com.axelor.apps.account.db.repo.AccountManagementRepository; import com.axelor.apps.account.exception.IExceptionMessage; import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.FamilleProduit; import com.axelor.apps.base.db.Product; import com.axelor.apps.base.service.tax.AccountManagementServiceImpl; import com.axelor.apps.base.service.tax.FiscalPositionService; @@ -31,8 +33,11 @@ import com.axelor.apps.base.service.tax.TaxService; import com.axelor.exception.AxelorException; import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.i18n.I18n; +import com.axelor.inject.Beans; import com.axelor.meta.CallMethod; import com.google.inject.Inject; +import com.google.inject.persist.Transactional; + import java.lang.invoke.MethodHandles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -222,4 +227,55 @@ public class AccountManagementServiceAccountImpl extends AccountManagementServic return fixedAssetCategory; } + + @Transactional(rollbackOn = {Exception.class}) + public void setAccountManagementForProduct(Product product,Company company) { + FamilleProduit familleProduit = product.getFamilleProduit(); + FamilleProduit sousFamilleProduit = product.getSousFamilleProduit(); + + if (product == null || familleProduit == null || company == null) { + return; + } + + if (product.getAccountManagementList() == null || product.getAccountManagementList().isEmpty()) { + AccountManagement accountManagement = new AccountManagement(); + accountManagement.setCompany(company); + accountManagement.setProduct(product); + + // Use sousFamilleProduit accounts if they exist, otherwise fallback to familleProduit + accountManagement.setPurchaseAccount( + (sousFamilleProduit != null && sousFamilleProduit.getPurchaseAccount() != null) ? + sousFamilleProduit.getPurchaseAccount() : + familleProduit.getPurchaseAccount() + ); + + accountManagement.setStockAccount( + (sousFamilleProduit != null && sousFamilleProduit.getStockAccount() != null) ? + sousFamilleProduit.getStockAccount() : + familleProduit.getStockAccount() + ); + + accountManagement.setConsumptionAccount( + (sousFamilleProduit != null && sousFamilleProduit.getConsumptionAccount() != null) ? + sousFamilleProduit.getConsumptionAccount() : + familleProduit.getConsumptionAccount() + ); + + accountManagement.setSaleAccount( + (sousFamilleProduit != null && sousFamilleProduit.getSaleAccount() != null) ? + sousFamilleProduit.getSaleAccount() : + familleProduit.getSaleAccount() + ); + + accountManagement.setPurchFixedAssetsAccount( + (sousFamilleProduit != null && sousFamilleProduit.getPurchFixedAssetsAccount() != null) ? + sousFamilleProduit.getPurchFixedAssetsAccount() : + familleProduit.getPurchFixedAssetsAccount() + ); + + accountManagement.setTypeSelect(1); // Product type + Beans.get(AccountManagementRepository.class).save(accountManagement); + } + } + } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/InvoiceServiceImpl.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/InvoiceServiceImpl.java index 70cc78d..f39bc18 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/InvoiceServiceImpl.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/InvoiceServiceImpl.java @@ -20,6 +20,8 @@ package com.axelor.apps.account.service.invoice; import com.axelor.apps.account.db.Account; import com.axelor.apps.account.db.AccountConfig; import com.axelor.apps.account.db.AccountingSituation; +import com.axelor.apps.account.db.AnalyticJournal; +import com.axelor.apps.account.db.AnalyticMoveLine; import com.axelor.apps.account.db.Invoice; import com.axelor.apps.account.db.InvoiceLine; import com.axelor.apps.account.db.InvoicePayment; @@ -36,6 +38,7 @@ import com.axelor.apps.account.db.repo.MoveRepository; import com.axelor.apps.account.db.repo.PaymentModeRepository; import com.axelor.apps.account.exception.IExceptionMessage; import com.axelor.apps.account.service.AccountingSituationService; +import com.axelor.apps.account.service.AnalyticMoveLineService; import com.axelor.apps.account.service.app.AppAccountService; import com.axelor.apps.account.service.config.AccountConfigService; import com.axelor.apps.account.service.invoice.factory.CancelFactory; @@ -46,6 +49,7 @@ import com.axelor.apps.account.service.invoice.generator.invoice.RefundInvoice; import com.axelor.apps.account.service.invoice.print.InvoicePrintService; import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentToolService; import com.axelor.apps.base.db.Alarm; +import com.axelor.apps.base.db.AppBudget; import com.axelor.apps.base.db.BankDetails; import com.axelor.apps.base.db.CancelReason; import com.axelor.apps.base.db.Company; @@ -53,6 +57,7 @@ import com.axelor.apps.base.db.Currency; import com.axelor.apps.base.db.Partner; import com.axelor.apps.base.db.PriceList; import com.axelor.apps.base.db.Sequence; +import com.axelor.apps.base.db.repo.AppBudgetRepository; import com.axelor.apps.base.db.repo.BankDetailsRepository; import com.axelor.apps.base.db.repo.PriceListRepository; import com.axelor.apps.base.service.PartnerService; @@ -102,6 +107,7 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ protected PartnerService partnerService; protected InvoiceLineService invoiceLineService; protected AccountConfigService accountConfigService; + protected AnalyticMoveLineService analyticMoveLineService; @Inject public InvoiceServiceImpl( @@ -113,7 +119,8 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ AppAccountService appAccountService, PartnerService partnerService, InvoiceLineService invoiceLineService, - AccountConfigService accountConfigService) { + AccountConfigService accountConfigService, + AnalyticMoveLineService analyticMoveLineService) { this.validateFactory = validateFactory; this.ventilateFactory = ventilateFactory; @@ -124,6 +131,7 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ this.partnerService = partnerService; this.invoiceLineService = invoiceLineService; this.accountConfigService = accountConfigService; + this.analyticMoveLineService = analyticMoveLineService; } // WKF @@ -445,7 +453,10 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ public void setSequence(Invoice invoice) throws AxelorException { if (invoice.getId() != null && Strings.isNullOrEmpty(invoice.getInvoiceId())) { - invoice.setInvoiceId(Beans.get(SequenceService.class).getSequenceNumber(getSequence(invoice), Beans.get(AppBaseService.class).getTodayDate())); + invoice.setInvoiceId( + Beans.get(SequenceService.class) + .getSequenceNumber( + getSequence(invoice), Beans.get(AppBaseService.class).getTodayDate())); } } @@ -461,15 +472,17 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ return accountConfigService.getSuppRefSequence(accountConfig); case InvoiceRepository.OPERATION_TYPE_CLIENT_SALE: - if(invoice.getOperationSubTypeSelect() == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_DISCOUNT){ - return accountConfigService.getFinancialCustInvSequence(accountConfig); - }else{ - return accountConfigService.getCustInvSequence(accountConfig); - } + if (invoice.getOperationSubTypeSelect() + == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_DISCOUNT) { + return accountConfigService.getFinancialCustInvSequence(accountConfig); + } else { + return accountConfigService.getCustInvSequence(accountConfig); + } case InvoiceRepository.OPERATION_TYPE_CLIENT_REFUND: - if(invoice.getOperationSubTypeSelect() == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_REFUNDS){ + if (invoice.getOperationSubTypeSelect() + == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_REFUNDS) { return accountConfigService.getFinancialCustRefSequence(accountConfig); - }else{ + } else { return accountConfigService.getCustRefSequence(accountConfig); } @@ -482,7 +495,6 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ } } - public Invoice mergeInvoiceProcess( List invoiceList, Company company, @@ -1016,4 +1028,55 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ .getResultList() .size(); } + + public List checkInconsistentAnalyticDistribution(Invoice invoice) + throws AxelorException { + List invoiceLines = invoice.getInvoiceLineList(); + List products = new ArrayList<>(); + for (InvoiceLine invoiceLine : invoiceLines) { + BigDecimal sumPourcentagePerLine = + invoiceLine + .getAnalyticMoveLineList() + .stream() + .map(AnalyticMoveLine::getPercentage) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (sumPourcentagePerLine.compareTo(BigDecimal.valueOf(100.0)) > 0) { + products.add(invoiceLine.getProductName()); + break; + } + } + return products; + } + + public Boolean isMissingAnalyticMoveLine(Invoice invoice) { + Boolean isMissing = false; + for (InvoiceLine invoiceLine : invoice.getInvoiceLineList()) { + if (invoiceLine.getAnalyticMoveLineList().isEmpty()) { + isMissing = true; + break; + } + } + return isMissing; + } + + @Transactional + public void generateAnalyticMoveLines(Invoice invoice) { + List invoiceLines = invoice.getInvoiceLineList(); + for (InvoiceLine invoiceLine : invoiceLines) { + AnalyticMoveLine aml = new AnalyticMoveLine(); + AppBudget AppBudget = Beans.get(AppBudgetRepository.class).all().fetchOne(); + AnalyticJournal deAnalyticJournal = AppBudget.getDefaultAnalyticJournal(); + if (invoice.getAnalyticJournal() != null) { + aml.setAnalyticJournal(invoice.getAnalyticJournal()); + } else { + aml.setAnalyticJournal(deAnalyticJournal); + } + aml.setAnalyticAccount(invoice.getAnalyticAccount()); + aml.setAnalyticAxis(invoice.getAnalyticAxis()); + aml.setPercentage(BigDecimal.valueOf(100)); + analyticMoveLineService.updateAnalyticMoveLine( + aml, invoiceLine.getCompanyExTaxTotal(), invoice.getInvoiceDate()); + invoiceLine.addAnalyticMoveLineListItem(aml); + } + } } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/workflow/ventilate/VentilateState.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/workflow/ventilate/VentilateState.java index 745f5fb..1587890 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/workflow/ventilate/VentilateState.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/invoice/workflow/ventilate/VentilateState.java @@ -319,15 +319,17 @@ public class VentilateState extends WorkflowInvoice { return accountConfigService.getSuppRefSequence(accountConfig); case InvoiceRepository.OPERATION_TYPE_CLIENT_SALE: - if(invoice.getOperationSubTypeSelect() == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_DISCOUNT){ - return accountConfigService.getFinancialCustInvSequence(accountConfig); - }else{ - return accountConfigService.getCustInvSequence(accountConfig); - } + if (invoice.getOperationSubTypeSelect() + == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_DISCOUNT) { + return accountConfigService.getFinancialCustInvSequence(accountConfig); + } else { + return accountConfigService.getCustInvSequence(accountConfig); + } case InvoiceRepository.OPERATION_TYPE_CLIENT_REFUND: - if(invoice.getOperationSubTypeSelect() == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_REFUNDS){ + if (invoice.getOperationSubTypeSelect() + == InvoiceRepository.OPERATION_SUB_TYPE_FINANCIAL_REFUNDS) { return accountConfigService.getFinancialCustRefSequence(accountConfig); - }else{ + } else { return accountConfigService.getCustRefSequence(accountConfig); } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/move/MoveLineService.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/move/MoveLineService.java index 15b3936..39cbaaf 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/move/MoveLineService.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/move/MoveLineService.java @@ -38,14 +38,13 @@ import com.axelor.apps.account.exception.IExceptionMessage; import com.axelor.apps.account.service.AccountManagementAccountService; import com.axelor.apps.account.service.AnalyticMoveLineService; import com.axelor.apps.account.service.FiscalPositionAccountService; -import com.axelor.apps.account.service.ReconcileServiceImpl; import com.axelor.apps.account.service.TaxAccountService; import com.axelor.apps.account.service.TaxPaymentMoveLineService; import com.axelor.apps.account.service.app.AppAccountService; import com.axelor.apps.account.service.config.AccountConfigService; import com.axelor.apps.account.service.invoice.InvoiceToolService; import com.axelor.apps.account.service.payment.PaymentService; -import com.axelor.apps.account.service.payment.PaymentServiceImpl; +import com.axelor.apps.account.util.MoveLineKey; import com.axelor.apps.base.db.Company; import com.axelor.apps.base.db.Currency; import com.axelor.apps.base.db.Partner; @@ -486,28 +485,31 @@ public class MoveLineService { moveLineId++, origin, invoiceLine.getProductName()); - if(invoiceLine.getAnalyticDistributionTemplate() != null) + if (invoiceLine.getAnalyticDistributionTemplate() != null) moveLine.setAnalyticDistributionTemplate(invoiceLine.getAnalyticDistributionTemplate()); - if (invoiceLine.getAnalyticMoveLineList() != null - && !invoiceLine.getAnalyticMoveLineList().isEmpty()) { - for (AnalyticMoveLine invoiceAnalyticMoveLine : invoiceLine.getAnalyticMoveLineList()) { - System.out.println("-----------------invoiceAnalyticMoveLine.getAccount().getName()----------------------------"); - System.out.println(invoiceAnalyticMoveLine.getAccount()); - AnalyticMoveLine analyticMoveLine = - analyticMoveLineRepository.copy(invoiceAnalyticMoveLine, false); - analyticMoveLine.setTypeSelect(AnalyticMoveLineRepository.STATUS_REAL_ACCOUNTING); - analyticMoveLine.setInvoiceLine(null); - analyticMoveLine.setAccount(moveLine.getAccount()); - analyticMoveLine.setAccountType(moveLine.getAccount().getAccountType()); - analyticMoveLineService.updateAnalyticMoveLine( - analyticMoveLine, - moveLine.getDebit().add(moveLine.getCredit()), - moveLine.getDate()); - moveLine.addAnalyticMoveLineListItem(analyticMoveLine); - } - } else { + // if (invoiceLine.getAnalyticMoveLineList() != null + // && !invoiceLine.getAnalyticMoveLineList().isEmpty()) { + // for (AnalyticMoveLine invoiceAnalyticMoveLine : invoiceLine.getAnalyticMoveLineList()) { + // System.out.println( + // "-----------------invoiceAnalyticMoveLine.getAccount().getName()----------------------------"); + // System.out.println(invoiceAnalyticMoveLine.getId()); + // System.out.println(invoiceAnalyticMoveLine.toString()); + // System.out.println( "-----------------invoiceAnalyticMoveLine.getAccount().getName()----------------------------"); + // AnalyticMoveLine analyticMoveLine = + // analyticMoveLineRepository.copy(invoiceAnalyticMoveLine, false); + // analyticMoveLine.setTypeSelect(AnalyticMoveLineRepository.STATUS_REAL_ACCOUNTING); + // analyticMoveLine.setInvoiceLine(null); + // analyticMoveLine.setAccount(moveLine.getAccount()); + // analyticMoveLine.setAccountType(moveLine.getAccount().getAccountType()); + // analyticMoveLineService.updateAnalyticMoveLine( + // analyticMoveLine, + // moveLine.getDebit().add(moveLine.getCredit()), + // moveLine.getDate()); + // moveLine.addAnalyticMoveLineListItem(analyticMoveLine); + // } + // } else { generateAnalyticMoveLines(moveLine); - } + // } TaxLine taxLine = invoiceLine.getTaxLine(); if (taxLine != null) { @@ -655,65 +657,60 @@ public class MoveLineService { } public MoveLine findConsolidateMoveLine( - Map, MoveLine> map, MoveLine moveLine, List keys) { - if (map != null && !map.isEmpty()) { + Map map, MoveLine moveLine, MoveLineKey keys) { - Map, MoveLine> copyMap = new HashMap, MoveLine>(map); - while (!copyMap.isEmpty()) { + if (map == null || map.isEmpty()) { + return null; + } - if (map.containsKey(keys)) { + // Quick lookup by composite key (account, taxLine, template) + MoveLine existing = map.get(keys); + if (existing == null) { + return null; + } - MoveLine moveLineIt = map.get(keys); - int count = 0; - if (moveLineIt.getAnalyticMoveLineList() == null - && moveLine.getAnalyticMoveLineList() == null) { - return moveLineIt; - } else if (moveLineIt.getAnalyticMoveLineList() == null - || moveLine.getAnalyticMoveLineList() == null) { - break; - } - List list1 = moveLineIt.getAnalyticMoveLineList(); - List list2 = moveLine.getAnalyticMoveLineList(); - List copyList = new ArrayList(list1); - if (list1.size() == list2.size()) { - for (AnalyticMoveLine analyticDistributionLine : list2) { - for (AnalyticMoveLine analyticDistributionLineIt : copyList) { - if (analyticDistributionLine - .getAnalyticAxis() - .equals(analyticDistributionLineIt.getAnalyticAxis()) - && analyticDistributionLine - .getAnalyticAccount() - .equals(analyticDistributionLineIt.getAnalyticAccount()) - && analyticDistributionLine - .getAccount() - .equals(analyticDistributionLineIt.getAccount()) - && analyticDistributionLine - .getPercentage() - .equals(analyticDistributionLineIt.getPercentage()) - && ((analyticDistributionLine.getAnalyticJournal() == null - && analyticDistributionLineIt.getAnalyticJournal() == null) - || analyticDistributionLine - .getAnalyticJournal() - .equals(analyticDistributionLineIt.getAnalyticJournal()))) { - copyList.remove(analyticDistributionLineIt); - count++; - break; - } - } - } - if (count == list1.size()) { - return moveLineIt; - } - } - } else { - return null; - } - } + // Fast exit if both have no analytic lines + List list1 = existing.getAnalyticMoveLineList(); + List list2 = moveLine.getAnalyticMoveLineList(); + if ((list1 == null || list1.isEmpty()) && (list2 == null || list2.isEmpty())) { + return existing; + } + + // If one has analytics but not the other → not same + if ((list1 == null || list1.isEmpty()) ^ (list2 == null || list2.isEmpty())) { + return null; + } + + // ⚙️ Build lightweight hash sets for quick equality check + Set set1 = new HashSet<>(list1.size()); + Set set2 = new HashSet<>(list2.size()); + + for (AnalyticMoveLine a1 : list1) { + set1.add(buildAnalyticKey(a1)); + } + for (AnalyticMoveLine a2 : list2) { + set2.add(buildAnalyticKey(a2)); + } + + // Compare as sets — same content, order doesn’t matter + if (set1.equals(set2)) { + return existing; } return null; } + /** Build a lightweight key for an AnalyticMoveLine to avoid deep equals. */ + private static String buildAnalyticKey(AnalyticMoveLine a) { + Long axisId = a.getAnalyticAxis() != null ? a.getAnalyticAxis().getId() : null; + Long accId = a.getAnalyticAccount() != null ? a.getAnalyticAccount().getId() : null; + Long glId = a.getAccount() != null ? a.getAccount().getId() : null; + BigDecimal pct = a.getPercentage() != null ? a.getPercentage() : BigDecimal.ZERO; + Long journalId = a.getAnalyticJournal() != null ? a.getAnalyticJournal().getId() : null; + + return axisId + ":" + accId + ":" + glId + ":" + pct + ":" + journalId; + } + /** * Consolider des lignes d'écritures par compte comptable. * @@ -721,16 +718,12 @@ public class MoveLineService { */ public List consolidateMoveLines(List moveLines) { - Map, MoveLine> map = new HashMap, MoveLine>(); + Map map = new HashMap<>(); MoveLine consolidateMoveLine = null; for (MoveLine moveLine : moveLines) { - List keys = new ArrayList(); - - keys.add(moveLine.getAccount()); - keys.add(moveLine.getTaxLine()); - keys.add(moveLine.getAnalyticDistributionTemplate()); + MoveLineKey keys = new MoveLineKey(moveLine); consolidateMoveLine = this.findConsolidateMoveLine(map, moveLine, keys); if (consolidateMoveLine != null) { @@ -1046,7 +1039,7 @@ public class MoveLineService { JPA.clear(); } } - + // } // else{ // throw new AxelorException( diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherConfirmService.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherConfirmService.java index 8fd2ff6..a66675d 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherConfirmService.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherConfirmService.java @@ -572,7 +572,7 @@ public class PaymentVoucherConfirmService { paymentDate, paymentMode, MoveRepository.TECHNICAL_ORIGIN_AUTOMATIC); - + move.setPaymentVoucher(paymentVoucher); List payVoucherElementToPayList = @@ -638,8 +638,8 @@ public class PaymentVoucherConfirmService { if (isDebitToPay) { reconcileService.balanceCredit(moveLine); } - if(paymentVoucher.getPartner().getId() == 2040L){ - this.updateCashRegisterTheoricalSolde(paymentVoucher, isDebitToPay,paidAmount); + if (paymentVoucher.getPartner().getId() == 2040L) { + this.updateCashRegisterTheoricalSolde(paymentVoucher, isDebitToPay, paidAmount); } // Beans.get(PaymentVoucherLoadService.class).updateVoucher(paymentVoucher); } @@ -1025,10 +1025,8 @@ public class PaymentVoucherConfirmService { */ @Transactional(rollbackOn = {Exception.class}) public void confirmCashPayment(PaymentVoucher paymentVoucher) throws AxelorException { - if(paymentVoucher.getGeneratedMoveList().size() > 0){ - throw new AxelorException( - TraceBackRepository.CATEGORY_INCONSISTENCY, - "Already ventilated."); + if (paymentVoucher.getGeneratedMoveList().size() > 0) { + throw new AxelorException(TraceBackRepository.CATEGORY_INCONSISTENCY, "Already ventilated."); } PaymentMode paymentMode = paymentVoucher.getPaymentMode(); Company company = paymentVoucher.getCompany(); @@ -1038,8 +1036,7 @@ public class PaymentVoucherConfirmService { // LocalDate paymentDate = paymentVoucher.getPaymentDate(); LocalDate paymentDate = paymentVoucher.getPaymentEmissionDate(); - Account paymentModeAccount = - paymentVoucher.getCashAccountConfig().getAccount(); + Account paymentModeAccount = paymentVoucher.getCashAccountConfig().getAccount(); Move move = moveService @@ -1095,11 +1092,13 @@ public class PaymentVoucherConfirmService { move.getMoveLineList().add(moveLine2); String label = paymentVoucher.getLabel() != null ? paymentVoucher.getLabel() : ""; - String fullName = (paymentVoucher.getRecipientPartner() != null && paymentVoucher.getRecipientPartner().getFullName() != null) - ? paymentVoucher.getRecipientPartner().getFullName() - : ""; + String fullName = + (paymentVoucher.getRecipientPartner() != null + && paymentVoucher.getRecipientPartner().getFullName() != null) + ? paymentVoucher.getRecipientPartner().getFullName() + : ""; - String description = label +" "+ fullName; + String description = label + " " + fullName; moveLine.setDescription(description); moveLine2.setDescription(description); @@ -1107,7 +1106,7 @@ public class PaymentVoucherConfirmService { moveService.getMoveValidateService().validate(move); paymentVoucher.setGeneratedMove(move); paymentVoucher.addGeneratedMoveListItem(move); - + // confirmed is ventilation equivalent paymentVoucher.setConfirmedByUser(AuthUtils.getUser()); paymentVoucher.setConfirmationDate(LocalDate.now()); @@ -1120,8 +1119,10 @@ public class PaymentVoucherConfirmService { } @Transactional(rollbackOn = {Exception.class}) - public void updateCashRegisterTheoricalSolde(PaymentVoucher paymentVoucher,Boolean isDebitCredit,BigDecimal amount) { - System.out.println("updateCashRegisterTheoricalSolde : : : " + paymentVoucher.getCashAccountConfig()); + public void updateCashRegisterTheoricalSolde( + PaymentVoucher paymentVoucher, Boolean isDebitCredit, BigDecimal amount) { + System.out.println( + "updateCashRegisterTheoricalSolde : : : " + paymentVoucher.getCashAccountConfig()); if (paymentVoucher.getCashAccountConfig() != null || paymentVoucher.getPartner() != null) { CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class); CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne(); @@ -1148,66 +1149,64 @@ public class PaymentVoucherConfirmService { throw new AxelorException( TraceBackRepository.CATEGORY_INCONSISTENCY, "Cash account is not set for the payment voucher."); - } - CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class); - CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne(); - - BigDecimal solde = cashRegister.getTheoricalSolde(); - if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 1) { - solde = solde.subtract(paymentVoucher.getPaidAmount()); - }else{ - solde = solde.add(paymentVoucher.getPaidAmount()); - } + } + CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class); + CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne(); - paymentVoucher.setInitialTheoricalSolde(cashRegister.getTheoricalSolde()); - paymentVoucher.setTheoricalSolde(solde); - paymentVoucher.setCashStatusSelect(PaymentVoucherRepository.STATUS_WAITING_FOR_DEPOSIT_SLIP); - paymentVoucher.setApprovalDate(LocalDate.now()); - paymentVoucher.setApprovedByUser(AuthUtils.getUser()); - Boolean isDebitToPay = false; + BigDecimal solde = cashRegister.getTheoricalSolde(); + if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 1) { + solde = solde.subtract(paymentVoucher.getPaidAmount()); + } else { + solde = solde.add(paymentVoucher.getPaidAmount()); + } + + paymentVoucher.setInitialTheoricalSolde(cashRegister.getTheoricalSolde()); + paymentVoucher.setTheoricalSolde(solde); + paymentVoucher.setCashStatusSelect(PaymentVoucherRepository.STATUS_WAITING_FOR_DEPOSIT_SLIP); + paymentVoucher.setApprovalDate(LocalDate.now()); + paymentVoucher.setApprovedByUser(AuthUtils.getUser()); + Boolean isDebitToPay = false; if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 1) { isDebitToPay = true; } - updateCashRegisterTheoricalSolde(paymentVoucher, isDebitToPay,paymentVoucher.getPaidAmount()); + updateCashRegisterTheoricalSolde(paymentVoucher, isDebitToPay, paymentVoucher.getPaidAmount()); } @Transactional(rollbackOn = {Exception.class}) public void cancelCashPaymentVoucher(PaymentVoucher paymentVoucher) throws AxelorException { - if (paymentVoucher.getCashAccountConfig() == null) { - throw new AxelorException( - TraceBackRepository.CATEGORY_INCONSISTENCY, - "Cash account config is not set for the payment voucher."); - } - if (paymentVoucher.getCashAccountConfig().getAccount() == null) { - throw new AxelorException( - TraceBackRepository.CATEGORY_INCONSISTENCY, - "Cash account is not set for the payment voucher."); - } - CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class); - CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne(); - - BigDecimal solde = cashRegister.getTheoricalSolde(); - if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 2) { - solde = solde.subtract(paymentVoucher.getPaidAmount()); - }else{ - solde = solde.add(paymentVoucher.getPaidAmount()); - } + if (paymentVoucher.getCashAccountConfig() == null) { + throw new AxelorException( + TraceBackRepository.CATEGORY_INCONSISTENCY, + "Cash account config is not set for the payment voucher."); + } + if (paymentVoucher.getCashAccountConfig().getAccount() == null) { + throw new AxelorException( + TraceBackRepository.CATEGORY_INCONSISTENCY, + "Cash account is not set for the payment voucher."); + } + CashRegisterRepository casRegisterRepository = Beans.get(CashRegisterRepository.class); + CashRegister cashRegister = casRegisterRepository.all().order("-createdOn").fetchOne(); - paymentVoucher.setInitialTheoricalSolde(cashRegister.getTheoricalSolde()); - paymentVoucher.setTheoricalSolde(solde); - paymentVoucher.setCashStatusSelect(4); - Boolean isDebitToPay = false; + BigDecimal solde = cashRegister.getTheoricalSolde(); + if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 2) { + solde = solde.subtract(paymentVoucher.getPaidAmount()); + } else { + solde = solde.add(paymentVoucher.getPaidAmount()); + } - if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 1) { - isDebitToPay = true; - } - // Beans.get(MoveServiceImpl.class). - updateCashRegisterTheoricalSolde(paymentVoucher, !isDebitToPay,paymentVoucher.getPaidAmount()); - for (Move move : paymentVoucher.getGeneratedMoveList()) { - Beans.get(MoveCancelService.class).cancel(move); - } + paymentVoucher.setInitialTheoricalSolde(cashRegister.getTheoricalSolde()); + paymentVoucher.setTheoricalSolde(solde); + paymentVoucher.setCashStatusSelect(4); + Boolean isDebitToPay = false; + + if (Integer.parseInt(paymentVoucher.getDebitCreditSelect()) == 1) { + isDebitToPay = true; + } + // Beans.get(MoveServiceImpl.class). + updateCashRegisterTheoricalSolde(paymentVoucher, !isDebitToPay, paymentVoucher.getPaidAmount()); + for (Move move : paymentVoucher.getGeneratedMoveList()) { + Beans.get(MoveCancelService.class).cancel(move); + } } - - } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherSequenceService.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherSequenceService.java index 43ea508..b9a2df6 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherSequenceService.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/service/payment/paymentvoucher/PaymentVoucherSequenceService.java @@ -28,6 +28,7 @@ import com.axelor.apps.base.service.administration.SequenceService; import com.axelor.exception.AxelorException; import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.i18n.I18n; +import com.axelor.inject.Beans; import com.google.common.base.Strings; import com.google.inject.Inject; @@ -58,9 +59,18 @@ public class PaymentVoucherSequenceService { Company company = paymentVoucher.getCompany(); // Sequence seq = Beans.get(SequenceRepository.class).find(new Long("126")); - return sequenceService.getSequenceNumber( + String sequence = ""; + if (paymentVoucher.getOperationTypeSelect() == 9) { + sequence = sequenceService + .getSequenceNumber("CashSequence", paymentVoucher.getCompany()); + } else { + sequence = sequenceService.getSequenceNumber( paymentModeService.getPaymentModeSequence( paymentMode, company, paymentVoucher.getCompanyBankDetails())); + + } + + return sequence; // return sequenceService.getSequenceNumber(seq); } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/util/MoveLineKey.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/util/MoveLineKey.java new file mode 100644 index 0000000..9f01a7d --- /dev/null +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/util/MoveLineKey.java @@ -0,0 +1,34 @@ +package com.axelor.apps.account.util; + +import com.axelor.apps.account.db.MoveLine; +import java.util.Objects; + +public class MoveLineKey { + private final Long accountId; + private final Long taxLineId; + private final Long analyticTemplateId; + + public MoveLineKey(MoveLine line) { + this.accountId = line.getAccount() != null ? line.getAccount().getId() : null; + this.taxLineId = line.getTaxLine() != null ? line.getTaxLine().getId() : null; + this.analyticTemplateId = + line.getAnalyticDistributionTemplate() != null + ? line.getAnalyticDistributionTemplate().getId() + : null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MoveLineKey)) return false; + MoveLineKey that = (MoveLineKey) o; + return Objects.equals(accountId, that.accountId) + && Objects.equals(taxLineId, that.taxLineId) + && Objects.equals(analyticTemplateId, that.analyticTemplateId); + } + + @Override + public int hashCode() { + return Objects.hash(accountId, taxLineId, analyticTemplateId); + } +} diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/AccountManagementController.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/AccountManagementController.java new file mode 100644 index 0000000..f18c4fb --- /dev/null +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/AccountManagementController.java @@ -0,0 +1,21 @@ +package com.axelor.apps.account.web; + +import com.axelor.apps.account.service.AccountManagementAccountService; +import com.axelor.apps.account.service.AccountManagementServiceAccountImpl; +import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.Product; +import com.axelor.apps.base.db.repo.ProductRepository; +import com.axelor.auth.AuthUtils; +import com.axelor.inject.Beans; +import com.axelor.rpc.ActionRequest; +import com.axelor.rpc.ActionResponse; +import com.axelor.rpc.Context; + +public class AccountManagementController { + public void searchMoveLine(ActionRequest request, ActionResponse response) { + Product product = request.getContext().asType(Product.class); + product = Beans.get(ProductRepository.class).find(product.getId()); + Company company = AuthUtils.getUser().getActiveCompany(); + Beans.get(AccountManagementServiceAccountImpl.class).setAccountManagementForProduct(product, company); + } +} diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/CashInventoryController.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/CashInventoryController.java index d300bc0..9c4ead3 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/CashInventoryController.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/CashInventoryController.java @@ -16,7 +16,6 @@ import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; import com.google.common.base.Strings; import com.google.inject.Inject; - import java.lang.invoke.MethodHandles; import java.math.BigDecimal; import java.util.List; @@ -27,8 +26,7 @@ import org.slf4j.LoggerFactory; public class CashInventoryController { private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - @Inject - private final ConvertNumberToFrenchWordsService convertService; + @Inject private final ConvertNumberToFrenchWordsService convertService; @Inject public CashInventoryController(ConvertNumberToFrenchWordsService convertService) { @@ -38,7 +36,7 @@ public class CashInventoryController { /** * Prints the cash inventory report. * - * @param request The action request containing the context. + * @param request The action request containing the context. * @param response The action response to set the view with the report link. * @throws AxelorException If an error occurs during report generation. */ @@ -52,12 +50,13 @@ public class CashInventoryController { name += " " + cashInventory.getRef(); } - String fileLink = ReportFactory.createReport( - String.format(IReport.CASH_INVENTORY, cashInventory.getOperationTypeSelect()), - name + "-${date}") - .addParam("cashInventoryId", cashInventory.getId()) - .generate() - .getFileLink(); + String fileLink = + ReportFactory.createReport( + String.format(IReport.CASH_INVENTORY, cashInventory.getOperationTypeSelect()), + name + "-${date}") + .addParam("cashInventoryId", cashInventory.getId()) + .generate() + .getFileLink(); logger.debug("Printing " + name); @@ -67,7 +66,7 @@ public class CashInventoryController { /** * Computes the theoretical solde in words for the cash inventory. * - * @param request The action request containing the context. + * @param request The action request containing the context. * @param response The action response to set the theoretical solde in words. */ public void convertTheoricalSoldeInWords(ActionRequest request, ActionResponse response) { @@ -85,7 +84,7 @@ public class CashInventoryController { /** * Initializes the theoretical solde in words for the cash inventory. * - * @param request The action request containing the context. + * @param request The action request containing the context. * @param response The action response to set the theoretical solde value. */ public void initThetoricalSolde(ActionRequest request, ActionResponse response) { @@ -96,14 +95,16 @@ public class CashInventoryController { /** * Computes the totals for the cash inventory. * - * @param request The action request containing the context. + * @param request The action request containing the context. * @param response The action response to set the computed values. */ public void computeToTals(ActionRequest request, ActionResponse response) { CashInventory cashInventory = request.getContext().asType(CashInventory.class); - if(cashInventory.getCashInventoryLines() != null && cashInventory.getCashInventoryLines().size() != 0){ - Map map = Beans.get(CashInventoryService.class).computeToTals(cashInventory); + if (cashInventory.getCashInventoryLines() != null + && cashInventory.getCashInventoryLines().size() != 0) { + Map map = + Beans.get(CashInventoryService.class).computeToTals(cashInventory); response.setValues(map); } } @@ -111,16 +112,18 @@ public class CashInventoryController { /** * Retrieves the last cash data and calculates the theoretical solde. * - * @param request The action request containing the context. + * @param request The action request containing the context. * @param response The action response to set the theoretical solde value. */ public void getLastCashData(ActionRequest request, ActionResponse response) { - CashInventory lastCashInventory = Beans.get(CashInventoryRepository.class).all().order("-id").fetchOne(); + CashInventory lastCashInventory = + Beans.get(CashInventoryRepository.class).all().order("-id").fetchOne(); - List cashVouchers = Beans.get(PaymentVoucherRepository.class) - .all() - .filter("self.operationTypeSelect = 9 and self.cashStatusSelect = 3") - .fetch(); + List cashVouchers = + Beans.get(PaymentVoucherRepository.class) + .all() + .filter("self.operationTypeSelect = 9 and self.cashStatusSelect = 3") + .fetch(); BigDecimal totalSolde = lastCashInventory.getPhysicalSolde(); @@ -142,19 +145,22 @@ public class CashInventoryController { } // generate sequence for cash inventory - public void generateSequence(ActionRequest request, ActionResponse response) throws AxelorException { + public void generateSequence(ActionRequest request, ActionResponse response) + throws AxelorException { CashInventory cashInventory = request.getContext().asType(CashInventory.class); if (cashInventory.getRef() == null || cashInventory.getRef().isEmpty()) { - String sequence = Beans.get(CashInventoryService.class).getCashInventorySequence(cashInventory); + String sequence = + Beans.get(CashInventoryService.class).getCashInventorySequence(cashInventory); response.setValue("ref", sequence); } } - - public void approveCashInventory(ActionRequest request, ActionResponse response) throws AxelorException{ - CashInventory cashInventoryContext = request.getContext().asType(CashInventory.class); - CashInventory cashInventory = Beans.get(CashInventoryRepository.class).find(cashInventoryContext.getId()); - Beans.get(CashInventoryService.class).approveCashInventory(cashInventory); - response.setReload(true); + public void approveCashInventory(ActionRequest request, ActionResponse response) + throws AxelorException { + CashInventory cashInventoryContext = request.getContext().asType(CashInventory.class); + CashInventory cashInventory = + Beans.get(CashInventoryRepository.class).find(cashInventoryContext.getId()); + Beans.get(CashInventoryService.class).approveCashInventory(cashInventory); + response.setReload(true); } } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/InvoiceController.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/InvoiceController.java index 4912890..96b2ac8 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/InvoiceController.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/InvoiceController.java @@ -27,11 +27,11 @@ import com.axelor.apps.account.db.PaymentMode; import com.axelor.apps.account.db.PaymentVoucher; import com.axelor.apps.account.db.repo.InvoiceRepository; import com.axelor.apps.account.exception.IExceptionMessage; -import com.axelor.apps.account.report.IReport; import com.axelor.apps.account.service.AccountingSituationService; import com.axelor.apps.account.service.IrrecoverableService; import com.axelor.apps.account.service.app.AppAccountService; import com.axelor.apps.account.service.invoice.InvoiceService; +import com.axelor.apps.account.service.invoice.InvoiceServiceImpl; import com.axelor.apps.account.service.invoice.InvoiceToolService; import com.axelor.apps.account.service.invoice.print.InvoicePrintService; import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCreateService; @@ -1014,11 +1014,11 @@ public class InvoiceController { System.out.println(paymentVoucher.getOperationTypeSelect() == 9); System.out.println(paymentVoucher.getDebitCreditSelect() == "1"); - if(paymentVoucher.getOperationTypeSelect() == 9){ - if(paymentVoucher.getDebitCreditSelect().equals("1")){ - reportType = "CashPaymentRequestDebit.rptdesign"; - }else if(paymentVoucher.getDebitCreditSelect().equals("2")){ - reportType = "CashPaymentRequestCredit.rptdesign"; + if (paymentVoucher.getOperationTypeSelect() == 9) { + if (paymentVoucher.getDebitCreditSelect().equals("1")) { + reportType = "CashPaymentRequestDebit.rptdesign"; + } else if (paymentVoucher.getDebitCreditSelect().equals("2")) { + reportType = "CashPaymentRequestCredit.rptdesign"; } } @@ -1100,4 +1100,40 @@ public class InvoiceController { } } } + + public void isMissingAnalyticMoveLine(ActionRequest request, ActionResponse response) + throws AxelorException { + Invoice invoice = request.getContext().asType(Invoice.class); + Invoice inv = Beans.get(InvoiceRepository.class).find(invoice.getId()); + Boolean isMissingAnalyticMoveLine = + Beans.get(InvoiceServiceImpl.class).isMissingAnalyticMoveLine(inv); + if (isMissingAnalyticMoveLine) { + throw new AxelorException( + invoice, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "The analytic account is missing on one of the lines."); + } + } + + public void generateAnalyticMoveLines(ActionRequest request, ActionResponse response) { + Invoice invoice = request.getContext().asType(Invoice.class); + Invoice inv = Beans.get(InvoiceRepository.class).find(invoice.getId()); + Beans.get(InvoiceServiceImpl.class).generateAnalyticMoveLines(inv); + response.setAlert("The analytic move lines have been generated."); + } + + public void checkInconsistentAnalyticDistribution(ActionRequest request, ActionResponse response) + throws AxelorException { + Context context = request.getContext(); + Invoice invoice = context.asType(Invoice.class); + invoice = Beans.get(InvoiceRepository.class).find(invoice.getId()); + List products = + Beans.get(InvoiceServiceImpl.class).checkInconsistentAnalyticDistribution(invoice); + if (products.isEmpty()) { + return; + } + response.setAlert( + "The analytic distribution of at least one invoice line is inconsistent with the total amount of the line. Please check the analytic distribution of each invoice line." + + String.join(", ", products)); + } } diff --git a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/PaymentVoucherController.java b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/PaymentVoucherController.java index 214532c..6ef8605 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/PaymentVoucherController.java +++ b/modules/axelor-open-suite/axelor-account/src/main/java/com/axelor/apps/account/web/PaymentVoucherController.java @@ -338,11 +338,11 @@ public class PaymentVoucherController { System.out.println(paymentVoucher.getOperationTypeSelect() == 9); System.out.println(paymentVoucher.getDebitCreditSelect() == "1"); - if(paymentVoucher.getOperationTypeSelect() == 9){ - if(paymentVoucher.getDebitCreditSelect() == "1"){ - reportType = "CashPaymentRequestDebit.rptdesign"; - }else if(paymentVoucher.getDebitCreditSelect() == "2"){ - reportType = "CashPaymentRequestCredit.rptdesign"; + if (paymentVoucher.getOperationTypeSelect() == 9) { + if (paymentVoucher.getDebitCreditSelect() == "1") { + reportType = "CashPaymentRequestDebit.rptdesign"; + } else if (paymentVoucher.getDebitCreditSelect() == "2") { + reportType = "CashPaymentRequestCredit.rptdesign"; } } @@ -495,8 +495,8 @@ public class PaymentVoucherController { .map()); } - public void validateCashPaymentVoucher( - ActionRequest request, ActionResponse response) throws AxelorException { + public void validateCashPaymentVoucher(ActionRequest request, ActionResponse response) + throws AxelorException { PaymentVoucher paymentVoucher = request.getContext().asType(PaymentVoucher.class); paymentVoucher = Beans.get(PaymentVoucherRepository.class).find(paymentVoucher.getId()); @@ -508,9 +508,8 @@ public class PaymentVoucherController { } } - - public void cancelCashPaymentVoucher( - ActionRequest request, ActionResponse response) throws AxelorException { + public void cancelCashPaymentVoucher(ActionRequest request, ActionResponse response) + throws AxelorException { PaymentVoucher paymentVoucher = request.getContext().asType(PaymentVoucher.class); paymentVoucher = Beans.get(PaymentVoucherRepository.class).find(paymentVoucher.getId()); @@ -521,4 +520,4 @@ public class PaymentVoucherController { TraceBackService.trace(response, e); } } -} \ No newline at end of file +} diff --git a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticAccount.xml b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticAccount.xml index 339015b..a92b6ad 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticAccount.xml +++ b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticAccount.xml @@ -61,6 +61,8 @@ ]]> + + diff --git a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticMoveLine.xml b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticMoveLine.xml index 351c5db..1cae9da 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticMoveLine.xml +++ b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AnalyticMoveLine.xml @@ -32,6 +32,8 @@ public static final int STATUS_FORECAST_ORDER = 1; public static final int STATUS_FORECAST_INVOICE = 2; public static final int STATUS_REAL_ACCOUNTING = 3; + public static final int STATUS_PURCHASE_REQUEST = 4; + public static final int STATUS_STOCK_MOVE = 5; ]]> diff --git a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AppBudget.xml b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AppBudget.xml index ced8a1a..2f3dd10 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AppBudget.xml +++ b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/AppBudget.xml @@ -10,10 +10,15 @@ + + + + + diff --git a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Budget.xml b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Budget.xml index 9b394df..975caaf 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Budget.xml +++ b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Budget.xml @@ -20,9 +20,12 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Invoice.xml b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Invoice.xml index 9226249..247031b 100644 --- a/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Invoice.xml +++ b/modules/axelor-open-suite/axelor-account/src/main/resources/domains/Invoice.xml @@ -155,6 +155,10 @@ + + + + keyCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + verify(response).setValue(keyCaptor.capture(), valueCaptor.capture()); + assertEquals("theoricalSoldeInWords", keyCaptor.getValue()); + assertEquals("mille dinars algériens et zéro Cts", valueCaptor.getValue()); + } + // @Test + // public void testConvert_withNoDecimalPart() { + // // Given + // when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("42")); + // when(convertService.convert(42L)).thenReturn("quarante-deux"); + // when(convertService.convert(0L)).thenReturn("zéro"); - // Then - ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); - verify(response).setValue(keyCaptor.capture(), valueCaptor.capture()); - assertEquals("theoricalSoldeInWords", keyCaptor.getValue()); - assertEquals("mille dinars algériens et zéro Cts", valueCaptor.getValue()); - } - - // @Test - // public void testConvert_withNoDecimalPart() { - // // Given - // when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("42")); - // when(convertService.convert(42L)).thenReturn("quarante-deux"); - // when(convertService.convert(0L)).thenReturn("zéro"); - - - // // Then - // ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); - // ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); - // verify(response).setValue(keyCaptor.capture(), valueCaptor.capture()); - // assertEquals("theoricalSoldeInWords", keyCaptor.getValue()); - // assertEquals("quarante-deux dinars algériens et zéro Cts", - // valueCaptor.getValue()); - // } -} \ No newline at end of file + // // Then + // ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); + // ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + // verify(response).setValue(keyCaptor.capture(), valueCaptor.capture()); + // assertEquals("theoricalSoldeInWords", keyCaptor.getValue()); + // assertEquals("quarante-deux dinars algériens et zéro Cts", + // valueCaptor.getValue()); + // } +} diff --git a/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/db/repo/ProductBaseRepository.java b/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/db/repo/ProductBaseRepository.java index be4dc2b..a6a26c0 100644 --- a/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/db/repo/ProductBaseRepository.java +++ b/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/db/repo/ProductBaseRepository.java @@ -68,7 +68,7 @@ public class ProductBaseRepository extends ProductRepository { FULL_NAME_FORMAT, product.getCode(), product.getName()); } - product = super.save(product); + // product = super.save(product); if (product.getBarCode() == null && appBaseService.getAppBase().getActivateBarCodeGeneration()) { try { diff --git a/modules/axelor-open-suite/axelor-base/src/main/resources/domains/FamilleProduit.xml b/modules/axelor-open-suite/axelor-base/src/main/resources/domains/FamilleProduit.xml index a2ebcd9..2de0de6 100644 --- a/modules/axelor-open-suite/axelor-base/src/main/resources/domains/FamilleProduit.xml +++ b/modules/axelor-open-suite/axelor-base/src/main/resources/domains/FamilleProduit.xml @@ -9,10 +9,11 @@ - + - - + + + diff --git a/modules/axelor-open-suite/axelor-business-project/src/main/java/com/axelor/apps/businessproject/service/InvoiceServiceProjectImpl.java b/modules/axelor-open-suite/axelor-business-project/src/main/java/com/axelor/apps/businessproject/service/InvoiceServiceProjectImpl.java index 4858d56..ad4f6d6 100644 --- a/modules/axelor-open-suite/axelor-business-project/src/main/java/com/axelor/apps/businessproject/service/InvoiceServiceProjectImpl.java +++ b/modules/axelor-open-suite/axelor-business-project/src/main/java/com/axelor/apps/businessproject/service/InvoiceServiceProjectImpl.java @@ -21,6 +21,7 @@ import com.axelor.apps.ReportFactory; import com.axelor.apps.account.db.Invoice; import com.axelor.apps.account.db.repo.InvoiceRepository; import com.axelor.apps.account.exception.IExceptionMessage; +import com.axelor.apps.account.service.AnalyticMoveLineService; import com.axelor.apps.account.service.app.AppAccountService; import com.axelor.apps.account.service.config.AccountConfigService; import com.axelor.apps.account.service.invoice.InvoiceLineService; @@ -52,7 +53,8 @@ public class InvoiceServiceProjectImpl extends InvoiceServiceSupplychainImpl { AppAccountService appAccountService, PartnerService partnerService, InvoiceLineService invoiceLineService, - AccountConfigService accountConfigService) { + AccountConfigService accountConfigService, + AnalyticMoveLineService analyticMoveLineService) { super( validateFactory, ventilateFactory, @@ -62,7 +64,8 @@ public class InvoiceServiceProjectImpl extends InvoiceServiceSupplychainImpl { appAccountService, partnerService, invoiceLineService, - accountConfigService); + accountConfigService, + analyticMoveLineService); } public List editInvoiceAnnex(Invoice invoice, String invoiceIds, boolean toAttach) diff --git a/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/service/manuforder/ManufOrderStockMoveService.java b/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/service/manuforder/ManufOrderStockMoveService.java index 6553503..05f4be4 100644 --- a/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/service/manuforder/ManufOrderStockMoveService.java +++ b/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/service/manuforder/ManufOrderStockMoveService.java @@ -792,28 +792,29 @@ public class ManufOrderStockMoveService { stockConfigService.getProductionVirtualStockLocation(stockConfig); stockMove.setToStockLocation(virtualStockLocation); stockMove.setPartner(manufOrder.getCompany().getPartner()); - for (BillOfMaterialConsumption billOfMaterialConsumption : manufOrder.getBillOfMaterialConsumptionList()) { - StockMoveLine stockMoveLine = stockMoveLineService.createStockMoveLine( - billOfMaterialConsumption.getProduct(), - billOfMaterialConsumption.getProduct().getName(), - billOfMaterialConsumption.getProduct().getDescription(), - billOfMaterialConsumption.getRealQty(), - BigDecimal.ZERO, - BigDecimal.ZERO, - billOfMaterialConsumption.getProduct().getUnit(), - stockMove, - StockMoveLineService.TYPE_OUT_PRODUCTIONS, - false, - BigDecimal.ZERO); + for (BillOfMaterialConsumption billOfMaterialConsumption : + manufOrder.getBillOfMaterialConsumptionList()) { + StockMoveLine stockMoveLine = + stockMoveLineService.createStockMoveLine( + billOfMaterialConsumption.getProduct(), + billOfMaterialConsumption.getProduct().getName(), + billOfMaterialConsumption.getProduct().getDescription(), + billOfMaterialConsumption.getRealQty(), + BigDecimal.ZERO, + BigDecimal.ZERO, + billOfMaterialConsumption.getProduct().getUnit(), + stockMove, + StockMoveLineService.TYPE_OUT_PRODUCTIONS, + false, + BigDecimal.ZERO); stockMoveLine.setTrackingNumber(billOfMaterialConsumption.getTrackingNumber()); stockMove.addStockMoveLineListItem(stockMoveLine); } if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) { - stockMoveService.plan(stockMove); - manufOrder.addOutStockMoveListItem(stockMove); + stockMoveService.plan(stockMove); + manufOrder.addOutStockMoveListItem(stockMove); + manufOrder.setRealEndDateT(LocalDateTime.now()); } - } - } diff --git a/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/web/ManufOrderController.java b/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/web/ManufOrderController.java index de6b959..68685c3 100644 --- a/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/web/ManufOrderController.java +++ b/modules/axelor-open-suite/axelor-production/src/main/java/com/axelor/apps/production/web/ManufOrderController.java @@ -761,25 +761,23 @@ public class ManufOrderController { } public void consumeStockMoveTemp(ActionRequest request, ActionResponse response) { - try { - Context context = request.getContext(); - List manufOrders = new ArrayList<>(); - if (context.get("id") != null) { - Long manufOrderId = (Long) request.getContext().get("id"); - manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId)); - } else if (context.get("_ids") != null) { - manufOrders = - Beans.get(ManufOrderRepository.class) - .all() - .filter( - "self.id in ?1", - context.get("_ids")) - .fetch(); - } - for (ManufOrder manufOrder : manufOrders) { - Beans.get(ManufOrderStockMoveService.class).createTempStockMove(manufOrder); - } - response.setReload(true); + try { + Context context = request.getContext(); + List manufOrders = new ArrayList<>(); + if (context.get("id") != null) { + Long manufOrderId = (Long) request.getContext().get("id"); + manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId)); + } else if (context.get("_ids") != null) { + manufOrders = + Beans.get(ManufOrderRepository.class) + .all() + .filter("self.id in ?1", context.get("_ids")) + .fetch(); + } + for (ManufOrder manufOrder : manufOrders) { + Beans.get(ManufOrderStockMoveService.class).createTempStockMove(manufOrder); + } + response.setReload(true); } catch (Exception e) { TraceBackService.trace(response, e); } diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/db/repo/PurchaseOrderManagementRepository.java b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/db/repo/PurchaseOrderManagementRepository.java index 98bcfc1..941923c 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/db/repo/PurchaseOrderManagementRepository.java +++ b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/db/repo/PurchaseOrderManagementRepository.java @@ -50,7 +50,7 @@ public class PurchaseOrderManagementRepository extends PurchaseOrderRepository { try { purchaseOrder = super.save(purchaseOrder); Beans.get(PurchaseOrderService.class).setDraftSequence(purchaseOrder); - Beans.get(PurchaseOrderService.class).setPurchaseOrderBarCodeSeq(purchaseOrder); + Beans.get(PurchaseOrderService.class).setPurchaseOrderBarCodeSeq(purchaseOrder); return purchaseOrder; } catch (Exception e) { throw new PersistenceException(e.getLocalizedMessage()); diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineService.java b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineService.java index d0685d9..28088e2 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineService.java +++ b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineService.java @@ -84,7 +84,7 @@ public interface PurchaseOrderLineService { Unit unit) throws AxelorException; - public PurchaseOrderLine createPurchaseOrderLine( + public PurchaseOrderLine createPurchaseOrderLine( PurchaseOrder purchaseOrder, Product product, String productName, diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineServiceImpl.java b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineServiceImpl.java index a337d1b..337e9ac 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineServiceImpl.java +++ b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/PurchaseOrderLineServiceImpl.java @@ -730,10 +730,17 @@ public class PurchaseOrderLineServiceImpl implements PurchaseOrderLineService { } @Override - public PurchaseOrderLine createPurchaseOrderLine(PurchaseOrder purchaseOrder, Product product, String productName, - String description, BigDecimal qty, Unit unit, PurchaseRequestLine purchaseRequestLine) throws AxelorException { - PurchaseOrderLine purchaseOrderLine = this.createPurchaseOrderLine(purchaseOrder, product, productName, description, qty, unit); - return purchaseOrderLine; - + public PurchaseOrderLine createPurchaseOrderLine( + PurchaseOrder purchaseOrder, + Product product, + String productName, + String description, + BigDecimal qty, + Unit unit, + PurchaseRequestLine purchaseRequestLine) + throws AxelorException { + PurchaseOrderLine purchaseOrderLine = + this.createPurchaseOrderLine(purchaseOrder, product, productName, description, qty, unit); + return purchaseOrderLine; } } diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/print/PurchaseRequestPrintServiceImpl.java b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/print/PurchaseRequestPrintServiceImpl.java index 921ce5d..36398d2 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/print/PurchaseRequestPrintServiceImpl.java +++ b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/service/print/PurchaseRequestPrintServiceImpl.java @@ -42,7 +42,7 @@ public class PurchaseRequestPrintServiceImpl implements PurchaseRequestPrintServ public String printPurchaseRequest(PurchaseRequest purchaseRequest, String formatPdf) throws AxelorException { - String fileName = getPurchaseRequestFilesName(false, formatPdf); + String fileName = getPurchaseRequestFilesName(false, formatPdf); return PdfTool.getFileLinkFromPdfFile(print(purchaseRequest, formatPdf), fileName); } @@ -84,7 +84,6 @@ public class PurchaseRequestPrintServiceImpl implements PurchaseRequestPrintServ ReportSettings reportSetting = ReportFactory.createReport(IReport.PURCHASE_REQUEST, title + " - ${date}"); - return reportSetting .addParam("PurchaseRequestId", purchaseRequest.getId()) .addParam("Locale", locale) @@ -103,8 +102,6 @@ public class PurchaseRequestPrintServiceImpl implements PurchaseRequestPrintServ @Override public String getFileName(PurchaseRequest purchaseRequest) { - return I18n.get("Purchase request") - + " " - + purchaseRequest.getPurchaseRequestSeq(); + return I18n.get("Purchase request") + " " + purchaseRequest.getPurchaseRequestSeq(); } } diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/web/PurchaseOrderController.java b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/web/PurchaseOrderController.java index e930a89..8723aa0 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/web/PurchaseOrderController.java +++ b/modules/axelor-open-suite/axelor-purchase/src/main/java/com/axelor/apps/purchase/web/PurchaseOrderController.java @@ -696,7 +696,7 @@ public class PurchaseOrderController { Beans.get(PurchaseOrderRepository.class) .find(request.getContext().asType(PurchaseOrder.class).getId()); - if (purchaseOrder.getCurrency().getId() != 41){ + if (purchaseOrder.getCurrency().getId() != 41) { ImportationFolder importationFolder = Beans.get(PurchaseOrderServiceImpl.class).generateImportationFolder(purchaseOrder); diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseOrder.xml b/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseOrder.xml index e1151e4..7d609c4 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseOrder.xml +++ b/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseOrder.xml @@ -101,6 +101,12 @@ + + + + + + diff --git a/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseRequest.xml b/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseRequest.xml index de3c12b..3fb4293 100644 --- a/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseRequest.xml +++ b/modules/axelor-open-suite/axelor-purchase/src/main/resources/domains/PurchaseRequest.xml @@ -43,6 +43,12 @@ + + + + + + @@ -63,6 +69,7 @@ + diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockLocationLineStockRepository.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockLocationLineStockRepository.java index b71a5bc..18cb69c 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockLocationLineStockRepository.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockLocationLineStockRepository.java @@ -18,18 +18,84 @@ package com.axelor.apps.stock.db.repo; import com.axelor.apps.base.db.Product; +import com.axelor.apps.base.service.BarcodeGeneratorService; +import com.axelor.apps.base.service.app.AppBaseService; import com.axelor.apps.stock.db.StockLocationLine; import com.axelor.apps.stock.service.WeightedAveragePriceService; +import com.axelor.apps.tool.service.TranslationService; +import com.axelor.exception.AxelorException; import com.axelor.inject.Beans; +import com.axelor.meta.MetaFiles; +import com.axelor.meta.db.MetaFile; +import com.google.inject.Inject; +import java.io.IOException; +import java.io.InputStream; +import javax.validation.ValidationException; public class StockLocationLineStockRepository extends StockLocationLineRepository { + @Inject private MetaFiles metaFiles; + + @Inject protected AppBaseService appBaseService; + + @Inject protected TranslationService translationService; + + protected static final String FULL_NAME_FORMAT = "[%s] %s"; + + @Inject protected BarcodeGeneratorService barcodeGeneratorService; + @Override public StockLocationLine save(StockLocationLine entity) { Product product = entity.getProduct(); if (entity.getIsAvgPriceChanged()) { Beans.get(WeightedAveragePriceService.class).computeAvgPriceForProduct(product); } + if (entity.getBarCodeSeq() == null + && appBaseService.getAppBase().getActivateBarCodeGeneration() + && entity.getDetailsStockLocation() != null) { + try { + boolean addPadding = false; + InputStream inStream; + if (!appBaseService.getAppBase().getEditProductBarcodeType()) { + inStream = + barcodeGeneratorService.createBarCode( + String.valueOf(entity.getProduct().getId()) + + "-" + + String.valueOf(entity.getDetailsStockLocation().getId()) + + "-" + + String.valueOf(entity.getTrackingNumber().getId()), + appBaseService.getAppBase().getBarcodeTypeConfig(), + addPadding); + } else { + System.out.println( + String.valueOf(entity.getProduct().getId()) + + "-" + + String.valueOf(entity.getDetailsStockLocation().getId()) + + "-" + + String.valueOf(entity.getTrackingNumber().getId())); + + inStream = + barcodeGeneratorService.createBarCode( + String.valueOf(entity.getProduct().getId()) + + "-" + + String.valueOf(entity.getDetailsStockLocation().getId()) + + "-" + + String.valueOf(entity.getTrackingNumber().getId()), + appBaseService.getAppBase().getBarcodeTypeConfig(), + addPadding); + } + if (inStream != null) { + MetaFile barcodeFile = + metaFiles.upload( + inStream, String.format("StockLocationLineCode%d.png", entity.getId())); + entity.setBarCodeSeq(barcodeFile); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (AxelorException e) { + throw new ValidationException(e.getMessage()); + } + } return super.save(entity); } } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockMoveLineLocationStockRepository.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockMoveLineLocationStockRepository.java new file mode 100644 index 0000000..e26a71e --- /dev/null +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/db/repo/StockMoveLineLocationStockRepository.java @@ -0,0 +1,57 @@ +package com.axelor.apps.stock.db.repo; + +import com.axelor.apps.base.service.BarcodeGeneratorService; +import com.axelor.apps.base.service.app.AppBaseService; +import com.axelor.apps.stock.db.StockMoveLineLocation; +import com.axelor.apps.tool.service.TranslationService; +import com.axelor.exception.AxelorException; +import com.axelor.meta.MetaFiles; +import com.axelor.meta.db.MetaFile; +import com.google.inject.Inject; +import java.io.IOException; +import java.io.InputStream; +import javax.validation.ValidationException; + +public class StockMoveLineLocationStockRepository extends StockMoveLineLocationRepository { + + @Inject private MetaFiles metaFiles; + + @Inject protected AppBaseService appBaseService; + + @Inject protected TranslationService translationService; + + protected static final String FULL_NAME_FORMAT = "[%s] %s"; + + @Inject protected BarcodeGeneratorService barcodeGeneratorService; + + @Override + public StockMoveLineLocation save(StockMoveLineLocation entity) { + if (entity.getBarCodeSeq() == null + && appBaseService.getAppBase().getActivateBarCodeGeneration()) { + try { + boolean addPadding = false; + InputStream inStream; + if (!appBaseService.getAppBase().getEditProductBarcodeType()) { + inStream = + barcodeGeneratorService.createBarCode( + entity.getCode(), appBaseService.getAppBase().getBarcodeTypeConfig(), addPadding); + } else { + inStream = + barcodeGeneratorService.createBarCode( + entity.getCode(), appBaseService.getAppBase().getBarcodeTypeConfig(), addPadding); + } + if (inStream != null) { + MetaFile barcodeFile = + metaFiles.upload( + inStream, String.format("StockMoveLineLocationCode%d.png", entity.getId())); + entity.setBarCodeSeq(barcodeFile); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (AxelorException e) { + throw new ValidationException(e.getMessage()); + } + } + return super.save(entity); + } +} diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockLocationLineServiceImpl.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockLocationLineServiceImpl.java index e2a00fb..7018ea1 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockLocationLineServiceImpl.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockLocationLineServiceImpl.java @@ -47,6 +47,7 @@ import java.lang.invoke.MethodHandles; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; +import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,8 +81,17 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { this.wapHistoryRepo = wapHistoryRepo; } + private static final List NO_UNIT_CONVERSION_LOCATION_IDS = Arrays.asList(12L,97L, 98L, 99L, 100L, 103L, 104L, 105L, + 106L); + + private boolean isNoUnitConversionLocation(StockLocation location) { + return location != null + && location.getId() != null + && NO_UNIT_CONVERSION_LOCATION_IDS.contains(location.getId()); + } + @Override - @Transactional(rollbackOn = {Exception.class}) + @Transactional(rollbackOn = { Exception.class }) public void updateLocation( StockLocation stockLocation, Product product, @@ -105,7 +115,8 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { isIncrement, lastFutureStockMoveDate); - if (trackingNumber != null ) { + if (trackingNumber != null) { + LOG.debug("trackingNumber != null in updateLocation ****** "); this.updateDetailLocation( stockLocation, product, @@ -117,10 +128,12 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { lastFutureStockMoveDate, trackingNumber); } + LOG.debug("updateLocation passed qll updateLocation "); + } @Override - @Transactional(rollbackOn = {Exception.class}) + @Transactional(rollbackOn = { Exception.class }) public void updateLocation( StockLocation stockLocation, Product product, @@ -132,19 +145,33 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { LocalDate lastFutureStockMoveDate) throws AxelorException { - StockLocationLine stockLocationLine = this.getOrCreateStockLocationLine(stockLocation, product); + StockLocationLine stockLocationLine; + + if (isNoUnitConversionLocation(stockLocation)) { + LOG.debug("updateLocation ** isNoUnitConversionLocation"); + stockLocationLine = this.getOrCreateStockLocationLine(stockLocation, product, product.getUnit()); + } else { + stockLocationLine = this.getOrCreateStockLocationLine(stockLocation, product); + } if (stockLocationLine == null) { return; } - UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); - Unit stockLocationLineUnit = stockLocationLine.getUnit(); + LOG.debug( + "Mise à jour du stock : Entrepot? {}, Produit? {}, Quantité? {}, Actuel? {}, Futur? {}, Incrément? {}, Date? {} ", + stockLocation.getName(), + product.getCode()); - if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) { - qty = - unitConversionService.convert( - stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product); + // bypassing labo + if (!isNoUnitConversionLocation(stockLocation)) { + UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); + Unit stockLocationLineUnit = stockLocationLine.getUnit(); + + if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) { + qty = unitConversionService.convert( + stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product); + } } LOG.debug( @@ -163,6 +190,7 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { maxStockRules(product, qty, stockLocationLine, current, future); } + stockLocationLine = this.updateLocation( stockLocationLine, @@ -176,6 +204,9 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { this.checkStockMin(stockLocationLine, false); + LOG.debug("updateLocation passed min max stock rules "); + + stockLocationLineRepo.save(stockLocationLine); } @@ -265,20 +296,33 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { TrackingNumber trackingNumber) throws AxelorException { - StockLocationLine detailLocationLine = - this.getOrCreateDetailLocationLine(stockLocation, product, trackingNumber); + StockLocationLine detailLocationLine; + + if (isNoUnitConversionLocation(stockLocation)) { + detailLocationLine = this.getDetailLocationLineUnit( + stockLocation, product, trackingNumber, product.getUnit()); + + if (detailLocationLine == null) { + detailLocationLine = this.createDetailLocationLine(stockLocation, product, trackingNumber); + detailLocationLine.setUnit(stockMoveLineUnit); + } + } else { + detailLocationLine = this.getOrCreateDetailLocationLine(stockLocation, product, trackingNumber); + } if (detailLocationLine == null) { return; } - UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); - Unit stockLocationLineUnit = detailLocationLine.getUnit(); + if (!isNoUnitConversionLocation(stockLocation)) { - if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) { - qty = - unitConversionService.convert( - stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product); + UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); + Unit stockLocationLineUnit = detailLocationLine.getUnit(); + + if (stockLocationLineUnit != null && !stockLocationLineUnit.equals(stockMoveLineUnit)) { + qty = unitConversionService.convert( + stockMoveLineUnit, stockLocationLineUnit, qty, qty.scale(), product); + } } LOG.debug( @@ -426,8 +470,13 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { StockLocationLine detailLocationLine = this.getDetailLocationLine(detailLocation, product, trackingNumber); - if (detailLocationLine == null) { + List listOfIds = Arrays.asList(12L, 97L, 98L, 99L, 100L, 103L, 104L, 105L, 106L); + if (listOfIds.contains(detailLocation.getId())) { + detailLocationLine = this.getDetailLocationLineUnit(detailLocation, product, trackingNumber, product.getUnit()); + } + + if (detailLocationLine == null) { detailLocationLine = this.createDetailLocationLine(detailLocation, product, trackingNumber); } @@ -476,17 +525,16 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { public StockLocationLine getDetailLocationLine( StockLocation stockLocation, Product product, TrackingNumber trackingNumber) { - return stockLocationLineRepo - .all() - .filter( - "self.detailsStockLocation.id = :_stockLocationId " - + "AND self.product.id = :_productId " - + "AND self.trackingNumber.id = :_trackingNumberId " - ) - .bind("_stockLocationId", stockLocation.getId()) - .bind("_productId", product.getId()) - .bind("_trackingNumberId", trackingNumber.getId()) - .fetchOne(); + return stockLocationLineRepo + .all() + .filter( + "self.detailsStockLocation.id = :_stockLocationId " + + "AND self.product.id = :_productId " + + "AND self.trackingNumber.id = :_trackingNumberId ") + .bind("_stockLocationId", stockLocation.getId()) + .bind("_productId", product.getId()) + .bind("_trackingNumberId", trackingNumber.getId()) + .fetchOne(); } @Override @@ -544,7 +592,7 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { } @Override - @Transactional(rollbackOn = {Exception.class}) + @Transactional(rollbackOn = { Exception.class }) public void updateStockLocationFromProduct(StockLocationLine stockLocationLine, Product product) throws AxelorException { @@ -564,13 +612,12 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { BigDecimal oldAvgPrice = stockLocationLine.getAvgPrice(); UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); - BigDecimal currentQty = - unitConversionService.convert( - stockLocationUnit, - productUnit, - stockLocationLine.getCurrentQty(), - stockLocationLine.getCurrentQty().scale(), - product); + BigDecimal currentQty = unitConversionService.convert( + stockLocationUnit, + productUnit, + stockLocationLine.getCurrentQty(), + stockLocationLine.getCurrentQty().scale(), + product); stockLocationLine.setCurrentQty(currentQty); stockLocationLine.setUnit(product.getUnit()); @@ -586,14 +633,14 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { return stockLocationLine; } - protected static final String STOCK_MOVE_LINE_FILTER = - "(self.stockMove.archived IS NULL OR self.archived IS FALSE) " - + "AND self.stockMove.statusSelect = :planned " - + "AND self.product.id = :productId "; + protected static final String STOCK_MOVE_LINE_FILTER = "(self.stockMove.archived IS NULL OR self.archived IS FALSE) " + + "AND self.stockMove.statusSelect = :planned " + + "AND self.product.id = :productId "; @Override public BigDecimal computeFutureQty(StockLocationLine stockLocationLine) throws AxelorException { - // future quantity is current quantity minus planned outgoing stock move lines plus planned + // future quantity is current quantity minus planned outgoing stock move lines + // plus planned // incoming stock move lines. UnitConversionService unitConversionService = Beans.get(UnitConversionService.class); @@ -601,31 +648,36 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { BigDecimal futureQty = stockLocationLine.getCurrentQty(); - List incomingStockMoveLineList = - findIncomingPlannedStockMoveLines(stockLocationLine); - List outgoingStockMoveLineList = - findOutgoingPlannedStockMoveLines(stockLocationLine); + List incomingStockMoveLineList = findIncomingPlannedStockMoveLines(stockLocationLine); + List outgoingStockMoveLineList = findOutgoingPlannedStockMoveLines(stockLocationLine); for (StockMoveLine incomingStockMoveLine : incomingStockMoveLineList) { - BigDecimal qtyToAdd = - unitConversionService.convert( - incomingStockMoveLine.getUnit(), - stockLocationLine.getUnit(), - incomingStockMoveLine.getRealQty(), - incomingStockMoveLine.getRealQty().scale(), - product); + // bypassing labo + BigDecimal qtyToAdd = incomingStockMoveLine.getRealQty(); + if(!isNoUnitConversionLocation(stockLocationLine.getStockLocation())){ + qtyToAdd = unitConversionService.convert( + incomingStockMoveLine.getUnit(), + stockLocationLine.getUnit(), + incomingStockMoveLine.getRealQty(), + incomingStockMoveLine.getRealQty().scale(), + product); + } futureQty = futureQty.add(qtyToAdd); } for (StockMoveLine outgoingStockMoveLine : outgoingStockMoveLineList) { - BigDecimal qtyToSubtract = - unitConversionService.convert( - outgoingStockMoveLine.getUnit(), - stockLocationLine.getUnit(), - outgoingStockMoveLine.getRealQty(), - outgoingStockMoveLine.getRealQty().scale(), - product); - futureQty = futureQty.subtract(qtyToSubtract); + BigDecimal qtyToSubtract = outgoingStockMoveLine.getRealQty(); + + if(!isNoUnitConversionLocation(stockLocationLine.getStockLocation())){ + + qtyToSubtract = unitConversionService.convert( + outgoingStockMoveLine.getUnit(), + stockLocationLine.getUnit(), + outgoingStockMoveLine.getRealQty(), + outgoingStockMoveLine.getRealQty().scale(), + product); + } + futureQty = futureQty.subtract(qtyToSubtract); } return futureQty; @@ -634,18 +686,16 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { protected List findIncomingPlannedStockMoveLines( StockLocationLine stockLocationLine) { boolean isDetailsStockLocationLine = stockLocationLine.getDetailsStockLocation() != null; - String incomingStockMoveLineFilter = - STOCK_MOVE_LINE_FILTER + "AND self.stockMove.toStockLocation.id = :stockLocationId"; + String incomingStockMoveLineFilter = STOCK_MOVE_LINE_FILTER + + "AND self.stockMove.toStockLocation.id = :stockLocationId"; if (isDetailsStockLocationLine) { - incomingStockMoveLineFilter = - incomingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId"; + incomingStockMoveLineFilter = incomingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId"; } - Query stockMoveLineQuery = - stockMoveLineRepository - .all() - .filter(incomingStockMoveLineFilter) - .bind("planned", StockMoveRepository.STATUS_PLANNED) - .bind("productId", stockLocationLine.getProduct().getId()); + Query stockMoveLineQuery = stockMoveLineRepository + .all() + .filter(incomingStockMoveLineFilter) + .bind("planned", StockMoveRepository.STATUS_PLANNED) + .bind("productId", stockLocationLine.getProduct().getId()); if (isDetailsStockLocationLine) { stockMoveLineQuery .bind("stockLocationId", stockLocationLine.getDetailsStockLocation().getId()) @@ -659,18 +709,16 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { protected List findOutgoingPlannedStockMoveLines( StockLocationLine stockLocationLine) { boolean isDetailsStockLocationLine = stockLocationLine.getDetailsStockLocation() != null; - String outgoingStockMoveLineFilter = - STOCK_MOVE_LINE_FILTER + "AND self.stockMove.fromStockLocation.id = :stockLocationId"; + String outgoingStockMoveLineFilter = STOCK_MOVE_LINE_FILTER + + "AND self.stockMove.fromStockLocation.id = :stockLocationId"; if (isDetailsStockLocationLine) { - outgoingStockMoveLineFilter = - outgoingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId"; + outgoingStockMoveLineFilter = outgoingStockMoveLineFilter + " AND self.trackingNumber.id = :trackingNumberId"; } - Query stockMoveLineQuery = - stockMoveLineRepository - .all() - .filter(outgoingStockMoveLineFilter) - .bind("planned", StockMoveRepository.STATUS_PLANNED) - .bind("productId", stockLocationLine.getProduct().getId()); + Query stockMoveLineQuery = stockMoveLineRepository + .all() + .filter(outgoingStockMoveLineFilter) + .bind("planned", StockMoveRepository.STATUS_PLANNED) + .bind("productId", stockLocationLine.getProduct().getId()); if (isDetailsStockLocationLine) { stockMoveLineQuery @@ -686,25 +734,21 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { public String getStockLocationLineListForAProduct( Long productId, Long companyId, Long stockLocationId) { - String query = - "self.product.id = " - + productId - + " AND self.stockLocation.typeSelect != " - + StockLocationRepository.TYPE_VIRTUAL; + String query = "self.product.id = " + + productId + + " AND self.stockLocation.typeSelect != " + + StockLocationRepository.TYPE_VIRTUAL; if (companyId != 0L) { query += " AND self.stockLocation.company.id = " + companyId; if (stockLocationId != 0L) { - StockLocation stockLocation = - Beans.get(StockLocationRepository.class).find(stockLocationId); - List stockLocationList = - Beans.get(StockLocationService.class) - .getAllLocationAndSubLocation(stockLocation, false); + StockLocation stockLocation = Beans.get(StockLocationRepository.class).find(stockLocationId); + List stockLocationList = Beans.get(StockLocationService.class) + .getAllLocationAndSubLocation(stockLocation, false); if (!stockLocationList.isEmpty() && stockLocation.getCompany().getId().equals(companyId)) { - query += - " AND self.stockLocation.id IN (" - + StringTool.getIdListString(stockLocationList) - + ") "; + query += " AND self.stockLocation.id IN (" + + StringTool.getIdListString(stockLocationList) + + ") "; } } } @@ -714,9 +758,8 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { @Override public String getAvailableStockForAProduct(Long productId, Long companyId, Long stockLocationId) { String query = this.getStockLocationLineListForAProduct(productId, companyId, stockLocationId); - query += - " AND (self.currentQty != 0 OR self.futureQty != 0) " - + " AND (self.stockLocation.isNotInCalculStock = false OR self.stockLocation.isNotInCalculStock IS NULL)"; + query += " AND (self.currentQty != 0 OR self.futureQty != 0) " + + " AND (self.stockLocation.isNotInCalculStock = false OR self.stockLocation.isNotInCalculStock IS NULL)"; return query; } @@ -750,8 +793,8 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { @Override public BigDecimal getTrackingNumberAvailableQty( StockLocation stockLocation, TrackingNumber trackingNumber) { - StockLocationLine detailStockLocationLine = - getDetailLocationLine(stockLocation, trackingNumber.getProduct(), trackingNumber); + StockLocationLine detailStockLocationLine = getDetailLocationLine(stockLocation, trackingNumber.getProduct(), + trackingNumber); BigDecimal availableQty = BigDecimal.ZERO; @@ -760,4 +803,88 @@ public class StockLocationLineServiceImpl implements StockLocationLineService { } return availableQty; } + + public StockLocationLine getStockLocationLineUnit(StockLocation stockLocation, Product product, Unit unit) { + + if (product == null || !product.getStockManaged()) { + return null; + } + + return stockLocationLineRepo + .all() + .filter("self.stockLocation.id = :_stockLocationId " + " AND self.product.id = :_productId " + + " AND self.unit.id = :_unitId") + .bind("_stockLocationId", stockLocation.getId()) + .bind("_productId", product.getId()) + .bind("_unitId", unit.getId()) + .fetchOne(); + } + + public StockLocationLine getDetailLocationLineUnit( + StockLocation stockLocation, Product product, TrackingNumber trackingNumber, Unit unit) { + + return stockLocationLineRepo + .all() + .filter( + "self.detailsStockLocation.id = :_stockLocationId " + + "AND self.product.id = :_productId " + + "AND self.trackingNumber.id = :_trackingNumberId " + + "AND self.unit.id = :_unitId ") + .bind("_stockLocationId", stockLocation.getId()) + .bind("_productId", product.getId()) + .bind("_trackingNumberId", trackingNumber.getId()) + .bind("_unitId", unit.getId()) + .fetchOne(); + } + + public StockLocationLine createLocationLineUnit(StockLocation stockLocation, Product product, Unit unit) { + + LOG.debug( + "Création d'une ligne de stock : Entrepot? {}, Produit? {} ", + stockLocation.getName(), + product.getCode()); + + StockLocationLine stockLocationLine = new StockLocationLine(); + + stockLocationLine.setStockLocation(stockLocation); + stockLocation.addStockLocationLineListItem(stockLocationLine); + stockLocationLine.setProduct(product); + stockLocationLine.setUnit(unit); + stockLocationLine.setCurrentQty(BigDecimal.ZERO); + stockLocationLine.setFutureQty(BigDecimal.ZERO); + stockLocationLine.setConformitySelect(4); // Quarantine + + return stockLocationLine; + } + + public StockLocationLine getOrCreateStockLocationLine( + StockLocation stockLocation, Product product, Unit stockMoveLineUnit) { + + if (!product.getStockManaged()) { + return null; + } + + StockLocationLine line; + + if (isNoUnitConversionLocation(stockLocation)) { + LOG.debug("getOrCreateStockLocationLine stockMoveLineUnit ** isNoUnitConversionLocation"); + // 🔥 STRICT (location + product + unit) + line = getStockLocationLineUnit(stockLocation, product, product.getUnit()); + + if (line == null) { + LOG.debug("getOrCreateStockLocationLine stockMoveLineUnit ** isNoUnitConversionLocation line == null"); + line = createLocationLineUnit(stockLocation, product, product.getUnit()); + } + } else { + // 🧠 Default Axelor behavior + line = getStockLocationLine(stockLocation, product); + + if (line == null) { + line = createLocationLine(stockLocation, product); + } + } + + return line; + } + } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveLineServiceImpl.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveLineServiceImpl.java index aaacfc3..c2a2053 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveLineServiceImpl.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveLineServiceImpl.java @@ -470,39 +470,54 @@ public class StockMoveLineServiceImpl implements StockMoveLineService { lastFutureStockMoveDate, stockMoveLine.getTrackingNumber(), stockMoveLine.getInternalTrackingNumber()); - if (fromStockLocation.getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL) { - this.updateAveragePriceLocationLine(toStockLocation, stockMoveLine, fromStatus, toStatus); - weightedAveragePriceService.computeAvgPriceForProduct(stockMoveLine.getProduct()); - } - List listOfIds = Arrays.asList(10L, 11L, 13L, 14L, 15L, 16L,50L); - if(toStatus == StockMoveRepository.STATUS_REALIZED){ + + log.debug("locations updated succes*******"); + + List listOfIdsLab = Arrays.asList(97L, 98L, 99L, 100L, 103L, 104L, 105L, 106L); + if(!listOfIdsLab.contains(toStockLocation.getId())){ + if (fromStockLocation.getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL && stockMoveLine.getStockMove().getPartner() != null) { + if(stockMoveLine.getStockMove().getPartner().getId() != 853L){ + this.updateAveragePriceLocationLine(toStockLocation, stockMoveLine, fromStatus, toStatus); + weightedAveragePriceService.computeAvgPriceForProduct(stockMoveLine.getProduct()); + } + } + List listOfIds = Arrays.asList(10L, 11L, 13L, 14L, 15L, 16L, 50L); + if (toStatus == StockMoveRepository.STATUS_REALIZED) { if (stockMoveLine.getTrackingNumber() != null && stockMoveLine.getRealQty().compareTo(BigDecimal.ZERO) != 0) { if (stockMoveLine.getStockMove() != null) { if (stockMoveLine.getStockMove().getTypeSelect() == StockMoveRepository.TYPE_INCOMING) { - StockLocationLine toStockLocationLine = Beans.get(StockLocationLineService.class).getDetailLocationLine( + StockLocationLine toStockLocationLine = + Beans.get(StockLocationLineService.class) + .getDetailLocationLine( toStockLocation, product, stockMoveLine.getTrackingNumber()); - // TODO WMS + // TODO WMS if (toStockLocationLine != null) { // if(listOfIds.contains(fromStockLocation.getId())){ - if(stockMoveLine.getStockMove().getPartner().getId() != 853L){ - toStockLocationLine.setConformitySelect(StockLocationRepository.TYPE_QUARANTINE); + if (stockMoveLine.getStockMove().getPartner().getId() != 853L) { + toStockLocationLine.setConformitySelect( + StockLocationRepository.TYPE_QUARANTINE); Beans.get(StockLocationLineRepository.class).save(toStockLocationLine); - }else{ - if(stockMoveLine.getRealQty().compareTo(toStockLocationLine.getCurrentQty()) == 0){ - toStockLocationLine.setConformitySelect(StockLocationRepository.TYPE_QUARANTINE); + } else { + if (stockMoveLine.getRealQty().compareTo(toStockLocationLine.getCurrentQty()) + == 0) { + toStockLocationLine.setConformitySelect( + StockLocationRepository.TYPE_QUARANTINE); Beans.get(StockLocationLineRepository.class).save(toStockLocationLine); } } } } else if (stockMoveLine.getStockMove().getTypeSelect() == StockMoveRepository.TYPE_INTERNAL) { - StockLocationLine fromStockLocationLine = Beans.get(StockLocationLineService.class).getDetailLocationLine( - fromStockLocation, product, stockMoveLine.getTrackingNumber() ); - StockLocationLine toStockLocationLine = Beans.get(StockLocationLineService.class).getDetailLocationLine( - toStockLocation, product, stockMoveLine.getTrackingNumber()); + StockLocationLine fromStockLocationLine = Beans.get(StockLocationLineService.class) + .getDetailLocationLine(fromStockLocation, product, stockMoveLine.getTrackingNumber()); + StockLocationLine toStockLocationLine = Beans.get(StockLocationLineService.class) + .getDetailLocationLine(toStockLocation, product, stockMoveLine.getTrackingNumber()); log.debug( "fromStockLocationLine: {}, toStockLocationLine: {}, fromStockLocationLineConformity: {}, toStockLocationLineConformity: {}", - fromStockLocationLine,toStockLocationLine,fromStockLocationLine.getConformitySelect(),toStockLocationLine.getConformitySelect()); - + fromStockLocationLine, + toStockLocationLine, + fromStockLocationLine.getConformitySelect(), + toStockLocationLine.getConformitySelect()); + if (toStockLocationLine != null && toStockLocationLine.getCurrentQty().compareTo(BigDecimal.ZERO) != 0) { toStockLocationLine.setConformitySelect(fromStockLocationLine.getConformitySelect()); toStockLocationLine.setAnalysisFolderValidated(fromStockLocationLine.getAnalysisFolderValidated()); @@ -513,7 +528,9 @@ public class StockMoveLineServiceImpl implements StockMoveLineService { } } } + } + } } } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveService.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveService.java index b8a075d..c25587f 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveService.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveService.java @@ -241,11 +241,10 @@ public interface StockMoveService { public void massCancel(List requestIds, CancelReason raison, String raisonStr) throws AxelorException; - - public void massDraft(List requestIds) throws AxelorException; - public void massPlan(List requestIds) throws AxelorException; + public void massDraft(List requestIds) throws AxelorException; - public void massRealize(List requestIds) throws AxelorException; + public void massPlan(List requestIds) throws AxelorException; + public void massRealize(List requestIds) throws AxelorException; } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveServiceImpl.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveServiceImpl.java index 87c56d5..b90c4a6 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveServiceImpl.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveServiceImpl.java @@ -45,6 +45,7 @@ import com.axelor.apps.stock.db.StockLocation; import com.axelor.apps.stock.db.StockMove; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.StockMoveLineLocationLine; +import com.axelor.apps.stock.db.TrackingNumber; import com.axelor.apps.stock.db.repo.InternalTrackingNumberRepository; import com.axelor.apps.stock.db.repo.InventoryLineRepository; import com.axelor.apps.stock.db.repo.InventoryRepository; @@ -60,8 +61,6 @@ import com.axelor.exception.AxelorException; import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.i18n.I18n; import com.axelor.inject.Beans; -import com.axelor.rpc.ActionRequest; -import com.axelor.rpc.ActionResponse; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.inject.Inject; @@ -79,9 +78,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; - import javax.persistence.Query; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -421,22 +418,40 @@ public class StockMoveServiceImpl implements StockMoveService { System.out.println("Checking oooo..................."); // checking ..... - if (stockMove.getFromStockLocation().getTypeSelect() != StockLocationRepository.TYPE_VIRTUAL) - { - List listOfIds = Arrays.asList(10L, 11L, 13L, 14L, 15L, 16L, 13L, 12L, 54L, 55L, 58L,50L); - System.out.println("Checking..................."); - System.out.println(listOfIds.contains(stockMove.getToStockLocation().getId())); - System.out.println("Checking..................."); + if (stockMove.getFromStockLocation().getTypeSelect() != StockLocationRepository.TYPE_VIRTUAL) { + List listOfIds = + Arrays.asList(10L, 11L, 13L, 14L, 15L, 16L, 13L, 12L, 54L, 55L, 58L, 50L); - if(listOfIds.contains(stockMove.getToStockLocation().getId())){ - checkIfQuarantine(stockMove); - checkIfNonConformityTag(stockMove); - } + if (listOfIds.contains(stockMove.getToStockLocation().getId())) { + StockConfig stockConfig = + Beans.get(StockConfigRepository.class) + .all() + .filter("self.company = ?", stockMove.getCompany()) + .fetchOne(); + + List excludedProducts = + stockConfig != null + ? stockConfig.getExludedProductsFromWrkFlw() + : Collections.emptyList(); + + List excludedProductIds = + excludedProducts + .stream() + .filter(Objects::nonNull) + .map(Product::getId) + .collect(Collectors.toList()); + + System.out.println("***********************************excludedProductIds***************"); + System.out.println(excludedProductIds); + + checkIfQuarantine(stockMove, excludedProductIds); + checkIfNonConformityTag(stockMove, excludedProductIds); + } } checkExpirationDates(stockMove); - if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING) { + if (stockMove.getTypeSelect() == StockMoveRepository.TYPE_INCOMING) { partnerProductQualityRatingService.calculate(stockMove); stockMove @@ -454,64 +469,75 @@ public class StockMoveServiceImpl implements StockMoveService { stockMoveLineLocationLine.setTrackingNumber(line.getTrackingNumber()); st.addStockMoveLineLocationLineSetItem(stockMoveLineLocationLine); }); - Long partnerId = stockMove.getPartner().getId(); - Long familleId = line.getProduct().getFamilleProduit().getId(); - BigDecimal realQty = line.getRealQty(); + Long partnerId = stockMove.getPartner().getId(); + Long familleId = line.getProduct().getFamilleProduit().getId(); + BigDecimal realQty = line.getRealQty(); - if (!partnerId.equals(853L) && (familleId == 59L || familleId == 67L || familleId == 68L)) { - System.out.println("****!partnerId.equals(853L) && (familleId == 59L || familleId == 67L || familleId == 68L)"); - InternalTrackingNumberService internalTrackingNumberService = Beans.get(InternalTrackingNumberService.class); - InternalTrackingNumberRepository internalTrackingNumberRepo = Beans.get(InternalTrackingNumberRepository.class); - StockMoveLineRepository stockMoveLineRepo = Beans.get(StockMoveLineRepository.class); + if (!partnerId.equals(853L) + && (familleId == 59L || familleId == 67L || familleId == 68L)) { + System.out.println( + "****!partnerId.equals(853L) && (familleId == 59L || familleId == 67L || familleId == 68L)"); + InternalTrackingNumberService internalTrackingNumberService = + Beans.get(InternalTrackingNumberService.class); + InternalTrackingNumberRepository internalTrackingNumberRepo = + Beans.get(InternalTrackingNumberRepository.class); + StockMoveLineRepository stockMoveLineRepo = + Beans.get(StockMoveLineRepository.class); - try { - InternalTrackingNumber internalTrackingNumber = internalTrackingNumberService.getInternalTrackingNumber( - line.getProduct(), line.getTrackingNumber()); + try { + InternalTrackingNumber internalTrackingNumber = + internalTrackingNumberService.getInternalTrackingNumber( + line.getProduct(), line.getTrackingNumber()); - if (realQty.compareTo(BigDecimal.ZERO) > 0) { + if (realQty.compareTo(BigDecimal.ZERO) > 0) { if (internalTrackingNumber == null) { - InternalTrackingNumber internal = internalTrackingNumberService.createInternalTrackingNumber( - line.getProduct(), - line.getStockMove().getCompany(), - stockMove.getEstimatedDate(), - line.getTrackingNumber()); - System.out.println("***********************************internal***************"); - System.out.println(internal); - System.out.println("***********************************internal**********************"); + InternalTrackingNumber internal = + internalTrackingNumberService.createInternalTrackingNumber( + line.getProduct(), + line.getStockMove().getCompany(), + stockMove.getEstimatedDate(), + line.getTrackingNumber()); + System.out.println( + "***********************************internal***************"); + System.out.println(internal); + System.out.println( + "***********************************internal**********************"); - line.setInternalTrackingNumber(internal); - internalTrackingNumberRepo.save(internal); + line.setInternalTrackingNumber(internal); + internalTrackingNumberRepo.save(internal); } else { - LocalDate createdDate = internalTrackingNumber.getReceptionDate(); - LocalDate estimatedDate = stockMove.getEstimatedDate(); - System.out.println("***********************************internal not null***************"); - System.out.println(internalTrackingNumber); - System.out.println(createdDate); - System.out.println(estimatedDate); - System.out.println("***********************************internal noy null**********************"); + LocalDate createdDate = internalTrackingNumber.getReceptionDate(); + LocalDate estimatedDate = stockMove.getEstimatedDate(); + System.out.println( + "***********************************internal not null***************"); + System.out.println(internalTrackingNumber); + System.out.println(createdDate); + System.out.println(estimatedDate); + System.out.println( + "***********************************internal noy null**********************"); - if (createdDate.equals(estimatedDate)) { - line.setInternalTrackingNumber(internalTrackingNumber); - stockMoveLineRepo.save(line); - }else{ - InternalTrackingNumber internal = internalTrackingNumberService.createInternalTrackingNumber( - line.getProduct(), - line.getStockMove().getCompany(), - stockMove.getEstimatedDate(), - line.getTrackingNumber()); + if (createdDate.equals(estimatedDate)) { + line.setInternalTrackingNumber(internalTrackingNumber); + stockMoveLineRepo.save(line); + } else { + InternalTrackingNumber internal = + internalTrackingNumberService.createInternalTrackingNumber( + line.getProduct(), + line.getStockMove().getCompany(), + stockMove.getEstimatedDate(), + line.getTrackingNumber()); - line.setInternalTrackingNumber(internal); - stockMoveLineRepo.save(line); - } + line.setInternalTrackingNumber(internal); + stockMoveLineRepo.save(line); + } } + } + } catch (AxelorException e) { + e.printStackTrace(); // Consider logging this instead of printing. } - } catch (AxelorException e) { - e.printStackTrace(); // Consider logging this instead of printing. - } - } - }); - + } + }); } setRealizedStatus(stockMove); @@ -1507,7 +1533,7 @@ public class StockMoveServiceImpl implements StockMoveService { newStockMoveLine.setPpa(moveLine.getPpa()); newStockMoveLine.setPvg(moveLine.getPvg()); newStockMoveLine.setStklim(moveLine.getStklim()); - newStockMoveLine.setShp(moveLine.getShp()); + newStockMoveLine.setShp(moveLine.getShp()); newStockMoveLine.setRealQty(moveLine.getQty()); newStockMoveLine.setProductTypeSelect(moveLine.getProductTypeSelect()); // add stock move line @@ -1591,57 +1617,84 @@ public class StockMoveServiceImpl implements StockMoveService { } } - - public void checkIfQuarantine(StockMove stockMove) + public void checkIfQuarantine(StockMove stockMove, List excludedProductIds) throws AxelorException { - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID"+ - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.INTERNAL_TRACKING_NUMBER = LOCATION_LINE.INTERNAL_TRACKING_NUMBER and LINE.product = LOCATION_LINE.product"+ - " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2"); - sql.setParameter(1, stockMove.getFromStockLocation().getId()); - sql.setParameter(2, stockMove.getId()); + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE" + + " LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON" + + " LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER" + + + // " AND LINE.INTERNAL_TRACKING_NUMBER = LOCATION_LINE.INTERNAL_TRACKING_NUMBER" + + " AND LINE.product = LOCATION_LINE.product" + + " WHERE LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1" + + " AND LOCATION_LINE.CONFORMITY_SELECT != 2" + + " AND LOCATION_LINE.CONFORMITY_SELECT != 5" + + " AND LINE.STOCK_MOVE = ?2"); - System.out.println("*****************checkIfQuarantine******************"); - System.out.println(sql.getResultList().size() > 0); - System.out.println("******************checkIfQuarantine*****************"); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "Vous avez une ligne en etat qurantaine"); - } + if (excludedProductIds != null && !excludedProductIds.isEmpty()) { + queryBuilder.append(" AND LINE.product NOT IN (:excludedProducts)"); } - public void checkIfNonConformityTag(StockMove stockMove) - throws AxelorException { - - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID" + - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.INTERNAL_TRACKING_NUMBER = LOCATION_LINE.INTERNAL_TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + - " where LOCATION_LINE.DETAILS_STOCK_LOCATION = :location AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = :move"); - - sql.setParameter("location", stockMove.getFromStockLocation().getId()); - sql.setParameter("move", stockMove.getId()); + System.out.println("checkIfQuarantine query : "); + System.out.println(queryBuilder.toString()); - System.out.println("*****************checkIfNonConformityTag*****************"); - System.out.println(sql.getResultList().size() > 0); - System.out.println("*****************checkIfNonConformityTag****************"); + Query sql = JPA.em().createNativeQuery(queryBuilder.toString()); + sql.setParameter(1, stockMove.getFromStockLocation().getId()); + sql.setParameter(2, stockMove.getId()); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "Vous avez une ligne non étiquetée"); + if (excludedProductIds != null && !excludedProductIds.isEmpty()) { + sql.setParameter("excludedProducts", excludedProductIds); + } + + if (!sql.getResultList().isEmpty()) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une ligne en état quarantaine"); } } - @Override - public void massPlan(List moveIds) throws AxelorException { + public void checkIfNonConformityTag(StockMove stockMove, List excludedProductIds) + throws AxelorException { + + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE" + + " LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON" + + " LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER" + + + // " AND LINE.INTERNAL_TRACKING_NUMBER = LOCATION_LINE.INTERNAL_TRACKING_NUMBER" + + " AND LINE.product = LOCATION_LINE.product" + + " WHERE LOCATION_LINE.DETAILS_STOCK_LOCATION = :location" + + " AND (LOCATION_LINE.is_conform_tag IS NULL OR LOCATION_LINE.is_conform_tag != TRUE)" + + " AND LINE.STOCK_MOVE = :move"); + + if (excludedProductIds != null && !excludedProductIds.isEmpty()) { + queryBuilder.append(" AND LINE.product NOT IN (:excludedProducts)"); + } + + Query sql = JPA.em().createNativeQuery(queryBuilder.toString()); + sql.setParameter("location", stockMove.getFromStockLocation().getId()); + sql.setParameter("move", stockMove.getId()); + + if (excludedProductIds != null && !excludedProductIds.isEmpty()) { + sql.setParameter("excludedProducts", excludedProductIds); + } + + if (!sql.getResultList().isEmpty()) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une ligne non étiquetée"); + } + } + + @Override + public void massPlan(List moveIds) throws AxelorException { if (moveIds == null || moveIds.isEmpty()) { throw new AxelorException( TraceBackRepository.CATEGORY_MISSING_FIELD, "Please Select at least one StockMove"); @@ -1654,10 +1707,10 @@ public class StockMoveServiceImpl implements StockMoveService { this.plan(stockMove); } } - } + } - @Override - public void massRealize(List moveIds) throws AxelorException { + @Override + public void massRealize(List moveIds) throws AxelorException { if (moveIds == null || moveIds.isEmpty()) { throw new AxelorException( TraceBackRepository.CATEGORY_MISSING_FIELD, "Please Select at least one StockMove"); @@ -1670,11 +1723,11 @@ public class StockMoveServiceImpl implements StockMoveService { this.realize(stockMove); } } - } + } - @Override - @Transactional(rollbackOn = {Exception.class}) - public void massDraft(List moveIds) throws AxelorException { + @Override + @Transactional(rollbackOn = {Exception.class}) + public void massDraft(List moveIds) throws AxelorException { if (moveIds == null || moveIds.isEmpty()) { throw new AxelorException( TraceBackRepository.CATEGORY_MISSING_FIELD, "Please Select at least one StockMove"); @@ -1687,11 +1740,74 @@ public class StockMoveServiceImpl implements StockMoveService { stockMove.setStatusSelect(StockMoveRepository.STATUS_DRAFT); } } - } + } - // public boolean isProductExcluded(StockMove stockMove){ + // public boolean isExcludedFromWorkflow(StockMove stockMove){ // StockConfig stockConfig = - // Beans.get(StockConfigRepository.class).all().filter("self.company = ?", stockMove.getCompany()).fetchOne(); - // stockConfig.getExludedProductsFromWrkFlw(); + // Beans.get(StockConfigRepository.class).all().filter("self.company = ?", + // stockMove.getCompany()).fetchOne(); + // List producs = stockConfig.getExludedProductsFromWrkFlw(); + // // all of stockmove lines products should be in the excluded products to say that the + // stockmove is excluded from workflow + // for (StockMoveLine line : stockMove.getStockMoveLineList()) { + // if(!producs.contains(line.getProduct())){ + // return false; + // } + // } + // return true; // } + + @Transactional + public void createStockMoveLine( + StockMove stockMove, + Product product, + StockLocation stockLocation, + TrackingNumber trackingNumber) + throws AxelorException { + if (stockMove.getFromStockLocation() == stockLocation) { + System.out.println("yes ***********************"); + } + StockMoveLine stockMoveLine = + Beans.get(StockMoveLineService.class) + .createStockMoveLine( + product, + product.getName(), + "", + BigDecimal.ZERO, + product.getAvgPrice(), + product.getAvgPrice(), + product.getAvgPrice(), + product.getUnit(), + stockMove, + trackingNumber); + stockMove.addStockMoveLineListItem(stockMoveLine); + Beans.get(StockMoveRepository.class).save(stockMove); + } + + public void checkIfNonValidationFolder(StockMove stockMove) throws AxelorException { + + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE" + + " LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON" + + " LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER" + + + // " AND LINE.INTERNAL_TRACKING_NUMBER = LOCATION_LINE.INTERNAL_TRACKING_NUMBER" + + " AND LINE.product = LOCATION_LINE.product" + + " WHERE LOCATION_LINE.DETAILS_STOCK_LOCATION = :location" + + " AND (LOCATION_LINE.analysisFolderValidated IS NULL OR LOCATION_LINE.analysisFolderValidated != TRUE)" + + " AND LINE.STOCK_MOVE = :move"); + + Query sql = JPA.em().createNativeQuery(queryBuilder.toString()); + sql.setParameter("location", stockMove.getFromStockLocation().getId()); + sql.setParameter("move", stockMove.getId()); + + if (!sql.getResultList().isEmpty()) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une ligne de mouvement sans dossier d'analyse validé"); + } + } } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveToolServiceImpl.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveToolServiceImpl.java index 746c1c0..27729bc 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveToolServiceImpl.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/service/StockMoveToolServiceImpl.java @@ -133,14 +133,14 @@ public class StockMoveToolServiceImpl implements StockMoveToolService { break; case StockMoveRepository.TYPE_INCOMING_CLIENT: - ref = sequenceService.getSequenceNumber(SequenceRepository.INCOMING_CLIENT, company); - if (ref == null) { - throw new AxelorException( - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - I18n.get(IExceptionMessage.STOCK_MOVE_3), - company.getName()); - } - break; + ref = sequenceService.getSequenceNumber(SequenceRepository.INCOMING_CLIENT, company); + if (ref == null) { + throw new AxelorException( + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + I18n.get(IExceptionMessage.STOCK_MOVE_3), + company.getName()); + } + break; case StockMoveRepository.TYPE_OUTGOING_CLIENT: ref = sequenceService.getSequenceNumber(SequenceRepository.OUTGOING_CLIENT, company); @@ -217,7 +217,7 @@ public class StockMoveToolServiceImpl implements StockMoveToolService { } // sequence for production request - public String getInternalSequence(int internalSequenceType, Company company ,LocalDate date) + public String getInternalSequence(int internalSequenceType, Company company, LocalDate date) throws AxelorException { String ref = ""; diff --git a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/web/StockMoveController.java b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/web/StockMoveController.java index 89d9906..8385433 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/web/StockMoveController.java +++ b/modules/axelor-open-suite/axelor-stock/src/main/java/com/axelor/apps/stock/web/StockMoveController.java @@ -20,18 +20,24 @@ package com.axelor.apps.stock.web; import com.axelor.apps.base.db.CancelReason; import com.axelor.apps.base.db.PrintingSettings; import com.axelor.apps.base.db.Product; +import com.axelor.apps.base.db.repo.ProductRepository; import com.axelor.apps.base.service.BarcodeGeneratorService; import com.axelor.apps.base.service.TradingNameService; import com.axelor.apps.base.service.app.AppBaseService; import com.axelor.apps.report.engine.ReportSettings; +import com.axelor.apps.stock.db.StockLocation; import com.axelor.apps.stock.db.StockMove; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.StockMoveLineLocation; +import com.axelor.apps.stock.db.TrackingNumber; +import com.axelor.apps.stock.db.repo.StockLocationRepository; import com.axelor.apps.stock.db.repo.StockMoveLineLocationRepository; import com.axelor.apps.stock.db.repo.StockMoveLineRepository; import com.axelor.apps.stock.db.repo.StockMoveRepository; +import com.axelor.apps.stock.db.repo.TrackingNumberRepository; import com.axelor.apps.stock.exception.IExceptionMessage; import com.axelor.apps.stock.service.StockMoveService; +import com.axelor.apps.stock.service.StockMoveServiceImpl; import com.axelor.apps.stock.service.StockMoveToolService; import com.axelor.apps.stock.service.stockmove.print.ConformityCertificatePrintService; import com.axelor.apps.stock.service.stockmove.print.PickingStockMovePrintService; @@ -181,14 +187,13 @@ public class StockMoveController { List requestIds = (List) request.getContext().get("_ids"); CancelReason raison = (CancelReason) request.getContext().get("cancelRaison"); String raisonStr = (String) request.getContext().get("cancelRaisonStr"); - StockMove stockMove = request.getContext().asType(StockMove.class); - + // StockMove stockMove = request.getContext().asType(StockMove.class); System.out.println("*********************************************"); System.out.println(raisonStr); System.out.println(raison); - System.out.println(stockMove.getCancelReason()); - System.out.println(stockMove.getCancelReasonStr()); + // System.out.println(stockMove.getCancelReason()); + // System.out.println(stockMove.getCancelReasonStr()); System.out.println("*********************************************"); if (requestIds == null || requestIds.isEmpty()) { @@ -205,7 +210,7 @@ public class StockMoveController { } } - public void massPlan(ActionRequest request, ActionResponse response) { + public void massPlan(ActionRequest request, ActionResponse response) { @SuppressWarnings("unchecked") List requestIds = (List) request.getContext().get("_ids"); @@ -224,7 +229,7 @@ public class StockMoveController { } } - public void massRealize(ActionRequest request, ActionResponse response) { + public void massRealize(ActionRequest request, ActionResponse response) { @SuppressWarnings("unchecked") List requestIds = (List) request.getContext().get("_ids"); @@ -824,12 +829,12 @@ public class StockMoveController { Query sql = JPA.em() .createNativeQuery( - "SELECT LINE.ID"+ -" FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product"+ -" where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2"); + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2"); sql.setParameter(1, stockMove.getFromStockLocation().getId()); sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size()",sql.getResultList().size()); + logger.debug("sql.getResultList().size()", sql.getResultList().size()); if (sql.getResultList().size() > 0) { throw new AxelorException( stockMove, @@ -838,25 +843,25 @@ public class StockMoveController { } } - public void checkIfNonConformityTag(ActionRequest request, ActionResponse response) + public void checkIfNonConformityTag(ActionRequest request, ActionResponse response) throws AxelorException { - - StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); - StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID " + - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" - +" where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = ?2"); - sql.setParameter(1, stockMove.getToStockLocation().getId()); - sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size()",sql.getResultList().size()); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "vous avez une ligne non étiqueté"); + + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + Query sql = + JPA.em() + .createNativeQuery( + "SELECT LINE.ID " + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = ?2"); + sql.setParameter(1, stockMove.getToStockLocation().getId()); + sql.setParameter(2, stockMove.getId()); + logger.debug("sql.getResultList().size()", sql.getResultList().size()); + if (sql.getResultList().size() > 0) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "vous avez une ligne non étiqueté"); } } @@ -900,4 +905,38 @@ public class StockMoveController { public void showPopup(ActionRequest request, ActionResponse response) { response.setAlert("Hello words!!"); } + + public void createStockMoveLine(ActionRequest request, ActionResponse response) + throws AxelorException { + Context context = request.getContext(); + String stockLocationLineStr = context.get("productHolder").toString(); + if (stockLocationLineStr != null && !stockLocationLineStr.isEmpty()) { + String[] stockLocationLineArr = stockLocationLineStr.split("-"); + String productId = stockLocationLineArr[0]; + String locationId = stockLocationLineArr[1]; + String trackingId = stockLocationLineArr[2]; + Product product = Beans.get(ProductRepository.class).find(Long.valueOf(productId)); + StockLocation stockLocation = + Beans.get(StockLocationRepository.class).find(Long.valueOf(locationId)); + TrackingNumber trackingNumber = + Beans.get(TrackingNumberRepository.class).find(Long.valueOf(trackingId)); + StockMove stockMove = context.asType(StockMove.class); + stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId()); + if (stockMove.getFromStockLocation() == stockLocation) { + System.out.println("yes ***********************"); + } + Beans.get(StockMoveServiceImpl.class) + .createStockMoveLine(stockMove, product, stockLocation, trackingNumber); + response.setReload(true); + } + } + + public void checkIfNonValidationFolder(ActionRequest request, ActionResponse response) + throws AxelorException { + + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + Beans.get(StockMoveServiceImpl.class).checkIfNonValidationFolder(stockMove); + logger.debug("checkIfNonValidationFolder ......... "); + } } diff --git a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocation.xml b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocation.xml index 92a2cf6..bfad8dc 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocation.xml +++ b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocation.xml @@ -7,6 +7,7 @@ + diff --git a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocationLine.xml b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocationLine.xml index 4b7c4c7..62570e5 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocationLine.xml +++ b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockLocationLine.xml @@ -36,6 +36,8 @@ + + diff --git a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockMoveLine.xml b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockMoveLine.xml index 9f4aba1..4e03419 100644 --- a/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockMoveLine.xml +++ b/modules/axelor-open-suite/axelor-stock/src/main/resources/domains/StockMoveLine.xml @@ -52,14 +52,23 @@ - + @@ -104,16 +113,16 @@ public static final int CONFORMITY_NON_COMPLIANT = 3; public static final int TYPE_NORMAL = 0; - public static final int TYPE_TITLE = 1; - public static final int TYPE_PACK = 2; - - public static final int PACK_PRICE_ONLY = 0; - public static final int SUBLINE_PRICE_ONLY = 1; - - // AVAILABLE STATUS SELECT - public static final int STATUS_AVAILABLE = 1; - public static final int STATUS_AVAILABLE_FOR_PRODUCT = 2; - public static final int STATUS_MISSING = 3; + public static final int TYPE_TITLE = 1; + public static final int TYPE_PACK = 2; + + public static final int PACK_PRICE_ONLY = 0; + public static final int SUBLINE_PRICE_ONLY = 1; + + // AVAILABLE STATUS SELECT + public static final int STATUS_AVAILABLE = 1; + public static final int STATUS_AVAILABLE_FOR_PRODUCT = 2; + public static final int STATUS_MISSING = 3; ]]> diff --git a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/db/repo/PurchaseOrderSupplierLineManagementRepository.java b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/db/repo/PurchaseOrderSupplierLineManagementRepository.java index 12256a0..d44fa10 100644 --- a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/db/repo/PurchaseOrderSupplierLineManagementRepository.java +++ b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/db/repo/PurchaseOrderSupplierLineManagementRepository.java @@ -1,26 +1,36 @@ package com.axelor.apps.suppliermanagement.db.repo; +import com.axelor.apps.account.db.BudgetDistribution; +import com.axelor.apps.suppliermanagement.db.PurchaseOrderSupplierLine; +import com.axelor.apps.supplychain.service.BudgetSupplychainService; +import com.axelor.inject.Beans; import java.math.BigDecimal; import java.util.List; import java.util.Map; -import com.axelor.apps.account.db.BudgetDistribution; -import com.axelor.apps.suppliermanagement.db.PurchaseOrderSupplierLine; +public class PurchaseOrderSupplierLineManagementRepository + extends PurchaseOrderSupplierLineRepository { -public class PurchaseOrderSupplierLineManagementRepository extends PurchaseOrderSupplierLineRepository { - - @Override - public Map populate(Map json, Map context) { - Long supplierLine = (Long) json.get("id"); - PurchaseOrderSupplierLine line = find(supplierLine); - List budgetDistributiList = line.getPurchaseOrderLine().getBudgetDistributionList(); - BigDecimal totalBudgetAmountAvailable = BigDecimal.ZERO; - for (BudgetDistribution budgetDistribution : budgetDistributiList) { - totalBudgetAmountAvailable = totalBudgetAmountAvailable.add(budgetDistribution.getBudgetAmountAvailable()); - } - json.put("budgetRemainingAmount", totalBudgetAmountAvailable); - - return super.populate(json, context); + @Override + public Map populate(Map json, Map context) { + Long supplierLine = (Long) json.get("id"); + PurchaseOrderSupplierLine line = find(supplierLine); + List budgetDistributiList = + line.getPurchaseOrderLine().getBudgetDistributionList(); + BigDecimal totalBudgetAmountAvailable = BigDecimal.ZERO; + BigDecimal budgetRemainingAmountAfterValidation = BigDecimal.ZERO; + for (BudgetDistribution budgetDistribution : budgetDistributiList) { + Beans.get(BudgetSupplychainService.class) + .computeBudgetDistributionSumAmount( + budgetDistribution, line.getPurchaseOrderLine().getPurchaseOrder().getOrderDate()); + totalBudgetAmountAvailable = + totalBudgetAmountAvailable.add(budgetDistribution.getBudgetAmountAvailable()); } + budgetRemainingAmountAfterValidation = + totalBudgetAmountAvailable.subtract(line.getInTaxTotal()); + json.put("budgetRemainingAmount", totalBudgetAmountAvailable); + json.put("budgetRemainingAmountAfterValidation", budgetRemainingAmountAfterValidation); + return super.populate(json, context); + } } diff --git a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierLineService.java b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierLineService.java index 23a5eea..49d7bd7 100644 --- a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierLineService.java +++ b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierLineService.java @@ -99,16 +99,18 @@ public class PurchaseOrderSupplierLineService { purchaseOrderSupplierLine.setAcceptanceDate(appPurchaseService.getTodayDate()); purchaseOrderSupplierLine.setAcceptedByUser(AuthUtils.getUser()); - if(!purchaseOrderLine.getBudgetDistributionList().isEmpty()){ - Beans.get(PurchaseOrderLineServiceSupplychainImpl.class) - .computeBudgetDistributionSumAmount(purchaseOrderLine, purchaseOrderLine.getPurchaseOrder()); - } + poSupplierLineRepo.save(purchaseOrderSupplierLine); + + // Budget Module + // if(!purchaseOrderLine.getBudgetDistributionList().isEmpty()){ + // Beans.get(PurchaseOrderLineServiceSupplychainImpl.class) + // .computeBudgetDistributionSumAmount(purchaseOrderLine,purchaseOrderLine.getPurchaseOrder()); + // } if(!purchaseOrderLine.getAnalyticMoveLineList().isEmpty()){ Beans.get(PurchaseOrderLineServiceSupplychainImpl.class).computeAnalyticDistribution(purchaseOrderLine); } - poSupplierLineRepo.save(purchaseOrderSupplierLine); } @Transactional(rollbackOn = {Exception.class}) diff --git a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierService.java b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierService.java index b18707c..2cdfb17 100644 --- a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierService.java +++ b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/service/PurchaseOrderSupplierService.java @@ -17,6 +17,11 @@ */ package com.axelor.apps.suppliermanagement.service; +import com.axelor.apps.account.db.AnalyticMoveLine; +import com.axelor.apps.account.db.Budget; +import com.axelor.apps.account.db.BudgetDistribution; +import com.axelor.apps.account.db.repo.AnalyticMoveLineRepository; +import com.axelor.apps.account.db.repo.BudgetDistributionRepository; import com.axelor.apps.base.db.Blocking; import com.axelor.apps.base.db.Company; import com.axelor.apps.base.db.Partner; @@ -33,7 +38,6 @@ import com.axelor.apps.purchase.db.SupplierCatalog; import com.axelor.apps.purchase.db.repo.PurchaseOrderLineRepository; import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository; import com.axelor.apps.purchase.db.repo.PurchaseRequestRepository; -import com.axelor.apps.purchase.service.PurchaseOrderLineService; import com.axelor.apps.purchase.service.app.AppPurchaseService; import com.axelor.apps.stock.service.StockLocationService; import com.axelor.apps.suppliermanagement.db.PurchaseOrderSupplierLine; @@ -231,9 +235,9 @@ public class PurchaseOrderSupplierService { } } // PurchaseOrderNew.setStatusSelect(0);//Approuve - createPurchaseOrderSupplierLineTCO( - PurchaseOrderNew.getPurchaseOrderLineList(), - purchaseOrderLinesBySupplierPartner.get(supplierPartner)); + // createPurchaseOrderSupplierLineTCO( + // PurchaseOrderNew.getPurchaseOrderLineList(), + // purchaseOrderLinesBySupplierPartner.get(supplierPartner)); } purchaseRequest.setPurchaseOrderSet(hash_Set); @@ -323,7 +327,8 @@ public class PurchaseOrderSupplierService { "Création d'une ligne de commande fournisseur pour le produit : {}", new Object[] {purchaseOrderLine.getProductName()}); - return purchaseOrderLineServiceSupplychainImpl.createPurchaseOrderLineFromSplit(purchaseOrderLine); + return purchaseOrderLineServiceSupplychainImpl.createPurchaseOrderLineFromSplit( + purchaseOrderLine); } @Transactional(rollbackOn = {Exception.class}) @@ -392,8 +397,23 @@ public class PurchaseOrderSupplierService { "SOPHAL Création d'une ligne de commande fournisseur pour le produit : {}", new Object[] {purchaseOrderLine.getProductName()}); - PurchaseOrderLine PurchaseOrderLineNew = - purchaseOrderLineServiceSupplychainImpl.createPurchaseOrderLineFromSplit(purchaseOrderLine); + PurchaseOrderLine PurchaseOrderLineNew = purchaseOrderLineServiceSupplychainImpl.createPurchaseOrderLineFromSplit(purchaseOrderLine); + + // List analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList(); + // List budgetDistributions = purchaseOrderLine.getBudgetDistributionList(); + + // if (analyticMoveLines.size() != 0) { + // for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { + // AnalyticMoveLine analyticMoveLineCopy = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); + // PurchaseOrderLineNew.addAnalyticMoveLineListItem(analyticMoveLineCopy); + // } + // } + // if (budgetDistributions.size() != 0) { + // for (BudgetDistribution budgetDistribution : budgetDistributions) { + // BudgetDistribution budgetDistributionCopy = Beans.get(BudgetDistributionRepository.class).copy(budgetDistribution, false); + // PurchaseOrderLineNew.addBudgetDistributionListItem(budgetDistributionCopy); + // } + // } PurchaseOrderLineNew.setPrice(purchaseOrderLine.getPrice()); PurchaseOrderLineNew.setInTaxPrice(purchaseOrderLine.getInTaxPrice()); @@ -424,27 +444,29 @@ public class PurchaseOrderSupplierService { .fetch(); for (PurchaseOrderSupplierLine poLineSupplierParnet : PurchaseOrderSupplierLineParnetList) { + PurchaseOrderSupplierLine po = + Beans.get(PurchaseOrderSupplierLineRepository.class).copy(poLineSupplierParnet, false); - PurchaseOrderSupplierLine po = new PurchaseOrderSupplierLine(); - po.setPrice(poLineSupplierParnet.getPrice()); - po.setExTaxTotal(poLineSupplierParnet.getExTaxTotal()); - po.setInTaxTotal(poLineSupplierParnet.getInTaxTotal()); - po.setTaxTotal(poLineSupplierParnet.getTaxTotal()); - po.setAvailableQty(poLineSupplierParnet.getAvailableQty()); + // PurchaseOrderSupplierLine po = new PurchaseOrderSupplierLine(); + // po.setPrice(poLineSupplierParnet.getPrice()); + // po.setExTaxTotal(poLineSupplierParnet.getExTaxTotal()); + // po.setInTaxTotal(poLineSupplierParnet.getInTaxTotal()); + // po.setTaxTotal(poLineSupplierParnet.getTaxTotal()); + // po.setAvailableQty(poLineSupplierParnet.getAvailableQty()); po.setPurchaseOrderLine(purchaseOrderLineNew.get(index)); - po.setStateSelect(poLineSupplierParnet.getStateSelect()); - po.setTaxLine(poLineSupplierParnet.getTaxLine()); - po.setSupplierPartner(poLineSupplierParnet.getSupplierPartner()); - po.setComments(poLineSupplierParnet.getComments()); - po.setArchived(poLineSupplierParnet.getArchived()); - po.setEstimatedDelivDate(poLineSupplierParnet.getEstimatedDelivDate()); - po.setAcceptanceDate(poLineSupplierParnet.getAcceptanceDate()); - po.setAcceptedByUser(poLineSupplierParnet.getAcceptedByUser()); - po.setRefusalDate(poLineSupplierParnet.getRefusalDate()); - po.setRefusedByUser(poLineSupplierParnet.getRefusedByUser()); - po.setAttrs(poLineSupplierParnet.getAttrs()); - po.setCurrency(poLineSupplierParnet.getCurrency()); - po.setPriceDiscounted(poLineSupplierParnet.getPriceDiscounted()); + // po.setStateSelect(poLineSupplierParnet.getStateSelect()); + // po.setTaxLine(poLineSupplierParnet.getTaxLine()); + // po.setSupplierPartner(poLineSupplierParnet.getSupplierPartner()); + // po.setComments(poLineSupplierParnet.getComments()); + // po.setArchived(poLineSupplierParnet.getArchived()); + // po.setEstimatedDelivDate(poLineSupplierParnet.getEstimatedDelivDate()); + // po.setAcceptanceDate(poLineSupplierParnet.getAcceptanceDate()); + // po.setAcceptedByUser(poLineSupplierParnet.getAcceptedByUser()); + // po.setRefusalDate(poLineSupplierParnet.getRefusalDate()); + // po.setRefusedByUser(poLineSupplierParnet.getRefusedByUser()); + // po.setAttrs(poLineSupplierParnet.getAttrs()); + // po.setCurrency(poLineSupplierParnet.getCurrency()); + // po.setPriceDiscounted(poLineSupplierParnet.getPriceDiscounted()); // LOG.debug("purchaseOrderLineNew.getId(): {}",purchaseOrderLineNew.get(index).getId()); Beans.get(PurchaseOrderSupplierLineRepository.class).save(po); } diff --git a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderController.java b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderController.java index b878574..0f146cd 100644 --- a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderController.java +++ b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderController.java @@ -95,9 +95,8 @@ public class PurchaseOrderController { PurchaseOrderLineRepository purchaseOrderLineRepo = Beans.get(PurchaseOrderLineRepository.class); for (HashMap map : selectedPurchaseOrderLineMapList) { - PurchaseOrderLine purchaseOrderLine = - (PurchaseOrderLine) Mapper.toBean(PurchaseOrderLine.class, map); - purchaseOrderLineList.add(purchaseOrderLineRepo.find(purchaseOrderLine.getId())); + PurchaseOrderLine poline = (PurchaseOrderLine) Mapper.toBean(PurchaseOrderLine.class, map); + purchaseOrderLineList.add(purchaseOrderLineRepo.find(poline.getId())); } if (purchaseOrderLineList.isEmpty()) { @@ -121,6 +120,7 @@ public class PurchaseOrderController { Beans.get(PurchaseOrderSupplierLineService.class) .setPurchaseOrderLinesPartner(purchaseOrderLineList, partner); response.setCanClose(true); + response.setAlert(String.format(I18n.get("Purchase order lines set successfully to partner %s"),partner.getName())); } catch (Exception e) { TraceBackService.trace(response, e); } diff --git a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderSupplierLineController.java b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderSupplierLineController.java index a52bbdf..0bfcdc5 100644 --- a/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderSupplierLineController.java +++ b/modules/axelor-open-suite/axelor-supplier-management/src/main/java/com/axelor/apps/suppliermanagement/web/PurchaseOrderSupplierLineController.java @@ -30,6 +30,7 @@ import com.axelor.apps.suppliermanagement.db.repo.PurchaseOrderSupplierLineRepos import com.axelor.apps.suppliermanagement.service.PurchaseOrderSupplierLineService; import com.axelor.apps.supplychain.service.PurchaseOrderServiceSupplychainImpl; import com.axelor.exception.service.TraceBackService; +import com.axelor.i18n.I18n; import com.axelor.inject.Beans; import com.axelor.meta.schema.actions.ActionView; import com.axelor.rpc.ActionRequest; @@ -66,6 +67,17 @@ public class PurchaseOrderSupplierLineController { public void accept(ActionRequest request, ActionResponse response) { + // check if budgetRemainingAmountAfterValidation is less than zero then throw error + if (request.getContext().get("budgetRemainingAmountAfterValidation") != null) { + BigDecimal budgetRemainingAmountAfterValidation = + new BigDecimal( + String.valueOf(request.getContext().get("budgetRemainingAmountAfterValidation"))); + if (budgetRemainingAmountAfterValidation.compareTo(BigDecimal.ZERO) < 0) { + response.setNotify(I18n.get( + "The budget remaining amount after validation is less than zero. Cannot accept the supplier line.")); + } + } + PurchaseOrderSupplierLine purchaseOrderSupplierLine = Beans.get(PurchaseOrderSupplierLineRepository.class) .find(request.getContext().asType(PurchaseOrderSupplierLine.class).getId()); @@ -289,12 +301,13 @@ public class PurchaseOrderSupplierLineController { } } + public void computeBudgetDistribution(ActionRequest request, ActionResponse response) { - public void computeBudgetDistribution(ActionRequest request, ActionResponse response){ - - PurchaseOrderSupplierLine purchaseOrderSupplierLine = Beans.get(PurchaseOrderSupplierLineRepository.class) + PurchaseOrderSupplierLine purchaseOrderSupplierLine = + Beans.get(PurchaseOrderSupplierLineRepository.class) .find(request.getContext().asType(PurchaseOrderSupplierLine.class).getId()); - Beans.get(PurchaseOrderServiceSupplychainImpl.class).computeBudgetDistribution(purchaseOrderSupplierLine.getPurchaseOrderLine()); + Beans.get(PurchaseOrderServiceSupplychainImpl.class) + .computeBudgetDistribution(purchaseOrderSupplierLine.getPurchaseOrderLine()); response.setReload(true); } } diff --git a/modules/axelor-open-suite/axelor-supplychain/build.gradle b/modules/axelor-open-suite/axelor-supplychain/build.gradle index 6eaf06e..c6c07b8 100644 --- a/modules/axelor-open-suite/axelor-supplychain/build.gradle +++ b/modules/axelor-open-suite/axelor-supplychain/build.gradle @@ -16,4 +16,6 @@ dependencies { compile project(":modules:axelor-sale") compile project(":modules:axelor-stock") compile project(":modules:axelor-account") + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:4.8.0' } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AccountingCutOffServiceImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AccountingCutOffServiceImpl.java index 0187837..377e59d 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AccountingCutOffServiceImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AccountingCutOffServiceImpl.java @@ -71,6 +71,7 @@ import com.axelor.db.JPA; import com.axelor.db.Query; import com.axelor.exception.AxelorException; import com.axelor.exception.db.repo.TraceBackRepository; +import com.axelor.exception.service.TraceBackService; import com.axelor.i18n.I18n; import com.axelor.inject.Beans; import com.google.inject.Inject; @@ -773,18 +774,38 @@ public class AccountingCutOffServiceImpl implements AccountingCutOffService { return move; } - public List massGenerationMove(List ids) throws AxelorException { + public Map> massGenerationMove(List ids) { - List movesId = new ArrayList<>(); + List successMoves = new ArrayList<>(); + List failedStockMoves = new ArrayList<>(); for (Long id : ids) { - StockMove stockMove = Beans.get(StockMoveRepository.class).find(id); - Move move = this.generateStockAccountMove(stockMove); - movesId.add(move.getId()); + try { + StockMove stockMove = Beans.get(StockMoveRepository.class).find(id); + + Move move = this.generateStockAccountMove(stockMove); + successMoves.add(move.getId()); + + } catch (Exception e) { + + // Log technical error + TraceBackService.trace(e); + + // Mark stock move as failed + failedStockMoves.add(id); + + // IMPORTANT: continue loop + continue; + } } - ; - return movesId; - } + + Map> result = new HashMap<>(); + result.put("success", successMoves); + result.put("failed", failedStockMoves); + + return result; +} + public boolean isDebit(StockMove stockMove) throws AxelorException { boolean isDebit; diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AnalyticMoveLineServiceImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AnalyticMoveLineServiceImpl.java new file mode 100644 index 0000000..0583da8 --- /dev/null +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/AnalyticMoveLineServiceImpl.java @@ -0,0 +1,15 @@ +package com.axelor.apps.supplychain.service; + +import com.axelor.apps.account.db.AnalyticMoveLine; +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class AnalyticMoveLineServiceImpl { + public BigDecimal computeAmount(AnalyticMoveLine analyticMoveLine) { + + return analyticMoveLine + .getPercentage() + .multiply(analyticMoveLine.getPurchaseOrderLine().getExTaxTotal()) + .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP); + } +} diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/BudgetSupplychainService.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/BudgetSupplychainService.java index fa7444e..eaa2d84 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/BudgetSupplychainService.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/BudgetSupplychainService.java @@ -60,14 +60,29 @@ public class BudgetSupplychainService extends BudgetService { Beans.get(BudgetDistributionRepository.class) .all() .filter( - "self.budget.id = ?1 AND self.purchaseOrderLine.purchaseOrder.statusSelect in (?2,?3)", + "self.budget.id = ?1 AND (self.purchaseOrderLine.purchaseOrder.statusSelect in (?2,?3) OR self.saleOrderLine.saleOrder.statusSelect in (?2,?3) )", budget.getId(), PurchaseOrderRepository.STATUS_VALIDATED, PurchaseOrderRepository.STATUS_FINISHED) .fetch(); + System.out.println("*********************************"); + System.out.println(budgetDistributionList); + System.out.println("*********************************"); for (BudgetDistribution budgetDistribution : budgetDistributionList) { - LocalDate orderDate = - budgetDistribution.getPurchaseOrderLine().getPurchaseOrder().getOrderDate(); + LocalDate orderDate = LocalDate.now(); + if (budgetDistribution.getSaleOrderLine() != null) + orderDate = + budgetDistribution + .getSaleOrderLine() + .getSaleOrder() + .getConfirmationDateTime() + .toLocalDate(); + else + orderDate = budgetDistribution.getPurchaseOrderLine().getPurchaseOrder().getOrderDate(); + System.out.println("*********************************"); + System.out.println(orderDate); + System.out.println(orderDate); + System.out.println("*********************************"); if (orderDate != null) { for (BudgetLine budgetLine : budget.getBudgetLineList()) { LocalDate fromDate = budgetLine.getFromDate(); @@ -132,6 +147,9 @@ public class BudgetSupplychainService extends BudgetService { budgetAmountAvailable = budgetAmountAvailable.add(amount); } } + System.out.println("$$$$$$$$$$$$$$$$$$$$$$$budgetAmountAvailable$$$$$$$$$$$$$$$$$$$$$$$$$$$"); + System.out.println(budgetAmountAvailable); + System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$budgetAmountAvailable$$$$$$$$$$$$$$$$$$$$$$$$$$"); budgetDistribution.setBudgetAmountAvailable(budgetAmountAvailable); } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ImportationFolderServiceImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ImportationFolderServiceImpl.java index 4f09304..f32dc55 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ImportationFolderServiceImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ImportationFolderServiceImpl.java @@ -104,7 +104,7 @@ public class ImportationFolderServiceImpl { .getToStockLocation(); TaxLine taxLine = null; if (stockLocation.getId() == 86) { - // if (stockLocation.getId() == 61 || stockLocation.getId() == 60) { + // if (stockLocation.getId() == 61 || stockLocation.getId() == 60) { taxLine = Beans.get(TaxLineRepository.class).find(27L); } else { if (importationFolder.getStockMoveLineList().get(0).getPurchaseOrderLine() @@ -336,27 +336,36 @@ public class ImportationFolderServiceImpl { stockMovesMap .values() .stream() - .filter(t -> - t.get(0) - .getStockMoveLineList() - .stream() - .filter(line -> line.getRealQty() != null && line.getRealQty().compareTo(BigDecimal.ZERO) > 0) - .findFirst() - .isPresent()) - .mapToDouble(t -> - t.get(0) - .getStockMoveLineList() - .stream() - .filter(line -> line.getRealQty() != null && line.getRealQty().compareTo(BigDecimal.ZERO) > 0) - .findFirst() - .map(line -> line.getFreight() - .multiply(stockMoveLines.get(0).getCurrencyRate()) - .setScale(2, RoundingMode.HALF_EVEN) - .doubleValue()) - .orElse(0.0)) // In case no valid line found, fallback to 0.0 + .filter( + t -> + t.get(0) + .getStockMoveLineList() + .stream() + .filter( + line -> + line.getRealQty() != null + && line.getRealQty().compareTo(BigDecimal.ZERO) > 0) + .findFirst() + .isPresent()) + .mapToDouble( + t -> + t.get(0) + .getStockMoveLineList() + .stream() + .filter( + line -> + line.getRealQty() != null + && line.getRealQty().compareTo(BigDecimal.ZERO) > 0) + .findFirst() + .map( + line -> + line.getFreight() + .multiply(stockMoveLines.get(0).getCurrencyRate()) + .setScale(2, RoundingMode.HALF_EVEN) + .doubleValue()) + .orElse(0.0)) // In case no valid line found, fallback to 0.0 .sum()); - System.out.println("*****************freight**************************************"); System.out.println(freight); System.out.println("*******************************************************"); diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderLineServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderLineServiceSupplychainImpl.java index 470caef..abf690b 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderLineServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderLineServiceSupplychainImpl.java @@ -17,6 +17,7 @@ */ package com.axelor.apps.supplychain.service; +import com.axelor.apps.account.db.AccountManagement; import com.axelor.apps.account.db.AnalyticAccount; import com.axelor.apps.account.db.AnalyticDistributionTemplate; import com.axelor.apps.account.db.AnalyticMoveLine; @@ -43,6 +44,7 @@ import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.inject.Beans; import com.google.common.base.Preconditions; import com.google.inject.Inject; +import com.google.inject.persist.Transactional; import java.lang.invoke.MethodHandles; import java.math.BigDecimal; import java.math.RoundingMode; @@ -111,6 +113,7 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe return purchaseOrderLine; } + @Transactional @Override public PurchaseOrderLine createPurchaseOrderLine( PurchaseOrder purchaseOrder, @@ -123,40 +126,72 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe throws AxelorException { PurchaseOrderLine purchaseOrderLine = - super.createPurchaseOrderLine(purchaseOrder, product, productName, description, qty, unit,purchaseRequestLine); - + super.createPurchaseOrderLine( + purchaseOrder, product, productName, description, qty, unit, purchaseRequestLine); + List analyticMoveLines = purchaseRequestLine.getAnalyticMoveLineList(); + List accountManagementList = + purchaseRequestLine.getProduct().getAccountManagementList(); + if (accountManagementList == null || accountManagementList.size() == 0) { + throw new AxelorException( + purchaseRequestLine.getPurchaseRequest(), + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une lignes expression des besoins sans Compte d'achat défini sur le produit"); - // if(analyticMoveLines.size() == 0){ - // throw new AxelorException( - // purchaseRequestLine.getPurchaseRequest(), - // TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - // "Vous avez une lignes expression des besoins sans Compte analytique"); - // } + } else if (accountManagementList.get(0).getPurchaseAccount() == null) { + throw new AxelorException( + purchaseRequestLine.getPurchaseRequest(), + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une lignes expression des besoins sans Compte d'achat défini sur le produit"); + } + String accountCode = + accountManagementList.get(0).getPurchaseAccount().getCode().substring(0, 2); - if(analyticMoveLines.size() != 0){ + if(analyticMoveLines.size() == 0){ + throw new AxelorException( + purchaseRequestLine.getPurchaseRequest(), + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une lignes expression des besoins sans Compte analytique"); + } + + if (analyticMoveLines.size() != 0) { for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { AnalyticMoveLine analyticMoveLineCopy = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); - System.out.println("**************************************************"); + purchaseOrderLine.addAnalyticMoveLineListItem(analyticMoveLineCopy); + this.computeBudgetDistributionSumAmount(purchaseOrderLine, purchaseOrder); + + System.out.println("*********************createPurchaseOrderLine*****************************"); System.out.println(analyticMoveLine.getAnalyticAccount()); System.out.println(analyticMoveLine.getAnalyticAccount().getParent()); - // Budget budget = Beans.get(BudgetRepository.class).findByAnalyticAccount(analyticMoveLine.getAnalyticAccount()); - Budget budget = findBudgetRecursive(analyticMoveLine.getAnalyticAccount()); + + Budget budget = findBudgetRecursive(analyticMoveLine.getAnalyticAccount(), accountCode); System.out.println(budget); - System.out.println("**************************************************"); - if(budget != null){ + System.out.println(analyticMoveLineCopy); + System.out.println("***************************createPurchaseOrderLine***********************"); + if (budget != null) { BudgetDistribution newBudgetDistribution = new BudgetDistribution(); - newBudgetDistribution.setAmount(purchaseOrderLine.getCompanyExTaxTotal().multiply(analyticMoveLine.getPercentage()).divide(new BigDecimal("100")).setScale(2, RoundingMode.HALF_EVEN)); + newBudgetDistribution.setAmount(BigDecimal.ZERO); newBudgetDistribution.setBudget(budget); newBudgetDistribution.setPurchaseOrderLine(purchaseOrderLine); newBudgetDistribution.setAnalyticMoveLine(analyticMoveLineCopy); + Beans.get(BudgetSupplychainService.class) + .computeBudgetDistributionSumAmount(newBudgetDistribution, purchaseOrder.getOrderDate()); + Beans.get(BudgetDistributionRepository.class).save(newBudgetDistribution); - Beans.get(PurchaseOrderLineServiceSupplychainImpl.class).computeBudgetDistributionSumAmount(purchaseOrderLine, purchaseOrder); + purchaseOrderLine.addBudgetDistributionListItem(newBudgetDistribution); + }else{ + // throw new AxelorException( + // purchaseRequestLine.getPurchaseRequest(), + // TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + // "Vous n'avez pas de budget défini pour le compte analytique : " + // + analyticMoveLine.getAnalyticAccount().getCode() + // + " et le compte comptable : " + // + accountCode); } - purchaseOrderLine.addAnalyticMoveLineListItem(analyticMoveLineCopy); + } } - + // purchaseOrderLine.setAmountInvoiced(BigDecimal.ZERO); // // purchaseOrderLine.setIsInvoiced(false); @@ -164,12 +199,12 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe return purchaseOrderLine; } - public PurchaseOrderLine createPurchaseOrderLineFromSplit( - PurchaseOrderLine purchaseOrderLine) + public PurchaseOrderLine createPurchaseOrderLineFromSplit(PurchaseOrderLine purchaseOrderLine) throws AxelorException { - PurchaseOrderLine purchaseOrderLineNew = Beans.get(PurchaseOrderLineRepository.class).copy(purchaseOrderLine, true); - + PurchaseOrderLine purchaseOrderLineNew = + Beans.get(PurchaseOrderLineRepository.class).copy(purchaseOrderLine, true); + List analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList(); // if(analyticMoveLines.size() == 0){ @@ -180,10 +215,11 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe // } // for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { - // AnalyticMoveLine analyticMoveLineCopy = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); + // AnalyticMoveLine analyticMoveLineCopy = + // Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); // purchaseOrderLineNew.addAnalyticMoveLineListItem(analyticMoveLineCopy); // } - + // purchaseOrderLine.setAmountInvoiced(BigDecimal.ZERO); // // purchaseOrderLine.setIsInvoiced(false); @@ -259,6 +295,7 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe return BigDecimal.ZERO; } + @Transactional public void computeBudgetDistributionSumAmount( PurchaseOrderLine purchaseOrderLine, PurchaseOrder purchaseOrder) { List budgetDistributionList = purchaseOrderLine.getBudgetDistributionList(); @@ -268,32 +305,29 @@ public class PurchaseOrderLineServiceSupplychainImpl extends PurchaseOrderLineSe if (budgetDistributionList != null && !budgetDistributionList.isEmpty()) { for (BudgetDistribution budgetDistribution : budgetDistributionList) { - budgetDistributionSumAmount = - budgetDistributionSumAmount.add(budgetDistribution.getAmount()); + System.out.println("???????????????????????????????? budgetDistribution"); + System.out.println(budgetDistribution); + System.out.println("???????????????????????????????? budgetDistribution"); + budgetDistribution.setAmount(purchaseOrderLine.getExTaxTotal().multiply(budgetDistribution.getAnalyticMoveLine().getPercentage().divide(new BigDecimal("100"))).setScale(2, RoundingMode.HALF_EVEN)); + budgetDistributionSumAmount = budgetDistributionSumAmount.add(budgetDistribution.getAmount()); Beans.get(BudgetSupplychainService.class) .computeBudgetDistributionSumAmount(budgetDistribution, computeDate); + Beans.get(BudgetDistributionRepository.class).save(budgetDistribution); } } purchaseOrderLine.setBudgetDistributionSumAmount(budgetDistributionSumAmount); } - - private Budget findBudgetRecursive(AnalyticAccount account) { + public Budget findBudgetRecursive(AnalyticAccount account, String accountCode) { while (account != null) { - System.out.println("???????????????????????????????? while loop"); - Budget budget = Beans.get(BudgetRepository.class) - .findByAnalyticAccount(account); - if (budget != null) { - System.out.println("???????????????????????????????? while loop inside if"); - System.out.println(account); - System.out.println(budget); - - return budget; // found budget, stop here - } - account = account.getParent(); // go up one level + Budget budget = + Beans.get(BudgetRepository.class) + .findByAnalyticAccountAndAccountCode(account, accountCode,true); + if (budget != null) { + return budget; // found budget, stop here + } + account = account.getParent(); // go up one level } return null; // no budget found in hierarchy } - - } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderServiceSupplychainImpl.java index fed7cfe..199f39e 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderServiceSupplychainImpl.java @@ -17,6 +17,8 @@ */ package com.axelor.apps.supplychain.service; +import com.axelor.apps.account.db.AccountManagement; +import com.axelor.apps.account.db.AnalyticMoveLine; import com.axelor.apps.account.db.Budget; import com.axelor.apps.account.db.BudgetDistribution; import com.axelor.apps.account.db.BudgetLine; @@ -58,6 +60,7 @@ import com.google.inject.persist.Transactional; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.math.BigDecimal; +import java.math.RoundingMode; import java.net.MalformedURLException; import java.time.LocalDate; import java.util.HashMap; @@ -120,8 +123,8 @@ public class PurchaseOrderServiceSupplychainImpl extends PurchaseOrderServiceImp externalReference, supplierPartner.getFullName()); - - System.out.println("Création d'une commande fournisseur : Société = {}, Reference externe = {}, Fournisseur = {}"); + System.out.println( + "Création d'une commande fournisseur : Société = {}, Reference externe = {}, Fournisseur = {}"); PurchaseOrder purchaseOrder = super.createPurchaseOrder( @@ -437,7 +440,8 @@ public class PurchaseOrderServiceSupplychainImpl extends PurchaseOrderServiceImp public void finishSockMoves( PurchaseOrder purchaseOrder, CancelReason raison, String cancelReasonStr) throws AxelorException { - Beans.get(PurchaseOrderStockServiceImpl.class).cancelReceiptWithRaison(purchaseOrder, raison, cancelReasonStr); + Beans.get(PurchaseOrderStockServiceImpl.class) + .cancelReceiptWithRaison(purchaseOrder, raison, cancelReasonStr); this.finishPurchaseOrder(purchaseOrder, cancelReasonStr); } @@ -446,9 +450,75 @@ public class PurchaseOrderServiceSupplychainImpl extends PurchaseOrderServiceImp List budgetDistributionList = purchaseOrderLine.getBudgetDistributionList(); for (BudgetDistribution budgetDistribution : budgetDistributionList) { BigDecimal percent = budgetDistribution.getAnalyticMoveLine().getPercentage(); - budgetDistribution.setAmount(purchaseOrderLine.getCompanyExTaxTotal().multiply(percent).setScale(2).divide(new BigDecimal("100"))); + budgetDistribution.setAmount( + purchaseOrderLine + .getCompanyExTaxTotal() + .multiply(percent) + .setScale(2) + .divide(new BigDecimal("100"))); Beans.get(BudgetDistributionRepository.class).save(budgetDistribution); - Beans.get(PurchaseOrderLineServiceSupplychainImpl.class).computeBudgetDistributionSumAmount(purchaseOrderLine, purchaseOrderLine.getPurchaseOrder()); + Beans.get(PurchaseOrderLineServiceSupplychainImpl.class) + .computeBudgetDistributionSumAmount( + purchaseOrderLine, purchaseOrderLine.getPurchaseOrder()); } } + + public boolean isMissingAnalyticMoveLine(PurchaseOrder purchaseOrder) { + boolean isMissing = false; + List prLines = purchaseOrder.getPurchaseOrderLineList(); + for (PurchaseOrderLine prLine : prLines) { + if (prLine.getAnalyticMoveLineList().isEmpty()) { + isMissing = true; + break; + } + } + return isMissing; + } + + @Transactional + public void generateBudgetDistributionLines(PurchaseOrder purchaseOrder) throws AxelorException { + List prLines = purchaseOrder.getPurchaseOrderLineList(); + for (PurchaseOrderLine pOrderLine : prLines) { + List accountManagementList = pOrderLine.getProduct().getAccountManagementList(); + String accountCode = accountManagementList.get(0).getPurchaseAccount().getCode().substring(0, 2); + List analyticMoveLines = pOrderLine.getAnalyticMoveLineList(); + if(analyticMoveLines.size() == 0){ + throw new AxelorException( + purchaseOrder, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + I18n.get("One of the purchase order line has no analytic move line associated with it. Please make sure that the product has an analytic account and a purchase account defined."), + purchaseOrder.getPurchaseOrderSeq()); + } + for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { + Budget budget = Beans.get(PurchaseOrderLineServiceSupplychainImpl.class).findBudgetRecursive(analyticMoveLine.getAnalyticAccount(), accountCode); + if (budget != null) { + BudgetDistribution newBudgetDistribution = new BudgetDistribution(); + newBudgetDistribution.setAmount( + pOrderLine + .getCompanyExTaxTotal() + .multiply(analyticMoveLine.getPercentage()) + .divide(new BigDecimal("100")) + .setScale(2, RoundingMode.HALF_EVEN)); + newBudgetDistribution.setBudget(budget); + newBudgetDistribution.setPurchaseOrderLine(pOrderLine); + newBudgetDistribution.setAnalyticMoveLine(analyticMoveLine); + Beans.get(BudgetSupplychainService.class) + .computeBudgetDistributionSumAmount(newBudgetDistribution, purchaseOrder.getOrderDate()); + + Beans.get(BudgetDistributionRepository.class).save(newBudgetDistribution); + pOrderLine.addBudgetDistributionListItem(newBudgetDistribution); + }else{ + throw new AxelorException( + purchaseOrder.getPurchaseOrderSeq(), + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous n'avez pas de budget défini pour le compte analytique : " + + analyticMoveLine.getAnalyticAccount().getCode() + + " et le compte comptable : " + + accountCode); + } + } + + } + + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderStockServiceImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderStockServiceImpl.java index f111ea9..2e53778 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderStockServiceImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseOrderStockServiceImpl.java @@ -63,6 +63,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -340,11 +341,13 @@ public class PurchaseOrderStockServiceImpl implements PurchaseOrderStockService } else if (purchaseOrderLine.getIsTitleLine()) { stockMoveLine = createTitleStockMoveLine(purchaseOrderLine, stockMove); } - stockMoveLine.setAnalyticDistributionTemplate(purchaseOrderLine.getAnalyticDistributionTemplate()); + stockMoveLine.setAnalyticDistributionTemplate( + purchaseOrderLine.getAnalyticDistributionTemplate()); List analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList(); - if(analyticMoveLines.size() != 0){ + if (analyticMoveLines.size() != 0) { for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { - AnalyticMoveLine analyticMoveLineCopy = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); + AnalyticMoveLine analyticMoveLineCopy = + Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); stockMoveLine.addAnalyticMoveLineListItem(analyticMoveLineCopy); } } @@ -389,26 +392,33 @@ public class PurchaseOrderStockServiceImpl implements PurchaseOrderStockService RoundingMode.HALF_EVEN); } - if (unit != null && !unit.equals(purchaseOrderLine.getUnit())) { - qty = - unitConversionService.convert( - purchaseOrderLine.getUnit(), unit, qty, qty.scale(), product); + List stockMoveLineIdList = + Arrays.asList( + 12L, 97L, 99L, 103L, 105L); // Dummy list to illustrate existing stock move lines + if (!stockMoveLineIdList.contains(stockMove.getToStockLocation().getId())) { + LOG.info("Stock move line not laboratory."); - priceDiscounted = - unitConversionService.convert( - unit, - purchaseOrderLine.getUnit(), - priceDiscounted, - appBaseService.getNbDecimalDigitForUnitPrice(), - product); + if (unit != null && !unit.equals(purchaseOrderLine.getUnit())) { + qty = + unitConversionService.convert( + purchaseOrderLine.getUnit(), unit, qty, qty.scale(), product); - companyUnitPriceUntaxed = - unitConversionService.convert( - unit, - purchaseOrderLine.getUnit(), - companyUnitPriceUntaxed, - appBaseService.getNbDecimalDigitForUnitPrice(), - product); + priceDiscounted = + unitConversionService.convert( + unit, + purchaseOrderLine.getUnit(), + priceDiscounted, + appBaseService.getNbDecimalDigitForUnitPrice(), + product); + + companyUnitPriceUntaxed = + unitConversionService.convert( + unit, + purchaseOrderLine.getUnit(), + companyUnitPriceUntaxed, + appBaseService.getNbDecimalDigitForUnitPrice(), + product); + } } BigDecimal shippingCoef = diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseRequestServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseRequestServiceSupplychainImpl.java index 02cc557..f977c60 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseRequestServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/PurchaseRequestServiceSupplychainImpl.java @@ -17,10 +17,11 @@ */ package com.axelor.apps.supplychain.service; -import java.util.List; - +import com.axelor.apps.account.db.AnalyticJournal; import com.axelor.apps.account.db.AnalyticMoveLine; import com.axelor.apps.account.db.repo.AnalyticMoveLineRepository; +import com.axelor.apps.base.db.AppBudget; +import com.axelor.apps.base.db.repo.AppBudgetRepository; import com.axelor.apps.purchase.db.PurchaseOrder; import com.axelor.apps.purchase.db.PurchaseOrderLine; import com.axelor.apps.purchase.db.PurchaseRequest; @@ -31,6 +32,9 @@ import com.axelor.apps.stock.db.repo.StockLocationRepository; import com.axelor.exception.AxelorException; import com.axelor.inject.Beans; import com.google.inject.Inject; +import com.google.inject.persist.Transactional; +import java.math.BigDecimal; +import java.util.List; public class PurchaseRequestServiceSupplychainImpl extends PurchaseRequestServiceImpl { @@ -57,12 +61,49 @@ public class PurchaseRequestServiceSupplychainImpl extends PurchaseRequestServic return key; } - public void setPurchaseOrderLineAnalyticMoveLine(PurchaseRequestLine purchaseRequestLine,PurchaseOrderLine purchaseOrderLine){ - List analyticMoveLines = purchaseRequestLine.getAnalyticMoveLineList(); - - for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { - AnalyticMoveLine aml = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, true); - purchaseOrderLine.addAnalyticMoveLineListItem(aml); + public void setPurchaseOrderLineAnalyticMoveLine( + PurchaseRequestLine purchaseRequestLine, PurchaseOrderLine purchaseOrderLine) { + List analyticMoveLines = purchaseRequestLine.getAnalyticMoveLineList(); + + for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { + AnalyticMoveLine aml = + Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, true); + purchaseOrderLine.addAnalyticMoveLineListItem(aml); + } + } + + // check if there is missing analytic move line in one of purchase request line when there is no + // in purchase request + public boolean isMissingAnalyticMoveLine(PurchaseRequest purchaseRequest) { + boolean isMissing = false; + List prLines = purchaseRequest.getPurchaseRequestLineList(); + for (PurchaseRequestLine prLine : prLines) { + if (prLine.getAnalyticMoveLineList().isEmpty()) { + isMissing = true; + break; } + } + return isMissing; + } + + // Generate analytic move line in purchase request line from purchase request analytic account and + // axis + @Transactional(rollbackOn = {Exception.class}) + public void generateAnalyticMoveLines(PurchaseRequest purchaseRequest) { + List prLines = purchaseRequest.getPurchaseRequestLineList(); + for (PurchaseRequestLine prLine : prLines) { + prLine.getAnalyticMoveLineList().clear(); + if(prLine.getAnalyticMoveLineList().isEmpty()){ + AnalyticMoveLine aml = new AnalyticMoveLine(); + AppBudget AppBudget = Beans.get(AppBudgetRepository.class).all().fetchOne(); + AnalyticJournal deAnalyticJournal = AppBudget.getDefaultAnalyticJournal(); + aml.setAnalyticJournal(deAnalyticJournal); + aml.setAnalyticAccount(purchaseRequest.getAnalyticAccount()); + aml.setAnalyticAxis(purchaseRequest.getAnalyticAxis()); + aml.setAmount(BigDecimal.ZERO); + aml.setPercentage(BigDecimal.valueOf(100)); + prLine.addAnalyticMoveLineListItem(aml); + } + } } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ReservedQtyServiceImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ReservedQtyServiceImpl.java index 152fcbe..0bc2933 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ReservedQtyServiceImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/ReservedQtyServiceImpl.java @@ -42,6 +42,7 @@ import com.axelor.inject.Beans; import com.google.inject.Inject; import com.google.inject.persist.Transactional; import java.math.BigDecimal; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -794,8 +795,13 @@ public class ReservedQtyServiceImpl implements ReservedQtyService { Unit startUnit, Unit endUnit, BigDecimal qtyToConvert, Product product) throws AxelorException { if (startUnit != null && !startUnit.equals(endUnit)) { - return unitConversionService.convert( + List listOfCategoryIds = Arrays.asList(58L, 60L, 61L, 62L, 63L, 153L, 169L, 173L); + if(listOfCategoryIds.contains(product.getFamilleProduit().getId())){ + return qtyToConvert; + }else{ + return unitConversionService.convert( startUnit, endUnit, qtyToConvert, qtyToConvert.scale(), product); + } } else { return qtyToConvert; } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/SaleOrderServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/SaleOrderServiceSupplychainImpl.java index d60232f..c9d37cd 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/SaleOrderServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/SaleOrderServiceSupplychainImpl.java @@ -17,9 +17,14 @@ */ package com.axelor.apps.supplychain.service; +import com.axelor.apps.account.db.AnalyticJournal; +import com.axelor.apps.account.db.AnalyticMoveLine; +import com.axelor.apps.account.db.repo.AnalyticMoveLineRepository; +import com.axelor.apps.base.db.AppBudget; import com.axelor.apps.base.db.AppSupplychain; import com.axelor.apps.base.db.CancelReason; import com.axelor.apps.base.db.Partner; +import com.axelor.apps.base.db.repo.AppBudgetRepository; import com.axelor.apps.base.db.repo.PriceListRepository; import com.axelor.apps.base.service.PartnerPriceListService; import com.axelor.apps.base.service.PartnerService; @@ -250,4 +255,36 @@ public class SaleOrderServiceSupplychainImpl extends SaleOrderServiceImpl { return null; } } + + // Generate analytic move line in purchase request line from purchase request analytic account and + // axis + @Transactional(rollbackOn = {Exception.class}) + public void generateAnalyticMoveLines(SaleOrder saleOrder) throws AxelorException { + AppBudget AppBudget = Beans.get(AppBudgetRepository.class).all().fetchOne(); + AnalyticJournal deAnalyticJournal = AppBudget.getDefaultSaleAnalyticJournal(); + List saleOrderLines = saleOrder.getSaleOrderLineList(); + if (deAnalyticJournal == null) { + throw new AxelorException( + saleOrder, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "There is no default sale analytic journal defined in Budget app settings."); + } + if (saleOrder.getClientPartner().getAnalyticAccount() == null + || saleOrder.getClientPartner().getAnalyticAxis() == null) { + throw new AxelorException( + saleOrder, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "The client partner does not have analytic account or analytic axis defined."); + } + for (SaleOrderLine saleOrderLine : saleOrderLines) { + AnalyticMoveLine aml = new AnalyticMoveLine(); + aml.setAnalyticJournal(deAnalyticJournal); + aml.setAnalyticAccount(saleOrder.getClientPartner().getAnalyticAccount()); + aml.setAnalyticAxis(saleOrder.getClientPartner().getAnalyticAxis()); + aml.setAmount(saleOrderLine.getInTaxTotal()); + aml.setPercentage(BigDecimal.valueOf(100)); + aml.setTypeSelect(AnalyticMoveLineRepository.STATUS_FORECAST_ORDER); + saleOrderLine.addAnalyticMoveLineListItem(aml); + } + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockConfigService.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockConfigService.java index aa68a6c..ebbf0d0 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockConfigService.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockConfigService.java @@ -9,23 +9,22 @@ import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.i18n.I18n; public class StockConfigService { - public StockConfig getStockConfig(Company company) throws AxelorException { + public StockConfig getStockConfig(Company company) throws AxelorException { - StockConfig stockConfig = company.getStockConfig(); + StockConfig stockConfig = company.getStockConfig(); - if (stockConfig == null) { - throw new AxelorException( - company, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - I18n.get(IExceptionMessage.STOCK_CONFIG_1), - company.getName()); - } - - return stockConfig; + if (stockConfig == null) { + throw new AxelorException( + company, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + I18n.get(IExceptionMessage.STOCK_CONFIG_1), + company.getName()); } - public StockLocation getWarehouseNonCompliant(StockConfig stockConfig) - throws AxelorException { + return stockConfig; + } + + public StockLocation getWarehouseNonCompliant(StockConfig stockConfig) throws AxelorException { if (stockConfig.getPickupDefaultStockLocation() == null) { throw new AxelorException( stockConfig, diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImpl.java index ab61f11..50d0bfb 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImpl.java @@ -20,8 +20,10 @@ package com.axelor.apps.supplychain.service; import com.axelor.apps.account.db.AnalyticMoveLine; import com.axelor.apps.account.db.repo.AnalyticMoveLineRepository; import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.Period; import com.axelor.apps.base.db.Product; import com.axelor.apps.base.db.Unit; +import com.axelor.apps.base.db.repo.PeriodRepository; import com.axelor.apps.base.db.repo.ProductRepository; import com.axelor.apps.base.service.PriceListService; import com.axelor.apps.base.service.UnitConversionService; @@ -35,13 +37,15 @@ import com.axelor.apps.purchase.service.PurchaseOrderLineService; import com.axelor.apps.purchase.service.PurchaseOrderLineServiceImpl; import com.axelor.apps.purchase.service.PurchaseOrderServiceImpl; import com.axelor.apps.sale.db.SaleOrderLine; -import com.axelor.apps.stock.db.InternalTrackingNumber; +import com.axelor.apps.stock.db.Inventory; +import com.axelor.apps.stock.db.InventoryLine; import com.axelor.apps.stock.db.StockLocation; import com.axelor.apps.stock.db.StockLocationLine; import com.axelor.apps.stock.db.StockMove; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.TrackingNumber; import com.axelor.apps.stock.db.TrackingNumberConfiguration; +import com.axelor.apps.stock.db.repo.InventoryRepository; import com.axelor.apps.stock.db.repo.StockLocationLineRepository; import com.axelor.apps.stock.db.repo.StockLocationRepository; import com.axelor.apps.stock.db.repo.StockMoveLineRepository; @@ -68,6 +72,7 @@ import com.google.inject.Inject; import com.google.inject.persist.Transactional; import com.google.inject.servlet.RequestScoped; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @@ -153,11 +158,12 @@ public class StockMoveLineServiceSupplychainImpl extends StockMoveLineServiceImp stockMoveLine.setPvg(saleOrderLine.getPvg()); stockMoveLine.setStklim(saleOrderLine.getStklim()); stockMoveLine.setUg(saleOrderLine.getUg()); + this.inheretAccountAnalyticMoveLine(null, saleOrderLine, stockMoveLine); } TrackingNumberConfiguration trackingNumberConfiguration = product.getTrackingNumberConfiguration(); - if(purchaseOrderLine != null){ + if (purchaseOrderLine != null) { System.out.println("Stepper loading ::::::::::::::::"); // this.inheretAccountAnalyticMoveLine(purchaseOrderLine,stockMoveLine); } @@ -180,15 +186,31 @@ public class StockMoveLineServiceSupplychainImpl extends StockMoveLineServiceImp } @Transactional - private void inheretAccountAnalyticMoveLine(PurchaseOrderLine purchaseOrderLine, StockMoveLine stockMoveLine) { - List analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList(); - if(analyticMoveLines.size() != 0){ - for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { - AnalyticMoveLine aml = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); - aml.setStockMoveLine(stockMoveLine); - stockMoveLine.addAnalyticMoveLineListItem(aml); + private void inheretAccountAnalyticMoveLine( + PurchaseOrderLine purchaseOrderLine, + SaleOrderLine saleOrderLine, + StockMoveLine stockMoveLine) { + List analyticMoveLines = new ArrayList<>(); + if (purchaseOrderLine != null) { + analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList(); + } else { + analyticMoveLines = saleOrderLine.getAnalyticMoveLineList(); + } + if (analyticMoveLines.size() != 0) { + for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) { + AnalyticMoveLine aml = + Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false); + if (purchaseOrderLine != null) { + aml.setPurchaseOrderLine(purchaseOrderLine); } + if (saleOrderLine != null) { + aml.setSaleOrderLine(saleOrderLine); + } + aml.setDate(LocalDate.now()); + aml.setStockMoveLine(stockMoveLine); + stockMoveLine.addAnalyticMoveLineListItem(aml); } + } } @Override @@ -845,6 +867,45 @@ public class StockMoveLineServiceSupplychainImpl extends StockMoveLineServiceImp return qtyTot; } + private BigDecimal getOnHandAtDate(Product product,Company company,LocalDate fromDate,Boolean excludeValorisationStocklocation) { + + // 1) current on-hand (today) + BigDecimal currentQty = getTotalQty(product, company, excludeValorisationStocklocation); + + // 2) movements since fromDate up to today (inclusive) + List moves = + stockMoveLineRepository + .all() + .filter( + "self.product = ?1 " + + "AND self.stockMove.realDate >= ?2 " + + "AND self.stockMove.realDate <= ?3 " + + "AND self.stockMove.statusSelect = ?4 " + + "AND self.realQty > 0", + product, + fromDate, + LocalDate.now(), + StockMoveRepository.STATUS_REALIZED) + .order("stockMove.realDate") + .order("id") + .fetch(); + + BigDecimal incoming = BigDecimal.ZERO; + BigDecimal outgoing = BigDecimal.ZERO; + + for (StockMoveLine line : moves) { + if (isIncoming(line)) { + incoming = incoming.add(line.getRealQty()); + } else if (isOutgoing(line)) { + outgoing = outgoing.add(line.getRealQty()); + } + } + + // 3) reverse the delta to go back in time + // qtyAtStart = current - incoming + outgoing + return currentQty.subtract(incoming).add(outgoing); + } + // lines public List lines = new ArrayList<>(); @@ -954,4 +1015,450 @@ public class StockMoveLineServiceSupplychainImpl extends StockMoveLineServiceImp } return slicedLines; } + + /* ============================================================ + * PART 1 — Helper methods + * ============================================================ */ + + private boolean isNc1(StockLocation loc) { + if (loc == null) return false; + Boolean flag = loc.getExcludeValorisation(); + return flag != null && flag == true; + } + + // Determine if movement INCREASES usable stock + private boolean isIncoming(StockMoveLine line) { + StockLocation from = line.getStockMove().getFromStockLocation(); + StockLocation to = line.getStockMove().getToStockLocation(); + + boolean fromNc1 = isNc1(from); + boolean toNc1 = isNc1(to); + + // NC1 → Normal = incoming + if (fromNc1 && !toNc1) return true; + + // 2) Supplier arrival (partner != 853) = incoming & WAP update + if (from.getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL) { + return true; // Supplier incoming + } + + return false; + } + + // Determine if movement DECREASES usable stock + private boolean isOutgoing(StockMoveLine line) { + StockLocation from = line.getStockMove().getFromStockLocation(); + StockLocation to = line.getStockMove().getToStockLocation(); + + boolean fromNc1 = isNc1(from); + boolean toNc1 = isNc1(to); + + // Normal → NC1 = outgoing + if (!fromNc1 && toNc1) return true; + + // Normal → supplier / virtual = outgoing + if (to.getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL) { + return true; + } + + return false; + } + + // WAP formula + private BigDecimal computeWap( + BigDecimal oldQty, + BigDecimal oldWap, + BigDecimal incomingQty, + BigDecimal incomingPrice, + int scale) { + + if (incomingQty.compareTo(BigDecimal.ZERO) <= 0) return oldWap; + + return oldQty + .multiply(oldWap) + .add(incomingQty.multiply(incomingPrice)) + .divide(oldQty.add(incomingQty), scale, RoundingMode.HALF_UP); + } + + // Extract initial Qty + WAP from inventory or last movement + private InitialWapState getInitialStateForPeriod( + Product product, LocalDate periodStart, Company company) throws AxelorException { + + InitialWapState state = new InitialWapState(); + state.quantity = BigDecimal.ZERO; + state.wap = BigDecimal.ZERO; + + // ------------------------------------------------- + // 1) INVENTORY (highest priority) + // ------------------------------------------------- + Inventory lastInventory = + Beans.get(InventoryRepository.class) + .all() + .filter( + "self.period.statusSelect = ?1 AND self.statusSelect = ?2 AND self.period.toDate < ?3", + PeriodRepository.STATUS_CLOSED, + InventoryRepository.STATUS_VALIDATED, + periodStart) + .order("-id") + .fetchOne(); + + if (lastInventory != null) { + List invLines = + lastInventory + .getInventoryLineList() + .stream() + .filter(l -> l.getProduct() != null && l.getProduct().getId().equals(product.getId())) + .collect(Collectors.toList()); + + if (!invLines.isEmpty()) { + + // 1) Sum quantities + BigDecimal totalQty = BigDecimal.ZERO; + for (InventoryLine l : invLines) { + if (l.getRealQty() != null) { + totalQty = totalQty.add(l.getRealQty()); + } + } + + // 2) Take PMP from any line (same for all) + BigDecimal pmp = invLines.get(0).getGapValue(); + if (pmp == null) { + pmp = BigDecimal.ZERO; + } + + state.quantity = totalQty; + state.wap = pmp; + return state; + } + } + + // ------------------------------------------------- + // 2) LAST MOVEMENT BEFORE PERIOD + // ------------------------------------------------- + StockMoveLine lastBefore = + stockMoveLineRepository + .all() + .filter( + "self.product = ?1 AND self.stockMove.realDate < ?2 AND self.realQty > 0 " + + "AND self.stockMove.statusSelect = ?3", + product, + periodStart, + StockMoveRepository.STATUS_REALIZED) + .order("-stockMove.realDate") + .order("-id") + .fetchOne(); + + if (lastBefore != null) { + state.wap = + lastBefore.getWapPrice() != null + ? lastBefore.getWapPrice() + : lastBefore.getUnitPriceUntaxed(); + + // Quantity at that date = physical stock + // state.quantity = getTotalQty(product, company, false); + state.quantity = getOnHandAtDate(product, company, periodStart, false); + return state; + } + + // ------------------------------------------------- + // 3) NO INVENTORY + NO PREVIOUS MOVE + // → TAKE FIRST SUPPLIER RECEPTION OF THE PERIOD + // ------------------------------------------------- + StockMoveLine firstReception = + stockMoveLineRepository + .all() + .filter( + "self.product = ?1 " + + "AND self.stockMove.realDate >= ?2 " + + "AND self.stockMove.statusSelect = ?3 " + + "AND self.realQty > 0 " + + "AND self.stockMove.partner IS NOT NULL " + + "AND self.stockMove.partner.id != 853 " + + "AND self.stockMove.typeSelect != ?4", + product, + periodStart, + StockMoveRepository.STATUS_REALIZED, + StockMoveRepository.TYPE_INTERNAL) + .order("stockMove.realDate") + .order("id") + .fetchOne(); + + if (firstReception != null) { + state.quantity = firstReception.getRealQty(); + state.wap = firstReception.getUnitPriceUntaxed(); + return state; + } + + // ------------------------------------------------- + // 4) ABSOLUTE FALLBACK (rare, but safe) + // ------------------------------------------------- + state.quantity = BigDecimal.ZERO; + state.wap = BigDecimal.ZERO; + return state; + } + + // Helper struct + private static class InitialWapState { + BigDecimal quantity; + BigDecimal wap; + } + + /* ============================================================ + * PART 2 — Rewritten valorize() for a single movement + * ============================================================ */ + + @Transactional + public void valorizeRewritten( + Long productId, + Long stockMoveId, + LocalDate toDateTime, + BigDecimal incomingPrice, + Boolean updatePo, + Boolean excludeValorisation) + throws AxelorException { + + Product product = Beans.get(ProductRepository.class).find(productId); + StockMove currentMove = Beans.get(StockMoveRepository.class).find(stockMoveId); + + int scale = appBaseService.getNbDecimalDigitForUnitPrice(); + + // Retrieve all movements up to the target date + List allLines = + getStockMoveLines(product, toDateTime, currentMove, excludeValorisation); + + // Slice movements from the current one forward + List affected = sliceStockMoveLines(allLines, stockMoveId, product); + + if (affected.isEmpty()) return; + + InitialWapState state = new InitialWapState(); + state.quantity = getTotalQty(product, currentMove.getCompany(), excludeValorisation); + state.wap = getPreviousWap(affected.get(0), excludeValorisation); + + // Apply current movement WAP update + for (StockMoveLine line : affected) { + + boolean incoming = isIncoming(line); + boolean outgoing = isOutgoing(line); + + BigDecimal qty = line.getRealQty(); + + System.out.println("Processing line Code"+ line.getProduct().getCode() +" qty " + qty + " | " + line.getProductName() + " | incoming : " + incoming + " | outgoing : " + outgoing + " | stockmove : " + line.getStockMove().getStockMoveSeq()); + System.out.println("qty before : " + state.quantity + " | wap before : " + state.wap + " | qty : " + qty + " | incomingPrice : " + incomingPrice); + if (incoming) { + // Recalculate WAP + state.wap = computeWap(state.quantity, state.wap, qty, incomingPrice, scale); + state.quantity = state.quantity.add(qty); + + line.setWapPrice(state.wap); + line.setUnitPriceUntaxed(state.wap); + line.setUnitPriceTaxed(state.wap); + line.setCompanyUnitPriceUntaxed(state.wap); + line.setIsWapUpdated(true); + + } else if (outgoing) { + // Outgoing takes last WAP but does not change it + line.setWapPrice(state.wap); + line.setUnitPriceUntaxed(state.wap); + line.setUnitPriceTaxed(state.wap); + line.setCompanyUnitPriceUntaxed(state.wap); + + state.quantity = state.quantity.subtract(qty); + line.setIsWapUpdated(true); + } + + Beans.get(StockMoveLineRepository.class).save(line); + } + + // Update product and all stock locations + product.setAvgPrice(state.wap); + Beans.get(ProductRepository.class).save(product); + + List locLines = + Beans.get(StockLocationLineServiceImpl.class).getStockLocationLines(product); + for (StockLocationLine loc : locLines) { + loc.setAvgPrice(state.wap); + Beans.get(StockLocationLineRepository.class).save(loc); + } + } + + /* ============================================================ + * PART 3 — Full period valorization + * ============================================================ */ + + @Transactional + public void valorizeCurrentPeriod(Long productCategoryId, Company company, Long productId) + throws AxelorException { + Period period = + Beans.get(PeriodRepository.class) + .all() + .filter("self.statusSelect = ?1", PeriodRepository.STATUS_OPENED) + .order("-id") + .fetchOne(); + LocalDate start = period.getFromDate(); + LocalDate end = period.getToDate(); + List products = new ArrayList<>(); + + if (productId != null) { + // Valorize specific product only + Product p = Beans.get(ProductRepository.class).find(productId); + if (p == null) { + throw new AxelorException( + TraceBackRepository.CATEGORY_MISSING_FIELD, + String.format("Product with id %s not found", productId)); + } + products.add(p); + } else { + // Valorize all products in category + products = + Beans.get(ProductRepository.class) + .all() + .filter("self.familleProduit.id = ?1 and (self.archived is null OR self.archived is false) ", productCategoryId) + .fetch(); + } + + System.out.println(products.size()); + int all = products.size(); + int size = products.size(); + + int BATCH_SIZE = 10; // start with 10 (safe) + int i = 0; + + for (Product product : products) { + + // 1) Get initial state + InitialWapState state = getInitialStateForPeriod(product, start, company); + System.out.println(String.valueOf(size) + "/" + String.valueOf(all)); + size--; + // 2) Fetch chronological movements within the period + List moves = + stockMoveLineRepository + .all() + .filter( + "self.product = ?1 AND self.stockMove.realDate >= ?2 " + + "AND self.stockMove.realDate <= ?3 AND self.realQty > 0 AND self.stockMove.statusSelect = ?4", + product, + start, + end, + StockMoveRepository.STATUS_REALIZED) + .order("stockMove.realDate") + .order("id") + .fetch(); + + int scale = appBaseService.getNbDecimalDigitForUnitPrice(); + + // 3) Loop through period movements + for (StockMoveLine line : moves) { + + boolean incoming = isIncoming(line); + boolean outgoing = isOutgoing(line); + BigDecimal qty = line.getRealQty(); + + BigDecimal price = line.getUnitPriceUntaxed(); + + System.out.println("Processing line Code"+ line.getProduct().getCode() +" qty " + qty + " | " + line.getProductName() + " | incoming : " + incoming + " | outgoing : " + outgoing + " | stockmove : " + line.getStockMove().getStockMoveSeq()); + System.out.println("qty before : " + state.quantity + " | wap before : " + state.wap + " | qty : " + qty + " | incomingPrice : " + price); + + if (incoming) { + boolean isSupplierIncoming = + line.getStockMove().getPartner() != null + && line.getStockMove().getPartner().getId() != 853; + + if (isSupplierIncoming) { + // TRUE WAP UPDATE + state.wap = computeWap(state.quantity, state.wap, qty, price, scale); + } + state.quantity = state.quantity.add(qty); + + line.setWapPrice(state.wap); + if (!isSupplierIncoming) { + line.setUnitPriceUntaxed(state.wap); + line.setUnitPriceTaxed(state.wap); + line.setCompanyUnitPriceUntaxed(state.wap); + } + + } else if (outgoing) { + + line.setWapPrice(state.wap); + line.setUnitPriceUntaxed(state.wap); + line.setUnitPriceTaxed(state.wap); + line.setCompanyUnitPriceUntaxed(state.wap); + + state.quantity = state.quantity.subtract(qty); + } else { + + line.setWapPrice(state.wap); + line.setUnitPriceUntaxed(state.wap); + line.setUnitPriceTaxed(state.wap); + line.setCompanyUnitPriceUntaxed(state.wap); + } + + line.setIsWapUpdated(true); + Beans.get(StockMoveLineRepository.class).save(line); + } + + // 4) Update product & stock locations at end of valorization + JPA.em() + .createQuery("UPDATE Product p SET p.avgPrice = :wap WHERE p.id = :id") + .setParameter("wap", state.wap) + .setParameter("id", product.getId()) + .executeUpdate(); + + + JPA.em() + .createQuery( + "UPDATE StockLocationLine s SET s.avgPrice = :wap WHERE s.product.id = :pid") + .setParameter("wap", state.wap) + .setParameter("pid", product.getId()) + .executeUpdate(); + + // ---- BATCH CONTROL ---- + i++; + if (i % BATCH_SIZE == 0) { + JPA.em().flush(); + JPA.em().clear(); // 🔥 THIS IS CRITICAL + System.out.println("Batch flushed at product " + i); + } + } + // Final flush + JPA.em().flush(); + JPA.em().clear(); + } + + // public void valorizeCurrentPeriod(){ + // List products = Beans.get(StockLocationLineRepository.class) + // .all() + // .filter("self.stockLocation.isNotInCalculStock = false") + // .fetch() + // .stream() + // .map(t -> t.getProduct()) + // .distinct() + // .collect(Collectors.toList()); + + // Period lastPeriod = Beans.get(PeriodRepository.class).all().filter("self.statusSelect = + // ?1",PeriodRepository.STATUS_CLOSED).order("-id").fetchOne(); + + // Inventory lastIventory = Beans.get(InventoryRepository.class).all() + // .filter("self.period = ?1 AND self.statusSelect = + // ?2",PeriodRepository.STATUS_CLOSED,InventoryRepository.STATUS_VALIDATED) + // .fetchOne(); + + // List inventoriedProducts = lastIventory.getInventoryLineList() + // .stream() + // .map(t -> t.getProduct()) + // .distinct() + // .collect(Collectors.toList()); + + // for (Product product : products) { + // stockMoveLineRepository.all() + // .filter("self.product = ?1 and self.stockMove.estimatedDate BETWEEN ?1 AND ?2 and + // self.stockMove.statusSelect = ?3 AND self.realQty > + // 0",product,lastPeriod.getFromDate(),lastPeriod.getToDate(),StockMoveRepository.STATUS_REALIZED) + // .order("stockMove.realDate") + // .order("id") + // .fetch(); + + // } + // } + } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveServiceSupplychainImpl.java index 9941d91..a133242 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/StockMoveServiceSupplychainImpl.java @@ -17,7 +17,17 @@ */ package com.axelor.apps.supplychain.service; +import com.axelor.apps.account.db.Account; +import com.axelor.apps.account.db.AccountManagement; +import com.axelor.apps.account.db.AnalyticAccount; +import com.axelor.apps.account.db.AnalyticJournal; +import com.axelor.apps.account.db.AnalyticMoveLine; +import com.axelor.apps.account.db.Budget; +import com.axelor.apps.account.db.repo.BudgetRepository; +import com.axelor.apps.account.service.AnalyticMoveLineService; +import com.axelor.apps.base.db.AppBudget; import com.axelor.apps.base.db.AppSupplychain; +import com.axelor.apps.base.db.repo.AppBudgetRepository; import com.axelor.apps.base.db.repo.ProductRepository; import com.axelor.apps.base.service.UnitConversionService; import com.axelor.apps.base.service.app.AppBaseService; @@ -31,6 +41,7 @@ import com.axelor.apps.sale.db.repo.SaleOrderRepository; import com.axelor.apps.stock.db.StockMove; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.TrackingNumber; +import com.axelor.apps.stock.db.repo.StockLocationRepository; import com.axelor.apps.stock.db.repo.StockMoveLineRepository; import com.axelor.apps.stock.db.repo.StockMoveRepository; import com.axelor.apps.stock.service.PartnerProductQualityRatingService; @@ -49,6 +60,7 @@ import com.google.inject.Inject; import com.google.inject.persist.Transactional; import java.lang.invoke.MethodHandles; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -168,6 +180,10 @@ public class StockMoveServiceSupplychainImpl extends StockMoveServiceImpl update.executeUpdate(); } + if (stockMove.getToStockLocation().getTypeSelect() == StockLocationRepository.TYPE_VIRTUAL) { + // this.generateAnalyticMoveLinesAndBudgetStockMoveLine(stockMove); + } + return newStockSeq; } @@ -508,4 +524,106 @@ public class StockMoveServiceSupplychainImpl extends StockMoveServiceImpl return null; } } + + @Transactional + public void generateAnalyticMoveLinesAndBudgetStockMoveLine(StockMove stockMove) + throws AxelorException { + AppBudget AppBudget = Beans.get(AppBudgetRepository.class).all().fetchOne(); + AnalyticJournal deAnalyticJournal = AppBudget.getDefaultAnalyticJournal(); + + for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) { + AnalyticAccount analyticAccount = stockMove.getAnalyticAccount(); + List accountManagementList = + stockMoveLine.getProduct().getAccountManagementList(); + if (accountManagementList == null || accountManagementList.size() == 0) { + throw new AxelorException( + stockMove.getStockMoveSeq(), + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une lignes de stock sans compte de consommation défini sur le produit"); + + } else if (accountManagementList.get(0).getConsumptionAccount() == null) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "Vous avez une lignes de stock sans compte de consommation sur le produit"); + } + String accountCode = + accountManagementList.get(0).getConsumptionAccount().getCode().substring(0, 3); + + AnalyticMoveLine analyticMoveLine = + this.generateAnalyticMoveLines( + stockMoveLine, + accountManagementList.get(0).getConsumptionAccount(), + deAnalyticJournal); + + // Budget budget = findBudgetRecursive(analyticAccount,accountCode); + // if(budget != null){ + // BudgetDistribution budgetDistribution = new BudgetDistribution(); + // + // budgetDistribution.setAmount(stockMoveLine.getCompanyUnitPriceUntaxed().multiply(stockMoveLine.getRealQty()).setScale(2, RoundingMode.HALF_EVEN)); + // budgetDistribution.setBudget(budget); + // budgetDistribution.setStockMoveLine(stockMoveLine); + // budgetDistribution.setAnalyticMoveLine(analyticMoveLine); + // + // Beans.get(BudgetSupplychainService.class).computeBudgetDistributionSumAmount(budgetDistribution, stockMove.getEstimatedDate()); + // Beans.get(BudgetDistributionRepository.class).save(budgetDistribution); + // }else{ + // throw new AxelorException( + // stockMove, + // TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + // "Aucun budget trouvé pour le compte comptable "+ accountCode + " et + // le compte analytique "+ analyticAccount.getName()); + // } + } + } + + private Budget findBudgetRecursive(AnalyticAccount account, String accountCode) { + AnalyticAccount current = account; + while (current != null) { + // capture id in a local effectively-final variable for use in the lambda + Long currentAnalyticAccountId = current.getId(); + Optional budget = + Beans.get(BudgetRepository.class) + .all() + .fetch() + .stream() + .filter( + t -> { + String accCode = t.getAccountAccountCode(); + Long analyticId = + t.getAnalyticAccount() != null ? t.getAnalyticAccount().getId() : null; + return Objects.equals(accCode, accountCode) + && Objects.equals(analyticId, currentAnalyticAccountId); + }) + .findAny(); + + if (budget.isPresent()) { + return budget.get(); // found budget, stop here + } + current = current.getParent(); // go up one level + } + return null; // no budget found in hierarchy + } + + @Transactional + public AnalyticMoveLine generateAnalyticMoveLines( + StockMoveLine stockMoveLine, Account consumptionAccount, AnalyticJournal deAnalyticJournal) + throws AxelorException { + AnalyticMoveLine aml = new AnalyticMoveLine(); + aml.setAnalyticJournal(deAnalyticJournal); + aml.setAnalyticAccount(stockMoveLine.getStockMove().getAnalyticAccount()); + aml.setAnalyticAxis(stockMoveLine.getStockMove().getAnalyticAxis()); + aml.setPercentage(BigDecimal.valueOf(100)); + aml.setAccount(consumptionAccount); + Beans.get(AnalyticMoveLineService.class) + .updateAnalyticMoveLine( + aml, + stockMoveLine + .getCompanyUnitPriceUntaxed() + .multiply(stockMoveLine.getRealQty()) + .setScale(2, RoundingMode.HALF_EVEN), + stockMoveLine.getStockMove().getEstimatedDate()); + stockMoveLine.addAnalyticMoveLineListItem(aml); + return aml; + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/invoice/InvoiceServiceSupplychainImpl.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/invoice/InvoiceServiceSupplychainImpl.java index 11d073a..62b336c 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/invoice/InvoiceServiceSupplychainImpl.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/service/invoice/InvoiceServiceSupplychainImpl.java @@ -22,6 +22,7 @@ import com.axelor.apps.account.db.InvoiceLine; import com.axelor.apps.account.db.MoveLine; import com.axelor.apps.account.db.repo.InvoiceLineRepository; import com.axelor.apps.account.db.repo.InvoiceRepository; +import com.axelor.apps.account.service.AnalyticMoveLineService; import com.axelor.apps.account.service.app.AppAccountService; import com.axelor.apps.account.service.config.AccountConfigService; import com.axelor.apps.account.service.invoice.InvoiceLineService; @@ -64,7 +65,8 @@ public class InvoiceServiceSupplychainImpl extends InvoiceServiceImpl AppAccountService appAccountService, PartnerService partnerService, InvoiceLineService invoiceLineService, - AccountConfigService accountConfigService) { + AccountConfigService accountConfigService, + AnalyticMoveLineService analyticMoveLineService) { super( validateFactory, ventilateFactory, @@ -74,7 +76,8 @@ public class InvoiceServiceSupplychainImpl extends InvoiceServiceImpl appAccountService, partnerService, invoiceLineService, - accountConfigService); + accountConfigService, + analyticMoveLineService); } @Override diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseOrderController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseOrderController.java index 24a227f..2bc908b 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseOrderController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseOrderController.java @@ -444,4 +444,28 @@ public class PurchaseOrderController { TraceBackService.trace(response, e); } } + + public void isMissingAnalyticMoveLine(ActionRequest request, ActionResponse response) + throws AxelorException { + PurchaseOrder purchaseOrder = request.getContext().asType(PurchaseOrder.class); + PurchaseOrder pr = Beans.get(PurchaseOrderRepository.class).find(purchaseOrder.getId()); + Boolean isMissingAnalyticMoveLine = + Beans.get(PurchaseOrderServiceSupplychainImpl.class).isMissingAnalyticMoveLine(pr); + if (isMissingAnalyticMoveLine) { + throw new AxelorException( + pr, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "The analytic account is missing on one of the lines."); + } + } + + + public void generateBudgetDistributionLines(ActionRequest request, ActionResponse response) throws AxelorException { + PurchaseOrder purchaseOrder = request.getContext().asType(PurchaseOrder.class); + purchaseOrder = Beans.get(PurchaseOrderRepository.class).find(purchaseOrder.getId()); + Beans.get(PurchaseOrderServiceSupplychainImpl.class) + .generateBudgetDistributionLines(purchaseOrder); + response.setAlert(I18n.get("Budget distribution lines generated successfully")); + } + } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseRequestController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseRequestController.java index a7120b6..c702fce 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseRequestController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/PurchaseRequestController.java @@ -18,7 +18,11 @@ package com.axelor.apps.supplychain.web; import com.axelor.apps.purchase.db.PurchaseRequest; +import com.axelor.apps.purchase.db.repo.PurchaseRequestRepository; import com.axelor.apps.stock.service.StockLocationService; +import com.axelor.apps.supplychain.service.PurchaseRequestServiceSupplychainImpl; +import com.axelor.exception.AxelorException; +import com.axelor.exception.db.repo.TraceBackRepository; import com.axelor.inject.Beans; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; @@ -37,4 +41,25 @@ public class PurchaseRequestController { .getDefaultReceiptStockLocation(purchaseRequest.getCompany())); } } + + public void isMissingAnalyticMoveLine(ActionRequest request, ActionResponse response) + throws AxelorException { + PurchaseRequest purchaseRequest = request.getContext().asType(PurchaseRequest.class); + PurchaseRequest pr = Beans.get(PurchaseRequestRepository.class).find(purchaseRequest.getId()); + Boolean isMissingAnalyticMoveLine = + Beans.get(PurchaseRequestServiceSupplychainImpl.class).isMissingAnalyticMoveLine(pr); + if (isMissingAnalyticMoveLine) { + throw new AxelorException( + pr, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "The analytic account is missing on one of the lines."); + } + } + + public void generateAnalyticMoveLines(ActionRequest request, ActionResponse response) { + PurchaseRequest purchaseRequest = request.getContext().asType(PurchaseRequest.class); + PurchaseRequest pr = Beans.get(PurchaseRequestRepository.class).find(purchaseRequest.getId()); + Beans.get(PurchaseRequestServiceSupplychainImpl.class).generateAnalyticMoveLines(pr); + response.setAlert("The analytic move lines have been generated."); + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SaleOrderController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SaleOrderController.java index fde3ea9..a727c98 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SaleOrderController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SaleOrderController.java @@ -861,4 +861,12 @@ public class SaleOrderController { TraceBackService.trace(response, e, ResponseMessageType.ERROR); } } + + public void generateAnalyticMoveLines(ActionRequest request, ActionResponse response) + throws AxelorException { + SaleOrder saleOrder = request.getContext().asType(SaleOrder.class); + SaleOrder so = Beans.get(SaleOrderRepository.class).find(saleOrder.getId()); + Beans.get(SaleOrderServiceSupplychainImpl.class).generateAnalyticMoveLines(so); + // response.setAlert("The analytic move lines have been generated."); + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveController.java index 52db592..1ffaf91 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveController.java @@ -19,7 +19,6 @@ package com.axelor.apps.supplychain.web; import com.axelor.apps.base.db.Company; import com.axelor.apps.sale.db.SaleOrder; -import com.axelor.apps.stock.db.StockLocation; import com.axelor.apps.stock.db.StockMove; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.repo.StockMoveRepository; @@ -27,7 +26,6 @@ import com.axelor.apps.supplychain.db.SupplyChainConfig; import com.axelor.apps.supplychain.exception.IExceptionMessage; import com.axelor.apps.supplychain.service.SaleOrderReservedQtyService; import com.axelor.apps.supplychain.service.SaleOrderStockService; -import com.axelor.apps.supplychain.service.StockConfigService; import com.axelor.apps.supplychain.service.StockMoveServiceSupplychain; import com.axelor.apps.supplychain.service.StockMoveServiceSupplychainImpl; import com.axelor.apps.supplychain.service.app.AppSupplychainService; @@ -40,13 +38,10 @@ import com.axelor.i18n.I18n; import com.axelor.inject.Beans; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; - import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Optional; - import javax.persistence.Query; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -151,7 +146,7 @@ public class StockMoveController { } } - public void checkIfQuarantine(ActionRequest request, ActionResponse response) + public void checkIfQuarantine(ActionRequest request, ActionResponse response) throws AxelorException { StockMove stockMoveFromRequest = request.getContext().asType(StockMove.class); @@ -160,12 +155,12 @@ public class StockMoveController { Query sql = JPA.em() .createNativeQuery( - "SELECT LINE.ID"+ -" FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product"+ -" where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2"); + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 2 AND LOCATION_LINE.CONFORMITY_SELECT != 5 AND LINE.STOCK_MOVE = ?2"); sql.setParameter(1, stockMove.getFromStockLocation().getId()); sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size()",sql.getResultList().size()); + logger.debug("sql.getResultList().size()", sql.getResultList().size()); if (sql.getResultList().size() > 0) { throw new AxelorException( stockMove, @@ -176,68 +171,71 @@ public class StockMoveController { public void checkIfDiffNonCompliant(ActionRequest request, ActionResponse response) throws AxelorException { - - StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); - StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID" + - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + - " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 3 AND LINE.STOCK_MOVE = ?2"); - sql.setParameter(1, stockMove.getFromStockLocation().getId()); - sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size()",sql.getResultList().size()); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "vous avez une ligne differente de NC"); + + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + Query sql = + JPA.em() + .createNativeQuery( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT != 3 AND LINE.STOCK_MOVE = ?2"); + sql.setParameter(1, stockMove.getFromStockLocation().getId()); + sql.setParameter(2, stockMove.getId()); + logger.debug("sql.getResultList().size()", sql.getResultList().size()); + if (sql.getResultList().size() > 0) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "vous avez une ligne differente de NC"); } } public void checkIfNonCompliant(ActionRequest request, ActionResponse response) throws AxelorException { - - StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); - StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID" + - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + - " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT = 3 AND LINE.STOCK_MOVE = ?2"); - sql.setParameter(1, stockMove.getFromStockLocation().getId()); - sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size()",sql.getResultList().size()); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "vous avez une ligne NC"); + + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + Query sql = + JPA.em() + .createNativeQuery( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.CONFORMITY_SELECT = 3 AND LINE.STOCK_MOVE = ?2"); + sql.setParameter(1, stockMove.getFromStockLocation().getId()); + sql.setParameter(2, stockMove.getId()); + logger.debug("sql.getResultList().size()", sql.getResultList().size()); + if (sql.getResultList().size() > 0) { + throw new AxelorException( + stockMove, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, "vous avez une ligne NC"); } } public void checkIfNonConformityTag(ActionRequest request, ActionResponse response) throws AxelorException { - - StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); - StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); - Query sql = - JPA.em() - .createNativeQuery( - "SELECT LINE.ID" + - " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + - " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = ?2"); - sql.setParameter(1, stockMove.getFromStockLocation().getId()); - sql.setParameter(2, stockMove.getId()); - logger.debug("sql.getResultList().size {}",sql.getResultList().size()); - if (sql.getResultList().size() > 0) { - throw new AxelorException( - stockMove, - TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, - "vous avez une ligne non étiqueté"); + + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + Query sql = + JPA.em() + .createNativeQuery( + "SELECT LINE.ID" + + " FROM STOCK_STOCK_MOVE_LINE LINE LEFT JOIN STOCK_STOCK_LOCATION_LINE LOCATION_LINE ON LINE.TRACKING_NUMBER = LOCATION_LINE.TRACKING_NUMBER and LINE.product = LOCATION_LINE.product" + + " where LOCATION_LINE.DETAILS_STOCK_LOCATION = ?1 AND LOCATION_LINE.is_conform_tag is not true AND LINE.STOCK_MOVE = ?2"); + sql.setParameter(1, stockMove.getFromStockLocation().getId()); + sql.setParameter(2, stockMove.getId()); + logger.debug("sql.getResultList().size {}", sql.getResultList().size()); + if (sql.getResultList().size() > 0) { + throw new AxelorException( + stockMove, + TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, + "vous avez une ligne non étiqueté"); } } + public void generateBudgetDistributionLines(ActionRequest request, ActionResponse response) + throws AxelorException { + StockMove stockMoveFromContext = request.getContext().asType(StockMove.class); + StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveFromContext.getId()); + } } diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveLineController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveLineController.java index 819e29d..b910176 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveLineController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/StockMoveLineController.java @@ -17,7 +17,8 @@ */ package com.axelor.apps.supplychain.web; -import com.axelor.apps.base.db.repo.FamilleProduitRepository; +import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.repo.CompanyRepository; import com.axelor.apps.stock.db.StockMoveLine; import com.axelor.apps.stock.db.repo.StockMoveLineRepository; import com.axelor.apps.supplychain.service.StockMoveLineServiceSupplychainImpl; @@ -32,8 +33,6 @@ import java.time.LocalDate; import java.util.List; import java.util.Map; -import org.apache.xpath.operations.Bool; - public class StockMoveLineController { public void getProductPrice(ActionRequest request, ActionResponse response) { @@ -192,35 +191,58 @@ public class StockMoveLineController { LocalDate fromDateTime = LocalDate.parse(request.getContext().get("fromDateTime").toString()); BigDecimal val = new BigDecimal(request.getContext().get("val").toString()); Boolean updatePo = false; - if(request.getContext().get("updatePo") != null){ - updatePo = (Boolean) request.getContext().get("updatePo"); + if (request.getContext().get("updatePo") != null) { + updatePo = (Boolean) request.getContext().get("updatePo"); } - Beans.get(StockMoveLineServiceSupplychainImpl.class).resetValorization(productId, stockMoveId, fromDateTime,false); - Beans.get(StockMoveLineServiceSupplychainImpl.class).fillStockMoveLines(productId, fromDateTime, stockMoveId,false); - Beans.get(StockMoveLineServiceSupplychainImpl.class).valorize(productId, stockMoveId, fromDateTime, val,updatePo,false); + Beans.get(StockMoveLineServiceSupplychainImpl.class) + .resetValorization(productId, stockMoveId, fromDateTime, false); + Beans.get(StockMoveLineServiceSupplychainImpl.class) + .fillStockMoveLines(productId, fromDateTime, stockMoveId, false); + Beans.get(StockMoveLineServiceSupplychainImpl.class) + .valorize(productId, stockMoveId, fromDateTime, val, updatePo, false); response.setNotify("Recalcul Terminé"); } catch (Exception e) { TraceBackService.trace(response, e); } } - @SuppressWarnings({"rawtypes", "unchecked"}) public void valorizeAll(ActionRequest request, ActionResponse response) { try { - - LocalDate fromDateTime = LocalDate.parse(request.getContext().get("fromDateTime").toString()); - Long productCategoryId = new Long((Integer) ((Map) request.getContext().get("productCategory")).get("id")); - Beans.get(StockMoveLineServiceSupplychainImpl.class).valorizeAll(fromDateTime,productCategoryId); - response.setNotify("Recalcul Terminé"); + // --------------------------- + // 1. Extract parameters + // --------------------------- + + Context ctx = request.getContext(); + + Long productCategoryId = null; + if (ctx.get("productCategory") != null) { + productCategoryId = Long.valueOf((Integer) ((Map) ctx.get("productCategory")).get("id")); + } + + Long productId = null; + if (ctx.get("product") != null) { + productId = Long.valueOf((Integer) ((Map) ctx.get("product")).get("id")); + } + + Long companyId = ((Company) ctx.get("company")).getId(); + Company company = Beans.get(CompanyRepository.class).find(companyId); + + // --------------------------- + // 2. Call valorization + // --------------------------- + + Beans.get(StockMoveLineServiceSupplychainImpl.class) + .valorizeCurrentPeriod(productCategoryId, company, productId); + + response.setNotify("Recalcul terminé avec succès."); + } catch (Exception e) { TraceBackService.trace(response, e); } } - - @SuppressWarnings({"rawtypes", "unchecked"}) public void showLines(ActionRequest request, ActionResponse response) { try { @@ -230,7 +252,7 @@ public class StockMoveLineController { LocalDate fromDateTime = LocalDate.parse(request.getContext().get("fromDateTime").toString()); BigDecimal val = new BigDecimal(request.getContext().get("val").toString()); Beans.get(StockMoveLineServiceSupplychainImpl.class) - .resetValorization(productId, stockMoveId, fromDateTime,false); + .resetValorization(productId, stockMoveId, fromDateTime, false); Beans.get(StockMoveLineServiceSupplychainImpl.class) .showLines(productId, stockMoveId, fromDateTime, val); response.setNotify("Recalcul Terminé"); diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SupplychainBatchController.java b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SupplychainBatchController.java index 382e3bb..e4d16c0 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SupplychainBatchController.java +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/java/com/axelor/apps/supplychain/web/SupplychainBatchController.java @@ -37,6 +37,7 @@ import com.axelor.rpc.ActionResponse; import com.google.common.base.Joiner; import com.google.inject.Singleton; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Singleton @@ -83,32 +84,50 @@ public class SupplychainBatchController { } public void massGenerationMove(ActionRequest request, ActionResponse response) { + + List ids; + try { - @SuppressWarnings("unchecked") - List ids = - (List) - (((List) request.getContext().get("_ids")) - .stream() - .filter(ObjectUtils::notEmpty) - .map(input -> Long.parseLong(input.toString())) - .collect(Collectors.toList())); - List moveList = Beans.get(AccountingCutOffServiceImpl.class).massGenerationMove(ids); + List rawIds = (List) request.getContext().get("_ids"); + + ids = + rawIds.stream() + .filter(ObjectUtils::notEmpty) + .map(id -> Long.parseLong(id.toString())) + .collect(Collectors.toList()); - if (moveList != null && !moveList.isEmpty()) { - response.setView( - ActionView.define(I18n.get(IExceptionMessage.MOVE_TEMPLATE_3)) - .model(Move.class.getName()) - .add("grid", "move-grid") - .add("form", "move-form") - .domain("self.id in (" + Joiner.on(",").join(moveList) + ")") - .map()); - } } catch (Exception e) { TraceBackService.trace(response, e); + return; + } + + Map> result = + Beans.get(AccountingCutOffServiceImpl.class).massGenerationMove(ids); + + List success = result.get("success"); + List failed = result.get("failed"); + + if (!success.isEmpty()) { + response.setView( + ActionView.define(I18n.get(IExceptionMessage.MOVE_TEMPLATE_3)) + .model(Move.class.getName()) + .add("grid", "move-grid") + .add("form", "move-form") + .domain("self.id in (" + Joiner.on(",").join(success) + ")") + .map()); + } + + if (!failed.isEmpty()) { + response.setFlash( + String.format( + "%d stock moves skipped due to configuration issues: %s", + failed.size(), + Joiner.on(", ").join(failed))); } } + public void generateInventoryLineMove(ActionRequest request, ActionResponse response) { try { diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/BudgetDistribution.xml b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/BudgetDistribution.xml index 7f75950..dc44f57 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/BudgetDistribution.xml +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/BudgetDistribution.xml @@ -10,6 +10,7 @@ + diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/Partner.xml b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/Partner.xml index 19bcd6c..c2ce3d6 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/Partner.xml +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/Partner.xml @@ -5,6 +5,8 @@ + + \ No newline at end of file diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockLocation.xml b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockLocation.xml index f82f821..8bade18 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockLocation.xml +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockLocation.xml @@ -12,6 +12,10 @@ + + + + diff --git a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockMoveLine.xml b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockMoveLine.xml index 6ff3a31..85eb1c6 100644 --- a/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockMoveLine.xml +++ b/modules/axelor-open-suite/axelor-supplychain/src/main/resources/domains/StockMoveLine.xml @@ -28,6 +28,8 @@ + + { + + private List data; + + public MockQuery(List data) { + this.data = data; + } + + public MockQuery filter(String s, Object... params) { + return this; + } + + public MockQuery order(String s) { + return this; + } + + public List fetch() { + return data; + } + + public T fetchOne() { + return data.isEmpty() ? null : data.get(0); + } +} diff --git a/modules/axelor-open-suite/axelor-supplychain/src/test/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImplTest.java b/modules/axelor-open-suite/axelor-supplychain/src/test/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImplTest.java new file mode 100644 index 0000000..2914296 --- /dev/null +++ b/modules/axelor-open-suite/axelor-supplychain/src/test/java/com/axelor/apps/supplychain/service/StockMoveLineServiceSupplychainImplTest.java @@ -0,0 +1,276 @@ +package com.axelor.apps.supplychain.service; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.Product; +import com.axelor.apps.base.service.PriceListService; +import com.axelor.apps.base.service.UnitConversionService; +import com.axelor.apps.base.service.app.AppBaseService; +import com.axelor.apps.base.service.tax.AccountManagementService; +import com.axelor.apps.purchase.db.repo.PurchaseOrderLineRepository; +import com.axelor.apps.purchase.service.PurchaseOrderLineService; +import com.axelor.apps.stock.db.StockLocation; +import com.axelor.apps.stock.db.StockMove; +import com.axelor.apps.stock.db.StockMoveLine; +import com.axelor.apps.stock.db.repo.StockMoveLineRepository; +import com.axelor.apps.stock.db.repo.TrackingNumberRepository; +import com.axelor.apps.stock.service.StockLocationLineService; +import com.axelor.apps.stock.service.StockMoveToolService; +import com.axelor.apps.stock.service.TrackingNumberService; +import com.axelor.apps.stock.service.WeightedAveragePriceService; +import com.axelor.db.Query; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class StockMoveLineServiceSupplychainImplTest { + + @Mock private StockMoveLineRepository stockMoveLineRepo; + @Mock private StockLocationLineService stockLocationLineService; + @Mock private AppBaseService appBaseService; + + // Additional required dependencies + @Mock private TrackingNumberService trackingNumberService; + @Mock private StockMoveToolService stockMoveToolService; + @Mock private UnitConversionService unitConversionService; + @Mock private WeightedAveragePriceService weightedAveragePriceService; + @Mock private TrackingNumberRepository trackingNumberRepo; + @Mock private AccountManagementService accountManagementService; + @Mock private PurchaseOrderLineRepository purchaseOrderLineRepo; + @Mock private PurchaseOrderLineService purchaseOrderLineService; + @Mock private PriceListService priceListService; + + private StockMoveLineServiceSupplychainImpl service; + private MockedStatic beansMock; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + when(appBaseService.getNbDecimalDigitForUnitPrice()).thenReturn(2); + + beansMock = mockStatic(Beans.class); + beansMock + .when(() -> Beans.get(StockLocationLineService.class)) + .thenReturn(stockLocationLineService); + beansMock.when(() -> Beans.get(StockMoveLineRepository.class)).thenReturn(stockMoveLineRepo); + + service = + new StockMoveLineServiceSupplychainImpl( + trackingNumberService, + appBaseService, + null, + stockMoveToolService, + stockMoveLineRepo, + stockLocationLineService, + unitConversionService, + weightedAveragePriceService, + trackingNumberRepo, + accountManagementService, + purchaseOrderLineRepo, + purchaseOrderLineService, + priceListService); + } + + // Helper to invoke private method computeWap() + private BigDecimal callComputeWap( + BigDecimal oldQty, + BigDecimal oldWap, + BigDecimal incomingQty, + BigDecimal incomingPrice, + int digits) + throws Exception { + + Method m = + StockMoveLineServiceSupplychainImpl.class.getDeclaredMethod( + "computeWap", + BigDecimal.class, + BigDecimal.class, + BigDecimal.class, + BigDecimal.class, + int.class); + + m.setAccessible(true); + return (BigDecimal) m.invoke(service, oldQty, oldWap, incomingQty, incomingPrice, digits); + } + + // Helper to invoke private boolean methods + private boolean callBooleanMethod(String methodName, StockMoveLine line) throws Exception { + Method m = + StockMoveLineServiceSupplychainImpl.class.getDeclaredMethod( + methodName, StockMoveLine.class); + m.setAccessible(true); + return (boolean) m.invoke(service, line); + } + + // ------------------------------------------------------------ + @Test + public void testComputeWapIncoming() throws Exception { + BigDecimal wap = + callComputeWap( + new BigDecimal("10"), + new BigDecimal("20"), + new BigDecimal("5"), + new BigDecimal("30"), + 2); + + assertEquals("23.33", wap.toString()); + } + + // ------------------------------------------------------------ + @Test + public void testOutgoingUsesExistingWap() throws Exception { + StockMove move = new StockMove(); + + StockLocation from = new StockLocation(); + from.setExcludeValorisation(false); + StockLocation to = new StockLocation(); + to.setTypeSelect(2); // virtual → outgoing + move.setFromStockLocation(from); + move.setToStockLocation(to); + + StockMoveLine line = new StockMoveLine(); + line.setStockMove(move); + + boolean outgoing = callBooleanMethod("isOutgoing", line); + + assertTrue(outgoing); + } + + // ------------------------------------------------------------ + @Test + public void testIncomingFromNc1() throws Exception { + StockMove move = new StockMove(); + + StockLocation from = new StockLocation(); + from.setExcludeValorisation(true); + StockLocation to = new StockLocation(); + to.setExcludeValorisation(false); + move.setFromStockLocation(from); + move.setToStockLocation(to); + + StockMoveLine line = new StockMoveLine(); + line.setStockMove(move); + + assertTrue(callBooleanMethod("isIncoming", line)); + assertFalse(callBooleanMethod("isOutgoing", line)); + } + + // ------------------------------------------------------------ + @Test + public void testOutgoingToNc1() throws Exception { + StockMove move = new StockMove(); + + StockLocation from = new StockLocation(); + from.setExcludeValorisation(false); + StockLocation to = new StockLocation(); + to.setExcludeValorisation(true); + move.setFromStockLocation(from); + move.setToStockLocation(to); + + StockMoveLine line = new StockMoveLine(); + line.setStockMove(move); + + assertTrue(callBooleanMethod("isOutgoing", line)); + assertFalse(callBooleanMethod("isIncoming", line)); + } + + // ------------------------------------------------------------ + @Test + public void testInitialWapFromLastMovement() throws Exception { + Product p = new Product(); + p.setId(10L); + + Company company = new Company(); + + LocalDate start = LocalDate.of(2024, 1, 1); + + StockMoveLine last = new StockMoveLine(); + last.setWapPrice(new BigDecimal("55")); + + // Mock Query + Query query = mock(Query.class); + when(stockMoveLineRepo.all()).thenReturn(query); + when(query.fetch()).thenReturn(Arrays.asList(last)); + + Method getInitial = + StockMoveLineServiceSupplychainImpl.class.getDeclaredMethod( + "getInitialStateForPeriod", Product.class, LocalDate.class, Company.class); + getInitial.setAccessible(true); + + Object state = getInitial.invoke(service, p, start, company); + + // Access private field 'wap' + BigDecimal wap = (BigDecimal) state.getClass().getField("wap").get(state); + + assertEquals("55", wap.toString()); + } + + // ------------------------------------------------------------ + @Test + public void testPeriodSequence() throws Exception { + StockMoveLine in1 = + mockMovement(false, false, false, false, new BigDecimal("10"), new BigDecimal("20")); + + StockMoveLine out = mockMovement(false, false, false, true, new BigDecimal("4"), null); + + StockMoveLine in2 = + mockMovement(false, false, false, false, new BigDecimal("6"), new BigDecimal("30")); + + Query q = mock(Query.class); + when(stockMoveLineRepo.all()).thenReturn(q); + when(q.fetch()).thenReturn(Arrays.asList(in1, out, in2)); + + BigDecimal wap1 = + callComputeWap( + BigDecimal.ZERO, BigDecimal.ZERO, new BigDecimal("10"), new BigDecimal("20"), 2); + + assertEquals("20.00", wap1.toString()); + + BigDecimal wap2 = + callComputeWap( + new BigDecimal("6"), + new BigDecimal("20"), + new BigDecimal("6"), + new BigDecimal("30"), + 2); + + assertEquals("25.00", wap2.toString()); + } + + private StockMoveLine mockMovement( + boolean fromNc1, + boolean toNc1, + boolean incomingVirtual, + boolean outgoingVirtual, + BigDecimal qty, + BigDecimal price) { + + StockMove move = new StockMove(); + + StockLocation from = new StockLocation(); + from.setExcludeValorisation(fromNc1); + from.setTypeSelect(incomingVirtual ? 2 : 0); + + StockLocation to = new StockLocation(); + to.setExcludeValorisation(toNc1); + to.setTypeSelect(outgoingVirtual ? 2 : 0); + + move.setFromStockLocation(from); + move.setToStockLocation(to); + + StockMoveLine line = new StockMoveLine(); + line.setStockMove(move); + line.setRealQty(qty); + if (price != null) line.setUnitPriceUntaxed(price); + + return line; + } +}