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:
@@ -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<Long> massGenerationMove(List<Long> ids) throws AxelorException {
|
||||
public Map<String, List<Long>> massGenerationMove(List<Long> ids) {
|
||||
|
||||
List<Long> movesId = new ArrayList<>();
|
||||
List<Long> successMoves = new ArrayList<>();
|
||||
List<Long> 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<String, List<Long>> result = new HashMap<>();
|
||||
result.put("success", successMoves);
|
||||
result.put("failed", failedStockMoves);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public boolean isDebit(StockMove stockMove) throws AxelorException {
|
||||
boolean isDebit;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("*******************************************************");
|
||||
|
||||
@@ -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<AnalyticMoveLine> analyticMoveLines = purchaseRequestLine.getAnalyticMoveLineList();
|
||||
List<AccountManagement> 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<AnalyticMoveLine> 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<BudgetDistribution> 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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<BudgetDistribution> 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<PurchaseOrderLine> 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<PurchaseOrderLine> prLines = purchaseOrder.getPurchaseOrderLineList();
|
||||
for (PurchaseOrderLine pOrderLine : prLines) {
|
||||
List<AccountManagement> accountManagementList = pOrderLine.getProduct().getAccountManagementList();
|
||||
String accountCode = accountManagementList.get(0).getPurchaseAccount().getCode().substring(0, 2);
|
||||
List<AnalyticMoveLine> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AnalyticMoveLine> 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<Long> 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 =
|
||||
|
||||
@@ -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<AnalyticMoveLine> 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<AnalyticMoveLine> 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<PurchaseRequestLine> 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<PurchaseRequestLine> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Long> 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;
|
||||
}
|
||||
|
||||
@@ -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<SaleOrderLine> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<AnalyticMoveLine> 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<AnalyticMoveLine> 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<StockMoveLine> 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<StockMoveLine> 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<InventoryLine> 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<StockMoveLine> allLines =
|
||||
getStockMoveLines(product, toDateTime, currentMove, excludeValorisation);
|
||||
|
||||
// Slice movements from the current one forward
|
||||
List<StockMoveLine> 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<StockLocationLine> 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<Product> 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<StockMoveLine> 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<Product> 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<Product> 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();
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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<AccountManagement> 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> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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é");
|
||||
|
||||
@@ -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<Long> ids;
|
||||
|
||||
try {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> ids =
|
||||
(List)
|
||||
(((List) request.getContext().get("_ids"))
|
||||
.stream()
|
||||
.filter(ObjectUtils::notEmpty)
|
||||
.map(input -> Long.parseLong(input.toString()))
|
||||
.collect(Collectors.toList()));
|
||||
List<Long> 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<String, List<Long>> result =
|
||||
Beans.get(AccountingCutOffServiceImpl.class).massGenerationMove(ids);
|
||||
|
||||
List<Long> success = result.get("success");
|
||||
List<Long> 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 {
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<many-to-one name="purchaseOrderLine" ref="com.axelor.apps.purchase.db.PurchaseOrderLine"/>
|
||||
<many-to-one name="purchaseRequestLine" ref="com.axelor.apps.purchase.db.PurchaseRequestLine"/>
|
||||
<many-to-one name="stockMoveLine" ref="com.axelor.apps.stock.db.StockMoveLine"/>
|
||||
<many-to-one name="saleOrderLine" ref="com.axelor.apps.sale.db.SaleOrderLine"/>
|
||||
<decimal name="budgetAmountAvailable"/>
|
||||
<string name="description" title="Description"/>
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
<entity name="Partner" lang="java">
|
||||
<one-to-many name="supplierRatingList" title="Supplier Rating List" ref="com.axelor.apps.supplychain.db.SupplierRating" mappedBy="supplierPartner"/>
|
||||
<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" />
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@@ -12,6 +12,10 @@
|
||||
<boolean name="usableOnSaleOrder" title="Usable on sale order" />
|
||||
<boolean name="isNotInCalculStock" title="Don't take in consideration for the stock calcul" />
|
||||
<boolean name="isNotInMrp" title="Don't take in consideration on MRP" />
|
||||
<boolean name="usableOnImmobilisation" title="Usable on immobilisation" />
|
||||
<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" />
|
||||
|
||||
|
||||
</entity>
|
||||
</domain-models>
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<many-to-one name="analyticAccount" ref="com.axelor.apps.account.db.AnalyticAccount" title="Analytic Acc."/>
|
||||
<many-to-one name="analyticAxis" ref="com.axelor.apps.account.db.AnalyticAxis" title="Analytic axis" />
|
||||
|
||||
<decimal name="budgetDistributionSumAmount" title="Total amount attributed"/>
|
||||
<one-to-many name="budgetDistributionList" ref="com.axelor.apps.account.db.BudgetDistribution" title="Budget Distribution" mappedBy="stockMoveLine"/>
|
||||
|
||||
<finder-method name="findAllBySaleOrder"
|
||||
using="com.axelor.apps.sale.db.SaleOrder:saleOrder" all="true"
|
||||
|
||||
@@ -2,25 +2,14 @@ package com.axelor.apps.supplychain;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.axelor.app.AxelorModule;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.module.AdminModule;
|
||||
import com.axelor.apps.base.module.BaseModule;
|
||||
import com.axelor.apps.base.service.ProductService;
|
||||
import com.axelor.apps.base.service.ProductServiceImpl;
|
||||
import com.axelor.apps.base.service.user.UserService;
|
||||
import com.axelor.apps.message.module.MessageModule;
|
||||
import com.axelor.apps.stock.module.StockModule;
|
||||
import com.axelor.apps.stock.service.StockMoveService;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
import com.axelor.apps.supplychain.TestStockMove.MyModule;
|
||||
import com.axelor.apps.tool.module.ToolModule;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.JpaModule;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
@@ -29,22 +18,21 @@ import com.axelor.test.GuiceModules;
|
||||
import com.axelor.test.GuiceRunner;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import com.axelor.apps.stock.db.StockMove;
|
||||
import com.axelor.apps.stock.db.repo.StockMoveRepository;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@RunWith(GuiceRunner.class)
|
||||
@GuiceModules({MyModule.class})
|
||||
public class TestStockMove {
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
static ProductRepository productRepository;
|
||||
|
||||
static ProductRepository productRepository ;
|
||||
|
||||
@Inject
|
||||
public StockMoveRepository stockMoveRepository;
|
||||
@Inject public StockMoveRepository stockMoveRepository;
|
||||
|
||||
public static class MyModule extends AxelorModule {
|
||||
|
||||
@@ -61,15 +49,10 @@ protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
}
|
||||
public static void setUpBeforeClass() throws Exception {}
|
||||
|
||||
@Test
|
||||
public void testFunc() throws AxelorException {
|
||||
assertNotNull(productRepository.all().fetch().get(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.axelor.apps.supplychain.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MockQuery<T> {
|
||||
|
||||
private List<T> data;
|
||||
|
||||
public MockQuery(List<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public MockQuery<T> filter(String s, Object... params) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockQuery<T> order(String s) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<T> fetch() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public T fetchOne() {
|
||||
return data.isEmpty() ? null : data.get(0);
|
||||
}
|
||||
}
|
||||
@@ -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<Beans> 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<StockMoveLine> 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<StockMoveLine> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user