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.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Invoice> invoiceList,
|
||||
Company company,
|
||||
@@ -1016,4 +1028,55 @@ public class InvoiceServiceImpl extends InvoiceRepository implements InvoiceServ
|
||||
.getResultList()
|
||||
.size();
|
||||
}
|
||||
|
||||
public List<String> checkInconsistentAnalyticDistribution(Invoice invoice)
|
||||
throws AxelorException {
|
||||
List<InvoiceLine> invoiceLines = invoice.getInvoiceLineList();
|
||||
List<String> 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<InvoiceLine> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<List<Object>, MoveLine> map, MoveLine moveLine, List<Object> keys) {
|
||||
if (map != null && !map.isEmpty()) {
|
||||
Map<MoveLineKey, MoveLine> map, MoveLine moveLine, MoveLineKey keys) {
|
||||
|
||||
Map<List<Object>, MoveLine> copyMap = new HashMap<List<Object>, 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<AnalyticMoveLine> list1 = moveLineIt.getAnalyticMoveLineList();
|
||||
List<AnalyticMoveLine> list2 = moveLine.getAnalyticMoveLineList();
|
||||
List<AnalyticMoveLine> copyList = new ArrayList<AnalyticMoveLine>(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<AnalyticMoveLine> list1 = existing.getAnalyticMoveLineList();
|
||||
List<AnalyticMoveLine> 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<String> set1 = new HashSet<>(list1.size());
|
||||
Set<String> 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<MoveLine> consolidateMoveLines(List<MoveLine> moveLines) {
|
||||
|
||||
Map<List<Object>, MoveLine> map = new HashMap<List<Object>, MoveLine>();
|
||||
Map<MoveLineKey, MoveLine> map = new HashMap<>();
|
||||
MoveLine consolidateMoveLine = null;
|
||||
|
||||
for (MoveLine moveLine : moveLines) {
|
||||
|
||||
List<Object> keys = new ArrayList<Object>();
|
||||
|
||||
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(
|
||||
|
||||
@@ -572,7 +572,7 @@ public class PaymentVoucherConfirmService {
|
||||
paymentDate,
|
||||
paymentMode,
|
||||
MoveRepository.TECHNICAL_ORIGIN_AUTOMATIC);
|
||||
|
||||
|
||||
move.setPaymentVoucher(paymentVoucher);
|
||||
|
||||
List<? extends PayVoucherElementToPay> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, BigDecimal> map = Beans.get(CashInventoryService.class).computeToTals(cashInventory);
|
||||
if (cashInventory.getCashInventoryLines() != null
|
||||
&& cashInventory.getCashInventoryLines().size() != 0) {
|
||||
Map<String, BigDecimal> 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<PaymentVoucher> cashVouchers = Beans.get(PaymentVoucherRepository.class)
|
||||
.all()
|
||||
.filter("self.operationTypeSelect = 9 and self.cashStatusSelect = 3")
|
||||
.fetch();
|
||||
List<PaymentVoucher> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
]]>
|
||||
</string>
|
||||
|
||||
<boolean name="isActive" title="Is active" default="true"/>
|
||||
|
||||
<unique-constraint columns="code,name,analyticAxis,parent" />
|
||||
</entity>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
]]></extra-code>
|
||||
|
||||
|
||||
@@ -10,10 +10,15 @@
|
||||
<boolean name="manageMultiBudget" title="Manage multi budgets on lines"/>
|
||||
<boolean name="budgetRequiredOnPO" title="Budget required on purchase order lines"/>
|
||||
|
||||
<many-to-one ref="com.axelor.apps.account.db.AnalyticJournal" name="defaultAnalyticJournal" />
|
||||
<many-to-one ref="com.axelor.apps.account.db.AnalyticJournal" name="defaultPurchaseAnalyticJournal" />
|
||||
<many-to-one ref="com.axelor.apps.account.db.AnalyticJournal" name="defaultSaleAnalyticJournal" />
|
||||
|
||||
<track>
|
||||
<field name="checkAvailableBudget" on="UPDATE"/>
|
||||
<field name="manageMultiBudget" on="UPDATE"/>
|
||||
<field name="budgetRequiredOnPO" on="UPDATE"/>
|
||||
<field name="defaultAnalyticJournal" on="UPDATE"/>
|
||||
</track>
|
||||
</entity>
|
||||
</domain-models>
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
<decimal name="amountForGeneration" title="Amount for each line"/>
|
||||
<boolean name="checkAvailableBudget" title="Check available budget"/>
|
||||
<many-to-one name="analyticAccount" ref="com.axelor.apps.account.db.AnalyticAccount" title="Analytic account"/>
|
||||
|
||||
<string name="accountAccountCode" title="Account code"/>
|
||||
<boolean name="isGrouped" title="Is grouped"/>
|
||||
<boolean name="isActive" title="Is active"/>
|
||||
|
||||
<finder-method name="findByAnalyticAccount" using="analyticAccount"/>
|
||||
<finder-method name="findByAnalyticAccountAndAccountCode" using="analyticAccount,accountAccountCode,isActive"/>
|
||||
|
||||
<extra-code><![CDATA[
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
|
||||
|
||||
<module name="base" package="com.axelor.apps.base.db"/>
|
||||
|
||||
<entity name="FamilleProduit">
|
||||
|
||||
<many-to-one name="purchaseAccount" ref="com.axelor.apps.account.db.Account" title="Purchase account"/>
|
||||
<many-to-one name="saleAccount" ref="com.axelor.apps.account.db.Account" title="Sale account"/>
|
||||
<many-to-one name="stockAccount" ref="com.axelor.apps.account.db.Account" title="Stock account"/>
|
||||
<many-to-one name="consumptionAccount" ref="com.axelor.apps.account.db.Account" title="Consumption account"/>
|
||||
<many-to-one name="purchFixedAssetsAccount" ref="com.axelor.apps.account.db.Account" title="Account of purchase fixed assets"/>
|
||||
<many-to-one name="purchaseTax" ref="com.axelor.apps.account.db.Tax" title="Purchase Tax"/>
|
||||
<many-to-one name="saleTax" ref="com.axelor.apps.account.db.Tax" title="Sale Tax"/>
|
||||
<many-to-one name="tax" ref="com.axelor.apps.account.db.Tax" title="Tax"/>
|
||||
|
||||
<track>
|
||||
<field name="purchaseAccount"/>
|
||||
<field name="saleAccount"/>
|
||||
<field name="stockAccount"/>
|
||||
<field name="consumptionAccount"/>
|
||||
<field name="purchFixedAssetsAccount"/>
|
||||
<field name="purchaseTax"/>
|
||||
<field name="saleTax"/>
|
||||
<field name="tax"/>
|
||||
</track>
|
||||
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@@ -155,6 +155,10 @@
|
||||
<many-to-one name="deliveryPartner" ref="com.axelor.apps.base.db.Partner" title="Delivery Supplier"/>
|
||||
|
||||
|
||||
<many-to-one name="analyticJournal" ref="com.axelor.apps.account.db.AnalyticJournal" title="Analytic journal"/>
|
||||
<many-to-one name="analyticAccount" ref="com.axelor.apps.account.db.AnalyticAccount" title="Analytic account"/>
|
||||
<many-to-one name="analyticAxis" ref="com.axelor.apps.account.db.AnalyticAxis" title="Analytic axis" />
|
||||
|
||||
<unique-constraint columns="invoiceId,company"/>
|
||||
|
||||
<extra-code><![CDATA[
|
||||
|
||||
@@ -1,80 +1,76 @@
|
||||
package com.axelor.apps.account.web;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.axelor.apps.account.db.CashInventory;
|
||||
import com.axelor.apps.base.service.ConvertNumberToFrenchWordsService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import java.math.BigDecimal;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
public class CashInventoryControllerTest {
|
||||
|
||||
private CashInventoryController controller;
|
||||
private ActionRequest request;
|
||||
private ActionResponse response;
|
||||
private CashInventory cashInventory;
|
||||
private ConvertNumberToFrenchWordsService convertService;
|
||||
private CashInventoryController controller;
|
||||
private ActionRequest request;
|
||||
private ActionResponse response;
|
||||
private CashInventory cashInventory;
|
||||
private ConvertNumberToFrenchWordsService convertService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
request = mock(ActionRequest.class);
|
||||
response = mock(ActionResponse.class);
|
||||
cashInventory = mock(CashInventory.class);
|
||||
convertService = mock(ConvertNumberToFrenchWordsService.class);
|
||||
@Before
|
||||
public void setUp() {
|
||||
request = mock(ActionRequest.class);
|
||||
response = mock(ActionResponse.class);
|
||||
cashInventory = mock(CashInventory.class);
|
||||
convertService = mock(ConvertNumberToFrenchWordsService.class);
|
||||
|
||||
controller = new CashInventoryController(convertService); // ✅ No Guice needed
|
||||
controller = new CashInventoryController(convertService); // ✅ No Guice needed
|
||||
|
||||
// Mock request.getContext().asType(CashInventory.class)
|
||||
com.axelor.rpc.Context context = mock(com.axelor.rpc.Context.class);
|
||||
when(request.getContext()).thenReturn(context);
|
||||
when(context.asType(CashInventory.class)).thenReturn(cashInventory);
|
||||
}
|
||||
// Mock request.getContext().asType(CashInventory.class)
|
||||
com.axelor.rpc.Context context = mock(com.axelor.rpc.Context.class);
|
||||
when(request.getContext()).thenReturn(context);
|
||||
when(context.asType(CashInventory.class)).thenReturn(cashInventory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvert_withDecimalSolde() {
|
||||
// Given
|
||||
when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("1234.56"));
|
||||
when(convertService.convert(1234L)).thenReturn("mille deux cent trente-quatre");
|
||||
when(convertService.convert(56L)).thenReturn("cinquante-six");
|
||||
@Test
|
||||
public void testConvert_withDecimalSolde() {
|
||||
// Given
|
||||
when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("1234.56"));
|
||||
when(convertService.convert(1234L)).thenReturn("mille deux cent trente-quatre");
|
||||
when(convertService.convert(56L)).thenReturn("cinquante-six");
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testConvert_withIntegerSolde() {
|
||||
// Given
|
||||
when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("1000.00"));
|
||||
when(convertService.convert(1000L)).thenReturn("mille");
|
||||
when(convertService.convert(0L)).thenReturn("zéro");
|
||||
|
||||
@Test
|
||||
public void testConvert_withIntegerSolde() {
|
||||
// Given
|
||||
when(cashInventory.getTheoricalSolde()).thenReturn(new BigDecimal("1000.00"));
|
||||
when(convertService.convert(1000L)).thenReturn("mille");
|
||||
when(convertService.convert(0L)).thenReturn("zéro");
|
||||
// Then
|
||||
ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> 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<String> keyCaptor = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> 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<String> keyCaptor = ArgumentCaptor.forClass(String.class);
|
||||
// ArgumentCaptor<String> 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());
|
||||
// }
|
||||
}
|
||||
// // Then
|
||||
// ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class);
|
||||
// ArgumentCaptor<String> 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());
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user