2 Commits

Author SHA1 Message Date
Kheireddine Mehdi
936c00cda4 fix(crm): prevent infinite loop when StateAdministration parent is itself 2026-03-10 09:20:00 +01:00
Kheireddine Mehdi
55e9f4455c feat: add StateAdministration entity and extend Address with state relation
- Create StateAdministration entity with hierarchical parent relation
- Add typeSelect selection (Region, Sub Region, Wilaya, Commune)
- Implement computed fullName for hierarchical display
- Extend Address entity with many-to-one relation to StateAdministration
2026-03-03 12:03:54 +01:00
7 changed files with 128 additions and 192 deletions

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db" />
<entity name="Address" lang="java">
<many-to-one name="state" ref="com.axelor.apps.crm.db.StateAdministration" title="State" />
</entity>
</domain-models>

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="crm" package="com.axelor.apps.crm.db"/>
<entity name="Facility" lang="java">
<string name="name" title="Name"/>
<one-to-many name="partnerList" title="Partners" ref="com.axelor.apps.base.db.Partner" mappedBy="facility"/>
<many-to-one name="facilityType" title="Facility Type" ref="com.axelor.apps.crm.db.FacilityType"/>
<string name="description" title="Description"/>
<track>
<field name="name"/>
<field name="description"/>
</track>
</entity>
</domain-models>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="crm" package="com.axelor.apps.crm.db"/>
<entity name="FacilityType" lang="java">
<string name="facilityTypeId" title="Id"/>
<string name="name" title="Name"/>
<one-to-many name="facilityList" title="Facility Type" ref="com.axelor.apps.crm.db.Facility" mappedBy="facilityType"/>
<string name="description" title="Description"/>
<track>
<field name="facilityTypeId"/>
<field name="name"/>
<field name="description"/>
</track>
</entity>
</domain-models>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="Partner" lang="java">
<!-- ICOP fields -->
<string name="accountNo" title="Account Number"/>
<integer name="speciality" title="Speciality" selection="crm.partner.speciality.selection" />
<integer name="function" title="Function" selection="crm.partner.function.selection" />
<integer name="structure" title="Structure" selection="crm.partner.structure.selection" />
<integer name="rating" title="Rating" selection="crm.partner.potentiality.selection" />
<many-to-one name="facility" title="Facility" ref="com.axelor.apps.crm.db.Facility"/>
<!--<many-to-many name="stateAdministrationList" title="State Administration" ref="com.axelor.apps.crm.db.StateAdministration"/>-->
<track>
<field name="accountNo"/>
<field name="speciality"/>
<field name="function"/>
<field name="structure"/>
<field name="rating"/>
</track>
</entity>
</domain-models>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="crm" package="com.axelor.apps.crm.db"/>
<entity name="StateAdministration" lang="java">
<string name="name" title="Name" required="true"/>
<string name="typeSelect" title="Type" selection="state.administration.type.select" required="true"/>
<many-to-one name="parent" ref="com.axelor.apps.crm.db.StateAdministration"/>
<string name="fullName" namecolumn="true" title="Full name">
<![CDATA[
String fullName = "";
com.axelor.apps.crm.db.StateAdministration current = this;
while (current != null && current.getName() != null) {
if (current == current.getParent()) {
break;
}
if (fullName.isEmpty()) {
fullName = current.getName();
} else {
fullName = current.getName() + " - " + fullName;
}
current = current.getParent();
}
return fullName;
]]>
</string>
</entity>
</domain-models>

View File

@@ -150,54 +150,4 @@
<option value="3">Commune</option> <option value="3">Commune</option>
</selection> </selection>
<selection name="crm.partner.function.selection">
<option value="1">doctor</option>
<option value="2">wholesaler</option>
<option value="3">pharmacy</option>
<option value="4">PCH/HOSPITAL</option>
</selection>
<selection name="crm.partner.orientation.selection">
<option value="1">CHU</option>
<option value="2">EPH</option>
<option value="3">EHS</option>
<option value="4">polyclinique</option>
<option value="5">treatment room</option>
<option value="6">office</option>
</selection>
<selection name="crm.partner.potentiality.selection">
<option value="1">A+</option>
<option value="2">B+</option>
<option value="3">C+</option>
<option value="4">D+</option>
<option value="5">E+</option>
<option value="6">A-</option>
<option value="7">B-</option>
<option value="8">C-</option>
<option value="9">D-</option>
<option value="10">E-</option>
</selection>
<selection name="crm.partner.region.selection">
<option value="1">center</option>
<option value="2">east</option>
<option value="3">west</option>
</selection>
<selection name="crm.partner.state.selection">
<option value="1">actif</option>
<option value="2">lapsed</option>
<option value="3">disbarred</option>
</selection>
<selection name="crm.partner.structure.selection">
<option value="1">public</option>
<option value="2">private</option>
</selection>
<selection name="crm.partner.sub.speciality.selection">
<option value="1">allergist</option>
</selection>
</object-views> </object-views>

View File

@@ -93,7 +93,7 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
@Override @Override
@Transactional(rollbackOn = { Exception.class }) @Transactional(rollbackOn = {Exception.class})
public Invoice createInvoice( public Invoice createInvoice(
StockMove stockMove, StockMove stockMove,
Integer operationSelect, Integer operationSelect,
@@ -130,8 +130,9 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
if (StockMoveRepository.ORIGIN_SALE_ORDER.equals(stockMove.getOriginTypeSelect())) { if (StockMoveRepository.ORIGIN_SALE_ORDER.equals(stockMove.getOriginTypeSelect())) {
invoice = createInvoiceFromSaleOrder(stockMove, saleOrderRepo.find(origin), qtyToInvoiceMap); invoice = createInvoiceFromSaleOrder(stockMove, saleOrderRepo.find(origin), qtyToInvoiceMap);
} else if (StockMoveRepository.ORIGIN_PURCHASE_ORDER.equals(stockMove.getOriginTypeSelect())) { } else if (StockMoveRepository.ORIGIN_PURCHASE_ORDER.equals(stockMove.getOriginTypeSelect())) {
invoice = createInvoiceFromPurchaseOrder( invoice =
stockMove, purchaseOrderRepo.find(origin), qtyToInvoiceMap); createInvoiceFromPurchaseOrder(
stockMove, purchaseOrderRepo.find(origin), qtyToInvoiceMap);
} else { } else {
invoice = createInvoiceFromOrderlessStockMove(stockMove, qtyToInvoiceMap); invoice = createInvoiceFromOrderlessStockMove(stockMove, qtyToInvoiceMap);
} }
@@ -146,9 +147,10 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
for (StockMoveLine subLine : stockMoveLine.getSubLineList()) { for (StockMoveLine subLine : stockMoveLine.getSubLineList()) {
BigDecimal qty = BigDecimal.ZERO; BigDecimal qty = BigDecimal.ZERO;
if (stockMoveLine.getQty().compareTo(BigDecimal.ZERO) != 0) { if (stockMoveLine.getQty().compareTo(BigDecimal.ZERO) != 0) {
qty = qtyToInvoiceItem qty =
.multiply(subLine.getQty()) qtyToInvoiceItem
.divide(stockMoveLine.getQty(), 2, RoundingMode.HALF_EVEN); .multiply(subLine.getQty())
.divide(stockMoveLine.getQty(), 2, RoundingMode.HALF_EVEN);
} }
qty = qty.setScale(2, RoundingMode.HALF_EVEN); qty = qty.setScale(2, RoundingMode.HALF_EVEN);
qtyToInvoiceMap.put(subLine.getId(), qty); qtyToInvoiceMap.put(subLine.getId(), qty);
@@ -157,13 +159,13 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
@Override @Override
@Transactional(rollbackOn = { Exception.class }) @Transactional(rollbackOn = {Exception.class})
public Invoice createInvoiceFromSaleOrder( public Invoice createInvoiceFromSaleOrder(
StockMove stockMove, SaleOrder saleOrder, Map<Long, BigDecimal> qtyToInvoiceMap) StockMove stockMove, SaleOrder saleOrder, Map<Long, BigDecimal> qtyToInvoiceMap)
throws AxelorException { throws AxelorException {
InvoiceGenerator invoiceGenerator = saleOrderInvoiceService.createInvoiceGenerator(saleOrder, InvoiceGenerator invoiceGenerator =
stockMove.getIsReversion()); saleOrderInvoiceService.createInvoiceGenerator(saleOrder, stockMove.getIsReversion());
Invoice invoice = invoiceGenerator.generate(); Invoice invoice = invoiceGenerator.generate();
@@ -208,13 +210,14 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
@Override @Override
@Transactional(rollbackOn = { Exception.class }) @Transactional(rollbackOn = {Exception.class})
public Invoice createInvoiceFromPurchaseOrder( public Invoice createInvoiceFromPurchaseOrder(
StockMove stockMove, PurchaseOrder purchaseOrder, Map<Long, BigDecimal> qtyToInvoiceMap) StockMove stockMove, PurchaseOrder purchaseOrder, Map<Long, BigDecimal> qtyToInvoiceMap)
throws AxelorException { throws AxelorException {
InvoiceGenerator invoiceGenerator = purchaseOrderInvoiceService.createInvoiceGenerator( InvoiceGenerator invoiceGenerator =
purchaseOrder, stockMove.getIsReversion()); purchaseOrderInvoiceService.createInvoiceGenerator(
purchaseOrder, stockMove.getIsReversion());
Invoice invoice = invoiceGenerator.generate(); Invoice invoice = invoiceGenerator.generate();
@@ -246,7 +249,7 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
@Override @Override
@Transactional(rollbackOn = { Exception.class }) @Transactional(rollbackOn = {Exception.class})
public Invoice createInvoiceFromOrderlessStockMove( public Invoice createInvoiceFromOrderlessStockMove(
StockMove stockMove, Map<Long, BigDecimal> qtyToInvoiceMap) throws AxelorException { StockMove stockMove, Map<Long, BigDecimal> qtyToInvoiceMap) throws AxelorException {
@@ -271,14 +274,15 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
} }
InvoiceGenerator invoiceGenerator = new InvoiceGeneratorSupplyChain(stockMove, invoiceOperationType) { InvoiceGenerator invoiceGenerator =
new InvoiceGeneratorSupplyChain(stockMove, invoiceOperationType) {
@Override @Override
public Invoice generate() throws AxelorException { public Invoice generate() throws AxelorException {
return super.createInvoiceHeader(); return super.createInvoiceHeader();
} }
}; };
Invoice invoice = invoiceGenerator.generate(); Invoice invoice = invoiceGenerator.generate();
@@ -336,13 +340,15 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
Long id = stockMoveLine.getId(); Long id = stockMoveLine.getId();
if (qtyToInvoiceMap != null) { if (qtyToInvoiceMap != null) {
if (qtyToInvoiceMap.containsKey(id)) { if (qtyToInvoiceMap.containsKey(id)) {
invoiceLineListCreated = this.createInvoiceLine(invoice, stockMoveLine, qtyToInvoiceMap.get(id)); invoiceLineListCreated =
this.createInvoiceLine(invoice, stockMoveLine, qtyToInvoiceMap.get(id));
} }
} else { } else {
invoiceLineListCreated = this.createInvoiceLine( invoiceLineListCreated =
invoice, this.createInvoiceLine(
stockMoveLine, invoice,
stockMoveLine.getRealQty().subtract(stockMoveLine.getQtyInvoiced())); stockMoveLine,
stockMoveLine.getRealQty().subtract(stockMoveLine.getQtyInvoiced()));
} }
if (invoiceLineListCreated != null) { if (invoiceLineListCreated != null) {
@@ -418,31 +424,32 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
stockMoveLine.getStockMove().getStockMoveSeq()); stockMoveLine.getStockMove().getStockMoveSeq());
} }
InvoiceLineGenerator invoiceLineGenerator = new InvoiceLineGeneratorSupplyChain( InvoiceLineGenerator invoiceLineGenerator =
invoice, new InvoiceLineGeneratorSupplyChain(
product, invoice,
stockMoveLine.getProductName(), product,
stockMoveLine.getDescription(), stockMoveLine.getProductName(),
qty, stockMoveLine.getDescription(),
stockMoveLine.getUnit(), qty,
sequence, stockMoveLine.getUnit(),
false, sequence,
stockMoveLine.getSaleOrderLine(), false,
stockMoveLine.getPurchaseOrderLine(), stockMoveLine.getSaleOrderLine(),
stockMoveLine, stockMoveLine.getPurchaseOrderLine(),
stockMoveLine.getIsSubLine(), stockMoveLine,
stockMoveLine.getPackPriceSelect()) { stockMoveLine.getIsSubLine(),
@Override stockMoveLine.getPackPriceSelect()) {
public List<InvoiceLine> creates() throws AxelorException { @Override
public List<InvoiceLine> creates() throws AxelorException {
InvoiceLine invoiceLine = this.createInvoiceLine(); InvoiceLine invoiceLine = this.createInvoiceLine();
List<InvoiceLine> invoiceLines = new ArrayList<InvoiceLine>(); List<InvoiceLine> invoiceLines = new ArrayList<InvoiceLine>();
invoiceLines.add(invoiceLine); invoiceLines.add(invoiceLine);
return invoiceLines; return invoiceLines;
} }
}; };
List<InvoiceLine> invoiceLines = invoiceLineGenerator.creates(); List<InvoiceLine> invoiceLines = invoiceLineGenerator.creates();
for (InvoiceLine invoiceLine : invoiceLines) { for (InvoiceLine invoiceLine : invoiceLines) {
@@ -458,8 +465,7 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
} }
/** /**
* Get a list of stock move lines consolidated by parent line (sale or purchase * Get a list of stock move lines consolidated by parent line (sale or purchase order).
* order).
* *
* @param stockMoveLineList * @param stockMoveLineList
* @return * @return
@@ -478,8 +484,8 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
if (stockMoveLine.getSaleOrderLine() != null) { if (stockMoveLine.getSaleOrderLine() != null) {
list = stockMoveLineSaleMap.get(stockMoveLine.getSaleOrderLine()); list = stockMoveLineSaleMap.get(stockMoveLine.getSaleOrderLine());
// if (list == null) { // if (list == null) {
// list = new ArrayList<>(); // list = new ArrayList<>();
// stockMoveLineSaleMap.put(stockMoveLine.getSaleOrderLine(), list); // stockMoveLineSaleMap.put(stockMoveLine.getSaleOrderLine(), list);
// } // }
// list.add(stockMoveLine); // list.add(stockMoveLine);
resultList.add(stockMoveLine); resultList.add(stockMoveLine);
@@ -490,9 +496,7 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
stockMoveLinePurchaseMap.put(stockMoveLine.getPurchaseOrderLine(), list); stockMoveLinePurchaseMap.put(stockMoveLine.getPurchaseOrderLine(), list);
} }
list.add(stockMoveLine); list.add(stockMoveLine);
resultList.add(stockMoveLine); } else { // if the stock move line does not have a parent line (sale or purchase order line)
} else { // if the stock move line does not have a parent line (sale or purchase order
// line)
resultList.add(stockMoveLine); resultList.add(stockMoveLine);
} }
} }
@@ -540,16 +544,18 @@ public class StockMoveInvoiceServiceImpl implements StockMoveInvoiceService {
stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId()); stockMove = Beans.get(StockMoveRepository.class).find(stockMove.getId());
if (stockMove.getInvoiceSet() != null && !stockMove.getInvoiceSet().isEmpty()) { if (stockMove.getInvoiceSet() != null && !stockMove.getInvoiceSet().isEmpty()) {
Double totalInvoicedQty = stockMove Double totalInvoicedQty =
.getStockMoveLineList() stockMove
.stream() .getStockMoveLineList()
.mapToDouble(sml -> Double.parseDouble(sml.getQtyInvoiced().toString())) .stream()
.sum(); .mapToDouble(sml -> Double.parseDouble(sml.getQtyInvoiced().toString()))
Double totalRealQty = stockMove .sum();
.getStockMoveLineList() Double totalRealQty =
.stream() stockMove
.mapToDouble(sml -> Double.parseDouble(sml.getRealQty().toString())) .getStockMoveLineList()
.sum(); .stream()
.mapToDouble(sml -> Double.parseDouble(sml.getRealQty().toString()))
.sum();
if (totalRealQty.compareTo(totalInvoicedQty) == 1) { if (totalRealQty.compareTo(totalInvoicedQty) == 1) {
invoiceStatus = 1; invoiceStatus = 1;
} else if (totalRealQty.compareTo(totalInvoicedQty) == 0) { } else if (totalRealQty.compareTo(totalInvoicedQty) == 0) {