First commit waiting for Budget Alert
This commit is contained in:
16
modules/axelor-open-suite/axelor-sale/build.gradle
Normal file
16
modules/axelor-open-suite/axelor-sale/build.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
apply plugin: "com.axelor.app-module"
|
||||
|
||||
apply from: "../version.gradle"
|
||||
|
||||
apply {
|
||||
version = openSuiteVersion
|
||||
}
|
||||
|
||||
axelor {
|
||||
title "Axelor Sale"
|
||||
description "Axelor Sale Module"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(":modules:axelor-crm")
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.db.repo;
|
||||
|
||||
import com.axelor.apps.sale.db.AdvancePayment;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeService;
|
||||
import com.axelor.inject.Beans;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class AdvancePaymentSaleRepository extends AdvancePaymentRepository {
|
||||
|
||||
@Override
|
||||
public AdvancePayment save(AdvancePayment advancePayment) {
|
||||
try {
|
||||
SaleOrder saleOrder = advancePayment.getSaleOrder();
|
||||
Beans.get(SaleOrderComputeService.class)._computeSaleOrder(saleOrder);
|
||||
return super.save(advancePayment);
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.db.repo;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleBatch;
|
||||
|
||||
public class SaleBatchSaleRepository extends SaleBatchRepository {
|
||||
|
||||
@Override
|
||||
public SaleBatch copy(SaleBatch entity, boolean deep) {
|
||||
SaleBatch copy = super.copy(entity, deep);
|
||||
copy.setBatchList(null);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.db.repo;
|
||||
|
||||
import com.axelor.apps.base.db.AppSale;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class SaleOrderLineSaleRepository extends SaleOrderLineRepository {
|
||||
|
||||
@Inject private SaleOrderLineService solService;
|
||||
|
||||
@Inject private AppSaleService appSaleService;
|
||||
|
||||
@Override
|
||||
public SaleOrderLine save(SaleOrderLine soLine) {
|
||||
|
||||
AppSale appSale = appSaleService.getAppSale();
|
||||
soLine = super.save(soLine);
|
||||
|
||||
if (appSale.getActive() && appSale.getProductPackMgt()) {
|
||||
soLine.setTotalPack(solService.computeTotalPack(soLine));
|
||||
}
|
||||
|
||||
return soLine;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.db.repo;
|
||||
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderMarginService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowServiceImpl;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import java.math.BigDecimal;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
public class SaleOrderManagementRepository extends SaleOrderRepository {
|
||||
|
||||
@Override
|
||||
public SaleOrder copy(SaleOrder entity, boolean deep) {
|
||||
|
||||
SaleOrder copy = super.copy(entity, deep);
|
||||
|
||||
copy.setStatusSelect(SaleOrderRepository.STATUS_DRAFT_QUOTATION);
|
||||
copy.setSaleOrderSeq(null);
|
||||
copy.clearBatchSet();
|
||||
copy.setImportId(null);
|
||||
copy.setCreationDate(Beans.get(AppBaseService.class).getTodayDate());
|
||||
copy.setConfirmationDateTime(null);
|
||||
copy.setConfirmedByUser(null);
|
||||
copy.setOrderDate(null);
|
||||
copy.setOrderNumber(null);
|
||||
copy.setVersionNumber(1);
|
||||
copy.setTotalCostPrice(null);
|
||||
copy.setTotalGrossMargin(null);
|
||||
copy.setMarginRate(null);
|
||||
copy.setEndOfValidityDate(null);
|
||||
copy.setDeliveryDate(null);
|
||||
copy.setOrderBeingEdited(false);
|
||||
if (copy.getAdvancePaymentAmountNeeded().compareTo(copy.getAdvanceTotal()) <= 0) {
|
||||
copy.setAdvancePaymentAmountNeeded(BigDecimal.ZERO);
|
||||
copy.setAdvancePaymentNeeded(false);
|
||||
copy.clearAdvancePaymentList();
|
||||
}
|
||||
|
||||
if (copy.getSaleOrderLineList() != null) {
|
||||
for (SaleOrderLine saleOrderLine : copy.getSaleOrderLineList()) {
|
||||
saleOrderLine.setDesiredDelivDate(null);
|
||||
saleOrderLine.setEstimatedDelivDate(null);
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder save(SaleOrder saleOrder) {
|
||||
try {
|
||||
computeSeq(saleOrder);
|
||||
computeFullName(saleOrder);
|
||||
computeSubMargin(saleOrder);
|
||||
Beans.get(SaleOrderMarginService.class).computeMarginSaleOrder(saleOrder);
|
||||
return super.save(saleOrder);
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void computeSeq(SaleOrder saleOrder) {
|
||||
try {
|
||||
if (saleOrder.getId() == null) {
|
||||
saleOrder = super.save(saleOrder);
|
||||
}
|
||||
if (Strings.isNullOrEmpty(saleOrder.getSaleOrderSeq()) && !saleOrder.getTemplate()) {
|
||||
if (saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_DRAFT_QUOTATION) {
|
||||
saleOrder.setSaleOrderSeq(
|
||||
Beans.get(SaleOrderWorkflowServiceImpl.class).getSequence(saleOrder.getCompany()));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void computeFullName(SaleOrder saleOrder) {
|
||||
try {
|
||||
if (saleOrder.getClientPartner() != null) {
|
||||
String fullName = saleOrder.getClientPartner().getName();
|
||||
if (!Strings.isNullOrEmpty(saleOrder.getSaleOrderSeq())) {
|
||||
fullName = saleOrder.getSaleOrderSeq() + "-" + fullName;
|
||||
}
|
||||
saleOrder.setFullName(fullName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void computeSubMargin(SaleOrder saleOrder) throws AxelorException {
|
||||
|
||||
if (saleOrder.getSaleOrderLineList() != null) {
|
||||
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
|
||||
Beans.get(SaleOrderLineService.class).computeSubMargin(saleOrder, saleOrderLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.exception;
|
||||
|
||||
import com.axelor.db.Model;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
|
||||
public class BlockedSaleOrderException extends AxelorException {
|
||||
|
||||
private static final long serialVersionUID = 4628915492959133388L;
|
||||
|
||||
public BlockedSaleOrderException(Model model, String message) {
|
||||
super(model, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.exception;
|
||||
|
||||
/**
|
||||
* Interface of Exceptions.
|
||||
*
|
||||
* @author dubaux
|
||||
*/
|
||||
public interface IExceptionMessage {
|
||||
|
||||
/** Sales Order Stock Move Service */
|
||||
static final String SALES_ORDER_STOCK_MOVE_1 = /*$$(*/
|
||||
"Invoice by delivery impose that all sale order lines must have service or stockable product with provision from stock" /*)*/;
|
||||
|
||||
/** Sales Order Service Impl */
|
||||
static final String SALES_ORDER_1 = /*$$(*/
|
||||
"The company %s doesn't have any configured sequence for sale orders" /*)*/;
|
||||
|
||||
static final String SALES_ORDER_COMPLETED = /*$$(*/ "This sale order is completed." /*)*/;
|
||||
|
||||
/** Sale Config Service */
|
||||
static final String SALE_CONFIG_1 = /*$$(*/
|
||||
"%s : You must configure Sales module for company %s" /*)*/;
|
||||
|
||||
/** Merge sale order */
|
||||
public static final String SALE_ORDER_MERGE_ERROR_CURRENCY = /*$$(*/
|
||||
"The currency is required and must be the same for all sale orders" /*)*/;
|
||||
|
||||
public static final String SALE_ORDER_MERGE_ERROR_CLIENT_PARTNER = /*$$(*/
|
||||
"The client Partner is required and must be the same for all sale orders" /*)*/;
|
||||
public static final String SALE_ORDER_MERGE_ERROR_COMPANY = /*$$(*/
|
||||
"The company is required and must be the same for all sale orders" /*)*/;
|
||||
|
||||
static final String SALE_ORDER_PRINT = /*$$(*/ "Please select the sale order(s) to print." /*)*/;
|
||||
static final String SALE_ORDER_MISSING_PRINTING_SETTINGS = /*$$(*/
|
||||
"Please fill printing settings on sale order %s." /*)*/;
|
||||
|
||||
/** Configurator creator */
|
||||
String CONFIGURATOR_CREATOR_SCRIPT_ERROR = /*$$(*/
|
||||
"This script has errors, please see server logs for more details." /*)*/;
|
||||
|
||||
String CONFIGURATOR_CREATOR_FORMULA_TYPE_ERROR = /*$$(*/
|
||||
"This script returned value is of type %s, it should return a value of type %s instead." /*)*/;
|
||||
String CONFIGURATOR_CREATOR_SCRIPT_WORKING = /*$$(*/ "The syntax of the script is correct." /*)*/;
|
||||
|
||||
/** Configurator Service */
|
||||
String CONFIGURATOR_PRODUCT_MISSING_NAME = /*$$(*/
|
||||
"You must configure a script to fill the created product name." /*)*/;
|
||||
|
||||
String CONFIGURATOR_PRODUCT_MISSING_CODE = /*$$(*/
|
||||
"You must configure a script to fill the created product code." /*)*/;
|
||||
String CONFIGURATOR_SALE_ORDER_LINE_MISSING_PRODUCT_NAME = /*$$(*/
|
||||
"You must configure a script to fill the product name in the created sale order line." /*)*/;
|
||||
|
||||
String CONFIGURATOR_ON_GENERATING_TYPE_ERROR = /*$$(*/
|
||||
"The field %s is of type %s, but the configured script returned value is of type %s." /*)*/;
|
||||
|
||||
static final String SALE_ORDER_EDIT_ORDER_NOTIFY = /*$$(*/
|
||||
"At least one sale order line has a stock move with availability request." /*)*/;
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.module;
|
||||
|
||||
import com.axelor.app.AxelorModule;
|
||||
import com.axelor.apps.base.db.repo.PartnerAddressRepository;
|
||||
import com.axelor.apps.base.service.PartnerServiceImpl;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.AdvancePaymentRepository;
|
||||
import com.axelor.apps.sale.db.repo.AdvancePaymentSaleRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleBatchRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleBatchSaleRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineSaleRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderManagementRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.service.AddressServiceSaleImpl;
|
||||
import com.axelor.apps.sale.service.AdvancePaymentService;
|
||||
import com.axelor.apps.sale.service.AdvancePaymentServiceImpl;
|
||||
import com.axelor.apps.sale.service.PartnerSaleServiceImpl;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.apps.sale.service.app.AppSaleServiceImpl;
|
||||
import com.axelor.apps.sale.service.config.SaleConfigService;
|
||||
import com.axelor.apps.sale.service.config.SaleConfigServiceImpl;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportServiceImpl;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorServiceImpl;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorFormulaService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorFormulaServiceImpl;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.OpportunitySaleOrderService;
|
||||
import com.axelor.apps.sale.service.saleorder.OpportunitySaleOrderServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderCreateService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderCreateServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderMarginService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderMarginServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.print.SaleOrderPrintService;
|
||||
import com.axelor.apps.sale.service.saleorder.print.SaleOrderPrintServiceImpl;
|
||||
|
||||
public class SaleModule extends AxelorModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AddressServiceSaleImpl.class);
|
||||
bind(PartnerServiceImpl.class).to(PartnerSaleServiceImpl.class);
|
||||
bind(SaleOrderService.class).to(SaleOrderServiceImpl.class);
|
||||
bind(SaleOrderLineService.class).to(SaleOrderLineServiceImpl.class);
|
||||
bind(SaleOrderRepository.class).to(SaleOrderManagementRepository.class);
|
||||
bind(SaleOrderWorkflowService.class).to(SaleOrderWorkflowServiceImpl.class);
|
||||
bind(SaleOrderMarginService.class).to(SaleOrderMarginServiceImpl.class);
|
||||
bind(SaleOrderCreateService.class).to(SaleOrderCreateServiceImpl.class);
|
||||
bind(SaleOrderComputeService.class).to(SaleOrderComputeServiceImpl.class);
|
||||
bind(OpportunitySaleOrderService.class).to(OpportunitySaleOrderServiceImpl.class);
|
||||
bind(AdvancePaymentService.class).to(AdvancePaymentServiceImpl.class);
|
||||
bind(AppSaleService.class).to(AppSaleServiceImpl.class);
|
||||
bind(SaleOrderLineRepository.class).to(SaleOrderLineSaleRepository.class);
|
||||
bind(SaleConfigService.class).to(SaleConfigServiceImpl.class);
|
||||
bind(SaleBatchRepository.class).to(SaleBatchSaleRepository.class);
|
||||
PartnerAddressRepository.modelPartnerFieldMap.put(SaleOrder.class.getName(), "clientPartner");
|
||||
bind(AdvancePaymentRepository.class).to(AdvancePaymentSaleRepository.class);
|
||||
bind(ConfiguratorCreatorService.class).to(ConfiguratorCreatorServiceImpl.class);
|
||||
bind(ConfiguratorService.class).to(ConfiguratorServiceImpl.class);
|
||||
bind(ConfiguratorFormulaService.class).to(ConfiguratorFormulaServiceImpl.class);
|
||||
bind(ConfiguratorCreatorImportService.class).to(ConfiguratorCreatorImportServiceImpl.class);
|
||||
bind(SaleOrderPrintService.class).to(SaleOrderPrintServiceImpl.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.report;
|
||||
|
||||
public interface IReport {
|
||||
|
||||
public static final String SALES_ORDER = "SaleOrder.rptdesign";
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.report;
|
||||
|
||||
public interface ITranslation {
|
||||
|
||||
public static final String SALES_ORDER_QUOTE = /*$$(*/ "SaleOrder.quote"; /*)*/
|
||||
public static final String SALES_ORDER_ORDER = /*$$(*/ "SaleOrder.order"; /*)*/
|
||||
public static final String SALES_ORDER_PROFORMA = /*$$(*/ "SaleOrder.proforma"; /*)*/
|
||||
public static final String SALES_ORDER_NO = /*$$(*/ "SaleOrder.no"; /*)*/
|
||||
public static final String SALES_ORDER_DATE = /*$$(*/ "SaleOrder.date"; /*)*/
|
||||
public static final String SALES_ORDER_PAYMENT_CONDITION = /*$$(*/
|
||||
"SaleOrder.paymentCondition"; /*)*/
|
||||
public static final String SALES_ORDER_PAYMENT_MODE = /*$$(*/ "SaleOrder.paymentMode"; /*)*/
|
||||
public static final String SALES_ORDER_SUPPLY_REF = /*$$(*/ "SaleOrder.supplyRef"; /*)*/
|
||||
public static final String SALES_ORDER_CUSTOMER_REF = /*$$(*/ "SaleOrder.customerRef"; /*)*/
|
||||
public static final String SALES_ORDER_CUSTOMER_CODE = /*$$(*/ "SaleOrder.customerCode"; /*)*/
|
||||
public static final String SALES_ORDER_SUPPLIER = /*$$(*/ "SaleOrder.supplier"; /*)*/
|
||||
public static final String SALES_ORDER_CUSTOMER = /*$$(*/ "SaleOrder.customer"; /*)*/
|
||||
public static final String SALES_ORDER_INVOICING_ADDRS = /*$$(*/ "SaleOrder.invoicingAddrs"; /*)*/
|
||||
public static final String SALES_ORDER_DELIVERY_ADDRESS = /*$$(*/
|
||||
"SaleOrder.deliveryAddress"; /*)*/
|
||||
public static final String SALES_ORDER_DESCRIPTION = /*$$(*/ "SaleOrder.description"; /*)*/
|
||||
public static final String SALES_ORDER_TAX = /*$$(*/ "SaleOrder.tax"; /*)*/
|
||||
public static final String SALES_ORDER_QTY_UNIT = /*$$(*/ "SaleOrder.qtyUnit"; /*)*/
|
||||
public static final String SALES_ORDER_UNIT_PRICE = /*$$(*/ "SaleOrder.unitPrice"; /*)*/
|
||||
public static final String SALES_ORDER_PRICE_EXCL_TAX = /*$$(*/ "SaleOrder.priceExclTax"; /*)*/
|
||||
public static final String SALES_ORDER_PRICE_INCL_TAX = /*$$(*/ "SaleOrder.priceInclTax"; /*)*/
|
||||
public static final String SALES_ORDER_BASE = /*$$(*/ "SaleOrder.base"; /*)*/
|
||||
public static final String SALES_ORDER_TAX_AMOUNT = /*$$(*/ "SaleOrder.taxAmount"; /*)*/
|
||||
public static final String SALES_ORDER_TOTAL_EXCL_TAX = /*$$(*/ "SaleOrder.totalExclTax"; /*)*/
|
||||
public static final String SALES_ORDER_TOTAL_TAX = /*$$(*/ "SaleOrder.totalTax"; /*)*/
|
||||
public static final String SALES_ORDER_TOTAL_INCL_TAX = /*$$(*/ "SaleOrder.totalInclTax"; /*)*/
|
||||
public static final String SALES_ORDER_NOTE = /*$$(*/ "SaleOrder.note"; /*)*/
|
||||
public static final String SALES_ORDER_CHEQUE = /*$$(*/ "SaleOrder.cheque"; /*)*/
|
||||
public static final String SALES_ORDER_BANK = /*$$(*/ "SaleOrder.bank"; /*)*/
|
||||
public static final String SALES_ORDER_SALEMAN_NAME = /*$$(*/ "SaleOrder.salemanName"; /*)*/
|
||||
public static final String SALES_ORDER_SALEMAN_EMAIL = /*$$(*/ "SaleOrder.salemanEmail"; /*)*/
|
||||
public static final String SALES_ORDER_SALEMAN_PHONE = /*$$(*/ "SaleOrder.salemanPhone"; /*)*/
|
||||
public static final String SALES_ORDER_DELIVERY_DATE = /*$$(*/ "SaleOrder.deliveryDate"; /*)*/
|
||||
public static final String SALES_ORDER_DELIVERY_CONDITION = /*$$(*/
|
||||
"SaleOrder.deliveryCondition"; /*)*/
|
||||
public static final String SALES_ORDER_PRODUCT_DESCRIPTION = /*$$(*/
|
||||
"SaleOrder.productDescription"; /*)*/
|
||||
public static final String SALES_ORDER_PRODUCT_CODE = /*$$(*/ "SaleOrder.productCode"; /*)*/
|
||||
public static final String SALES_ORDER_PRODUCT_NAME = /*$$(*/ "SaleOrder.productName"; /*)*/
|
||||
public static final String SALES_ORDER_DISCOUNT_AMOUNT = /*$$(*/ "SaleOrder.discountAmount"; /*)*/
|
||||
public static final String SALES_ORDER_TOTAL_EXCL_TAX_WITHOUT_DISCOUNT = /*$$(*/
|
||||
"SaleOrder.totalExclTaxWithoutDiscount"; /*)*/
|
||||
public static final String SALES_ORDER_TOTAL_DISCOUNT = /*$$(*/ "SaleOrder.totalDiscount"; /*)*/
|
||||
public static final String SALES_ORDER_AFTER_DISCOUNT = /*$$(*/ "SaleOrder.afterDiscount"; /*)*/
|
||||
public static final String SALES_ORDER_OTHERS = /*$$(*/ "SaleOrder.others"; /*)*/
|
||||
public static final String SALES_ORDER_SUBSCRIPTION_CONTRACT = /*$$(*/
|
||||
"SaleOrder.subscriptionContract"; /*)*/
|
||||
public static final String SALES_ORDER_SUBSCRIPTION_PERIODICITY = /*$$(*/
|
||||
"SaleOrder.periodicity"; /*)*/
|
||||
|
||||
public static final String SALES_ORDER_DURATION = /*$$(*/ "SaleOrder.duration"; /*)*/;
|
||||
public static final String SALES_ORDER_VALIDITY_DATE = /*$$(*/ "SaleOrder.validityDate"; /*)*/;
|
||||
public static final String SALES_ORDER_ESTIMATED_DELIVERY_DATE = /*$$(*/
|
||||
"SaleOrder.estimatedDeliveryDate"; /*)*/;
|
||||
public static final String SALES_ORDER_IS_ISPM_REQUIRED = /*$$(*/
|
||||
"SaleOrder.isIspmRequired"; /*)*/;
|
||||
|
||||
public static final String SALES_ORDER_PRODUCT_SEQUENCE = /*$$(*/
|
||||
"SaleOrder.productSequence"; /*)*/
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service;
|
||||
|
||||
import static com.axelor.apps.base.service.administration.AbstractBatch.FETCH_LIMIT;
|
||||
import static com.axelor.apps.tool.date.DateTool.toDate;
|
||||
import static com.axelor.apps.tool.date.DateTool.toLocalDateT;
|
||||
|
||||
import com.axelor.apps.base.db.ABCAnalysis;
|
||||
import com.axelor.apps.base.db.ABCAnalysisLine;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ABCAnalysisClassRepository;
|
||||
import com.axelor.apps.base.db.repo.ABCAnalysisLineRepository;
|
||||
import com.axelor.apps.base.db.repo.ABCAnalysisRepository;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.ABCAnalysisServiceImpl;
|
||||
import com.axelor.apps.base.service.UnitConversionService;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.Query;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ABCAnalysisServiceSaleImpl extends ABCAnalysisServiceImpl {
|
||||
protected SaleOrderLineRepository saleOrderLineRepository;
|
||||
|
||||
private static final String SELLABLE_TRUE = " AND self.sellable = TRUE";
|
||||
|
||||
@Inject
|
||||
public ABCAnalysisServiceSaleImpl(
|
||||
ABCAnalysisLineRepository abcAnalysisLineRepository,
|
||||
UnitConversionService unitConversionService,
|
||||
ABCAnalysisRepository abcAnalysisRepository,
|
||||
ProductRepository productRepository,
|
||||
SaleOrderLineRepository saleOrderLineRepository,
|
||||
ABCAnalysisClassRepository abcAnalysisClassRepository,
|
||||
SequenceService sequenceService) {
|
||||
super(
|
||||
abcAnalysisLineRepository,
|
||||
unitConversionService,
|
||||
abcAnalysisRepository,
|
||||
productRepository,
|
||||
abcAnalysisClassRepository,
|
||||
sequenceService);
|
||||
this.saleOrderLineRepository = saleOrderLineRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<ABCAnalysisLine> createABCAnalysisLine(
|
||||
ABCAnalysis abcAnalysis, Product product) throws AxelorException {
|
||||
ABCAnalysisLine abcAnalysisLine = null;
|
||||
BigDecimal productQty = BigDecimal.ZERO;
|
||||
BigDecimal productWorth = BigDecimal.ZERO;
|
||||
List<SaleOrderLine> saleOrderLineList;
|
||||
int offset = 0;
|
||||
|
||||
Query<SaleOrderLine> saleOrderLineQuery =
|
||||
saleOrderLineRepository
|
||||
.all()
|
||||
.filter(
|
||||
"(self.saleOrder.statusSelect = :statusConfirmed OR self.saleOrder.statusSelect = :statusCompleted) AND self.saleOrder.confirmationDateTime >= :startDate AND self.saleOrder.confirmationDateTime <= :endDate AND self.product.id = :productId")
|
||||
.bind("statusConfirmed", SaleOrderRepository.STATUS_ORDER_CONFIRMED)
|
||||
.bind("statusCompleted", SaleOrderRepository.STATUS_ORDER_COMPLETED)
|
||||
.bind("startDate", toLocalDateT(toDate(abcAnalysis.getStartDate())))
|
||||
.bind(
|
||||
"endDate",
|
||||
toLocalDateT(toDate(abcAnalysis.getEndDate()))
|
||||
.withHour(23)
|
||||
.withMinute(59)
|
||||
.withSecond(59))
|
||||
.bind("productId", product.getId())
|
||||
.order("id");
|
||||
|
||||
while (!(saleOrderLineList = saleOrderLineQuery.fetch(FETCH_LIMIT, offset)).isEmpty()) {
|
||||
offset += saleOrderLineList.size();
|
||||
abcAnalysis = abcAnalysisRepository.find(abcAnalysis.getId());
|
||||
|
||||
if (abcAnalysisLine == null) {
|
||||
abcAnalysisLine = super.createABCAnalysisLine(abcAnalysis, product).get();
|
||||
}
|
||||
|
||||
for (SaleOrderLine saleOrderLine : saleOrderLineList) {
|
||||
BigDecimal convertedQty =
|
||||
unitConversionService.convert(
|
||||
saleOrderLine.getUnit(), product.getUnit(), saleOrderLine.getQty(), 5, product);
|
||||
productQty = productQty.add(convertedQty);
|
||||
productWorth = productWorth.add(saleOrderLine.getCompanyExTaxTotal());
|
||||
}
|
||||
|
||||
super.incTotalQty(productQty);
|
||||
super.incTotalWorth(productWorth);
|
||||
|
||||
JPA.clear();
|
||||
}
|
||||
|
||||
if (abcAnalysisLine != null) {
|
||||
setQtyWorth(
|
||||
abcAnalysisLineRepository.find(abcAnalysisLine.getId()), productQty, productWorth);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(abcAnalysisLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProductCategoryQuery() {
|
||||
return super.getProductCategoryQuery() + SELLABLE_TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProductFamilyQuery() {
|
||||
return super.getProductFamilyQuery() + SELLABLE_TRUE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service;
|
||||
|
||||
import com.axelor.apps.base.service.AddressServiceImpl;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.db.JPA;
|
||||
|
||||
public class AddressServiceSaleImpl extends AddressServiceImpl {
|
||||
static {
|
||||
registerCheckUsedFunc(AddressServiceSaleImpl::checkAddressUsedSale);
|
||||
}
|
||||
|
||||
private static boolean checkAddressUsedSale(Long addressId) {
|
||||
return JPA.all(SaleOrder.class)
|
||||
.filter("self.mainInvoicingAddress.id = ?1 OR self.deliveryAddress.id = ?1", addressId)
|
||||
.fetchOne()
|
||||
!= null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service;
|
||||
|
||||
import com.axelor.apps.sale.db.AdvancePayment;
|
||||
import com.google.inject.persist.Transactional;
|
||||
|
||||
public interface AdvancePaymentService {
|
||||
|
||||
@Transactional
|
||||
public void cancelAdvancePayment(AdvancePayment advancePayment);
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service;
|
||||
|
||||
import com.axelor.apps.sale.db.AdvancePayment;
|
||||
import com.axelor.apps.sale.db.repo.AdvancePaymentRepository;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
|
||||
public class AdvancePaymentServiceImpl implements AdvancePaymentService {
|
||||
|
||||
@Inject protected AdvancePaymentRepository advancePaymentRepository;
|
||||
|
||||
@Transactional
|
||||
public void cancelAdvancePayment(AdvancePayment advancePayment) {
|
||||
|
||||
advancePayment.setStatusSelect(AdvancePaymentRepository.STATUS_CANCELED);
|
||||
advancePaymentRepository.save(advancePayment);
|
||||
|
||||
// Relancer le calcul du montant d'acompte sur le devis.
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service;
|
||||
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.repo.PartnerRepository;
|
||||
import com.axelor.apps.base.service.PartnerServiceImpl;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.db.JPA;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PartnerSaleServiceImpl extends PartnerServiceImpl {
|
||||
|
||||
@Inject
|
||||
public PartnerSaleServiceImpl(PartnerRepository partnerRepo, AppBaseService appBaseService) {
|
||||
super(partnerRepo, appBaseService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findPartnerMails(Partner partner) {
|
||||
List<Long> idList = new ArrayList<Long>();
|
||||
|
||||
idList.addAll(this.findMailsFromPartner(partner));
|
||||
idList.addAll(this.findMailsFromSaleOrder(partner));
|
||||
|
||||
Set<Partner> contactSet = partner.getContactPartnerSet();
|
||||
if (contactSet != null && !contactSet.isEmpty()) {
|
||||
for (Partner contact : contactSet) {
|
||||
idList.addAll(this.findMailsFromPartner(contact));
|
||||
idList.addAll(this.findMailsFromSaleOrderContact(contact));
|
||||
}
|
||||
}
|
||||
return idList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findContactMails(Partner partner) {
|
||||
List<Long> idList = new ArrayList<Long>();
|
||||
|
||||
idList.addAll(this.findMailsFromPartner(partner));
|
||||
idList.addAll(this.findMailsFromSaleOrderContact(partner));
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Long> findMailsFromSaleOrder(Partner partner) {
|
||||
String query =
|
||||
"SELECT DISTINCT(email.id) FROM Message as email, SaleOrder as so, Partner as part"
|
||||
+ " WHERE part.id = "
|
||||
+ partner.getId()
|
||||
+ " AND so.clientPartner = part.id AND email.mediaTypeSelect = 2 AND "
|
||||
+ "((email.relatedTo1Select = 'com.axelor.apps.sale.db.SaleOrder' AND email.relatedTo1SelectId = so.id) "
|
||||
+ "OR (email.relatedTo2Select = 'com.axelor.apps.sale.db.SaleOrder' AND email.relatedTo2SelectId = so.id))";
|
||||
return JPA.em().createQuery(query).getResultList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Long> findMailsFromSaleOrderContact(Partner partner) {
|
||||
String query =
|
||||
"SELECT DISTINCT(email.id) FROM Message as email, SaleOrder as so, Partner as part"
|
||||
+ " WHERE part.id = "
|
||||
+ partner.getId()
|
||||
+ " AND so.contactPartner = part.id AND email.mediaTypeSelect = 2 AND "
|
||||
+ "((email.relatedTo1Select = 'com.axelor.apps.sale.db.SaleOrder' AND email.relatedTo1SelectId = so.id) "
|
||||
+ "OR (email.relatedTo2Select = 'com.axelor.apps.sale.db.SaleOrder' AND email.relatedTo2SelectId = so.id))";
|
||||
return JPA.em().createQuery(query).getResultList();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.app;
|
||||
|
||||
import com.axelor.apps.base.db.AppSale;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
|
||||
public interface AppSaleService extends AppBaseService {
|
||||
|
||||
public AppSale getAppSale();
|
||||
|
||||
public void generateSaleConfigurations();
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.app;
|
||||
|
||||
import com.axelor.apps.base.db.AppSale;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.repo.AppSaleRepository;
|
||||
import com.axelor.apps.base.db.repo.CompanyRepository;
|
||||
import com.axelor.apps.base.service.app.AppBaseServiceImpl;
|
||||
import com.axelor.apps.sale.db.SaleConfig;
|
||||
import com.axelor.apps.sale.db.repo.SaleConfigRepository;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class AppSaleServiceImpl extends AppBaseServiceImpl implements AppSaleService {
|
||||
|
||||
@Inject private AppSaleRepository appSaleRepo;
|
||||
|
||||
@Inject private CompanyRepository companyRepo;
|
||||
|
||||
@Inject private SaleConfigRepository saleConfigRepo;
|
||||
|
||||
@Override
|
||||
public AppSale getAppSale() {
|
||||
return appSaleRepo.all().fetchOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void generateSaleConfigurations() {
|
||||
|
||||
List<Company> companies = companyRepo.all().filter("self.saleConfig is null").fetch();
|
||||
|
||||
for (Company company : companies) {
|
||||
SaleConfig saleConfig = new SaleConfig();
|
||||
saleConfig.setCompany(company);
|
||||
saleConfigRepo.save(saleConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.config;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.sale.db.SaleConfig;
|
||||
import com.axelor.exception.AxelorException;
|
||||
|
||||
public interface SaleConfigService {
|
||||
|
||||
public SaleConfig getSaleConfig(Company company) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.config;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.sale.db.SaleConfig;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
|
||||
public class SaleConfigServiceImpl implements SaleConfigService {
|
||||
|
||||
public SaleConfig getSaleConfig(Company company) throws AxelorException {
|
||||
|
||||
SaleConfig saleConfig = company.getSaleConfig();
|
||||
|
||||
if (saleConfig == null) {
|
||||
throw new AxelorException(
|
||||
company,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.SALE_CONFIG_1),
|
||||
I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.EXCEPTION),
|
||||
company.getName());
|
||||
}
|
||||
|
||||
return saleConfig;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ConfiguratorCreatorImportService {
|
||||
|
||||
/**
|
||||
* Import configurator creators from given XML config file. Use the default path for XML config
|
||||
* file.
|
||||
*
|
||||
* @param filePath the path to the data file.
|
||||
* @return the import log file.
|
||||
*/
|
||||
String importConfiguratorCreators(String filePath) throws IOException;
|
||||
|
||||
/**
|
||||
* Import configurator creators from given XML config file.
|
||||
*
|
||||
* @param filePath the path to the data file.
|
||||
* @param configFilePath the path to XML config file.
|
||||
* @return the import log file.
|
||||
*/
|
||||
String importConfiguratorCreators(String filePath, String configFilePath) throws IOException;
|
||||
}
|
||||
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.data.Listener;
|
||||
import com.axelor.data.xml.XMLImporter;
|
||||
import com.axelor.db.Model;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.meta.MetaFiles;
|
||||
import com.axelor.meta.db.MetaJsonField;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.xmlbeans.impl.common.IOUtil;
|
||||
|
||||
public class ConfiguratorCreatorImportServiceImpl implements ConfiguratorCreatorImportService {
|
||||
|
||||
protected ConfiguratorCreatorService configuratorCreatorService;
|
||||
|
||||
@Inject
|
||||
public ConfiguratorCreatorImportServiceImpl(
|
||||
ConfiguratorCreatorService configuratorCreatorService) {
|
||||
this.configuratorCreatorService = configuratorCreatorService;
|
||||
}
|
||||
|
||||
private static final String CONFIG_FILE_PATH =
|
||||
"/data-import/import-configurator-creator-config.xml";
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
@Override
|
||||
public String importConfiguratorCreators(String filePath) throws IOException {
|
||||
return importConfiguratorCreators(filePath, CONFIG_FILE_PATH);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
@Override
|
||||
public String importConfiguratorCreators(String filePath, String configFilePath)
|
||||
throws IOException {
|
||||
InputStream inputStream = this.getClass().getResourceAsStream(configFilePath);
|
||||
File configFile = File.createTempFile("config", ".xml");
|
||||
FileOutputStream fout = new FileOutputStream(configFile);
|
||||
IOUtil.copyCompletely(inputStream, fout);
|
||||
|
||||
Path path = MetaFiles.getPath(filePath);
|
||||
File tempDir = Files.createTempDir();
|
||||
File importFile = new File(tempDir, "configurator-creator.xml");
|
||||
Files.copy(path.toFile(), importFile);
|
||||
|
||||
XMLImporter importer = new XMLImporter(configFile.getAbsolutePath(), tempDir.getAbsolutePath());
|
||||
final StringBuilder importLog = new StringBuilder();
|
||||
Listener listener =
|
||||
new Listener() {
|
||||
|
||||
@Override
|
||||
public void imported(Integer imported, Integer total) {
|
||||
importLog.append("Total records: " + total + ", Total imported: " + total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imported(Model arg0) {
|
||||
try {
|
||||
completeAfterImport(arg0);
|
||||
} catch (AxelorException e) {
|
||||
importLog.append("Error in import: " + Arrays.toString(e.getStackTrace()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Model arg0, Exception err) {
|
||||
importLog.append("Error in import: " + Arrays.toString(err.getStackTrace()));
|
||||
}
|
||||
};
|
||||
|
||||
importer.addListener(listener);
|
||||
|
||||
importer.run();
|
||||
|
||||
FileUtils.forceDelete(configFile);
|
||||
|
||||
FileUtils.forceDelete(tempDir);
|
||||
|
||||
return importLog.toString();
|
||||
}
|
||||
|
||||
protected void completeAfterImport(Object arg0) throws AxelorException {
|
||||
if (arg0.getClass().equals(ConfiguratorCreator.class)) {
|
||||
completeAfterImport((ConfiguratorCreator) arg0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void completeAfterImport(ConfiguratorCreator creator) throws AxelorException {
|
||||
fixAttributesName(creator);
|
||||
configuratorCreatorService.updateAttributes(creator);
|
||||
configuratorCreatorService.updateIndicators(creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* When exported, attribute name finish with '_XX' where XX is the id of the creator. After
|
||||
* importing, we need to fix these values.
|
||||
*
|
||||
* @param creator
|
||||
* @throws AxelorException
|
||||
*/
|
||||
protected void fixAttributesName(ConfiguratorCreator creator) throws AxelorException {
|
||||
List<MetaJsonField> attributes = creator.getAttributes();
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
for (MetaJsonField attribute : attributes) {
|
||||
String name = attribute.getName();
|
||||
if (name != null && name.contains("_")) {
|
||||
attribute.setName(name.substring(0, name.lastIndexOf('_')) + '_' + creator.getId());
|
||||
}
|
||||
updateAttributeNameInFormulas(creator, name, attribute.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the changed attribute in all formula O2M.
|
||||
*
|
||||
* @param creator
|
||||
* @param oldName
|
||||
* @param newName
|
||||
*/
|
||||
protected void updateAttributeNameInFormulas(
|
||||
ConfiguratorCreator creator, String oldName, String newName) throws AxelorException {
|
||||
if (creator.getConfiguratorProductFormulaList() != null) {
|
||||
updateAttributeNameInFormulas(creator.getConfiguratorProductFormulaList(), oldName, newName);
|
||||
}
|
||||
if (creator.getConfiguratorSOLineFormulaList() != null) {
|
||||
updateAttributeNameInFormulas(creator.getConfiguratorSOLineFormulaList(), oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the changed attribute in formulas.
|
||||
*
|
||||
* @param formulas
|
||||
* @param oldAttributeName
|
||||
* @param newAttributeName
|
||||
*/
|
||||
protected void updateAttributeNameInFormulas(
|
||||
List<? extends ConfiguratorFormula> formulas,
|
||||
String oldAttributeName,
|
||||
String newAttributeName) {
|
||||
|
||||
formulas
|
||||
.stream()
|
||||
.forEach(
|
||||
configuratorFormula ->
|
||||
configuratorFormula.setFormula(
|
||||
configuratorFormula.getFormula().replace(oldAttributeName, newAttributeName)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.auth.db.User;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.script.ScriptBindings;
|
||||
|
||||
public interface ConfiguratorCreatorService {
|
||||
|
||||
/**
|
||||
* Add default view attrs for configurator attributes
|
||||
*
|
||||
* @param creator
|
||||
* @return
|
||||
*/
|
||||
void updateAttributes(ConfiguratorCreator creator);
|
||||
|
||||
/**
|
||||
* Add the {@link ConfiguratorFormula#metaField} that need to be shown in configurator in the
|
||||
* {@link ConfiguratorCreator#indicators} many-to-one.
|
||||
*
|
||||
* @param creator
|
||||
*/
|
||||
void updateIndicators(ConfiguratorCreator creator);
|
||||
|
||||
/**
|
||||
* Get the testing values in {@link ConfiguratorCreator#attributes}
|
||||
*
|
||||
* @param creator
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
ScriptBindings getTestingValues(ConfiguratorCreator creator) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Compute the correct domain to filter creator that are not authorized for the current user.
|
||||
*
|
||||
* @return the domain
|
||||
*/
|
||||
String getConfiguratorCreatorDomain();
|
||||
|
||||
/**
|
||||
* Add the current user to the authorized user list
|
||||
*
|
||||
* @param creator
|
||||
*/
|
||||
void authorizeUser(ConfiguratorCreator creator, User user);
|
||||
|
||||
/**
|
||||
* Add required fields of Product to the formula list
|
||||
*
|
||||
* @param creator
|
||||
*/
|
||||
void addRequiredFormulas(ConfiguratorCreator creator);
|
||||
|
||||
/**
|
||||
* Activates the creator and saves it.
|
||||
*
|
||||
* @param creator
|
||||
*/
|
||||
void activate(ConfiguratorCreator creator);
|
||||
}
|
||||
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.sale.db.Configurator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.apps.sale.db.ConfiguratorProductFormula;
|
||||
import com.axelor.apps.sale.db.ConfiguratorSOLineFormula;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.ConfiguratorCreatorRepository;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
import com.axelor.auth.AuthUtils;
|
||||
import com.axelor.auth.db.Group;
|
||||
import com.axelor.auth.db.User;
|
||||
import com.axelor.common.Inflector;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.annotations.Widget;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.db.mapper.Property;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.db.MetaField;
|
||||
import com.axelor.meta.db.MetaJsonField;
|
||||
import com.axelor.meta.db.MetaModel;
|
||||
import com.axelor.meta.db.repo.MetaFieldRepository;
|
||||
import com.axelor.meta.db.repo.MetaModelRepository;
|
||||
import com.axelor.script.ScriptBindings;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class ConfiguratorCreatorServiceImpl implements ConfiguratorCreatorService {
|
||||
|
||||
private ConfiguratorCreatorRepository configuratorCreatorRepo;
|
||||
|
||||
@Inject
|
||||
public ConfiguratorCreatorServiceImpl(ConfiguratorCreatorRepository configuratorCreatorRepo) {
|
||||
this.configuratorCreatorRepo = configuratorCreatorRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateAttributes(ConfiguratorCreator creator) {
|
||||
|
||||
if (creator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MetaJsonField field : creator.getAttributes()) {
|
||||
setContextToJsonField(creator, field);
|
||||
|
||||
// fill onChange if empty
|
||||
if (Strings.isNullOrEmpty(field.getOnChange())) {
|
||||
field.setOnChange("save,action-configurator-update-indicators,save");
|
||||
}
|
||||
}
|
||||
configuratorCreatorRepo.save(creator);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateIndicators(ConfiguratorCreator creator) {
|
||||
List<MetaJsonField> indicators =
|
||||
Optional.ofNullable(creator.getIndicators()).orElse(Collections.emptyList());
|
||||
|
||||
// add missing formulas
|
||||
List<? extends ConfiguratorFormula> formulas;
|
||||
|
||||
if (creator.getGenerateProduct()) {
|
||||
formulas = creator.getConfiguratorProductFormulaList();
|
||||
} else {
|
||||
formulas = creator.getConfiguratorSOLineFormulaList();
|
||||
}
|
||||
for (ConfiguratorFormula formula : formulas) {
|
||||
addIfMissing(formula, creator);
|
||||
}
|
||||
|
||||
// remove formulas
|
||||
List<MetaJsonField> fieldsToRemove = new ArrayList<>();
|
||||
for (MetaJsonField indicator : indicators) {
|
||||
if (isNotInFormulas(indicator, creator, formulas)) {
|
||||
fieldsToRemove.add(indicator);
|
||||
}
|
||||
}
|
||||
for (MetaJsonField indicatorToRemove : fieldsToRemove) {
|
||||
creator.removeIndicator(indicatorToRemove);
|
||||
}
|
||||
|
||||
updateIndicatorsAttrs(creator, formulas);
|
||||
|
||||
configuratorCreatorRepo.save(creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptBindings getTestingValues(ConfiguratorCreator creator) {
|
||||
Map<String, Object> attributesValues = new HashMap<>();
|
||||
List<MetaJsonField> attributes = creator.getAttributes();
|
||||
if (attributes != null) {
|
||||
for (MetaJsonField attribute : attributes) {
|
||||
Object defaultAttribute = getAttributesDefaultValue(attribute);
|
||||
if (defaultAttribute != null) {
|
||||
attributesValues.put(attribute.getName(), getAttributesDefaultValue(attribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ScriptBindings(attributesValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a default value to test a script.
|
||||
*
|
||||
* @param attribute
|
||||
* @return
|
||||
*/
|
||||
protected Object getAttributesDefaultValue(MetaJsonField attribute) {
|
||||
switch (attribute.getType()) {
|
||||
case "string":
|
||||
return "a";
|
||||
case "integer":
|
||||
return 1;
|
||||
case "decimal":
|
||||
return BigDecimal.ONE;
|
||||
case "boolean":
|
||||
return true;
|
||||
case "datetime":
|
||||
return LocalDateTime.of(LocalDate.now(), LocalTime.now());
|
||||
case "date":
|
||||
return LocalDate.now();
|
||||
case "time":
|
||||
return LocalTime.now();
|
||||
case "panel":
|
||||
return null;
|
||||
case "enum":
|
||||
return null;
|
||||
case "button":
|
||||
return null;
|
||||
case "separator":
|
||||
return null;
|
||||
case "many-to-one":
|
||||
return getAttributeRelationalField(attribute, "many-to-one");
|
||||
case "many-to-many":
|
||||
return getAttributeRelationalField(attribute, "many-to-many");
|
||||
case "one-to-many":
|
||||
return getAttributeRelationalField(attribute, "one-to-many");
|
||||
case "json-many-to-one":
|
||||
return null;
|
||||
case "json-many-to-many":
|
||||
return null;
|
||||
case "json-one-to-many":
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a default value to test a script for a relational field.
|
||||
*
|
||||
* @param attribute
|
||||
* @param relation
|
||||
* @return
|
||||
*/
|
||||
protected Object getAttributeRelationalField(MetaJsonField attribute, String relation) {
|
||||
try {
|
||||
Class targetClass = Class.forName(attribute.getTargetModel());
|
||||
if (relation.equals("many-to-one")) {
|
||||
return JPA.all(targetClass).fetchOne();
|
||||
} else if (relation.equals("one-to-many")) {
|
||||
return JPA.all(targetClass).fetch(1);
|
||||
} else if (relation.equals("many-to-many")) {
|
||||
return new HashSet(JPA.all(targetClass).fetch(1));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the {@link ConfiguratorFormula} in {@link ConfiguratorCreator#indicators} if the formula is
|
||||
* not represented by an existing indicator.
|
||||
*
|
||||
* @param formula
|
||||
* @param creator
|
||||
*/
|
||||
protected void addIfMissing(ConfiguratorFormula formula, ConfiguratorCreator creator) {
|
||||
MetaField formulaMetaField = formula.getMetaField();
|
||||
List<MetaJsonField> fields =
|
||||
Optional.ofNullable(creator.getIndicators()).orElse(Collections.emptyList());
|
||||
for (MetaJsonField field : fields) {
|
||||
if (field.getName().equals(formulaMetaField.getName() + "_" + creator.getId())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
String metaModelName = formulaMetaField.getMetaModel().getName();
|
||||
MetaJsonField newField = new MetaJsonField();
|
||||
newField.setModel(Configurator.class.getName());
|
||||
newField.setModelField("indicators");
|
||||
MetaField metaField =
|
||||
Beans.get(MetaFieldRepository.class)
|
||||
.all()
|
||||
.filter("self.metaModel.name = :metaModelName AND self.name = :name")
|
||||
.bind("metaModelName", metaModelName)
|
||||
.bind("name", formulaMetaField.getName())
|
||||
.fetchOne();
|
||||
String typeName;
|
||||
if (!Strings.isNullOrEmpty(metaField.getRelationship())) {
|
||||
typeName = metaField.getRelationship();
|
||||
completeDefaultGridAndForm(metaField, newField);
|
||||
} else {
|
||||
typeName = metaField.getTypeName();
|
||||
}
|
||||
completeSelection(metaField, newField);
|
||||
newField.setType(typeToJsonType(typeName));
|
||||
newField.setName(formulaMetaField.getName() + "_" + creator.getId());
|
||||
newField.setTitle(formulaMetaField.getLabel());
|
||||
creator.addIndicator(newField);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param field
|
||||
* @param creator
|
||||
* @return false if field is represented in the creator formula list true if field is missing in
|
||||
* the creator formula list
|
||||
*/
|
||||
protected boolean isNotInFormulas(
|
||||
MetaJsonField field,
|
||||
ConfiguratorCreator creator,
|
||||
List<? extends ConfiguratorFormula> formulas) {
|
||||
for (ConfiguratorFormula formula : formulas) {
|
||||
MetaField formulaMetaField = formula.getMetaField();
|
||||
if ((formulaMetaField.getName() + "_" + creator.getId()).equals(field.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill {@link MetaJsonField#gridView} and {@link MetaJsonField#formView} in the given meta json
|
||||
* field. The default views name are using the axelor naming convention, here product
|
||||
*
|
||||
* @param metaField a meta field which is a relational field.
|
||||
* @param newField a meta json field which is a relational field.
|
||||
*/
|
||||
protected void completeDefaultGridAndForm(MetaField metaField, MetaJsonField newField) {
|
||||
String name = metaField.getTypeName();
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
return;
|
||||
}
|
||||
final Inflector inflector = Inflector.getInstance();
|
||||
String prefix = inflector.dasherize(name);
|
||||
newField.setGridView(prefix + "-grid");
|
||||
newField.setFormView(prefix + "-form");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill {@link MetaJsonField#selection} searching in java class code.
|
||||
*
|
||||
* @param metaField a meta field.
|
||||
* @param newField a meta json field.
|
||||
*/
|
||||
protected void completeSelection(MetaField metaField, MetaJsonField newField) {
|
||||
try {
|
||||
Field correspondingField =
|
||||
Class.forName(
|
||||
metaField.getMetaModel().getPackageName()
|
||||
+ "."
|
||||
+ metaField.getMetaModel().getName())
|
||||
.getDeclaredField(metaField.getName());
|
||||
Widget widget = correspondingField.getAnnotation(Widget.class);
|
||||
if (widget == null) {
|
||||
return;
|
||||
}
|
||||
String selection = widget.selection();
|
||||
if (!Strings.isNullOrEmpty(selection)) {
|
||||
newField.setSelection(selection);
|
||||
}
|
||||
} catch (ClassNotFoundException | NoSuchFieldException e) {
|
||||
TraceBackService.trace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the type of a field to a type of a json field.
|
||||
*
|
||||
* @param nameType type of a field
|
||||
* @return corresponding type of json field
|
||||
*/
|
||||
protected String typeToJsonType(String nameType) {
|
||||
if (nameType.equals("BigDecimal")) {
|
||||
return "decimal";
|
||||
} else if (nameType.equals("ManyToOne")) {
|
||||
return "many-to-one";
|
||||
} else if (nameType.equals("OneToMany")) {
|
||||
return "one-to-many";
|
||||
} else if (nameType.equals("OneToOne")) {
|
||||
return "one-to-one";
|
||||
} else if (nameType.equals("ManyToMany")) {
|
||||
return "many-to-many";
|
||||
} else {
|
||||
return nameType.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the indicators views attrs using the formulas.
|
||||
*
|
||||
* @param creator
|
||||
*/
|
||||
protected void updateIndicatorsAttrs(
|
||||
ConfiguratorCreator creator, List<? extends ConfiguratorFormula> formulas) {
|
||||
List<MetaJsonField> indicators = creator.getIndicators();
|
||||
for (MetaJsonField indicator : indicators) {
|
||||
for (ConfiguratorFormula formula : formulas) {
|
||||
updateIndicatorAttrs(creator, indicator, formula);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update one indicator attrs in the view, using the corresponding formula. Do nothing if
|
||||
* indicator and formula do not represent the same field.
|
||||
*
|
||||
* @param indicator
|
||||
* @param formula
|
||||
*/
|
||||
protected void updateIndicatorAttrs(
|
||||
ConfiguratorCreator creator, MetaJsonField indicator, ConfiguratorFormula formula) {
|
||||
|
||||
int scale = Beans.get(AppBaseService.class).getNbDecimalDigitForUnitPrice();
|
||||
String fieldName = indicator.getName();
|
||||
fieldName = fieldName.substring(0, fieldName.indexOf('_'));
|
||||
|
||||
MetaField metaField = formula.getMetaField();
|
||||
|
||||
if (!metaField.getName().equals(fieldName)) {
|
||||
return;
|
||||
}
|
||||
if (formula.getShowOnConfigurator()) {
|
||||
indicator.setHidden(false);
|
||||
setContextToJsonField(creator, indicator);
|
||||
} else {
|
||||
indicator.setHidden(true);
|
||||
}
|
||||
if (metaField.getTypeName().equals("BigDecimal")) {
|
||||
indicator.setPrecision(20);
|
||||
indicator.setScale(scale);
|
||||
} else if (!Strings.isNullOrEmpty(metaField.getRelationship())) {
|
||||
indicator.setTargetModel(
|
||||
Beans.get(MetaModelRepository.class).findByName(metaField.getTypeName()).getFullName());
|
||||
}
|
||||
}
|
||||
|
||||
public String getConfiguratorCreatorDomain() {
|
||||
User user = AuthUtils.getUser();
|
||||
Group group = user.getGroup();
|
||||
|
||||
List<ConfiguratorCreator> configuratorCreatorList =
|
||||
configuratorCreatorRepo.all().filter("self.isActive = true").fetch();
|
||||
|
||||
if (configuratorCreatorList == null || configuratorCreatorList.isEmpty()) {
|
||||
return "self.id in (0)";
|
||||
}
|
||||
|
||||
configuratorCreatorList.removeIf(
|
||||
creator ->
|
||||
!creator.getAuthorizedUserSet().contains(user)
|
||||
&& !creator.getAuthorizedGroupSet().contains(group));
|
||||
|
||||
return "self.id in (" + StringTool.getIdListString(configuratorCreatorList) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void authorizeUser(ConfiguratorCreator creator, User user) {
|
||||
creator.addAuthorizedUserSetItem(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void addRequiredFormulas(ConfiguratorCreator creator) {
|
||||
for (Field field : Product.class.getDeclaredFields()) {
|
||||
if (field.getAnnotation(NotNull.class) != null) {
|
||||
creator.addConfiguratorProductFormulaListItem(createProductFormula(field.getName()));
|
||||
}
|
||||
}
|
||||
for (Field field : SaleOrderLine.class.getDeclaredFields()) {
|
||||
if (field.getAnnotation(NotNull.class) != null) {
|
||||
creator.addConfiguratorSOLineFormulaListItem(createSOLineFormula(field.getName()));
|
||||
}
|
||||
}
|
||||
configuratorCreatorRepo.save(creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configurator product formula with an empty formula for the given MetaField.
|
||||
*
|
||||
* @param name the name of the meta field.
|
||||
* @return the created configurator formula.
|
||||
*/
|
||||
protected ConfiguratorProductFormula createProductFormula(String name) {
|
||||
ConfiguratorProductFormula configuratorProductFormula = new ConfiguratorProductFormula();
|
||||
completeFormula(configuratorProductFormula, name, "Product");
|
||||
return configuratorProductFormula;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configurator product formula with an empty formula for the given MetaField.
|
||||
*
|
||||
* @param name the meta field name.
|
||||
* @return the created configurator formula.
|
||||
*/
|
||||
protected ConfiguratorSOLineFormula createSOLineFormula(String name) {
|
||||
ConfiguratorSOLineFormula configuratorSOLineFormula = new ConfiguratorSOLineFormula();
|
||||
completeFormula(configuratorSOLineFormula, name, "SaleOrderLine");
|
||||
return configuratorSOLineFormula;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the given configurator formula with correct metafields.
|
||||
*
|
||||
* @param configuratorFormula a configurator formula.
|
||||
* @param name the meta field name.
|
||||
* @param metaFieldType the name of the model owning the meta field.
|
||||
*/
|
||||
protected void completeFormula(
|
||||
ConfiguratorFormula configuratorFormula, String name, String metaFieldType) {
|
||||
|
||||
configuratorFormula.setShowOnConfigurator(true);
|
||||
configuratorFormula.setFormula("");
|
||||
|
||||
Long modelId =
|
||||
JPA.all(MetaModel.class).filter("self.name = ?", metaFieldType).fetchOne().getId();
|
||||
MetaField metaField =
|
||||
JPA.all(MetaField.class)
|
||||
.filter("self.name = ? AND self.metaModel.id = ?", name, modelId)
|
||||
.fetchOne();
|
||||
configuratorFormula.setMetaField(metaField);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void activate(ConfiguratorCreator creator) {
|
||||
creator.setIsActive(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the context field to a json field. Allows to limit the json field to the configurator
|
||||
* having the right configurator creator.
|
||||
*
|
||||
* @param creator
|
||||
* @param field
|
||||
*/
|
||||
protected void setContextToJsonField(ConfiguratorCreator creator, MetaJsonField field) {
|
||||
final String fieldName = "configuratorCreator";
|
||||
final Class<?> modelClass;
|
||||
final String modelName = field.getModel();
|
||||
|
||||
try {
|
||||
modelClass = Class.forName(modelName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// this should not happen
|
||||
TraceBackService.trace(e);
|
||||
return;
|
||||
}
|
||||
final Mapper mapper = Mapper.of(modelClass);
|
||||
final Property property = mapper.getProperty(fieldName);
|
||||
final String target = property == null ? null : property.getTarget().getName();
|
||||
final String targetName = property == null ? null : property.getTargetName();
|
||||
|
||||
field.setContextField(fieldName);
|
||||
field.setContextFieldTarget(target);
|
||||
field.setContextFieldTargetName(targetName);
|
||||
field.setContextFieldValue(creator.getId().toString());
|
||||
field.setContextFieldTitle(creator.getName());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.exception.AxelorException;
|
||||
|
||||
public interface ConfiguratorFormulaService {
|
||||
|
||||
/**
|
||||
* Check if the written formula is valid.
|
||||
*
|
||||
* @param formula
|
||||
* @param creator
|
||||
*/
|
||||
void checkFormula(ConfiguratorFormula formula, ConfiguratorCreator creator)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Get the name of the given object. Use EntityHelper to get the right class name for proxy
|
||||
* classes.
|
||||
*
|
||||
* @param calculatedValue a result from groovy script.
|
||||
* @return the name of the class.
|
||||
*/
|
||||
String getCalculatedClassName(Object calculatedValue);
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.db.EntityHelper;
|
||||
import com.axelor.db.Model;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.script.GroovyScriptHelper;
|
||||
import com.axelor.script.ScriptBindings;
|
||||
|
||||
public class ConfiguratorFormulaServiceImpl implements ConfiguratorFormulaService {
|
||||
|
||||
@Override
|
||||
public void checkFormula(ConfiguratorFormula formula, ConfiguratorCreator creator)
|
||||
throws AxelorException {
|
||||
ScriptBindings defaultValueBindings =
|
||||
Beans.get(ConfiguratorCreatorService.class).getTestingValues(creator);
|
||||
Object result = new GroovyScriptHelper(defaultValueBindings).eval(formula.getFormula());
|
||||
String wantedTypeName = formula.getMetaField().getTypeName();
|
||||
if (result == null) {
|
||||
throw new AxelorException(
|
||||
formula,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_CREATOR_SCRIPT_ERROR));
|
||||
} else {
|
||||
if (!Beans.get(ConfiguratorService.class)
|
||||
.areCompatible(wantedTypeName, getCalculatedClassName(result))) {
|
||||
throw new AxelorException(
|
||||
formula,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_CREATOR_FORMULA_TYPE_ERROR),
|
||||
result.getClass().getSimpleName(),
|
||||
wantedTypeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCalculatedClassName(Object calculatedValue) {
|
||||
if (calculatedValue instanceof Model) {
|
||||
return EntityHelper.getEntityClass(calculatedValue).getSimpleName();
|
||||
} else {
|
||||
return calculatedValue.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.sale.db.Configurator;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.meta.db.MetaJsonField;
|
||||
import com.axelor.rpc.JsonContext;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import wslite.json.JSONException;
|
||||
|
||||
public interface ConfiguratorService {
|
||||
|
||||
/**
|
||||
* Update the value of indicators using {@link
|
||||
* com.axelor.apps.sale.db.ConfiguratorCreator#configuratorFormulaList} and the current values in
|
||||
* {@link Configurator#attributes}
|
||||
*
|
||||
* @param configurator
|
||||
* @param attributes
|
||||
* @param indicators @return the new values of indicators
|
||||
*/
|
||||
void updateIndicators(Configurator configurator, JsonContext attributes, JsonContext indicators)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Give the result of a formula, with the script variables defined in the values map.
|
||||
*
|
||||
* @param groovyFormula
|
||||
* @param values
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
Object computeFormula(String groovyFormula, JsonContext values) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Generate the product, and the bill of material if we are in the right module
|
||||
*
|
||||
* @param configurator
|
||||
* @param jsonAttributes
|
||||
* @param jsonIndicators
|
||||
*/
|
||||
void generate(Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
|
||||
throws AxelorException, NoSuchMethodException;
|
||||
|
||||
/**
|
||||
* Generate a product from the configurator
|
||||
*
|
||||
* @param configurator
|
||||
* @param jsonAttributes
|
||||
* @param jsonIndicators
|
||||
*/
|
||||
void generateProduct(
|
||||
Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException,
|
||||
JSONException, ClassNotFoundException, AxelorException;
|
||||
|
||||
/**
|
||||
* Generate a product, then generate a sale order line with the created product, then add this
|
||||
* line to the sale order.
|
||||
*
|
||||
* @param configurator
|
||||
* @param saleOrder
|
||||
* @param jsonAttributes
|
||||
* @param jsonIndicators
|
||||
*/
|
||||
void addLineToSaleOrder(
|
||||
Configurator configurator,
|
||||
SaleOrder saleOrder,
|
||||
JsonContext jsonAttributes,
|
||||
JsonContext jsonIndicators)
|
||||
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
|
||||
InvocationTargetException, AxelorException;
|
||||
|
||||
/**
|
||||
* Check if the calculated value type is the same as the indicator type.
|
||||
*
|
||||
* @param calculatedValue the return value of a script.
|
||||
* @param indicator an indicator.
|
||||
* @throws AxelorException if the type don't match.
|
||||
*/
|
||||
void checkType(Object calculatedValue, MetaJsonField indicator) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Return true if {@code fromClassName} is a class that can be converted to {@code
|
||||
* targetClassName}. <br>
|
||||
* Else return false.
|
||||
*/
|
||||
boolean areCompatible(String targetClassName, String fromClassName);
|
||||
}
|
||||
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.configurator;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.sale.db.Configurator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.apps.sale.db.ConfiguratorSOLineFormula;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.ConfiguratorRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.Model;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.db.MetaField;
|
||||
import com.axelor.meta.db.MetaJsonField;
|
||||
import com.axelor.meta.db.MetaSelectItem;
|
||||
import com.axelor.meta.db.repo.MetaFieldRepository;
|
||||
import com.axelor.meta.db.repo.MetaSelectItemRepository;
|
||||
import com.axelor.rpc.JsonContext;
|
||||
import com.axelor.script.GroovyScriptHelper;
|
||||
import com.axelor.script.ScriptHelper;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import groovy.lang.MissingPropertyException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfiguratorServiceImpl implements ConfiguratorService {
|
||||
|
||||
@Override
|
||||
public void updateIndicators(
|
||||
Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
|
||||
throws AxelorException {
|
||||
if (configurator.getConfiguratorCreator() == null) {
|
||||
return;
|
||||
}
|
||||
List<MetaJsonField> indicators = configurator.getConfiguratorCreator().getIndicators();
|
||||
for (MetaJsonField indicator : indicators) {
|
||||
try {
|
||||
Object calculatedValue =
|
||||
computeIndicatorValue(configurator, indicator.getName(), jsonAttributes);
|
||||
checkType(calculatedValue, indicator);
|
||||
jsonIndicators.put(indicator.getName(), calculatedValue);
|
||||
} catch (MissingPropertyException e) {
|
||||
// if a field is missing, the value needs to be set to null
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkType(Object calculatedValue, MetaJsonField indicator) throws AxelorException {
|
||||
if (calculatedValue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String wantedClassName;
|
||||
String wantedType = jsonTypeToType(indicator.getType());
|
||||
String calculatedValueClassName =
|
||||
Beans.get(ConfiguratorFormulaService.class).getCalculatedClassName(calculatedValue);
|
||||
if (wantedType.equals("ManyToOne")
|
||||
|| wantedType.equals("ManyToMany")
|
||||
|| wantedType.equals("OneToMany")
|
||||
|| wantedType.equals("Custom-ManyToOne")
|
||||
|| wantedType.equals("Custom-ManyToMany")
|
||||
|| wantedType.equals("Custom-OneToMany")) {
|
||||
// it is a relational field so we get the target model class
|
||||
String targetName = indicator.getTargetModel();
|
||||
// get only the class without the package
|
||||
wantedClassName = targetName.substring(targetName.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
wantedClassName = wantedType;
|
||||
}
|
||||
if (!areCompatible(wantedClassName, calculatedValueClassName)) {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_ON_GENERATING_TYPE_ERROR),
|
||||
indicator.getName().substring(0, indicator.getName().indexOf('_')),
|
||||
wantedClassName,
|
||||
calculatedValueClassName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the type of a json field to a type of a field.
|
||||
*
|
||||
* @param nameType type of a json field
|
||||
* @return corresponding type of field
|
||||
*/
|
||||
protected String jsonTypeToType(String nameType) {
|
||||
MetaSelectItem item =
|
||||
Beans.get(MetaSelectItemRepository.class)
|
||||
.all()
|
||||
.filter("self.select.name = :_jsonFieldType AND self.value = :_value")
|
||||
.bind("_jsonFieldType", "json.field.type")
|
||||
.bind("_value", nameType)
|
||||
.fetchOne();
|
||||
if (item == null) {
|
||||
return "";
|
||||
} else {
|
||||
return item.getTitle().equals("Decimal") ? "BigDecimal" : item.getTitle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we only generate a product.
|
||||
*
|
||||
* @param configurator
|
||||
* @param jsonAttributes
|
||||
* @param jsonIndicators
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void generate(
|
||||
Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
|
||||
throws AxelorException {
|
||||
generateProduct(configurator, jsonAttributes, jsonIndicators);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void generateProduct(
|
||||
Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
|
||||
throws AxelorException {
|
||||
|
||||
cleanIndicators(jsonIndicators);
|
||||
Mapper mapper = Mapper.of(Product.class);
|
||||
Product product = new Product();
|
||||
for (String key : jsonIndicators.keySet()) {
|
||||
mapper.set(product, key, jsonIndicators.get(key));
|
||||
}
|
||||
fixRelationalFields(product);
|
||||
if (product.getProductTypeSelect() == null) {
|
||||
product.setProductTypeSelect(ProductRepository.PRODUCT_TYPE_STORABLE);
|
||||
}
|
||||
|
||||
if (product.getCode() == null) {
|
||||
throw new AxelorException(
|
||||
configurator,
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_PRODUCT_MISSING_CODE));
|
||||
}
|
||||
if (product.getName() == null) {
|
||||
throw new AxelorException(
|
||||
configurator,
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_PRODUCT_MISSING_NAME));
|
||||
}
|
||||
|
||||
configurator.setProduct(product);
|
||||
product.setConfigurator(configurator);
|
||||
Beans.get(ProductRepository.class).save(product);
|
||||
}
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
@Override
|
||||
public void addLineToSaleOrder(
|
||||
Configurator configurator,
|
||||
SaleOrder saleOrder,
|
||||
JsonContext jsonAttributes,
|
||||
JsonContext jsonIndicators)
|
||||
throws AxelorException {
|
||||
|
||||
SaleOrderLine saleOrderLine;
|
||||
if (configurator.getConfiguratorCreator().getGenerateProduct()) {
|
||||
// generate sale order line from product
|
||||
saleOrderLine = new SaleOrderLine();
|
||||
saleOrderLine.setSaleOrder(saleOrder);
|
||||
generateProduct(configurator, jsonAttributes, jsonIndicators);
|
||||
|
||||
saleOrderLine.setProduct(configurator.getProduct());
|
||||
this.fillSaleOrderWithProduct(saleOrderLine);
|
||||
Beans.get(SaleOrderLineService.class)
|
||||
.computeValues(saleOrderLine.getSaleOrder(), saleOrderLine);
|
||||
} else {
|
||||
saleOrderLine =
|
||||
generateSaleOrderLine(configurator, jsonAttributes, jsonIndicators, saleOrder);
|
||||
}
|
||||
Beans.get(SaleOrderComputeService.class).computeSaleOrder(saleOrder);
|
||||
|
||||
Beans.get(SaleOrderRepository.class).save(saleOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill fields of sale order line from its product
|
||||
*
|
||||
* @param saleOrderLine
|
||||
*/
|
||||
protected void fillSaleOrderWithProduct(SaleOrderLine saleOrderLine) throws AxelorException {
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
if (saleOrderLine.getProduct() != null) {
|
||||
saleOrderLineService.computeProductInformation(
|
||||
saleOrderLine, saleOrderLine.getSaleOrder(), null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void overwriteFieldToUpdate(
|
||||
Configurator configurator, SaleOrderLine saleOrderLine, JsonContext attributes)
|
||||
throws AxelorException {
|
||||
// update a field if its formula has updateFromSelect to update
|
||||
// from configurator
|
||||
List<ConfiguratorSOLineFormula> formulas =
|
||||
configurator.getConfiguratorCreator().getConfiguratorSOLineFormulaList();
|
||||
if (formulas != null) {
|
||||
Mapper mapper = Mapper.of(SaleOrderLine.class);
|
||||
for (ConfiguratorSOLineFormula formula : formulas) {
|
||||
// exclude the product field
|
||||
if (formula.getUpdateFromSelect() == ConfiguratorRepository.UPDATE_FROM_CONFIGURATOR) {
|
||||
// we add "_1" because computeIndicatorValue expect an indicator name.
|
||||
Object valueToUpdate =
|
||||
computeIndicatorValue(
|
||||
configurator, formula.getMetaField().getName() + "_1", attributes);
|
||||
// if many to one, go search value in database.
|
||||
if ("ManyToOne".equals(formula.getMetaField().getRelationship())) {
|
||||
fixRelationalField(saleOrderLine, (Model) valueToUpdate, formula.getMetaField());
|
||||
} else {
|
||||
mapper.set(saleOrderLine, formula.getMetaField().getName(), valueToUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the value of one indicator. Using the corresponding formula and the values in {@link
|
||||
* Configurator#attributes}
|
||||
*
|
||||
* @param configurator
|
||||
* @param indicatorName
|
||||
* @param jsonAttributes
|
||||
* @return
|
||||
*/
|
||||
protected Object computeIndicatorValue(
|
||||
Configurator configurator, String indicatorName, JsonContext jsonAttributes) {
|
||||
ConfiguratorCreator creator = configurator.getConfiguratorCreator();
|
||||
List<? extends ConfiguratorFormula> formulas;
|
||||
if (creator.getGenerateProduct()) {
|
||||
formulas = creator.getConfiguratorProductFormulaList();
|
||||
} else {
|
||||
formulas = creator.getConfiguratorSOLineFormulaList();
|
||||
}
|
||||
String groovyFormula = null;
|
||||
for (ConfiguratorFormula formula : formulas) {
|
||||
String fieldName = indicatorName;
|
||||
fieldName = fieldName.substring(0, fieldName.indexOf('_'));
|
||||
MetaField metaField = formula.getMetaField();
|
||||
if (metaField.getName().equals(fieldName)) {
|
||||
groovyFormula = formula.getFormula();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (groovyFormula == null || jsonAttributes == null) {
|
||||
return null;
|
||||
}
|
||||
return computeFormula(groovyFormula, jsonAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object computeFormula(String groovyFormula, JsonContext values) {
|
||||
|
||||
ScriptHelper scriptHelper = new GroovyScriptHelper(values);
|
||||
|
||||
return scriptHelper.eval(groovyFormula);
|
||||
}
|
||||
|
||||
public boolean areCompatible(String targetClassName, String fromClassName) {
|
||||
return targetClassName.equals(fromClassName)
|
||||
|| (targetClassName.equals("BigDecimal") && fromClassName.equals("Integer"))
|
||||
|| (targetClassName.equals("BigDecimal") && fromClassName.equals("String"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sale order line from the configurator
|
||||
*
|
||||
* @param configurator
|
||||
* @param jsonAttributes
|
||||
* @param jsonIndicators
|
||||
* @param saleOrder
|
||||
* @return
|
||||
*/
|
||||
protected SaleOrderLine generateSaleOrderLine(
|
||||
Configurator configurator,
|
||||
JsonContext jsonAttributes,
|
||||
JsonContext jsonIndicators,
|
||||
SaleOrder saleOrder)
|
||||
throws AxelorException {
|
||||
cleanIndicators(jsonIndicators);
|
||||
SaleOrderLine saleOrderLine = Mapper.toBean(SaleOrderLine.class, jsonIndicators);
|
||||
saleOrderLine.setSaleOrder(saleOrder);
|
||||
fixRelationalFields(saleOrderLine);
|
||||
this.fillSaleOrderWithProduct(saleOrderLine);
|
||||
this.overwriteFieldToUpdate(configurator, saleOrderLine, jsonAttributes);
|
||||
if (saleOrderLine.getProductName() == null) {
|
||||
throw new AxelorException(
|
||||
configurator,
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.CONFIGURATOR_SALE_ORDER_LINE_MISSING_PRODUCT_NAME));
|
||||
}
|
||||
saleOrderLine = Beans.get(SaleOrderLineRepository.class).save(saleOrderLine);
|
||||
Beans.get(SaleOrderLineService.class)
|
||||
.computeValues(saleOrderLine.getSaleOrder(), saleOrderLine);
|
||||
return saleOrderLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicator keys have this pattern : {field name}_{id} Transform the keys to have only the {field
|
||||
* name}.
|
||||
*
|
||||
* @param jsonIndicators
|
||||
*/
|
||||
protected void cleanIndicators(JsonContext jsonIndicators) {
|
||||
Map<String, Object> newKeyMap = new HashMap<>();
|
||||
for (Map.Entry entry : jsonIndicators.entrySet()) {
|
||||
String oldKey = entry.getKey().toString();
|
||||
newKeyMap.put(oldKey.substring(0, oldKey.indexOf('_')), entry.getValue());
|
||||
}
|
||||
jsonIndicators.clear();
|
||||
jsonIndicators.putAll(newKeyMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix relational fields of a product or a sale order line generated from a configurator. This
|
||||
* method may become useless on a future ADK update.
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
protected void fixRelationalFields(Model model) throws AxelorException {
|
||||
// get all many to one fields
|
||||
List<MetaField> manyToOneFields =
|
||||
Beans.get(MetaFieldRepository.class)
|
||||
.all()
|
||||
.filter("self.metaModel.name = :name " + "AND self.relationship = 'ManyToOne'")
|
||||
.bind("name", model.getClass().getSimpleName())
|
||||
.fetch();
|
||||
|
||||
Mapper mapper = Mapper.of(model.getClass());
|
||||
for (MetaField manyToOneField : manyToOneFields) {
|
||||
Model manyToOneValue = (Model) mapper.get(model, manyToOneField.getName());
|
||||
fixRelationalField(model, manyToOneValue, manyToOneField);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fixRelationalField(Model parentModel, Model value, MetaField metaField)
|
||||
throws AxelorException {
|
||||
if (value != null) {
|
||||
Mapper mapper = Mapper.of(parentModel.getClass());
|
||||
try {
|
||||
String className =
|
||||
String.format("%s.%s", metaField.getPackageName(), metaField.getTypeName());
|
||||
Model manyToOneDbValue = JPA.find((Class<Model>) Class.forName(className), value.getId());
|
||||
mapper.set(parentModel, metaField.getName(), manyToOneDbValue);
|
||||
} catch (Exception e) {
|
||||
throw new AxelorException(
|
||||
Configurator.class, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.crm.db.Opportunity;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
|
||||
public interface OpportunitySaleOrderService {
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public SaleOrder createSaleOrderFromOpportunity(Opportunity opportunity) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.db.Currency;
|
||||
import com.axelor.apps.base.db.repo.PriceListRepository;
|
||||
import com.axelor.apps.base.service.PartnerPriceListService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.crm.db.Opportunity;
|
||||
import com.axelor.apps.crm.db.repo.OpportunityRepository;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
|
||||
public class OpportunitySaleOrderServiceImpl implements OpportunitySaleOrderService {
|
||||
|
||||
@Inject protected SaleOrderCreateService saleOrderCreateService;
|
||||
|
||||
@Inject protected SaleOrderRepository saleOrderRepo;
|
||||
|
||||
@Inject protected AppBaseService appBaseService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public SaleOrder createSaleOrderFromOpportunity(Opportunity opportunity) throws AxelorException {
|
||||
Currency currency;
|
||||
if (opportunity.getCurrency() != null) {
|
||||
currency = opportunity.getCurrency();
|
||||
} else if (opportunity.getPartner() != null && opportunity.getPartner().getCurrency() != null) {
|
||||
currency = opportunity.getPartner().getCurrency();
|
||||
} else {
|
||||
currency = opportunity.getCompany().getCurrency();
|
||||
}
|
||||
|
||||
SaleOrder saleOrder = createSaleOrder(opportunity, currency);
|
||||
|
||||
opportunity.addSaleOrderListItem(saleOrder);
|
||||
|
||||
if (opportunity.getSalesStageSelect() < OpportunityRepository.SALES_STAGE_PROPOSITION) {
|
||||
opportunity.setSalesStageSelect(OpportunityRepository.SALES_STAGE_PROPOSITION);
|
||||
}
|
||||
|
||||
saleOrder.setTradingName(opportunity.getTradingName());
|
||||
|
||||
saleOrderRepo.save(saleOrder);
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
protected SaleOrder createSaleOrder(Opportunity opportunity, Currency currency)
|
||||
throws AxelorException {
|
||||
return saleOrderCreateService.createSaleOrder(
|
||||
opportunity.getUser(),
|
||||
opportunity.getCompany(),
|
||||
null,
|
||||
currency,
|
||||
null,
|
||||
opportunity.getName(),
|
||||
null,
|
||||
appBaseService.getTodayDate(),
|
||||
Beans.get(PartnerPriceListService.class)
|
||||
.getDefaultPriceList(opportunity.getPartner(), PriceListRepository.TYPE_SALE),
|
||||
opportunity.getPartner(),
|
||||
opportunity.getTeam());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public interface SaleOrderComputeService {
|
||||
|
||||
public SaleOrder _computeSaleOrderLineList(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
public SaleOrder computeSaleOrder(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Peupler un devis.
|
||||
*
|
||||
* <p>Cette fonction permet de déterminer les tva d'un devis.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void _populateSaleOrder(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Calculer le montant d'une facture.
|
||||
*
|
||||
* <p>Le calcul est basé sur les lignes de TVA préalablement créées.
|
||||
*
|
||||
* @param invoice
|
||||
* @param vatLines
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void _computeSaleOrder(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Permet de réinitialiser la liste des lignes de TVA
|
||||
*
|
||||
* @param saleOrder Un devis
|
||||
*/
|
||||
public void initSaleOrderLineTaxList(SaleOrder saleOrder);
|
||||
|
||||
/**
|
||||
* Return the total price, computed from the lines. This price is usually equals to {@link
|
||||
* SaleOrder#exTaxTotal} but not in all cases.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @return total price from the sale order lines
|
||||
*/
|
||||
public BigDecimal getTotalSaleOrderPrice(SaleOrder saleOrder);
|
||||
|
||||
public List<SaleOrderLine> removeSubLines(List<SaleOrderLine> lines);
|
||||
}
|
||||
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.db.AppSale;
|
||||
import com.axelor.apps.sale.db.AdvancePayment;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.SaleOrderLineTax;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderComputeServiceImpl implements SaleOrderComputeService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected SaleOrderLineService saleOrderLineService;
|
||||
protected SaleOrderLineTaxService saleOrderLineTaxService;
|
||||
|
||||
@Inject
|
||||
public SaleOrderComputeServiceImpl(
|
||||
SaleOrderLineService saleOrderLineService, SaleOrderLineTaxService saleOrderLineTaxService) {
|
||||
|
||||
this.saleOrderLineService = saleOrderLineService;
|
||||
this.saleOrderLineTaxService = saleOrderLineTaxService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder _computeSaleOrderLineList(SaleOrder saleOrder) throws AxelorException {
|
||||
|
||||
if (saleOrder.getSaleOrderLineList() != null) {
|
||||
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
|
||||
saleOrderLine.setCompanyExTaxTotal(
|
||||
saleOrderLineService.getAmountInCompanyCurrency(
|
||||
saleOrderLine.getExTaxTotal(), saleOrder));
|
||||
}
|
||||
}
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder computeSaleOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
|
||||
AppSale appSale = Beans.get(AppSaleService.class).getAppSale();
|
||||
if (appSale != null && appSale.getActive() && appSale.getProductPackMgt()) {
|
||||
this._addPackLines(saleOrder);
|
||||
}
|
||||
|
||||
this.initSaleOrderLineTaxList(saleOrder);
|
||||
|
||||
this._computeSaleOrderLineList(saleOrder);
|
||||
|
||||
this._populateSaleOrder(saleOrder);
|
||||
|
||||
this._computeSaleOrder(saleOrder);
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peupler un devis.
|
||||
*
|
||||
* <p>Cette fonction permet de déterminer les tva d'un devis.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @throws AxelorException
|
||||
*/
|
||||
@Override
|
||||
public void _populateSaleOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
if (saleOrder.getSaleOrderLineList() == null) {
|
||||
saleOrder.setSaleOrderLineList(new ArrayList<>());
|
||||
}
|
||||
|
||||
if (saleOrder.getSaleOrderLineTaxList() == null) {
|
||||
saleOrder.setSaleOrderLineTaxList(new ArrayList<>());
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Peupler un devis => lignes de devis: {} ",
|
||||
new Object[] {saleOrder.getSaleOrderLineList().size()});
|
||||
|
||||
// create Tva lines
|
||||
if (saleOrder.getClientPartner() != null) {
|
||||
saleOrder
|
||||
.getSaleOrderLineTaxList()
|
||||
.addAll(
|
||||
saleOrderLineTaxService.createsSaleOrderLineTax(
|
||||
saleOrder, saleOrder.getSaleOrderLineList()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the sale order total amounts
|
||||
*
|
||||
* @param saleOrder
|
||||
* @throws AxelorException
|
||||
*/
|
||||
@Override
|
||||
public void _computeSaleOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
|
||||
saleOrder.setExTaxTotal(BigDecimal.ZERO);
|
||||
saleOrder.setCompanyExTaxTotal(BigDecimal.ZERO);
|
||||
saleOrder.setTaxTotal(BigDecimal.ZERO);
|
||||
saleOrder.setInTaxTotal(BigDecimal.ZERO);
|
||||
|
||||
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
|
||||
saleOrder.setExTaxTotal(saleOrder.getExTaxTotal().add(saleOrderLine.getExTaxTotal()));
|
||||
|
||||
// In the company accounting currency
|
||||
saleOrder.setCompanyExTaxTotal(
|
||||
saleOrder.getCompanyExTaxTotal().add(saleOrderLine.getCompanyExTaxTotal()));
|
||||
}
|
||||
|
||||
for (SaleOrderLineTax saleOrderLineVat : saleOrder.getSaleOrderLineTaxList()) {
|
||||
|
||||
// In the sale order currency
|
||||
saleOrder.setTaxTotal(saleOrder.getTaxTotal().add(saleOrderLineVat.getTaxTotal()));
|
||||
}
|
||||
|
||||
saleOrder.setInTaxTotal(saleOrder.getExTaxTotal().add(saleOrder.getTaxTotal()));
|
||||
saleOrder.setAdvanceTotal(computeTotalAdvancePayment(saleOrder));
|
||||
logger.debug(
|
||||
"Montant de la facture: HTT = {}, HT = {}, Taxe = {}, TTC = {}",
|
||||
new Object[] {
|
||||
saleOrder.getExTaxTotal(), saleOrder.getTaxTotal(), saleOrder.getInTaxTotal()
|
||||
});
|
||||
}
|
||||
|
||||
protected BigDecimal computeTotalAdvancePayment(SaleOrder saleOrder) {
|
||||
List<AdvancePayment> advancePaymentList = saleOrder.getAdvancePaymentList();
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
if (advancePaymentList == null || advancePaymentList.isEmpty()) {
|
||||
return total;
|
||||
}
|
||||
for (AdvancePayment advancePayment : advancePaymentList) {
|
||||
total = total.add(advancePayment.getAmount());
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private void _addPackLines(SaleOrder saleOrder) {
|
||||
|
||||
if (saleOrder.getSaleOrderLineList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<SaleOrderLine> saleOrderLines = saleOrder.getSaleOrderLineList();
|
||||
|
||||
List<SaleOrderLine> lines = new ArrayList<SaleOrderLine>();
|
||||
lines.addAll(saleOrderLines);
|
||||
for (SaleOrderLine line : lines) {
|
||||
if (line.getSubLineList() == null || line.getSubLineList().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (SaleOrderLine subLine : line.getSubLineList()) {
|
||||
if (subLine.getSaleOrder() == null) {
|
||||
saleOrderLines.add(subLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de réinitialiser la liste des lignes de TVA
|
||||
*
|
||||
* @param saleOrder Un devis
|
||||
*/
|
||||
@Override
|
||||
public void initSaleOrderLineTaxList(SaleOrder saleOrder) {
|
||||
|
||||
if (saleOrder.getSaleOrderLineTaxList() == null) {
|
||||
saleOrder.setSaleOrderLineTaxList(new ArrayList<SaleOrderLineTax>());
|
||||
} else {
|
||||
saleOrder.getSaleOrderLineTaxList().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getTotalSaleOrderPrice(SaleOrder saleOrder) {
|
||||
BigDecimal price = BigDecimal.ZERO;
|
||||
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
|
||||
price = price.add(saleOrderLine.getQty().multiply(saleOrderLine.getPriceDiscounted()));
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SaleOrderLine> removeSubLines(List<SaleOrderLine> soLines) {
|
||||
|
||||
if (soLines == null) {
|
||||
return soLines;
|
||||
}
|
||||
|
||||
List<SaleOrderLine> subLines = new ArrayList<SaleOrderLine>();
|
||||
for (SaleOrderLine packLine : soLines) {
|
||||
if (packLine.getTypeSelect() == 2 && packLine.getSubLineList() != null) {
|
||||
packLine.getSubLineList().removeIf(it -> it.getId() != null && !soLines.contains(it));
|
||||
packLine.setTotalPack(
|
||||
packLine
|
||||
.getSubLineList()
|
||||
.stream()
|
||||
.map(it -> it.getExTaxTotal())
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||
subLines.addAll(packLine.getSubLineList());
|
||||
}
|
||||
}
|
||||
Iterator<SaleOrderLine> lines = soLines.iterator();
|
||||
|
||||
while (lines.hasNext()) {
|
||||
SaleOrderLine subLine = lines.next();
|
||||
if (subLine.getId() != null
|
||||
&& subLine.getParentLine() != null
|
||||
&& !subLines.contains(subLine)) {
|
||||
lines.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return soLines;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Currency;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.PriceList;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.auth.db.User;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.team.db.Team;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface SaleOrderCreateService {
|
||||
/**
|
||||
* Initialize a new sale order
|
||||
*
|
||||
* @param salemanUser User recorded as salesman on order, if <code>null</code>, will be set to
|
||||
* current user.
|
||||
* @param company Company bound to the order, if <code>null</code>, will be bound to salesman
|
||||
* active company.
|
||||
* @param contactPartner Customer contact to assign to the user, might be <code>null</code>.
|
||||
* @param currency Order's currency, should not be <code>null</code>.
|
||||
* @param deliveryDate Expected delivery date for order (might be <code>null</code>).
|
||||
* @param internalReference Unused (…)
|
||||
* @param externalReference Client reference for order, if any
|
||||
* @param orderDate Date of order (if <code>null</code>, will be set to today's date).
|
||||
* @param priceList Pricelist to use, if <code>null</code>, will default to partner's default
|
||||
* price list.
|
||||
* @param clientPartner Customer bound to the order, should not be <code>null</code>
|
||||
* @param team Team managing the order, if <code>null</code>, will default to salesman active
|
||||
* team.
|
||||
* @return The created order
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public SaleOrder createSaleOrder(
|
||||
User salemanUser,
|
||||
Company company,
|
||||
Partner contactPartner,
|
||||
Currency currency,
|
||||
LocalDate deliveryDate,
|
||||
String internalReference,
|
||||
String externalReference,
|
||||
LocalDate orderDate,
|
||||
PriceList priceList,
|
||||
Partner clientPartner,
|
||||
Team team)
|
||||
throws AxelorException;
|
||||
|
||||
public SaleOrder createSaleOrder(Company company) throws AxelorException;
|
||||
|
||||
public SaleOrder mergeSaleOrders(
|
||||
List<SaleOrder> saleOrderList,
|
||||
Currency currency,
|
||||
Partner clientPartner,
|
||||
Company company,
|
||||
Partner contactPartner,
|
||||
PriceList priceList,
|
||||
Team team)
|
||||
throws AxelorException;
|
||||
|
||||
@Transactional
|
||||
public SaleOrder createTemplate(SaleOrder context);
|
||||
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public SaleOrder createSaleOrder(
|
||||
SaleOrder context, Currency wizardCurrency, PriceList wizardPriceList) throws AxelorException;
|
||||
|
||||
public void updateSaleOrderLineList(SaleOrder saleOrder) throws AxelorException;
|
||||
}
|
||||
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Currency;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.PriceList;
|
||||
import com.axelor.apps.base.db.repo.PriceListRepository;
|
||||
import com.axelor.apps.base.service.PartnerPriceListService;
|
||||
import com.axelor.apps.base.service.PartnerService;
|
||||
import com.axelor.apps.base.service.TradingNameService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.auth.AuthUtils;
|
||||
import com.axelor.auth.db.User;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.team.db.Team;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderCreateServiceImpl implements SaleOrderCreateService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected PartnerService partnerService;
|
||||
protected SaleOrderRepository saleOrderRepo;
|
||||
protected AppSaleService appSaleService;
|
||||
protected SaleOrderService saleOrderService;
|
||||
protected SaleOrderComputeService saleOrderComputeService;
|
||||
|
||||
@Inject
|
||||
public SaleOrderCreateServiceImpl(
|
||||
PartnerService partnerService,
|
||||
SaleOrderRepository saleOrderRepo,
|
||||
AppSaleService appSaleService,
|
||||
SaleOrderService saleOrderService,
|
||||
SaleOrderComputeService saleOrderComputeService) {
|
||||
|
||||
this.partnerService = partnerService;
|
||||
this.saleOrderRepo = saleOrderRepo;
|
||||
this.appSaleService = appSaleService;
|
||||
this.saleOrderService = saleOrderService;
|
||||
this.saleOrderComputeService = saleOrderComputeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder createSaleOrder(Company company) throws AxelorException {
|
||||
SaleOrder saleOrder = new SaleOrder();
|
||||
saleOrder.setCreationDate(appSaleService.getTodayDate());
|
||||
if (company != null) {
|
||||
saleOrder.setCompany(company);
|
||||
saleOrder.setCurrency(company.getCurrency());
|
||||
}
|
||||
saleOrder.setSalemanUser(AuthUtils.getUser());
|
||||
saleOrder.setTeam(saleOrder.getSalemanUser().getActiveTeam());
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_DRAFT_QUOTATION);
|
||||
saleOrderService.computeEndOfValidityDate(saleOrder);
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder createSaleOrder(
|
||||
User salemanUser,
|
||||
Company company,
|
||||
Partner contactPartner,
|
||||
Currency currency,
|
||||
LocalDate deliveryDate,
|
||||
String internalReference,
|
||||
String externalReference,
|
||||
LocalDate orderDate,
|
||||
PriceList priceList,
|
||||
Partner clientPartner,
|
||||
Team team)
|
||||
throws AxelorException {
|
||||
|
||||
logger.debug(
|
||||
"Création d'un devis client : Société = {}, Reference externe = {}, Client = {}",
|
||||
new Object[] {company, externalReference, clientPartner.getFullName()});
|
||||
|
||||
SaleOrder saleOrder = new SaleOrder();
|
||||
saleOrder.setClientPartner(clientPartner);
|
||||
saleOrder.setCreationDate(appSaleService.getTodayDate());
|
||||
saleOrder.setContactPartner(contactPartner);
|
||||
saleOrder.setCurrency(currency);
|
||||
saleOrder.setExternalReference(externalReference);
|
||||
saleOrder.setDeliveryDate(deliveryDate);
|
||||
saleOrder.setOrderDate(orderDate);
|
||||
|
||||
saleOrder.setPrintingSettings(
|
||||
Beans.get(TradingNameService.class).getDefaultPrintingSettings(null, company));
|
||||
|
||||
if (salemanUser == null) {
|
||||
salemanUser = AuthUtils.getUser();
|
||||
}
|
||||
saleOrder.setSalemanUser(salemanUser);
|
||||
|
||||
if (team == null) {
|
||||
team = salemanUser.getActiveTeam();
|
||||
}
|
||||
saleOrder.setTeam(team);
|
||||
|
||||
if (company == null) {
|
||||
company = salemanUser.getActiveCompany();
|
||||
}
|
||||
saleOrder.setCompany(company);
|
||||
|
||||
saleOrder.setMainInvoicingAddress(partnerService.getInvoicingAddress(clientPartner));
|
||||
saleOrder.setDeliveryAddress(partnerService.getDeliveryAddress(clientPartner));
|
||||
|
||||
saleOrderService.computeAddressStr(saleOrder);
|
||||
|
||||
if (priceList == null) {
|
||||
priceList =
|
||||
Beans.get(PartnerPriceListService.class)
|
||||
.getDefaultPriceList(clientPartner, PriceListRepository.TYPE_SALE);
|
||||
}
|
||||
saleOrder.setPriceList(priceList);
|
||||
|
||||
saleOrder.setSaleOrderLineList(new ArrayList<>());
|
||||
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_DRAFT_QUOTATION);
|
||||
|
||||
saleOrderService.computeEndOfValidityDate(saleOrder);
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public SaleOrder mergeSaleOrders(
|
||||
List<SaleOrder> saleOrderList,
|
||||
Currency currency,
|
||||
Partner clientPartner,
|
||||
Company company,
|
||||
Partner contactPartner,
|
||||
PriceList priceList,
|
||||
Team team)
|
||||
throws AxelorException {
|
||||
|
||||
String numSeq = "";
|
||||
String externalRef = "";
|
||||
for (SaleOrder saleOrderLocal : saleOrderList) {
|
||||
if (!numSeq.isEmpty()) {
|
||||
numSeq += "-";
|
||||
}
|
||||
numSeq += saleOrderLocal.getSaleOrderSeq();
|
||||
|
||||
if (!externalRef.isEmpty()) {
|
||||
externalRef += "|";
|
||||
}
|
||||
if (saleOrderLocal.getExternalReference() != null) {
|
||||
externalRef += saleOrderLocal.getExternalReference();
|
||||
}
|
||||
}
|
||||
|
||||
SaleOrder saleOrderMerged =
|
||||
this.createSaleOrder(
|
||||
AuthUtils.getUser(),
|
||||
company,
|
||||
contactPartner,
|
||||
currency,
|
||||
null,
|
||||
numSeq,
|
||||
externalRef,
|
||||
LocalDate.now(),
|
||||
priceList,
|
||||
clientPartner,
|
||||
team);
|
||||
|
||||
this.attachToNewSaleOrder(saleOrderList, saleOrderMerged);
|
||||
|
||||
saleOrderComputeService.computeSaleOrder(saleOrderMerged);
|
||||
|
||||
saleOrderRepo.save(saleOrderMerged);
|
||||
|
||||
this.removeOldSaleOrders(saleOrderList);
|
||||
|
||||
return saleOrderMerged;
|
||||
}
|
||||
|
||||
// Attachment of all sale order lines to new sale order
|
||||
protected void attachToNewSaleOrder(List<SaleOrder> saleOrderList, SaleOrder saleOrderMerged) {
|
||||
for (SaleOrder saleOrder : saleOrderList) {
|
||||
int countLine = 1;
|
||||
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
|
||||
saleOrderLine.setSequence(countLine * 10);
|
||||
saleOrderMerged.addSaleOrderLineListItem(saleOrderLine);
|
||||
countLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old sale orders after merge
|
||||
protected void removeOldSaleOrders(List<SaleOrder> saleOrderList) {
|
||||
for (SaleOrder saleOrder : saleOrderList) {
|
||||
saleOrderRepo.remove(saleOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public SaleOrder createSaleOrder(
|
||||
SaleOrder context, Currency wizardCurrency, PriceList wizardPriceList)
|
||||
throws AxelorException {
|
||||
SaleOrder copy = saleOrderRepo.copy(context, true);
|
||||
copy.setCreationDate(appSaleService.getTodayDate());
|
||||
copy.setCurrency(wizardCurrency);
|
||||
copy.setPriceList(wizardPriceList);
|
||||
|
||||
saleOrderService.computeEndOfValidityDate(copy);
|
||||
|
||||
this.updateSaleOrderLineList(copy);
|
||||
|
||||
saleOrderComputeService.computeSaleOrder(copy);
|
||||
|
||||
copy.setTemplate(false);
|
||||
copy.setTemplateUser(null);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void updateSaleOrderLineList(SaleOrder saleOrder) throws AxelorException {
|
||||
List<SaleOrderLine> saleOrderLineList = saleOrder.getSaleOrderLineList();
|
||||
if (saleOrderLineList != null) {
|
||||
for (SaleOrderLine saleOrderLine : saleOrderLineList) {
|
||||
Beans.get(SaleOrderLineService.class)
|
||||
.fillPrice(saleOrderLine, saleOrder, saleOrderLine.getPackPriceSelect());
|
||||
Beans.get(SaleOrderLineService.class).computeValues(saleOrder, saleOrderLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SaleOrder createTemplate(SaleOrder context) {
|
||||
SaleOrder copy = saleOrderRepo.copy(context, true);
|
||||
copy.setTemplate(true);
|
||||
copy.setTemplateUser(AuthUtils.getUser());
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.account.db.TaxLine;
|
||||
import com.axelor.apps.base.db.PriceList;
|
||||
import com.axelor.apps.base.db.PriceListLine;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SaleOrderLineService {
|
||||
|
||||
/**
|
||||
* Update all fields of the sale order line from the product.
|
||||
*
|
||||
* @param saleOrderLine
|
||||
* @param saleOrder
|
||||
*/
|
||||
void computeProductInformation(
|
||||
SaleOrderLine saleOrderLine, SaleOrder saleOrder, Integer packPriceSelect)
|
||||
throws AxelorException;
|
||||
|
||||
SaleOrderLine resetProductInformation(SaleOrderLine line);
|
||||
|
||||
/**
|
||||
* Compute totals from a sale order line
|
||||
*
|
||||
* @param saleOrder
|
||||
* @param saleOrderLine
|
||||
* @return
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public Map<String, BigDecimal> computeValues(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Compute the excluded tax total amount of a sale order line.
|
||||
*
|
||||
* @param saleOrderLine the sale order line which total amount you want to compute.
|
||||
* @return The excluded tax total amount.
|
||||
*/
|
||||
public BigDecimal computeAmount(SaleOrderLine saleOrderLine);
|
||||
|
||||
/**
|
||||
* Compute the excluded tax total amount of a sale order line.
|
||||
*
|
||||
* @param quantity The quantity.
|
||||
* @param price The unit price.
|
||||
* @return The excluded tax total amount.
|
||||
*/
|
||||
public BigDecimal computeAmount(BigDecimal quantity, BigDecimal price);
|
||||
|
||||
public BigDecimal getExTaxUnitPrice(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, TaxLine taxLine) throws AxelorException;
|
||||
|
||||
public BigDecimal getInTaxUnitPrice(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, TaxLine taxLine) throws AxelorException;
|
||||
|
||||
public TaxLine getTaxLine(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException;
|
||||
|
||||
public BigDecimal getAmountInCompanyCurrency(BigDecimal exTaxTotal, SaleOrder saleOrder)
|
||||
throws AxelorException;
|
||||
|
||||
public BigDecimal getCompanyCostPrice(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException;
|
||||
|
||||
public PriceListLine getPriceListLine(
|
||||
SaleOrderLine saleOrderLine, PriceList priceList, BigDecimal price);
|
||||
|
||||
/**
|
||||
* Compute and return the discounted price of a sale order line.
|
||||
*
|
||||
* @param saleOrderLine the sale order line.
|
||||
* @param inAti whether or not the sale order line (and thus the discounted price) includes taxes.
|
||||
* @return the discounted price of the line, including taxes if inAti is true.
|
||||
*/
|
||||
public BigDecimal computeDiscount(SaleOrderLine saleOrderLine, Boolean inAti);
|
||||
|
||||
/**
|
||||
* Convert a product's unit price from incl. tax to ex. tax or the other way round.
|
||||
*
|
||||
* <p>If the price is ati, it will be converted to ex. tax, and if it isn't it will be converted
|
||||
* to ati.
|
||||
*
|
||||
* @param priceIsAti a boolean indicating if the price is ati.
|
||||
* @param taxLine the tax to apply.
|
||||
* @param price the unit price to convert.
|
||||
* @return the converted price as a BigDecimal.
|
||||
*/
|
||||
public BigDecimal convertUnitPrice(Boolean inAti, TaxLine taxLine, BigDecimal price);
|
||||
|
||||
public Map<String, Object> getDiscountsFromPriceLists(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, BigDecimal price);
|
||||
|
||||
public int getDiscountTypeSelect(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, BigDecimal price);
|
||||
|
||||
public Unit getSaleUnit(SaleOrderLine saleOrderLine);
|
||||
|
||||
public BigDecimal computeTotalPack(SaleOrderLine saleOrderLine);
|
||||
|
||||
public SaleOrder getSaleOrder(Context context);
|
||||
|
||||
public Map<String, BigDecimal> computeSubMargin(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException;
|
||||
|
||||
public BigDecimal getAvailableStock(SaleOrder saleOrder, SaleOrderLine saleOrderLine);
|
||||
|
||||
public BigDecimal getAllocatedStock(SaleOrder saleOrder, SaleOrderLine saleOrderLine);
|
||||
|
||||
public void checkMultipleQty(SaleOrderLine saleOrderLine, ActionResponse response);
|
||||
|
||||
/**
|
||||
* Fill price based on packPriceSelect only for packLine or subline. Works normal for standard
|
||||
* line.
|
||||
*
|
||||
* @param saleOrderLine
|
||||
* @param saleOrder
|
||||
* @param packPriceSelect
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void fillPrice(SaleOrderLine saleOrderLine, SaleOrder saleOrder, Integer packPriceSelect)
|
||||
throws AxelorException;
|
||||
|
||||
public boolean checkTaxRequired(SaleOrderLine saleOrderLine, Integer packPriceSelect);
|
||||
}
|
||||
@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.account.db.FiscalPosition;
|
||||
import com.axelor.apps.account.db.Tax;
|
||||
import com.axelor.apps.account.db.TaxEquiv;
|
||||
import com.axelor.apps.account.db.TaxLine;
|
||||
import com.axelor.apps.base.db.PriceList;
|
||||
import com.axelor.apps.base.db.PriceListLine;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.Unit;
|
||||
import com.axelor.apps.base.db.repo.PriceListLineRepository;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.CurrencyService;
|
||||
import com.axelor.apps.base.service.PriceListService;
|
||||
import com.axelor.apps.base.service.ProductMultipleQtyService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.base.service.tax.AccountManagementService;
|
||||
import com.axelor.apps.base.service.tax.FiscalPositionService;
|
||||
import com.axelor.apps.sale.db.PackLine;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderLineServiceImpl implements SaleOrderLineService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Inject protected CurrencyService currencyService;
|
||||
|
||||
@Inject protected PriceListService priceListService;
|
||||
|
||||
@Inject protected ProductMultipleQtyService productMultipleQtyService;
|
||||
|
||||
@Inject protected AppSaleService appSaleService;
|
||||
|
||||
@Inject protected AccountManagementService accountManagementService;
|
||||
|
||||
@Override
|
||||
public void computeProductInformation(
|
||||
SaleOrderLine saleOrderLine, SaleOrder saleOrder, Integer packPriceSelect)
|
||||
throws AxelorException {
|
||||
Product product = saleOrderLine.getProduct();
|
||||
saleOrderLine.setProductName(product.getName());
|
||||
saleOrderLine.setPvg(product.getPvg());
|
||||
saleOrderLine.setUg(product.getUg());
|
||||
saleOrderLine.setStklim(product.getStklim());
|
||||
saleOrderLine.setPpa(product.getPpa());
|
||||
saleOrderLine.setShp(product.getShp());
|
||||
saleOrderLine.setUnit(this.getSaleUnit(saleOrderLine));
|
||||
if (appSaleService.getAppSale().getIsEnabledProductDescriptionCopy()) {
|
||||
saleOrderLine.setDescription(product.getDescription());
|
||||
}
|
||||
|
||||
saleOrderLine.setTypeSelect(SaleOrderLineRepository.TYPE_NORMAL);
|
||||
saleOrderLine.setSubLineList(null);
|
||||
saleOrderLine.setPackPriceSelect(null);
|
||||
saleOrderLine.setDiscountTypeSelect(PriceListLineRepository.AMOUNT_TYPE_NONE);
|
||||
|
||||
if (appSaleService.getAppSale().getProductPackMgt()
|
||||
&& saleOrderLine
|
||||
.getProduct()
|
||||
.getProductTypeSelect()
|
||||
.equals(ProductRepository.PRODUCT_TYPE_PACK)
|
||||
&& !saleOrderLine.getIsSubLine()) {
|
||||
saleOrderLine.setTypeSelect(SaleOrderLineRepository.TYPE_PACK);
|
||||
saleOrderLine.setPackPriceSelect(packPriceSelect);
|
||||
saleOrderLine.setSubLineList(createPackLines(saleOrderLine, saleOrder));
|
||||
}
|
||||
|
||||
fillPrice(saleOrderLine, saleOrder, packPriceSelect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillPrice(SaleOrderLine saleOrderLine, SaleOrder saleOrder, Integer packPriceSelect)
|
||||
throws AxelorException {
|
||||
|
||||
boolean taxRequired = checkTaxRequired(saleOrderLine, packPriceSelect);
|
||||
|
||||
if (taxRequired) {
|
||||
fillTaxInformation(saleOrderLine, saleOrder);
|
||||
saleOrderLine.setCompanyCostPrice(this.getCompanyCostPrice(saleOrder, saleOrderLine));
|
||||
BigDecimal exTaxPrice;
|
||||
BigDecimal inTaxPrice;
|
||||
if (saleOrderLine.getProduct().getInAti()) {
|
||||
inTaxPrice = this.getInTaxUnitPrice(saleOrder, saleOrderLine, saleOrderLine.getTaxLine());
|
||||
inTaxPrice = fillDiscount(saleOrderLine, saleOrder, inTaxPrice);
|
||||
saleOrderLine.setInTaxPrice(inTaxPrice);
|
||||
saleOrderLine.setPrice(convertUnitPrice(true, saleOrderLine.getTaxLine(), inTaxPrice));
|
||||
} else {
|
||||
exTaxPrice = this.getExTaxUnitPrice(saleOrder, saleOrderLine, saleOrderLine.getTaxLine());
|
||||
exTaxPrice = fillDiscount(saleOrderLine, saleOrder, exTaxPrice);
|
||||
saleOrderLine.setPrice(exTaxPrice);
|
||||
saleOrderLine.setInTaxPrice(
|
||||
convertUnitPrice(false, saleOrderLine.getTaxLine(), exTaxPrice));
|
||||
}
|
||||
} else {
|
||||
saleOrderLine.setPrice(BigDecimal.ZERO);
|
||||
saleOrderLine.setInTaxPrice(BigDecimal.ZERO);
|
||||
saleOrderLine.setDiscountAmount(BigDecimal.ZERO);
|
||||
saleOrderLine.setDiscountTypeSelect(PriceListLineRepository.AMOUNT_TYPE_NONE);
|
||||
saleOrderLine.setCompanyCostPrice(BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<SaleOrderLine> createPackLines(SaleOrderLine saleOrderLine, SaleOrder saleOrder)
|
||||
throws AxelorException {
|
||||
List<SaleOrderLine> subLines = new ArrayList<SaleOrderLine>();
|
||||
|
||||
Integer sequence = saleOrderLine.getSequence();
|
||||
if (sequence == null) {
|
||||
sequence = 0;
|
||||
}
|
||||
if (saleOrder.getSaleOrderLineList() != null && sequence == 0) {
|
||||
for (SaleOrderLine orderLine : saleOrder.getSaleOrderLineList()) {
|
||||
if (orderLine.getSequence() > sequence) {
|
||||
sequence = orderLine.getSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (saleOrderLine.getSequence() == null) {
|
||||
saleOrderLine.setSequence(++sequence);
|
||||
}
|
||||
|
||||
for (PackLine packLine : saleOrderLine.getProduct().getPackLines()) {
|
||||
SaleOrderLine subLine = new SaleOrderLine();
|
||||
Product subProduct = packLine.getProduct();
|
||||
subLine.setProduct(subProduct);
|
||||
subLine.setQty(new BigDecimal(packLine.getQuantity()).multiply(saleOrderLine.getQty()));
|
||||
subLine.setIsSubLine(true);
|
||||
computeProductInformation(subLine, saleOrder, saleOrderLine.getPackPriceSelect());
|
||||
computeValues(saleOrder, subLine);
|
||||
subLine.setSequence(++sequence);
|
||||
subLines.add(subLine);
|
||||
}
|
||||
|
||||
return subLines;
|
||||
}
|
||||
|
||||
protected BigDecimal fillDiscount(
|
||||
SaleOrderLine saleOrderLine, SaleOrder saleOrder, BigDecimal price) {
|
||||
|
||||
Map<String, Object> discounts =
|
||||
this.getDiscountsFromPriceLists(saleOrder, saleOrderLine, price);
|
||||
|
||||
if (discounts != null) {
|
||||
if (discounts.get("price") != null) {
|
||||
price = (BigDecimal) discounts.get("price");
|
||||
}
|
||||
if (saleOrderLine.getProduct().getInAti() != saleOrder.getInAti()
|
||||
&& (Integer) discounts.get("discountTypeSelect")
|
||||
!= PriceListLineRepository.AMOUNT_TYPE_PERCENT) {
|
||||
saleOrderLine.setDiscountAmount(
|
||||
this.convertUnitPrice(
|
||||
saleOrderLine.getProduct().getInAti(),
|
||||
saleOrderLine.getTaxLine(),
|
||||
(BigDecimal) discounts.get("discountAmount")));
|
||||
} else {
|
||||
saleOrderLine.setDiscountAmount((BigDecimal) discounts.get("discountAmount"));
|
||||
}
|
||||
saleOrderLine.setDiscountTypeSelect((Integer) discounts.get("discountTypeSelect"));
|
||||
} else if (!saleOrder.getTemplate()) {
|
||||
saleOrderLine.setDiscountAmount(BigDecimal.ZERO);
|
||||
saleOrderLine.setDiscountTypeSelect(PriceListLineRepository.AMOUNT_TYPE_NONE);
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
protected void fillTaxInformation(SaleOrderLine saleOrderLine, SaleOrder saleOrder)
|
||||
throws AxelorException {
|
||||
|
||||
if (saleOrder.getClientPartner() != null) {
|
||||
TaxLine taxLine = this.getTaxLine(saleOrder, saleOrderLine);
|
||||
saleOrderLine.setTaxLine(taxLine);
|
||||
|
||||
FiscalPosition fiscalPosition = saleOrder.getClientPartner().getFiscalPosition();
|
||||
|
||||
Tax tax =
|
||||
accountManagementService.getProductTax(
|
||||
saleOrderLine.getProduct(), saleOrder.getCompany(), fiscalPosition, false);
|
||||
|
||||
TaxEquiv taxEquiv = Beans.get(FiscalPositionService.class).getTaxEquiv(fiscalPosition, tax);
|
||||
|
||||
saleOrderLine.setTaxEquiv(taxEquiv);
|
||||
} else {
|
||||
saleOrderLine.setTaxLine(null);
|
||||
saleOrderLine.setTaxEquiv(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTaxRequired(SaleOrderLine saleOrderLine, Integer packPriceSelect) {
|
||||
|
||||
if (appSaleService.getAppSale().getProductPackMgt()) {
|
||||
|
||||
if (saleOrderLine.getIsSubLine()
|
||||
&& packPriceSelect != null
|
||||
&& packPriceSelect == SaleOrderLineRepository.PACK_PRICE_ONLY) {
|
||||
return false;
|
||||
}
|
||||
if (saleOrderLine.getTypeSelect() == SaleOrderLineRepository.TYPE_PACK
|
||||
&& packPriceSelect != null
|
||||
&& packPriceSelect == SaleOrderLineRepository.SUBLINE_PRICE_ONLY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrderLine resetProductInformation(SaleOrderLine line) {
|
||||
line.setTaxLine(null);
|
||||
line.setTaxEquiv(null);
|
||||
line.setProductName(null);
|
||||
line.setUnit(null);
|
||||
line.setCompanyCostPrice(null);
|
||||
line.setDiscountAmount(null);
|
||||
line.setDiscountTypeSelect(PriceListLineRepository.AMOUNT_TYPE_NONE);
|
||||
line.setPrice(null);
|
||||
line.setInTaxPrice(null);
|
||||
line.setExTaxTotal(null);
|
||||
line.setInTaxTotal(null);
|
||||
line.setCompanyInTaxTotal(null);
|
||||
line.setCompanyExTaxTotal(null);
|
||||
if (appSaleService.getAppSale().getIsEnabledProductDescriptionCopy()) {
|
||||
line.setDescription(null);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, BigDecimal> computeValues(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException {
|
||||
|
||||
HashMap<String, BigDecimal> map = new HashMap<>();
|
||||
if (saleOrder == null
|
||||
|| saleOrderLine.getPrice() == null
|
||||
|| saleOrderLine.getInTaxPrice() == null
|
||||
|| saleOrderLine.getQty() == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
BigDecimal exTaxTotal;
|
||||
BigDecimal companyExTaxTotal;
|
||||
BigDecimal inTaxTotal;
|
||||
BigDecimal companyInTaxTotal;
|
||||
BigDecimal priceDiscounted = this.computeDiscount(saleOrderLine, saleOrder.getInAti());
|
||||
BigDecimal taxRate = BigDecimal.ZERO;
|
||||
BigDecimal subTotalCostPrice = BigDecimal.ZERO;
|
||||
|
||||
if (saleOrderLine.getTaxLine() != null) {
|
||||
taxRate = saleOrderLine.getTaxLine().getValue();
|
||||
}
|
||||
|
||||
if (!saleOrder.getInAti()) {
|
||||
exTaxTotal = this.computeAmount(saleOrderLine.getQty(), priceDiscounted);
|
||||
inTaxTotal = exTaxTotal.add(exTaxTotal.multiply(taxRate));
|
||||
companyExTaxTotal = this.getAmountInCompanyCurrency(exTaxTotal, saleOrder);
|
||||
companyInTaxTotal = companyExTaxTotal.add(companyExTaxTotal.multiply(taxRate));
|
||||
} else {
|
||||
inTaxTotal = this.computeAmount(saleOrderLine.getQty(), priceDiscounted);
|
||||
exTaxTotal = inTaxTotal.divide(taxRate.add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP);
|
||||
companyInTaxTotal = this.getAmountInCompanyCurrency(inTaxTotal, saleOrder);
|
||||
companyExTaxTotal =
|
||||
companyInTaxTotal.divide(taxRate.add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
if (saleOrderLine.getProduct() != null
|
||||
&& saleOrderLine.getProduct().getCostPrice().compareTo(BigDecimal.ZERO) != 0) {
|
||||
subTotalCostPrice =
|
||||
saleOrderLine.getProduct().getCostPrice().multiply(saleOrderLine.getQty());
|
||||
}
|
||||
|
||||
saleOrderLine.setInTaxTotal(inTaxTotal);
|
||||
saleOrderLine.setExTaxTotal(exTaxTotal);
|
||||
saleOrderLine.setPriceDiscounted(priceDiscounted);
|
||||
saleOrderLine.setCompanyInTaxTotal(companyInTaxTotal);
|
||||
saleOrderLine.setCompanyExTaxTotal(companyExTaxTotal);
|
||||
saleOrderLine.setSubTotalCostPrice(subTotalCostPrice);
|
||||
map.put("inTaxTotal", inTaxTotal);
|
||||
map.put("exTaxTotal", exTaxTotal);
|
||||
map.put("priceDiscounted", priceDiscounted);
|
||||
map.put("companyExTaxTotal", companyExTaxTotal);
|
||||
map.put("companyInTaxTotal", companyInTaxTotal);
|
||||
map.put("subTotalCostPrice", subTotalCostPrice);
|
||||
|
||||
map.putAll(this.computeSubMargin(saleOrder, saleOrderLine));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the excluded tax total amount of a sale order line.
|
||||
*
|
||||
* @param quantity The quantity.
|
||||
* @param price The unit price.
|
||||
* @return The excluded tax total amount.
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal computeAmount(SaleOrderLine saleOrderLine) {
|
||||
|
||||
BigDecimal price = this.computeDiscount(saleOrderLine, false);
|
||||
|
||||
return computeAmount(saleOrderLine.getQty(), price);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal computeAmount(BigDecimal quantity, BigDecimal price) {
|
||||
|
||||
BigDecimal amount =
|
||||
quantity
|
||||
.multiply(price)
|
||||
.setScale(AppSaleService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_EVEN);
|
||||
|
||||
logger.debug(
|
||||
"Calcul du montant HT avec une quantité de {} pour {} : {}",
|
||||
new Object[] {quantity, price, amount});
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getExTaxUnitPrice(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, TaxLine taxLine) throws AxelorException {
|
||||
return this.getUnitPrice(saleOrder, saleOrderLine, taxLine, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getInTaxUnitPrice(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, TaxLine taxLine) throws AxelorException {
|
||||
return this.getUnitPrice(saleOrder, saleOrderLine, taxLine, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A function used to get the unit price of a sale order line, either in ati or wt
|
||||
*
|
||||
* @param saleOrder the sale order containing the sale order line
|
||||
* @param saleOrderLine
|
||||
* @param taxLine the tax applied to the unit price
|
||||
* @param resultInAti whether you want the result in ati or not
|
||||
* @return the unit price of the sale order line
|
||||
* @throws AxelorException
|
||||
*/
|
||||
private BigDecimal getUnitPrice(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, TaxLine taxLine, boolean resultInAti)
|
||||
throws AxelorException {
|
||||
Product product = saleOrderLine.getProduct();
|
||||
|
||||
BigDecimal price =
|
||||
(product.getInAti() == resultInAti)
|
||||
? product.getSalePrice()
|
||||
: this.convertUnitPrice(product.getInAti(), taxLine, product.getSalePrice());
|
||||
|
||||
return currencyService
|
||||
.getAmountCurrencyConvertedAtDate(
|
||||
product.getSaleCurrency(), saleOrder.getCurrency(), price, saleOrder.getCreationDate())
|
||||
.setScale(5, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxLine getTaxLine(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException {
|
||||
|
||||
return Beans.get(AccountManagementService.class)
|
||||
.getTaxLine(
|
||||
saleOrder.getCreationDate(),
|
||||
saleOrderLine.getProduct(),
|
||||
saleOrder.getCompany(),
|
||||
saleOrder.getClientPartner().getFiscalPosition(),
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getAmountInCompanyCurrency(BigDecimal exTaxTotal, SaleOrder saleOrder)
|
||||
throws AxelorException {
|
||||
|
||||
return currencyService
|
||||
.getAmountCurrencyConvertedAtDate(
|
||||
saleOrder.getCurrency(),
|
||||
saleOrder.getCompany().getCurrency(),
|
||||
exTaxTotal,
|
||||
saleOrder.getCreationDate())
|
||||
.setScale(AppSaleService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getCompanyCostPrice(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException {
|
||||
|
||||
Product product = saleOrderLine.getProduct();
|
||||
|
||||
return currencyService
|
||||
.getAmountCurrencyConvertedAtDate(
|
||||
product.getPurchaseCurrency(),
|
||||
saleOrder.getCompany().getCurrency(),
|
||||
product.getCostPrice(),
|
||||
saleOrder.getCreationDate())
|
||||
.setScale(AppSaleService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriceListLine getPriceListLine(
|
||||
SaleOrderLine saleOrderLine, PriceList priceList, BigDecimal price) {
|
||||
|
||||
return priceListService.getPriceListLine(
|
||||
saleOrderLine.getProduct(), saleOrderLine.getQty(), priceList, price);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal computeDiscount(SaleOrderLine saleOrderLine, Boolean inAti) {
|
||||
|
||||
BigDecimal price = inAti ? saleOrderLine.getInTaxPrice() : saleOrderLine.getPrice();
|
||||
|
||||
int scale = Beans.get(AppBaseService.class).getNbDecimalDigitForSalePrice();
|
||||
|
||||
return priceListService
|
||||
.computeDiscount(
|
||||
price, saleOrderLine.getDiscountTypeSelect(), saleOrderLine.getDiscountAmount())
|
||||
.setScale(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal convertUnitPrice(Boolean priceIsAti, TaxLine taxLine, BigDecimal price) {
|
||||
|
||||
if (taxLine == null) {
|
||||
return price;
|
||||
}
|
||||
|
||||
if (priceIsAti) {
|
||||
price = price.divide(taxLine.getValue().add(BigDecimal.ONE), 5, BigDecimal.ROUND_HALF_UP);
|
||||
} else {
|
||||
price = price.add(price.multiply(taxLine.getValue()));
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getDiscountsFromPriceLists(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, BigDecimal price) {
|
||||
|
||||
Map<String, Object> discounts = null;
|
||||
|
||||
PriceList priceList = saleOrder.getPriceList();
|
||||
|
||||
if (priceList != null) {
|
||||
PriceListLine priceListLine = this.getPriceListLine(saleOrderLine, priceList, price);
|
||||
discounts = priceListService.getReplacedPriceAndDiscounts(priceList, priceListLine, price);
|
||||
|
||||
if (saleOrder.getTemplate()) {
|
||||
Integer manualDiscountAmountType = saleOrderLine.getDiscountTypeSelect();
|
||||
BigDecimal manualDiscountAmount = saleOrderLine.getDiscountAmount();
|
||||
Integer priceListDiscountAmountType = (Integer) discounts.get("discountTypeSelect");
|
||||
BigDecimal priceListDiscountAmount = (BigDecimal) discounts.get("discountAmount");
|
||||
|
||||
if (!manualDiscountAmountType.equals(priceListDiscountAmountType)
|
||||
&& manualDiscountAmountType.equals(PriceListLineRepository.AMOUNT_TYPE_PERCENT)
|
||||
&& priceListDiscountAmountType.equals(PriceListLineRepository.AMOUNT_TYPE_FIXED)) {
|
||||
priceListDiscountAmount =
|
||||
priceListDiscountAmount
|
||||
.multiply(new BigDecimal(100))
|
||||
.divide(price, 2, RoundingMode.HALF_UP);
|
||||
} else if (!manualDiscountAmountType.equals(priceListDiscountAmountType)
|
||||
&& manualDiscountAmountType.equals(PriceListLineRepository.AMOUNT_TYPE_FIXED)
|
||||
&& priceListDiscountAmountType.equals(PriceListLineRepository.AMOUNT_TYPE_PERCENT)) {
|
||||
priceListDiscountAmount =
|
||||
priceListDiscountAmount
|
||||
.multiply(price)
|
||||
.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
if (manualDiscountAmount.compareTo(priceListDiscountAmount) > 0) {
|
||||
discounts.put("discountAmount", manualDiscountAmount);
|
||||
discounts.put("discountTypeSelect", manualDiscountAmountType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDiscountTypeSelect(
|
||||
SaleOrder saleOrder, SaleOrderLine saleOrderLine, BigDecimal price) {
|
||||
PriceList priceList = saleOrder.getPriceList();
|
||||
if (priceList != null) {
|
||||
PriceListLine priceListLine = this.getPriceListLine(saleOrderLine, priceList, price);
|
||||
|
||||
return priceListLine.getTypeSelect();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unit getSaleUnit(SaleOrderLine saleOrderLine) {
|
||||
Unit unit = saleOrderLine.getProduct().getSalesUnit();
|
||||
if (unit == null) {
|
||||
unit = saleOrderLine.getProduct().getUnit();
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal computeTotalPack(SaleOrderLine saleOrderLine) {
|
||||
|
||||
BigDecimal totalPack = BigDecimal.ZERO;
|
||||
|
||||
if (saleOrderLine.getSubLineList() != null) {
|
||||
for (SaleOrderLine subLine : saleOrderLine.getSubLineList()) {
|
||||
totalPack = totalPack.add(subLine.getInTaxTotal());
|
||||
}
|
||||
}
|
||||
|
||||
return totalPack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder getSaleOrder(Context context) {
|
||||
|
||||
Context parentContext = context.getParent();
|
||||
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
SaleOrder saleOrder = saleOrderLine.getSaleOrder();
|
||||
|
||||
if (parentContext != null && !parentContext.getContextClass().equals(SaleOrder.class)) {
|
||||
parentContext = parentContext.getParent();
|
||||
}
|
||||
|
||||
if (parentContext != null && parentContext.getContextClass().equals(SaleOrder.class)) {
|
||||
saleOrder = parentContext.asType(SaleOrder.class);
|
||||
}
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, BigDecimal> computeSubMargin(SaleOrder saleOrder, SaleOrderLine saleOrderLine)
|
||||
throws AxelorException {
|
||||
|
||||
HashMap<String, BigDecimal> map = new HashMap<>();
|
||||
|
||||
BigDecimal subTotalCostPrice = BigDecimal.ZERO;
|
||||
BigDecimal subTotalGrossProfit = BigDecimal.ZERO;
|
||||
BigDecimal subMarginRate = BigDecimal.ZERO;
|
||||
BigDecimal subTotalMarkup = BigDecimal.ZERO;
|
||||
BigDecimal totalWT = BigDecimal.ZERO;
|
||||
|
||||
if (saleOrderLine.getProduct() != null
|
||||
&& saleOrderLine.getProduct().getCostPrice().compareTo(BigDecimal.ZERO) != 0
|
||||
&& saleOrderLine.getExTaxTotal().compareTo(BigDecimal.ZERO) != 0) {
|
||||
|
||||
totalWT =
|
||||
currencyService.getAmountCurrencyConvertedAtDate(
|
||||
saleOrder.getCurrency(),
|
||||
saleOrder.getCompany().getCurrency(),
|
||||
saleOrderLine.getExTaxTotal(),
|
||||
null);
|
||||
|
||||
logger.debug("Total WT in company currency: {}", totalWT);
|
||||
subTotalCostPrice = saleOrderLine.getSubTotalCostPrice();
|
||||
logger.debug("Subtotal cost price: {}", subTotalCostPrice);
|
||||
subTotalGrossProfit = totalWT.subtract(subTotalCostPrice);
|
||||
logger.debug("Subtotal gross margin: {}", subTotalGrossProfit);
|
||||
subMarginRate =
|
||||
subTotalGrossProfit.divide(totalWT, RoundingMode.HALF_EVEN).multiply(new BigDecimal(100));
|
||||
logger.debug("Subtotal gross margin rate: {}", subMarginRate);
|
||||
|
||||
if (subTotalCostPrice.compareTo(BigDecimal.ZERO) != 0) {
|
||||
subTotalMarkup =
|
||||
subTotalGrossProfit
|
||||
.divide(subTotalCostPrice, RoundingMode.HALF_EVEN)
|
||||
.multiply(new BigDecimal(100));
|
||||
logger.debug("Subtotal markup: {}", subTotalMarkup);
|
||||
}
|
||||
}
|
||||
|
||||
saleOrderLine.setSubTotalGrossMargin(subTotalGrossProfit);
|
||||
saleOrderLine.setSubMarginRate(subMarginRate);
|
||||
saleOrderLine.setSubTotalMarkup(subTotalMarkup);
|
||||
|
||||
map.put("subTotalGrossMargin", subTotalGrossProfit);
|
||||
map.put("subMarginRate", subMarginRate);
|
||||
map.put("subTotalMarkup", subTotalMarkup);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getAvailableStock(SaleOrder saleOrder, SaleOrderLine saleOrderLine) {
|
||||
// defined in supplychain
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getAllocatedStock(SaleOrder saleOrder, SaleOrderLine saleOrderLine) {
|
||||
// defined in supplychain
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkMultipleQty(SaleOrderLine saleOrderLine, ActionResponse response) {
|
||||
|
||||
Product product = saleOrderLine.getProduct();
|
||||
|
||||
if (product == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
productMultipleQtyService.checkMultipleQty(
|
||||
saleOrderLine.getQty(),
|
||||
product.getSaleProductMultipleQtyList(),
|
||||
product.getAllowToForceSaleQty(),
|
||||
response);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.account.db.TaxEquiv;
|
||||
import com.axelor.apps.account.db.TaxLine;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.SaleOrderLineTax;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderLineTaxService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Inject private SaleOrderToolService saleOrderToolService;
|
||||
|
||||
/**
|
||||
* Créer les lignes de TVA du devis. La création des lignes de TVA se basent sur les lignes de
|
||||
* devis ainsi que les sous-lignes de devis de celles-ci. Si une ligne de devis comporte des
|
||||
* sous-lignes de devis, alors on se base uniquement sur celles-ci.
|
||||
*
|
||||
* @param invoice La facture.
|
||||
* @param invoiceLines Les lignes de facture.
|
||||
* @param invoiceLineTaxes Les lignes des taxes de la facture.
|
||||
* @return La liste des lignes de taxe de la facture.
|
||||
*/
|
||||
public List<SaleOrderLineTax> createsSaleOrderLineTax(
|
||||
SaleOrder saleOrder, List<SaleOrderLine> saleOrderLineList) {
|
||||
|
||||
List<SaleOrderLineTax> saleOrderLineTaxList = new ArrayList<SaleOrderLineTax>();
|
||||
Map<TaxLine, SaleOrderLineTax> map = new HashMap<TaxLine, SaleOrderLineTax>();
|
||||
Set<String> specificNotes = new HashSet<String>();
|
||||
|
||||
boolean customerSpecificNote = false;
|
||||
if (saleOrder.getClientPartner().getFiscalPosition() != null) {
|
||||
customerSpecificNote =
|
||||
saleOrder.getClientPartner().getFiscalPosition().getCustomerSpecificNote();
|
||||
}
|
||||
|
||||
if (saleOrderLineList != null && !saleOrderLineList.isEmpty()) {
|
||||
|
||||
LOG.debug("Création des lignes de tva pour les lignes de factures.");
|
||||
|
||||
for (SaleOrderLine saleOrderLine : saleOrderLineList) {
|
||||
|
||||
TaxLine taxLine = saleOrderLine.getTaxLine();
|
||||
|
||||
if (taxLine != null) {
|
||||
|
||||
LOG.debug("Tax {}", taxLine);
|
||||
|
||||
if (map.containsKey(taxLine)) {
|
||||
|
||||
SaleOrderLineTax saleOrderLineTax = map.get(taxLine);
|
||||
|
||||
saleOrderLineTax.setExTaxBase(
|
||||
saleOrderLineTax.getExTaxBase().add(saleOrderLine.getExTaxTotal()));
|
||||
|
||||
} else {
|
||||
|
||||
SaleOrderLineTax saleOrderLineTax = new SaleOrderLineTax();
|
||||
saleOrderLineTax.setSaleOrder(saleOrder);
|
||||
|
||||
saleOrderLineTax.setExTaxBase(saleOrderLine.getExTaxTotal());
|
||||
|
||||
saleOrderLineTax.setTaxLine(taxLine);
|
||||
map.put(taxLine, saleOrderLineTax);
|
||||
}
|
||||
}
|
||||
|
||||
if (!customerSpecificNote) {
|
||||
TaxEquiv taxEquiv = saleOrderLine.getTaxEquiv();
|
||||
if (taxEquiv != null && taxEquiv.getSpecificNote() != null) {
|
||||
specificNotes.add(taxEquiv.getSpecificNote());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SaleOrderLineTax saleOrderLineTax : map.values()) {
|
||||
|
||||
// Dans la devise de la facture
|
||||
BigDecimal exTaxBase = saleOrderLineTax.getExTaxBase();
|
||||
BigDecimal taxTotal = BigDecimal.ZERO;
|
||||
if (saleOrderLineTax.getTaxLine() != null) {
|
||||
taxTotal =
|
||||
saleOrderToolService.computeAmount(exTaxBase, saleOrderLineTax.getTaxLine().getValue());
|
||||
saleOrderLineTax.setTaxTotal(taxTotal);
|
||||
}
|
||||
saleOrderLineTax.setInTaxTotal(exTaxBase.add(taxTotal));
|
||||
saleOrderLineTaxList.add(saleOrderLineTax);
|
||||
|
||||
LOG.debug(
|
||||
"Ligne de TVA : Total TVA => {}, Total HT => {}",
|
||||
new Object[] {saleOrderLineTax.getTaxTotal(), saleOrderLineTax.getInTaxTotal()});
|
||||
}
|
||||
|
||||
if (!customerSpecificNote) {
|
||||
saleOrder.setSpecificNotes(Joiner.on('\n').join(specificNotes));
|
||||
} else {
|
||||
saleOrder.setSpecificNotes(saleOrder.getClientPartner().getSpecificTaxNote());
|
||||
}
|
||||
|
||||
return saleOrderLineTaxList;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
|
||||
public interface SaleOrderMarginService {
|
||||
|
||||
public void computeMarginSaleOrder(SaleOrder saleOrder);
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderMarginServiceImpl implements SaleOrderMarginService {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Override
|
||||
public void computeMarginSaleOrder(SaleOrder saleOrder) {
|
||||
|
||||
BigDecimal accountedRevenue = BigDecimal.ZERO;
|
||||
BigDecimal totalCostPrice = BigDecimal.ZERO;
|
||||
BigDecimal totalGrossProfit = BigDecimal.ZERO;
|
||||
BigDecimal marginRate = BigDecimal.ZERO;
|
||||
BigDecimal markup = BigDecimal.ZERO;
|
||||
|
||||
if (saleOrder.getSaleOrderLineList() != null && !saleOrder.getSaleOrderLineList().isEmpty()) {
|
||||
|
||||
for (SaleOrderLine saleOrderLineList : saleOrder.getSaleOrderLineList()) {
|
||||
|
||||
if (saleOrderLineList.getProduct() == null
|
||||
|| saleOrderLineList.getSubTotalCostPrice().compareTo(BigDecimal.ZERO) == 0
|
||||
|| saleOrderLineList.getExTaxTotal().compareTo(BigDecimal.ZERO) == 0) {
|
||||
} else {
|
||||
|
||||
accountedRevenue = accountedRevenue.add(saleOrderLineList.getCompanyExTaxTotal());
|
||||
totalCostPrice = totalCostPrice.add(saleOrderLineList.getSubTotalCostPrice());
|
||||
totalGrossProfit = totalGrossProfit.add(saleOrderLineList.getSubTotalGrossMargin());
|
||||
marginRate =
|
||||
totalGrossProfit
|
||||
.divide(accountedRevenue, RoundingMode.HALF_EVEN)
|
||||
.multiply(new BigDecimal(100));
|
||||
markup =
|
||||
totalGrossProfit
|
||||
.divide(totalCostPrice, RoundingMode.HALF_EVEN)
|
||||
.multiply(new BigDecimal(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saleOrder.setAccountedRevenue(accountedRevenue);
|
||||
saleOrder.setTotalCostPrice(totalCostPrice);
|
||||
saleOrder.setTotalGrossMargin(totalGrossProfit);
|
||||
saleOrder.setMarginRate(marginRate);
|
||||
saleOrder.setMarkup(markup);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
|
||||
public interface SaleOrderService {
|
||||
|
||||
public String getFileName(SaleOrder saleOrder);
|
||||
|
||||
public SaleOrder computeEndOfValidityDate(SaleOrder saleOrder);
|
||||
|
||||
@Deprecated
|
||||
public String getReportLink(
|
||||
SaleOrder saleOrder, String name, String language, boolean proforma, String format)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Fill {@link SaleOrder#mainInvoicingAddressStr} and {@link SaleOrder#deliveryAddressStr}
|
||||
*
|
||||
* @param saleOrder
|
||||
*/
|
||||
public void computeAddressStr(SaleOrder saleOrder);
|
||||
|
||||
/**
|
||||
* Enable edit order.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @throws AxelorException
|
||||
*/
|
||||
boolean enableEditOrder(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Check modified confirmed order before saving it.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @param saleOrderView
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void checkModifiedConfirmedOrder(SaleOrder saleOrder, SaleOrder saleOrderView)
|
||||
throws AxelorException;
|
||||
|
||||
/**
|
||||
* Validate changes.
|
||||
*
|
||||
* @param saleOrder
|
||||
* @throws AxelorException
|
||||
*/
|
||||
void validateChanges(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
/**
|
||||
* Sort detail lines by sequence.
|
||||
*
|
||||
* @param saleOrder
|
||||
*/
|
||||
void sortSaleOrderLineList(SaleOrder saleOrder);
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.AddressService;
|
||||
import com.axelor.apps.base.service.DurationService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.report.IReport;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Comparator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderServiceImpl implements SaleOrderService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Override
|
||||
public String getFileName(SaleOrder saleOrder) {
|
||||
|
||||
return I18n.get("Sale order")
|
||||
+ " "
|
||||
+ saleOrder.getSaleOrderSeq()
|
||||
+ ((saleOrder.getVersionNumber() > 1) ? "-V" + saleOrder.getVersionNumber() : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleOrder computeEndOfValidityDate(SaleOrder saleOrder) {
|
||||
if (saleOrder.getDuration() != null && saleOrder.getCreationDate() != null) {
|
||||
saleOrder.setEndOfValidityDate(
|
||||
Beans.get(DurationService.class)
|
||||
.computeDuration(saleOrder.getDuration(), saleOrder.getCreationDate()));
|
||||
}
|
||||
return saleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String getReportLink(
|
||||
SaleOrder saleOrder, String name, String language, boolean proforma, String format)
|
||||
throws AxelorException {
|
||||
|
||||
return ReportFactory.createReport(IReport.SALES_ORDER, name + "-${date}")
|
||||
.addParam("Locale", language)
|
||||
.addParam("SaleOrderId", saleOrder.getId())
|
||||
.addParam("ProformaInvoice", proforma)
|
||||
.addFormat(format)
|
||||
.generate()
|
||||
.getFileLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeAddressStr(SaleOrder saleOrder) {
|
||||
AddressService addressService = Beans.get(AddressService.class);
|
||||
saleOrder.setMainInvoicingAddressStr(
|
||||
addressService.computeAddressStr(saleOrder.getMainInvoicingAddress()));
|
||||
saleOrder.setDeliveryAddressStr(
|
||||
addressService.computeAddressStr(saleOrder.getDeliveryAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public boolean enableEditOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
if (saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_ORDER_COMPLETED) {
|
||||
throw new AxelorException(
|
||||
saleOrder,
|
||||
TraceBackRepository.CATEGORY_INCONSISTENCY,
|
||||
I18n.get(IExceptionMessage.SALES_ORDER_COMPLETED));
|
||||
}
|
||||
|
||||
saleOrder.setOrderBeingEdited(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkModifiedConfirmedOrder(SaleOrder saleOrder, SaleOrder saleOrderView)
|
||||
throws AxelorException {
|
||||
// Nothing to check if we don't have supplychain.
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void validateChanges(SaleOrder saleOrder) throws AxelorException {
|
||||
// Nothing to do if we don't have supplychain.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sortSaleOrderLineList(SaleOrder saleOrder) {
|
||||
if (saleOrder.getSaleOrderLineList() != null) {
|
||||
saleOrder.getSaleOrderLineList().sort(Comparator.comparing(SaleOrderLine::getSequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.service.CurrencyService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderToolService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Inject protected CurrencyService currencyService;
|
||||
|
||||
/**
|
||||
* Calculer le montant HT d'une ligne de devis.
|
||||
*
|
||||
* @param quantity Quantité.
|
||||
* @param price Le prix.
|
||||
* @return Le montant HT de la ligne.
|
||||
*/
|
||||
public BigDecimal computeAmount(BigDecimal quantity, BigDecimal price) {
|
||||
|
||||
BigDecimal amount =
|
||||
quantity
|
||||
.multiply(price)
|
||||
.setScale(AppBaseService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_EVEN);
|
||||
|
||||
LOG.debug(
|
||||
"Calcul du montant HT avec une quantité de {} pour {} : {}",
|
||||
new Object[] {quantity, price, amount});
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
public BigDecimal getAccountingExTaxTotal(BigDecimal exTaxTotal, SaleOrder saleOrder)
|
||||
throws AxelorException {
|
||||
|
||||
return currencyService
|
||||
.getAmountCurrencyConvertedAtDate(
|
||||
saleOrder.getCurrency(),
|
||||
saleOrder.getClientPartner().getCurrency(),
|
||||
exTaxTotal,
|
||||
saleOrder.getCreationDate())
|
||||
.setScale(AppBaseService.DEFAULT_NB_DECIMAL_DIGITS, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.base.db.CancelReason;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.persist.Transactional;
|
||||
|
||||
public interface SaleOrderWorkflowService {
|
||||
|
||||
@Transactional
|
||||
public Partner validateCustomer(SaleOrder saleOrder);
|
||||
|
||||
public String getSequence(Company company) throws AxelorException;
|
||||
|
||||
public void cancelSaleOrder(
|
||||
SaleOrder saleOrder, CancelReason cancelReason, String cancelReasonStr);
|
||||
|
||||
public void finalizeQuotation(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
public void confirmSaleOrder(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
public void saveSaleOrderPDFAsAttachment(SaleOrder saleOrder) throws AxelorException;
|
||||
|
||||
public String getFileName(SaleOrder saleOrder);
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.db.Blocking;
|
||||
import com.axelor.apps.base.db.CancelReason;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.repo.BlockingRepository;
|
||||
import com.axelor.apps.base.db.repo.PartnerRepository;
|
||||
import com.axelor.apps.base.db.repo.SequenceRepository;
|
||||
import com.axelor.apps.base.service.BlockingService;
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.base.service.user.UserService;
|
||||
import com.axelor.apps.crm.db.Opportunity;
|
||||
import com.axelor.apps.crm.db.repo.OpportunityRepository;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.exception.BlockedSaleOrderException;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.report.IReport;
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.persist.Transactional;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import javax.persistence.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SaleOrderWorkflowServiceImpl implements SaleOrderWorkflowService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
protected SequenceService sequenceService;
|
||||
protected PartnerRepository partnerRepo;
|
||||
protected SaleOrderRepository saleOrderRepo;
|
||||
protected AppSaleService appSaleService;
|
||||
protected UserService userService;
|
||||
|
||||
@Inject
|
||||
public SaleOrderWorkflowServiceImpl(
|
||||
SequenceService sequenceService,
|
||||
PartnerRepository partnerRepo,
|
||||
SaleOrderRepository saleOrderRepo,
|
||||
AppSaleService appSaleService,
|
||||
UserService userService) {
|
||||
|
||||
this.sequenceService = sequenceService;
|
||||
this.partnerRepo = partnerRepo;
|
||||
this.saleOrderRepo = saleOrderRepo;
|
||||
this.appSaleService = appSaleService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Partner validateCustomer(SaleOrder saleOrder) {
|
||||
|
||||
Partner clientPartner = partnerRepo.find(saleOrder.getClientPartner().getId());
|
||||
clientPartner.setIsCustomer(true);
|
||||
clientPartner.setIsProspect(false);
|
||||
|
||||
return partnerRepo.save(clientPartner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSequence(Company company) throws AxelorException {
|
||||
|
||||
String seq = sequenceService.getSequenceNumber(SequenceRepository.SALES_ORDER, company);
|
||||
if (seq == null) {
|
||||
throw new AxelorException(
|
||||
company,
|
||||
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
|
||||
I18n.get(IExceptionMessage.SALES_ORDER_1),
|
||||
company.getName());
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelSaleOrder(
|
||||
SaleOrder saleOrder, CancelReason cancelReason, String cancelReasonStr) {
|
||||
Query q =
|
||||
JPA.em()
|
||||
.createQuery(
|
||||
"select count(*) FROM SaleOrder as self WHERE self.statusSelect in (?1 , ?2) AND self.clientPartner = ?3 ");
|
||||
q.setParameter(1, SaleOrderRepository.STATUS_ORDER_CONFIRMED);
|
||||
q.setParameter(2, SaleOrderRepository.STATUS_ORDER_COMPLETED);
|
||||
q.setParameter(3, saleOrder.getClientPartner());
|
||||
if ((long) q.getSingleResult() == 0) {
|
||||
saleOrder.getClientPartner().setIsCustomer(false);
|
||||
saleOrder.getClientPartner().setIsProspect(true);
|
||||
}
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_CANCELED);
|
||||
saleOrder.setCancelReason(cancelReason);
|
||||
if (Strings.isNullOrEmpty(cancelReasonStr)) {
|
||||
saleOrder.setCancelReasonStr(cancelReason.getName());
|
||||
} else {
|
||||
saleOrder.setCancelReasonStr(cancelReasonStr);
|
||||
}
|
||||
saleOrderRepo.save(saleOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(
|
||||
rollbackOn = {Exception.class},
|
||||
ignore = {BlockedSaleOrderException.class}
|
||||
)
|
||||
public void finalizeQuotation(SaleOrder saleOrder) throws AxelorException {
|
||||
Partner partner = saleOrder.getClientPartner();
|
||||
|
||||
Blocking blocking =
|
||||
Beans.get(BlockingService.class)
|
||||
.getBlocking(partner, saleOrder.getCompany(), BlockingRepository.SALE_BLOCKING);
|
||||
|
||||
if (blocking != null && saleOrder.getInTaxTotal().compareTo(blocking.getMaxAmount()) > 0) {
|
||||
|
||||
System.out.println("************************************");
|
||||
System.out.println(blocking);
|
||||
System.out.println(saleOrder.getInTaxTotal().compareTo(blocking.getMaxAmount()) > 0);
|
||||
System.out.println("************************************");
|
||||
saleOrder.setBlockedOnCustCreditExceed(true);
|
||||
// if (!saleOrder.getManualUnblock()) {
|
||||
saleOrderRepo.save(saleOrder);
|
||||
String reason =
|
||||
blocking.getBlockingReason() != null ? blocking.getBlockingReason().getName() : "";
|
||||
throw new BlockedSaleOrderException(
|
||||
partner, I18n.get("Client is sale blocked:") + " " + reason);
|
||||
// }
|
||||
}
|
||||
|
||||
if (saleOrder.getVersionNumber() == 1
|
||||
&& sequenceService.isEmptyOrDraftSequenceNumber(saleOrder.getSaleOrderSeq())) {
|
||||
saleOrder.setSaleOrderSeq(this.getSequence(saleOrder.getCompany()));
|
||||
}
|
||||
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_FINALIZED_QUOTATION);
|
||||
if (appSaleService.getAppSale().getPrintingOnSOFinalization()) {
|
||||
this.saveSaleOrderPDFAsAttachment(saleOrder);
|
||||
}
|
||||
saleOrderRepo.save(saleOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackOn = {Exception.class})
|
||||
public void confirmSaleOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_ORDER_CONFIRMED);
|
||||
saleOrder.setConfirmationDateTime(appSaleService.getTodayDateTime().toLocalDateTime());
|
||||
saleOrder.setConfirmedByUser(userService.getUser());
|
||||
|
||||
this.validateCustomer(saleOrder);
|
||||
|
||||
if (appSaleService.getAppSale().getCloseOpportunityUponSaleOrderConfirmation()) {
|
||||
Opportunity opportunity = saleOrder.getOpportunity();
|
||||
|
||||
if (opportunity != null) {
|
||||
opportunity.setSalesStageSelect(OpportunityRepository.SALES_STAGE_CLOSED_WON);
|
||||
}
|
||||
}
|
||||
|
||||
saleOrderRepo.save(saleOrder);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void completeSaleOrder(SaleOrder saleOrder) throws AxelorException {
|
||||
saleOrder.setStatusSelect(SaleOrderRepository.STATUS_ORDER_COMPLETED);
|
||||
saleOrder.setOrderBeingEdited(false);
|
||||
|
||||
saleOrderRepo.save(saleOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSaleOrderPDFAsAttachment(SaleOrder saleOrder) throws AxelorException {
|
||||
|
||||
if (saleOrder.getPrintingSettings() == null) {
|
||||
if (saleOrder.getCompany().getPrintingSettings() != null) {
|
||||
saleOrder.setPrintingSettings(saleOrder.getCompany().getPrintingSettings());
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.SALE_ORDER_MISSING_PRINTING_SETTINGS),
|
||||
saleOrder.getSaleOrderSeq()),
|
||||
saleOrder);
|
||||
}
|
||||
}
|
||||
|
||||
ReportFactory.createReport(IReport.SALES_ORDER, this.getFileName(saleOrder) + "-${date}")
|
||||
.addParam("Locale", ReportSettings.getPrintingLocale(saleOrder.getClientPartner()))
|
||||
.addParam("SaleOrderId", saleOrder.getId())
|
||||
.addParam("HeaderHeight", saleOrder.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", saleOrder.getPrintingSettings().getPdfFooterHeight())
|
||||
.toAttach(saleOrder)
|
||||
.generate()
|
||||
.getFileLink();
|
||||
|
||||
// String relatedModel = generalService.getPersistentClass(saleOrder).getCanonicalName();
|
||||
// required ?
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName(SaleOrder saleOrder) {
|
||||
String fileNamePrefix;
|
||||
if (saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_DRAFT_QUOTATION
|
||||
|| saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_FINALIZED_QUOTATION) {
|
||||
fileNamePrefix = "Sale quotation";
|
||||
} else {
|
||||
fileNamePrefix = "Sale order";
|
||||
}
|
||||
|
||||
return I18n.get(fileNamePrefix)
|
||||
+ " "
|
||||
+ saleOrder.getSaleOrderSeq()
|
||||
+ ((saleOrder.getVersionNumber() > 1) ? "-V" + saleOrder.getVersionNumber() : "");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder.print;
|
||||
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface SaleOrderPrintService {
|
||||
|
||||
/**
|
||||
* Print a list of sale orders in the same output.
|
||||
*
|
||||
* @param ids ids of the sale order.
|
||||
* @return the link to the generated file.
|
||||
* @throws IOException
|
||||
*/
|
||||
String printSaleOrders(List<Long> ids) throws IOException;
|
||||
|
||||
ReportSettings prepareReportSettings(SaleOrder saleOrder, boolean proforma, String format)
|
||||
throws AxelorException;
|
||||
|
||||
File print(SaleOrder saleOrder, boolean proforma, String format) throws AxelorException;
|
||||
|
||||
String printSaleOrder(SaleOrder saleOrder, boolean proforma, String format)
|
||||
throws AxelorException, IOException;
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.service.saleorder.print;
|
||||
|
||||
import com.axelor.apps.ReportFactory;
|
||||
import com.axelor.apps.base.service.ConvertNumberToFrenchWordsService;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.report.IReport;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderService;
|
||||
import com.axelor.apps.tool.ModelTool;
|
||||
import com.axelor.apps.tool.ThrowConsumer;
|
||||
import com.axelor.apps.tool.file.PdfTool;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.db.repo.TraceBackRepository;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.google.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SaleOrderPrintServiceImpl implements SaleOrderPrintService {
|
||||
|
||||
@Inject SaleOrderService saleOrderService;
|
||||
|
||||
@Override
|
||||
public String printSaleOrder(SaleOrder saleOrder, boolean proforma, String format)
|
||||
throws AxelorException, IOException {
|
||||
String fileName = getSaleOrderFilesName(false, format);
|
||||
return PdfTool.getFileLinkFromPdfFile(print(saleOrder, proforma, format), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printSaleOrders(List<Long> ids) throws IOException {
|
||||
List<File> printedSaleOrders = new ArrayList<>();
|
||||
ModelTool.apply(
|
||||
SaleOrder.class,
|
||||
ids,
|
||||
new ThrowConsumer<SaleOrder>() {
|
||||
@Override
|
||||
public void accept(SaleOrder saleOrder) throws Exception {
|
||||
printedSaleOrders.add(print(saleOrder, false, ReportSettings.FORMAT_PDF));
|
||||
}
|
||||
});
|
||||
String fileName = getSaleOrderFilesName(true, ReportSettings.FORMAT_PDF);
|
||||
return PdfTool.mergePdfToFileLink(printedSaleOrders, fileName);
|
||||
}
|
||||
|
||||
public File print(SaleOrder saleOrder, boolean proforma, String format) throws AxelorException {
|
||||
ReportSettings reportSettings = prepareReportSettings(saleOrder, proforma, format);
|
||||
return reportSettings.generate().getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportSettings prepareReportSettings(SaleOrder saleOrder, boolean proforma, String format)
|
||||
throws AxelorException {
|
||||
|
||||
if (saleOrder.getPrintingSettings() == null) {
|
||||
if (saleOrder.getCompany().getPrintingSettings() != null) {
|
||||
saleOrder.setPrintingSettings(saleOrder.getCompany().getPrintingSettings());
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
String.format(
|
||||
I18n.get(IExceptionMessage.SALE_ORDER_MISSING_PRINTING_SETTINGS),
|
||||
saleOrder.getSaleOrderSeq()),
|
||||
saleOrder);
|
||||
}
|
||||
}
|
||||
String locale = ReportSettings.getPrintingLocale(saleOrder.getClientPartner());
|
||||
|
||||
String title = saleOrderService.getFileName(saleOrder);
|
||||
|
||||
ReportSettings reportSetting =
|
||||
ReportFactory.createReport(IReport.SALES_ORDER, title + " - ${date}");
|
||||
|
||||
String[] arrOfStr = saleOrder.getInTaxTotal().toString().split("\\.");
|
||||
|
||||
String left =
|
||||
Beans.get(ConvertNumberToFrenchWordsService.class).convert(Long.parseLong(arrOfStr[0]));
|
||||
String right =
|
||||
Beans.get(ConvertNumberToFrenchWordsService.class).convert(Long.parseLong(arrOfStr[1]));
|
||||
String number = left + " dinars algériens et " + right + " Cts";
|
||||
|
||||
return reportSetting
|
||||
.addParam("NumberToWords", number)
|
||||
.addParam("SaleOrderId", saleOrder.getId())
|
||||
.addParam("Locale", locale)
|
||||
.addParam("ProformaInvoice", proforma)
|
||||
.addParam("HeaderHeight", saleOrder.getPrintingSettings().getPdfHeaderHeight())
|
||||
.addParam("FooterHeight", saleOrder.getPrintingSettings().getPdfFooterHeight())
|
||||
.addFormat(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the printed sale order.
|
||||
*
|
||||
* @param plural if there is one or multiple sale orders.
|
||||
*/
|
||||
protected String getSaleOrderFilesName(boolean plural, String format) {
|
||||
|
||||
return I18n.get(plural ? "Sale orders" : "Sale order")
|
||||
+ " - "
|
||||
+ Beans.get(AppBaseService.class).getTodayDate().format(DateTimeFormatter.BASIC_ISO_DATE)
|
||||
+ "."
|
||||
+ format;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.translation;
|
||||
|
||||
public interface ITranslation {
|
||||
|
||||
public static final String SALE_APP_NAME = /*$$(*/ "value:Sale"; /*)*/
|
||||
|
||||
public static final String ABC_ANALYSIS_START_DATE = /*$$(*/ "AbcAnalysis.startDate"; /*)*/
|
||||
public static final String ABC_ANALYSIS_END_DATE = /*$$(*/ "AbcAnalysis.endDate"; /*)*/
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.sale.db.AdvancePayment;
|
||||
import com.axelor.apps.sale.db.repo.AdvancePaymentRepository;
|
||||
import com.axelor.apps.sale.service.AdvancePaymentService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class AdvancePaymentController {
|
||||
|
||||
public void cancelAdvancePayment(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
AdvancePayment advancePayment = request.getContext().asType(AdvancePayment.class);
|
||||
advancePayment = Beans.get(AdvancePaymentRepository.class).find(advancePayment.getId());
|
||||
|
||||
Beans.get(AdvancePaymentService.class).cancelAdvancePayment(advancePayment);
|
||||
|
||||
response.setReload(true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.sale.service.app.AppSaleService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class AppSaleController {
|
||||
|
||||
public void generateSaleConfigurations(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Beans.get(AppSaleService.class).generateSaleConfigurations();
|
||||
|
||||
response.setReload(true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.sale.db.Configurator;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.ConfiguratorRepository;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.JsonContext;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ConfiguratorController {
|
||||
|
||||
/**
|
||||
* Called from configurator form view, set values for the indicators JSON field. call {@link
|
||||
* ConfiguratorService#updateIndicators(Configurator, JsonContext, JsonContext)}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void updateIndicators(ActionRequest request, ActionResponse response) {
|
||||
Configurator configurator = request.getContext().asType(Configurator.class);
|
||||
JsonContext jsonAttributes = (JsonContext) request.getContext().get("$attributes");
|
||||
JsonContext jsonIndicators = (JsonContext) request.getContext().get("$indicators");
|
||||
configurator = Beans.get(ConfiguratorRepository.class).find(configurator.getId());
|
||||
try {
|
||||
Beans.get(ConfiguratorService.class)
|
||||
.updateIndicators(configurator, jsonAttributes, jsonIndicators);
|
||||
response.setValue("indicators", request.getContext().get("indicators"));
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from configurator form view, call {@link
|
||||
* ConfiguratorService#generateProduct(Configurator, JsonContext, JsonContext)}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void generateProduct(ActionRequest request, ActionResponse response) {
|
||||
Configurator configurator = request.getContext().asType(Configurator.class);
|
||||
JsonContext jsonAttributes = (JsonContext) request.getContext().get("$attributes");
|
||||
JsonContext jsonIndicators = (JsonContext) request.getContext().get("$indicators");
|
||||
configurator = Beans.get(ConfiguratorRepository.class).find(configurator.getId());
|
||||
try {
|
||||
Beans.get(ConfiguratorService.class).generate(configurator, jsonAttributes, jsonIndicators);
|
||||
response.setReload(true);
|
||||
if (configurator.getProduct() != null) {
|
||||
response.setView(
|
||||
ActionView.define(I18n.get("Product generated"))
|
||||
.model(Product.class.getName())
|
||||
.add("form", "product-form")
|
||||
.add("grid", "product-grid")
|
||||
.context("_showRecord", configurator.getProduct().getId())
|
||||
.map());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
response.setError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from configurator wizard form view, call {@link
|
||||
* ConfiguratorService#addLineToSaleOrder(Configurator, SaleOrder, JsonContext, JsonContext)}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void generateForSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
Configurator configurator = request.getContext().asType(Configurator.class);
|
||||
long saleOrderId = ((Integer) request.getContext().get("_saleOrderId")).longValue();
|
||||
|
||||
JsonContext jsonAttributes = (JsonContext) request.getContext().get("$attributes");
|
||||
JsonContext jsonIndicators = (JsonContext) request.getContext().get("$indicators");
|
||||
|
||||
configurator = Beans.get(ConfiguratorRepository.class).find(configurator.getId());
|
||||
SaleOrder saleOrder = Beans.get(SaleOrderRepository.class).find(saleOrderId);
|
||||
try {
|
||||
Beans.get(ConfiguratorService.class)
|
||||
.addLineToSaleOrder(configurator, saleOrder, jsonAttributes, jsonIndicators);
|
||||
response.setCanClose(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
response.setReload(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from configurator view on selecting {@link Configurator#configuratorCreator}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void createDomainForCreator(ActionRequest request, ActionResponse response) {
|
||||
response.setAttr(
|
||||
"configuratorCreator",
|
||||
"domain",
|
||||
Beans.get(ConfiguratorCreatorService.class).getConfiguratorCreatorDomain());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.repo.ConfiguratorCreatorRepository;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportService;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorService;
|
||||
import com.axelor.auth.AuthUtils;
|
||||
import com.axelor.auth.db.User;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class ConfiguratorCreatorController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
/**
|
||||
* Called from the configurator creator form on formula changes
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void updateAndActivate(ActionRequest request, ActionResponse response) {
|
||||
ConfiguratorCreator creator = request.getContext().asType(ConfiguratorCreator.class);
|
||||
ConfiguratorCreatorService configuratorCreatorService =
|
||||
Beans.get(ConfiguratorCreatorService.class);
|
||||
creator = Beans.get(ConfiguratorCreatorRepository.class).find(creator.getId());
|
||||
configuratorCreatorService.updateAttributes(creator);
|
||||
configuratorCreatorService.updateIndicators(creator);
|
||||
configuratorCreatorService.activate(creator);
|
||||
response.setSignal("refresh-app", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the configurator creator form on new
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void configure(ActionRequest request, ActionResponse response) {
|
||||
ConfiguratorCreator creator = request.getContext().asType(ConfiguratorCreator.class);
|
||||
ConfiguratorCreatorService configuratorCreatorService =
|
||||
Beans.get(ConfiguratorCreatorService.class);
|
||||
creator = Beans.get(ConfiguratorCreatorRepository.class).find(creator.getId());
|
||||
User currentUser = AuthUtils.getUser();
|
||||
configuratorCreatorService.authorizeUser(creator, currentUser);
|
||||
try {
|
||||
configuratorCreatorService.addRequiredFormulas(creator);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
response.setError(e.getMessage());
|
||||
}
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from configurator creator grid view, on clicking import button. Call {@link
|
||||
* ConfiguratorCreatorService#importConfiguratorCreators(String)}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void importConfiguratorCreators(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
String pathDiff = (String) ((Map) request.getContext().get("dataFile")).get("filePath");
|
||||
String importLog =
|
||||
Beans.get(ConfiguratorCreatorImportService.class).importConfiguratorCreators(pathDiff);
|
||||
response.setValue("importLog", importLog);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.sale.db.ConfiguratorCreator;
|
||||
import com.axelor.apps.sale.db.ConfiguratorFormula;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.service.configurator.ConfiguratorFormulaService;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ConfiguratorFormulaController {
|
||||
|
||||
/**
|
||||
* Check the groovy script in the context
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void checkGroovyFormula(ActionRequest request, ActionResponse response) {
|
||||
ConfiguratorFormula configuratorFormula =
|
||||
request.getContext().asType(ConfiguratorFormula.class);
|
||||
ConfiguratorCreator creator =
|
||||
request.getContext().getParent().asType(ConfiguratorCreator.class);
|
||||
try {
|
||||
Beans.get(ConfiguratorFormulaService.class).checkFormula(configuratorFormula, creator);
|
||||
response.setAlert(I18n.get(IExceptionMessage.CONFIGURATOR_CREATOR_SCRIPT_WORKING));
|
||||
} catch (Exception e) {
|
||||
response.setError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.crm.db.Opportunity;
|
||||
import com.axelor.apps.crm.db.repo.OpportunityRepository;
|
||||
import com.axelor.apps.crm.translation.ITranslation;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.service.saleorder.OpportunitySaleOrderService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.i18n.I18n;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class OpportunitySaleOrderController {
|
||||
|
||||
public void generateSaleOrder(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
Opportunity opportunity = request.getContext().asType(Opportunity.class);
|
||||
opportunity = Beans.get(OpportunityRepository.class).find(opportunity.getId());
|
||||
SaleOrder saleOrder =
|
||||
Beans.get(OpportunitySaleOrderService.class).createSaleOrderFromOpportunity(opportunity);
|
||||
response.setReload(true);
|
||||
response.setView(
|
||||
ActionView.define(I18n.get(ITranslation.SALE_QUOTATION))
|
||||
.model(SaleOrder.class.getName())
|
||||
.add("form", "sale-order-form")
|
||||
.param("forceEdit", "true")
|
||||
.param("forceTitle", "true")
|
||||
.context("_showRecord", String.valueOf(saleOrder.getId()))
|
||||
.map());
|
||||
}
|
||||
|
||||
public void cancelSaleOrders(ActionRequest request, ActionResponse response) {
|
||||
Opportunity opportunity = request.getContext().asType(Opportunity.class);
|
||||
SaleOrderWorkflowService saleOrderWorkflowService = Beans.get(SaleOrderWorkflowService.class);
|
||||
if (opportunity.getSalesStageSelect() == OpportunityRepository.SALES_STAGE_CLOSED_LOST) {
|
||||
List<SaleOrder> saleOrderList = opportunity.getSaleOrderList();
|
||||
if (saleOrderList != null && !saleOrderList.isEmpty()) {
|
||||
for (SaleOrder saleOrder : saleOrderList) {
|
||||
if (saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_DRAFT_QUOTATION
|
||||
|| saleOrder.getStatusSelect() == SaleOrderRepository.STATUS_FINALIZED_QUOTATION) {
|
||||
saleOrderWorkflowService.cancelSaleOrder(saleOrder, null, opportunity.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.account.db.PaymentMode;
|
||||
import com.axelor.apps.base.db.BankDetails;
|
||||
import com.axelor.apps.base.db.Company;
|
||||
import com.axelor.apps.base.db.Currency;
|
||||
import com.axelor.apps.base.db.Partner;
|
||||
import com.axelor.apps.base.db.PriceList;
|
||||
import com.axelor.apps.base.db.PrintingSettings;
|
||||
import com.axelor.apps.base.db.Wizard;
|
||||
import com.axelor.apps.base.db.repo.CurrencyRepository;
|
||||
import com.axelor.apps.base.db.repo.PartnerRepository;
|
||||
import com.axelor.apps.base.db.repo.PriceListRepository;
|
||||
import com.axelor.apps.base.service.BankDetailsService;
|
||||
import com.axelor.apps.base.service.PartnerPriceListService;
|
||||
import com.axelor.apps.base.service.PartnerService;
|
||||
import com.axelor.apps.base.service.TradingNameService;
|
||||
import com.axelor.apps.report.engine.ReportSettings;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.axelor.apps.sale.exception.IExceptionMessage;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderCreateService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderMarginService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowServiceImpl;
|
||||
import com.axelor.apps.sale.service.saleorder.print.SaleOrderPrintService;
|
||||
import com.axelor.apps.tool.StringTool;
|
||||
import com.axelor.common.ObjectUtils;
|
||||
import com.axelor.db.JPA;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
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.axelor.meta.schema.actions.ActionView;
|
||||
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.axelor.team.db.Team;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.eclipse.birt.core.exception.BirtException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class SaleOrderController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
try {
|
||||
saleOrder = Beans.get(SaleOrderComputeService.class).computeSaleOrder(saleOrder);
|
||||
response.setValues(saleOrder);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void computeMargin(ActionRequest request, ActionResponse response) {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
try {
|
||||
Beans.get(SaleOrderMarginService.class).computeMarginSaleOrder(saleOrder);
|
||||
|
||||
response.setValue("accountedRevenue", saleOrder.getAccountedRevenue());
|
||||
response.setValue("totalCostPrice", saleOrder.getTotalCostPrice());
|
||||
response.setValue("totalGrossMargin", saleOrder.getTotalGrossMargin());
|
||||
response.setValue("marginRate", saleOrder.getMarginRate());
|
||||
response.setValue("markup", saleOrder.getMarkup());
|
||||
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that print the sale order as a Pdf
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws BirtException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void showSaleOrder(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
|
||||
this.exportSaleOrder(request, response, false, ReportSettings.FORMAT_PDF);
|
||||
}
|
||||
|
||||
/** Method that prints a proforma invoice as a PDF */
|
||||
public void printProformaInvoice(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
this.exportSaleOrder(request, response, true, ReportSettings.FORMAT_PDF);
|
||||
}
|
||||
|
||||
public void exportSaleOrderExcel(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
this.exportSaleOrder(request, response, false, ReportSettings.FORMAT_XLS);
|
||||
}
|
||||
|
||||
public void exportSaleOrderWord(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
this.exportSaleOrder(request, response, false, ReportSettings.FORMAT_DOC);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void exportSaleOrder(
|
||||
ActionRequest request, ActionResponse response, boolean proforma, String format) {
|
||||
|
||||
Context context = request.getContext();
|
||||
String fileLink;
|
||||
String title;
|
||||
SaleOrderPrintService saleOrderPrintService = Beans.get(SaleOrderPrintService.class);
|
||||
|
||||
try {
|
||||
if (!ObjectUtils.isEmpty(request.getContext().get("_ids"))) {
|
||||
List<Long> ids =
|
||||
Lists.transform(
|
||||
(List) request.getContext().get("_ids"),
|
||||
new Function<Object, Long>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Long apply(@Nullable Object input) {
|
||||
return Long.parseLong(input.toString());
|
||||
}
|
||||
});
|
||||
fileLink = saleOrderPrintService.printSaleOrders(ids);
|
||||
title = I18n.get("Sale orders");
|
||||
|
||||
} else if (context.get("id") != null) {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
title = Beans.get(SaleOrderService.class).getFileName(saleOrder);
|
||||
fileLink = saleOrderPrintService.printSaleOrder(saleOrder, proforma, format);
|
||||
|
||||
logger.debug("Printing " + title);
|
||||
} else {
|
||||
throw new AxelorException(
|
||||
TraceBackRepository.CATEGORY_MISSING_FIELD,
|
||||
I18n.get(IExceptionMessage.SALE_ORDER_PRINT));
|
||||
}
|
||||
response.setView(ActionView.define(title).add("html", fileLink).map());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
Beans.get(SaleOrderWorkflowService.class)
|
||||
.cancelSaleOrder(
|
||||
Beans.get(SaleOrderRepository.class).find(saleOrder.getId()),
|
||||
saleOrder.getCancelReason(),
|
||||
saleOrder.getCancelReasonStr());
|
||||
|
||||
response.setFlash(I18n.get("The sale order was canceled"));
|
||||
response.setCanClose(true);
|
||||
}
|
||||
|
||||
public void finalizeQuotation(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
saleOrder = Beans.get(SaleOrderRepository.class).find(saleOrder.getId());
|
||||
|
||||
try {
|
||||
Beans.get(SaleOrderWorkflowService.class).finalizeQuotation(saleOrder);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
public void completeSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
saleOrder = Beans.get(SaleOrderRepository.class).find(saleOrder.getId());
|
||||
|
||||
try {
|
||||
Beans.get(SaleOrderWorkflowServiceImpl.class).completeSaleOrder(saleOrder);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
|
||||
response.setReload(true);
|
||||
}
|
||||
|
||||
public void confirmSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
|
||||
try {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
Beans.get(SaleOrderWorkflowService.class)
|
||||
.confirmSaleOrder(Beans.get(SaleOrderRepository.class).find(saleOrder.getId()));
|
||||
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void generateViewSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
LinkedHashMap<String, Object> saleOrderTemplateContext =
|
||||
(LinkedHashMap<String, Object>) request.getContext().get("_saleOrderTemplate");
|
||||
Integer saleOrderId = (Integer) saleOrderTemplateContext.get("id");
|
||||
SaleOrder context = Beans.get(SaleOrderRepository.class).find(Long.valueOf(saleOrderId));
|
||||
|
||||
response.setView(
|
||||
ActionView.define("Sale order")
|
||||
.model(SaleOrder.class.getName())
|
||||
.add("form", "sale-order-form-wizard")
|
||||
.context("_idCopy", context.getId().toString())
|
||||
.context("_wizardCurrency", request.getContext().get("currency"))
|
||||
.context("_wizardPriceList", request.getContext().get("priceList"))
|
||||
.map());
|
||||
|
||||
response.setCanClose(true);
|
||||
}
|
||||
|
||||
public void generateViewTemplate(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder context = request.getContext().asType(SaleOrder.class);
|
||||
response.setView(
|
||||
ActionView.define("Template")
|
||||
.model(SaleOrder.class.getName())
|
||||
.add("form", "sale-order-template-form-wizard")
|
||||
.context("_idCopy", context.getId().toString())
|
||||
.map());
|
||||
}
|
||||
|
||||
public void generateSaleOrderWizard(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrderTemplate = request.getContext().asType(SaleOrder.class);
|
||||
Partner clientPartner = saleOrderTemplate.getClientPartner();
|
||||
|
||||
response.setView(
|
||||
ActionView.define("Create the quotation")
|
||||
.model(Wizard.class.getName())
|
||||
.add("form", "sale-order-template-wizard-form")
|
||||
.param("popup", "reload")
|
||||
.param("show-toolbar", "false")
|
||||
.param("show-confirm", "false")
|
||||
.param("width", "large")
|
||||
.param("popup-save", "false")
|
||||
.context("_saleOrderTemplate", saleOrderTemplate)
|
||||
.context("_clientPartnerCurrency", clientPartner.getCurrency())
|
||||
.map());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createSaleOrder(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
SaleOrder origin =
|
||||
Beans.get(SaleOrderRepository.class)
|
||||
.find(Long.parseLong(request.getContext().get("_idCopy").toString()));
|
||||
|
||||
if (origin != null) {
|
||||
LinkedHashMap<String, Object> wizardCurrencyContext =
|
||||
(LinkedHashMap<String, Object>) request.getContext().get("_wizardCurrency");
|
||||
Integer wizardCurrencyId = (Integer) wizardCurrencyContext.get("id");
|
||||
Currency wizardCurrency =
|
||||
Beans.get(CurrencyRepository.class).find(Long.valueOf(wizardCurrencyId));
|
||||
|
||||
PriceList wizardPriceList = null;
|
||||
if (request.getContext().get("_wizardPriceList") != null) {
|
||||
LinkedHashMap<String, Object> wizardPriceListContext =
|
||||
(LinkedHashMap<String, Object>) request.getContext().get("_wizardPriceList");
|
||||
Integer wizardPriceListId = (Integer) wizardPriceListContext.get("id");
|
||||
wizardPriceList =
|
||||
Beans.get(PriceListRepository.class).find(Long.valueOf(wizardPriceListId));
|
||||
}
|
||||
|
||||
SaleOrder copy =
|
||||
Beans.get(SaleOrderCreateService.class)
|
||||
.createSaleOrder(origin, wizardCurrency, wizardPriceList);
|
||||
response.setValues(Mapper.toMap(copy));
|
||||
}
|
||||
}
|
||||
|
||||
public void createTemplate(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
if (context.get("_idCopy") != null) {
|
||||
String idCopy = context.get("_idCopy").toString();
|
||||
SaleOrder origin = Beans.get(SaleOrderRepository.class).find(Long.parseLong(idCopy));
|
||||
SaleOrder copy = Beans.get(SaleOrderCreateService.class).createTemplate(origin);
|
||||
response.setValues(Mapper.toMap(copy));
|
||||
}
|
||||
}
|
||||
|
||||
public void computeEndOfValidityDate(ActionRequest request, ActionResponse response) {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
try {
|
||||
saleOrder = Beans.get(SaleOrderService.class).computeEndOfValidityDate(saleOrder);
|
||||
response.setValue("endOfValidityDate", saleOrder.getEndOfValidityDate());
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public void mergeSaleOrder(ActionRequest request, ActionResponse response) {
|
||||
List<SaleOrder> saleOrderList = new ArrayList<SaleOrder>();
|
||||
List<Long> saleOrderIdList = new ArrayList<Long>();
|
||||
boolean fromPopup = false;
|
||||
String lineToMerge;
|
||||
if (request.getContext().get("saleQuotationToMerge") != null) {
|
||||
lineToMerge = "saleQuotationToMerge";
|
||||
} else {
|
||||
lineToMerge = "saleOrderToMerge";
|
||||
}
|
||||
|
||||
if (request.getContext().get(lineToMerge) != null) {
|
||||
|
||||
if (request.getContext().get(lineToMerge) instanceof List) {
|
||||
// No confirmation popup, sale orders are content in a parameter list
|
||||
List<Map> saleOrderMap = (List<Map>) request.getContext().get(lineToMerge);
|
||||
for (Map map : saleOrderMap) {
|
||||
saleOrderIdList.add(new Long((Integer) map.get("id")));
|
||||
}
|
||||
} else {
|
||||
// After confirmation popup, sale order's id are in a string separated by ","
|
||||
String saleOrderIdListStr = (String) request.getContext().get(lineToMerge);
|
||||
for (String saleOrderId : saleOrderIdListStr.split(",")) {
|
||||
saleOrderIdList.add(new Long(saleOrderId));
|
||||
}
|
||||
fromPopup = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if currency, clientPartner and company are the same for all selected sale orders
|
||||
Currency commonCurrency = null;
|
||||
Partner commonClientPartner = null;
|
||||
Company commonCompany = null;
|
||||
Partner commonContactPartner = null;
|
||||
Team commonTeam = null;
|
||||
// Useful to determine if a difference exists between teams of all sale orders
|
||||
boolean existTeamDiff = false;
|
||||
// Useful to determine if a difference exists between contact partners of all sale orders
|
||||
boolean existContactPartnerDiff = false;
|
||||
PriceList commonPriceList = null;
|
||||
// Useful to determine if a difference exists between price lists of all sale orders
|
||||
boolean existPriceListDiff = false;
|
||||
|
||||
SaleOrder saleOrderTemp;
|
||||
int count = 1;
|
||||
for (Long saleOrderId : saleOrderIdList) {
|
||||
saleOrderTemp = JPA.em().find(SaleOrder.class, saleOrderId);
|
||||
saleOrderList.add(saleOrderTemp);
|
||||
if (count == 1) {
|
||||
commonCurrency = saleOrderTemp.getCurrency();
|
||||
commonClientPartner = saleOrderTemp.getClientPartner();
|
||||
commonCompany = saleOrderTemp.getCompany();
|
||||
commonContactPartner = saleOrderTemp.getContactPartner();
|
||||
commonTeam = saleOrderTemp.getTeam();
|
||||
commonPriceList = saleOrderTemp.getPriceList();
|
||||
} else {
|
||||
if (commonCurrency != null && !commonCurrency.equals(saleOrderTemp.getCurrency())) {
|
||||
commonCurrency = null;
|
||||
}
|
||||
if (commonClientPartner != null
|
||||
&& !commonClientPartner.equals(saleOrderTemp.getClientPartner())) {
|
||||
commonClientPartner = null;
|
||||
}
|
||||
if (commonCompany != null && !commonCompany.equals(saleOrderTemp.getCompany())) {
|
||||
commonCompany = null;
|
||||
}
|
||||
if (commonContactPartner != null
|
||||
&& !commonContactPartner.equals(saleOrderTemp.getContactPartner())) {
|
||||
commonContactPartner = null;
|
||||
existContactPartnerDiff = true;
|
||||
}
|
||||
if (commonTeam != null && !commonTeam.equals(saleOrderTemp.getTeam())) {
|
||||
commonTeam = null;
|
||||
existTeamDiff = true;
|
||||
}
|
||||
if (commonPriceList != null && !commonPriceList.equals(saleOrderTemp.getPriceList())) {
|
||||
commonPriceList = null;
|
||||
existPriceListDiff = true;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
StringBuilder fieldErrors = new StringBuilder();
|
||||
if (commonCurrency == null) {
|
||||
fieldErrors.append(I18n.get(IExceptionMessage.SALE_ORDER_MERGE_ERROR_CURRENCY));
|
||||
}
|
||||
if (commonClientPartner == null) {
|
||||
if (fieldErrors.length() > 0) {
|
||||
fieldErrors.append("<br/>");
|
||||
}
|
||||
fieldErrors.append(I18n.get(IExceptionMessage.SALE_ORDER_MERGE_ERROR_CLIENT_PARTNER));
|
||||
}
|
||||
if (commonCompany == null) {
|
||||
if (fieldErrors.length() > 0) {
|
||||
fieldErrors.append("<br/>");
|
||||
}
|
||||
fieldErrors.append(I18n.get(IExceptionMessage.SALE_ORDER_MERGE_ERROR_COMPANY));
|
||||
}
|
||||
|
||||
if (fieldErrors.length() > 0) {
|
||||
response.setFlash(fieldErrors.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if priceList or contactPartner are content in parameters
|
||||
if (request.getContext().get("priceList") != null) {
|
||||
commonPriceList =
|
||||
JPA.em()
|
||||
.find(
|
||||
PriceList.class,
|
||||
new Long((Integer) ((Map) request.getContext().get("priceList")).get("id")));
|
||||
}
|
||||
if (request.getContext().get("contactPartner") != null) {
|
||||
commonContactPartner =
|
||||
JPA.em()
|
||||
.find(
|
||||
Partner.class,
|
||||
new Long((Integer) ((Map) request.getContext().get("contactPartner")).get("id")));
|
||||
}
|
||||
if (request.getContext().get("team") != null) {
|
||||
commonTeam =
|
||||
JPA.em()
|
||||
.find(
|
||||
Team.class,
|
||||
new Long((Integer) ((Map) request.getContext().get("team")).get("id")));
|
||||
}
|
||||
|
||||
if (!fromPopup && (existContactPartnerDiff || existPriceListDiff || existTeamDiff)) {
|
||||
// Need to display intermediate screen to select some values
|
||||
ActionViewBuilder confirmView =
|
||||
ActionView.define("Confirm merge sale order")
|
||||
.model(Wizard.class.getName())
|
||||
.add("form", "sale-order-merge-confirm-form")
|
||||
.param("popup", "true")
|
||||
.param("show-toolbar", "false")
|
||||
.param("show-confirm", "false")
|
||||
.param("popup-save", "false")
|
||||
.param("forceEdit", "true");
|
||||
|
||||
if (existPriceListDiff) {
|
||||
confirmView.context("contextPriceListToCheck", "true");
|
||||
}
|
||||
if (existContactPartnerDiff) {
|
||||
confirmView.context("contextContactPartnerToCheck", "true");
|
||||
confirmView.context("contextPartnerId", commonClientPartner.getId().toString());
|
||||
}
|
||||
if (existTeamDiff) {
|
||||
confirmView.context("contextTeamToCheck", "true");
|
||||
}
|
||||
|
||||
confirmView.context(lineToMerge, Joiner.on(",").join(saleOrderIdList));
|
||||
|
||||
response.setView(confirmView.map());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
SaleOrder saleOrder =
|
||||
Beans.get(SaleOrderCreateService.class)
|
||||
.mergeSaleOrders(
|
||||
saleOrderList,
|
||||
commonCurrency,
|
||||
commonClientPartner,
|
||||
commonCompany,
|
||||
commonContactPartner,
|
||||
commonPriceList,
|
||||
commonTeam);
|
||||
if (saleOrder != null) {
|
||||
// Open the generated sale order in a new tab
|
||||
response.setView(
|
||||
ActionView.define("Sale order")
|
||||
.model(SaleOrder.class.getName())
|
||||
.add("grid", "sale-order-grid")
|
||||
.add("form", "sale-order-form")
|
||||
.param("forceEdit", "true")
|
||||
.context("_showRecord", String.valueOf(saleOrder.getId()))
|
||||
.map());
|
||||
response.setCanClose(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address string with their values.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void computeAddressStr(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
Beans.get(SaleOrderService.class).computeAddressStr(saleOrder);
|
||||
|
||||
response.setValues(saleOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on partner, company or payment change. Fill the bank details with a default value.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @throws AxelorException
|
||||
*/
|
||||
public void fillCompanyBankDetails(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
PaymentMode paymentMode = (PaymentMode) request.getContext().get("paymentMode");
|
||||
Company company = saleOrder.getCompany();
|
||||
Partner partner = saleOrder.getClientPartner();
|
||||
if (company == null) {
|
||||
return;
|
||||
}
|
||||
if (partner != null) {
|
||||
partner = Beans.get(PartnerRepository.class).find(partner.getId());
|
||||
}
|
||||
BankDetails defaultBankDetails =
|
||||
Beans.get(BankDetailsService.class)
|
||||
.getDefaultCompanyBankDetails(company, paymentMode, partner, null);
|
||||
response.setValue("companyBankDetails", defaultBankDetails);
|
||||
}
|
||||
|
||||
public void enableEditOrder(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder =
|
||||
Beans.get(SaleOrderRepository.class)
|
||||
.find(request.getContext().asType(SaleOrder.class).getId());
|
||||
|
||||
try {
|
||||
boolean checkAvailabiltyRequest =
|
||||
Beans.get(SaleOrderService.class).enableEditOrder(saleOrder);
|
||||
response.setReload(true);
|
||||
if (checkAvailabiltyRequest) {
|
||||
response.setNotify(I18n.get(IExceptionMessage.SALE_ORDER_EDIT_ORDER_NOTIFY));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from sale order form view, on clicking validate change button. Call {@link
|
||||
* SaleOrderService#validateChanges(SaleOrder)}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void validateChanges(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
SaleOrder saleOrderView = request.getContext().asType(SaleOrder.class);
|
||||
SaleOrder saleOrder = Beans.get(SaleOrderRepository.class).find(saleOrderView.getId());
|
||||
Beans.get(SaleOrderService.class).validateChanges(saleOrder);
|
||||
response.setReload(true);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on printing settings select. Set the domain for {@link SaleOrder#printingSettings}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void filterPrintingSettings(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
List<PrintingSettings> printingSettingsList =
|
||||
Beans.get(TradingNameService.class)
|
||||
.getPrintingSettingsList(saleOrder.getTradingName(), saleOrder.getCompany());
|
||||
String domain =
|
||||
String.format(
|
||||
"self.id IN (%s)",
|
||||
!printingSettingsList.isEmpty()
|
||||
? StringTool.getIdListString(printingSettingsList)
|
||||
: "0");
|
||||
response.setAttr("printingSettings", "domain", domain);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on trading name change. Set the default value for {@link SaleOrder#printingSettings}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void fillDefaultPrintingSettings(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
response.setValue(
|
||||
"printingSettings",
|
||||
Beans.get(TradingNameService.class)
|
||||
.getDefaultPrintingSettings(saleOrder.getTradingName(), saleOrder.getCompany()));
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from sale order form view on partner change. Get the default price list for the sale
|
||||
* order. Call {@link PartnerPriceListService#getDefaultPriceList(Partner, int)}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void fillPriceList(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder;
|
||||
if (request.getContext().get("_saleOrderTemplate") != null) {
|
||||
LinkedHashMap<String, Object> saleOrderTemplateContext =
|
||||
(LinkedHashMap<String, Object>) request.getContext().get("_saleOrderTemplate");
|
||||
Integer saleOrderId = (Integer) saleOrderTemplateContext.get("id");
|
||||
saleOrder = Beans.get(SaleOrderRepository.class).find(Long.valueOf(saleOrderId));
|
||||
} else {
|
||||
saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
}
|
||||
response.setValue(
|
||||
"priceList",
|
||||
saleOrder.getClientPartner() != null
|
||||
? Beans.get(PartnerPriceListService.class)
|
||||
.getDefaultPriceList(saleOrder.getClientPartner(), PriceListRepository.TYPE_SALE)
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from sale order view on price list select. Call {@link
|
||||
* PartnerPriceListService#getPriceListDomain(Partner, int)}.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void changePriceListDomain(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder;
|
||||
if (request.getContext().get("_saleOrderTemplate") != null) {
|
||||
LinkedHashMap<String, Object> saleOrderTemplateContext =
|
||||
(LinkedHashMap<String, Object>) request.getContext().get("_saleOrderTemplate");
|
||||
Integer saleOrderId = (Integer) saleOrderTemplateContext.get("id");
|
||||
saleOrder = Beans.get(SaleOrderRepository.class).find(Long.valueOf(saleOrderId));
|
||||
} else {
|
||||
saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
}
|
||||
String domain =
|
||||
Beans.get(PartnerPriceListService.class)
|
||||
.getPriceListDomain(saleOrder.getClientPartner(), PriceListRepository.TYPE_SALE);
|
||||
response.setAttr("priceList", "domain", domain);
|
||||
}
|
||||
|
||||
public void removeSubLines(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
response.setValue(
|
||||
"saleOrderLineList",
|
||||
Beans.get(SaleOrderComputeService.class)
|
||||
.removeSubLines(saleOrder.getSaleOrderLineList()));
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
response.setReload(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSaleOrderLineTax(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
|
||||
Beans.get(SaleOrderCreateService.class).updateSaleOrderLineList(saleOrder);
|
||||
|
||||
response.setValue("saleOrderLineList", saleOrder.getSaleOrderLineList());
|
||||
}
|
||||
|
||||
public void getSaleOrderPartnerDomain(ActionRequest request, ActionResponse response) {
|
||||
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
|
||||
Company company = saleOrder.getCompany();
|
||||
long companyId = company.getPartner().getId();
|
||||
String domain =
|
||||
String.format(
|
||||
"self.id != %d AND self.isContact = false AND (self.isCustomer = true or self.isProspect = true)",
|
||||
companyId);
|
||||
domain += " AND :company member of self.companySet";
|
||||
try {
|
||||
if (!(saleOrder.getSaleOrderLineList() == null
|
||||
|| saleOrder.getSaleOrderLineList().isEmpty())) {
|
||||
domain += Beans.get(PartnerService.class).getPartnerDomain(saleOrder.getClientPartner());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(e);
|
||||
response.setError(e.getMessage());
|
||||
}
|
||||
response.setAttr("clientPartner", "domain", domain);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,472 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.apps.sale.web;
|
||||
|
||||
import com.axelor.apps.account.db.TaxLine;
|
||||
import com.axelor.apps.base.db.Product;
|
||||
import com.axelor.apps.base.db.repo.PriceListLineRepository;
|
||||
import com.axelor.apps.base.db.repo.ProductRepository;
|
||||
import com.axelor.apps.base.service.app.AppBaseService;
|
||||
import com.axelor.apps.base.service.tax.FiscalPositionService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.axelor.db.mapper.Mapper;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.exception.service.TraceBackService;
|
||||
import com.axelor.inject.Beans;
|
||||
import com.axelor.rpc.ActionRequest;
|
||||
import com.axelor.rpc.ActionResponse;
|
||||
import com.axelor.rpc.Context;
|
||||
import com.google.inject.Singleton;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class SaleOrderLineController {
|
||||
|
||||
public void compute(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
|
||||
SaleOrder saleOrder = Beans.get(SaleOrderLineService.class).getSaleOrder(context);
|
||||
|
||||
try {
|
||||
compute(response, saleOrder, saleOrderLine);
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void computeSubMargin(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
|
||||
saleOrderLine.setSaleOrder(saleOrder);
|
||||
Map<String, BigDecimal> map = saleOrderLineService.computeSubMargin(saleOrder, saleOrderLine);
|
||||
|
||||
response.setValues(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the sale order line form. Update all fields when the product is changed.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void getProductInformation(ActionRequest request, ActionResponse response) {
|
||||
try {
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
|
||||
Product product = saleOrderLine.getProduct();
|
||||
|
||||
if (saleOrder == null || product == null) {
|
||||
resetProductInformation(response, saleOrderLine);
|
||||
return;
|
||||
}
|
||||
|
||||
Integer packPriceSelect = product.getPackPriceSelect();
|
||||
if (saleOrderLine.getIsSubLine()) {
|
||||
if (context.getParent().getContextClass().equals(SaleOrderLine.class)) {
|
||||
packPriceSelect = context.getParent().asType(SaleOrderLine.class).getPackPriceSelect();
|
||||
} else if (saleOrderLine.getParentLine() != null) {
|
||||
packPriceSelect = saleOrderLine.getParentLine().getPackPriceSelect();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
product = Beans.get(ProductRepository.class).find(product.getId());
|
||||
saleOrderLineService.computeProductInformation(saleOrderLine, saleOrder, packPriceSelect);
|
||||
response.setValue("saleSupplySelect", product.getSaleSupplySelect());
|
||||
response.setValues(saleOrderLine);
|
||||
} catch (Exception e) {
|
||||
resetProductInformation(response, saleOrderLine);
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetProductInformation(ActionResponse response, SaleOrderLine line) {
|
||||
Beans.get(SaleOrderLineService.class).resetProductInformation(line);
|
||||
response.setValue("saleSupplySelect", null);
|
||||
response.setValue("typeSelect", SaleOrderLineRepository.TYPE_NORMAL);
|
||||
response.setValue("packPriceSelect", null);
|
||||
response.setValue("subLineList", null);
|
||||
response.setValues(line);
|
||||
}
|
||||
|
||||
public void getTaxEquiv(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
SaleOrder saleOrder = Beans.get(SaleOrderLineService.class).getSaleOrder(context);
|
||||
|
||||
response.setValue("taxEquiv", null);
|
||||
|
||||
if (saleOrder == null
|
||||
|| saleOrderLine == null
|
||||
|| saleOrder.getClientPartner() == null
|
||||
|| saleOrderLine.getTaxLine() == null) return;
|
||||
|
||||
response.setValue(
|
||||
"taxEquiv",
|
||||
Beans.get(FiscalPositionService.class)
|
||||
.getTaxEquiv(
|
||||
saleOrder.getClientPartner().getFiscalPosition(),
|
||||
saleOrderLine.getTaxLine().getTax()));
|
||||
}
|
||||
|
||||
public void getDiscount(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
|
||||
if (saleOrder == null || saleOrderLine.getProduct() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Map<String, Object> discounts;
|
||||
if (saleOrderLine.getProduct().getInAti()) {
|
||||
discounts =
|
||||
saleOrderLineService.getDiscountsFromPriceLists(
|
||||
saleOrder,
|
||||
saleOrderLine,
|
||||
saleOrderLineService.getInTaxUnitPrice(
|
||||
saleOrder, saleOrderLine, saleOrderLine.getTaxLine()));
|
||||
} else {
|
||||
discounts =
|
||||
saleOrderLineService.getDiscountsFromPriceLists(
|
||||
saleOrder,
|
||||
saleOrderLine,
|
||||
saleOrderLineService.getExTaxUnitPrice(
|
||||
saleOrder, saleOrderLine, saleOrderLine.getTaxLine()));
|
||||
}
|
||||
|
||||
if (discounts != null) {
|
||||
BigDecimal price = (BigDecimal) discounts.get("price");
|
||||
if (price != null
|
||||
&& price.compareTo(
|
||||
saleOrderLine.getProduct().getInAti()
|
||||
? saleOrderLine.getInTaxPrice()
|
||||
: saleOrderLine.getPrice())
|
||||
!= 0) {
|
||||
if (saleOrderLine.getProduct().getInAti()) {
|
||||
response.setValue("inTaxPrice", price);
|
||||
response.setValue(
|
||||
"price",
|
||||
saleOrderLineService.convertUnitPrice(true, saleOrderLine.getTaxLine(), price));
|
||||
} else {
|
||||
response.setValue("price", price);
|
||||
response.setValue(
|
||||
"inTaxPrice",
|
||||
saleOrderLineService.convertUnitPrice(false, saleOrderLine.getTaxLine(), price));
|
||||
}
|
||||
}
|
||||
|
||||
if (saleOrderLine.getProduct().getInAti() != saleOrder.getInAti()
|
||||
&& (Integer) discounts.get("discountTypeSelect")
|
||||
!= PriceListLineRepository.AMOUNT_TYPE_PERCENT) {
|
||||
response.setValue(
|
||||
"discountAmount",
|
||||
saleOrderLineService.convertUnitPrice(
|
||||
saleOrderLine.getProduct().getInAti(),
|
||||
saleOrderLine.getTaxLine(),
|
||||
(BigDecimal) discounts.get("discountAmount")));
|
||||
} else {
|
||||
response.setValue("discountAmount", discounts.get("discountAmount"));
|
||||
}
|
||||
response.setValue("discountTypeSelect", discounts.get("discountTypeSelect"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ex. tax unit price of an invoice line from its in. tax unit price.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void updatePrice(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
|
||||
try {
|
||||
BigDecimal inTaxPrice = saleOrderLine.getInTaxPrice();
|
||||
TaxLine taxLine = saleOrderLine.getTaxLine();
|
||||
|
||||
response.setValue(
|
||||
"price",
|
||||
Beans.get(SaleOrderLineService.class).convertUnitPrice(true, taxLine, inTaxPrice));
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the in. tax unit price of an invoice line from its ex. tax unit price.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public void updateInTaxPrice(ActionRequest request, ActionResponse response) {
|
||||
Context context = request.getContext();
|
||||
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
|
||||
try {
|
||||
BigDecimal exTaxPrice = saleOrderLine.getPrice();
|
||||
TaxLine taxLine = saleOrderLine.getTaxLine();
|
||||
|
||||
response.setValue(
|
||||
"inTaxPrice",
|
||||
Beans.get(SaleOrderLineService.class).convertUnitPrice(false, taxLine, exTaxPrice));
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void convertUnitPrice(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
|
||||
SaleOrder saleOrder = Beans.get(SaleOrderLineService.class).getSaleOrder(context);
|
||||
|
||||
if (saleOrder == null
|
||||
|| saleOrderLine.getProduct() == null
|
||||
|| saleOrderLine.getPrice() == null
|
||||
|| saleOrderLine.getInTaxPrice() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
BigDecimal price = saleOrderLine.getPrice();
|
||||
BigDecimal inTaxPrice = price.add(price.multiply(saleOrderLine.getTaxLine().getValue()));
|
||||
|
||||
response.setValue("inTaxPrice", inTaxPrice);
|
||||
|
||||
} catch (Exception e) {
|
||||
response.setFlash(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void emptyLine(ActionRequest request, ActionResponse response) {
|
||||
SaleOrderLine saleOrderLine = request.getContext().asType(SaleOrderLine.class);
|
||||
if (saleOrderLine.getTypeSelect() != SaleOrderLineRepository.TYPE_NORMAL) {
|
||||
Map<String, Object> newSaleOrderLine = Mapper.toMap(new SaleOrderLine());
|
||||
newSaleOrderLine.put("qty", BigDecimal.ZERO);
|
||||
newSaleOrderLine.put("id", saleOrderLine.getId());
|
||||
newSaleOrderLine.put("version", saleOrderLine.getVersion());
|
||||
newSaleOrderLine.put("typeSelect", saleOrderLine.getTypeSelect());
|
||||
response.setValues(newSaleOrderLine);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkQty(ActionRequest request, ActionResponse response) {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
Beans.get(SaleOrderLineService.class).checkMultipleQty(saleOrderLine, response);
|
||||
}
|
||||
|
||||
public void updateSubLineQty(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
SaleOrderLine newkitLine = request.getContext().asType(SaleOrderLine.class);
|
||||
BigDecimal qty = BigDecimal.ZERO;
|
||||
BigDecimal oldKitQty = BigDecimal.ZERO;
|
||||
BigDecimal newKitQty = BigDecimal.ZERO;
|
||||
BigDecimal exTaxTotal = BigDecimal.ZERO;
|
||||
BigDecimal inTaxTotal = BigDecimal.ZERO;
|
||||
BigDecimal priceDiscounted = BigDecimal.ZERO;
|
||||
BigDecimal taxRate = BigDecimal.ZERO;
|
||||
BigDecimal companyExTaxTotal = BigDecimal.ZERO;
|
||||
BigDecimal companyInTaxTotal = BigDecimal.ZERO;
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
|
||||
if (newkitLine.getTypeSelect() == SaleOrderLineRepository.TYPE_PACK) {
|
||||
|
||||
if (newkitLine.getOldQty().compareTo(BigDecimal.ZERO) == 0) {
|
||||
oldKitQty = BigDecimal.ONE;
|
||||
} else {
|
||||
oldKitQty = newkitLine.getOldQty();
|
||||
}
|
||||
|
||||
if (newkitLine.getQty().compareTo(BigDecimal.ZERO) != 0) {
|
||||
newKitQty = newkitLine.getQty();
|
||||
}
|
||||
|
||||
List<SaleOrderLine> orderLines = newkitLine.getSubLineList();
|
||||
|
||||
if (orderLines != null) {
|
||||
if (newKitQty.compareTo(BigDecimal.ZERO) != 0) {
|
||||
for (SaleOrderLine line : orderLines) {
|
||||
qty = (line.getQty().divide(oldKitQty, 2, RoundingMode.HALF_EVEN)).multiply(newKitQty);
|
||||
priceDiscounted = saleOrderLineService.computeDiscount(line, saleOrder.getInAti());
|
||||
|
||||
if (line.getTaxLine() != null) {
|
||||
taxRate = line.getTaxLine().getValue();
|
||||
}
|
||||
|
||||
if (!saleOrder.getInAti()) {
|
||||
exTaxTotal = saleOrderLineService.computeAmount(qty, priceDiscounted);
|
||||
inTaxTotal = exTaxTotal.add(exTaxTotal.multiply(taxRate));
|
||||
companyExTaxTotal =
|
||||
saleOrderLineService.getAmountInCompanyCurrency(exTaxTotal, saleOrder);
|
||||
companyInTaxTotal = companyExTaxTotal.add(companyExTaxTotal.multiply(taxRate));
|
||||
} else {
|
||||
inTaxTotal = saleOrderLineService.computeAmount(qty, priceDiscounted);
|
||||
exTaxTotal =
|
||||
inTaxTotal.divide(taxRate.add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP);
|
||||
companyInTaxTotal =
|
||||
saleOrderLineService.getAmountInCompanyCurrency(inTaxTotal, saleOrder);
|
||||
companyExTaxTotal =
|
||||
companyInTaxTotal.divide(
|
||||
taxRate.add(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
line.setQty(qty.setScale(2, RoundingMode.HALF_EVEN));
|
||||
line.setPriceDiscounted(priceDiscounted);
|
||||
line.setExTaxTotal(exTaxTotal);
|
||||
line.setInTaxTotal(inTaxTotal);
|
||||
line.setCompanyExTaxTotal(companyExTaxTotal);
|
||||
line.setCompanyInTaxTotal(companyInTaxTotal);
|
||||
}
|
||||
} else {
|
||||
for (SaleOrderLine line : orderLines) {
|
||||
line.setQty(qty.setScale(2, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
}
|
||||
|
||||
response.setValue("oldQty", newKitQty);
|
||||
response.setValue("subLineList", orderLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateQtyUg(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
Context context = request.getContext();
|
||||
SaleOrderLine saleOrderLine = context.asType(SaleOrderLine.class);
|
||||
|
||||
int scale = Beans.get(AppBaseService.class).getNbDecimalDigitForSalePrice();
|
||||
|
||||
BigDecimal qtyUg = saleOrderLine.getProduct().getUg();
|
||||
BigDecimal qty = saleOrderLine.getQty();
|
||||
BigDecimal totalQty =
|
||||
qty.add(qty.multiply(qtyUg).divide(new BigDecimal(100), 4, RoundingMode.HALF_EVEN));
|
||||
Product product = saleOrderLine.getProduct();
|
||||
if (product.getUg() != null && product.getUg().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal ug =
|
||||
saleOrderLine
|
||||
.getProduct()
|
||||
.getUg()
|
||||
.divide(new BigDecimal(100), scale, RoundingMode.HALF_EVEN);
|
||||
BigDecimal ugAmount =
|
||||
ug.divide(ug.add(BigDecimal.ONE), scale, RoundingMode.HALF_EVEN)
|
||||
.multiply(new BigDecimal(100));
|
||||
response.setValue("discountTypeSelect", 1);
|
||||
response.setValue("discountAmount", ugAmount);
|
||||
response.setValue("qty", totalQty);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetPackLine(ActionRequest request, ActionResponse response) throws AxelorException {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
SaleOrderLine packLine = context.asType(SaleOrderLine.class);
|
||||
try {
|
||||
saleOrderLineService.fillPrice(packLine, saleOrder, packLine.getPackPriceSelect());
|
||||
compute(response, saleOrder, packLine);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetPackSubLine(ActionRequest request, ActionResponse response)
|
||||
throws AxelorException {
|
||||
|
||||
Context context = request.getContext();
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
SaleOrder saleOrder = saleOrderLineService.getSaleOrder(context);
|
||||
SaleOrderLine packLine = context.asType(SaleOrderLine.class);
|
||||
List<SaleOrderLine> subLines = packLine.getSubLineList();
|
||||
|
||||
try {
|
||||
if (subLines != null) {
|
||||
for (SaleOrderLine line : subLines) {
|
||||
saleOrderLineService.fillPrice(line, saleOrder, packLine.getPackPriceSelect());
|
||||
saleOrderLineService.computeValues(saleOrder, line);
|
||||
}
|
||||
response.setValue("subLineList", subLines);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TraceBackService.trace(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void compute(ActionResponse response, SaleOrder saleOrder, SaleOrderLine orderLine)
|
||||
throws AxelorException {
|
||||
|
||||
Map<String, BigDecimal> map =
|
||||
Beans.get(SaleOrderLineService.class).computeValues(saleOrder, orderLine);
|
||||
|
||||
map.put("price", orderLine.getPrice());
|
||||
map.put("inTaxPrice", orderLine.getInTaxPrice());
|
||||
map.put("companyCostPrice", orderLine.getCompanyCostPrice());
|
||||
map.put("discountAmount", orderLine.getDiscountAmount());
|
||||
|
||||
response.setValues(map);
|
||||
response.setAttr(
|
||||
"priceDiscounted",
|
||||
"hidden",
|
||||
map.getOrDefault("priceDiscounted", BigDecimal.ZERO)
|
||||
.compareTo(saleOrder.getInAti() ? orderLine.getInTaxPrice() : orderLine.getPrice())
|
||||
== 0);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.csv.script;
|
||||
|
||||
import com.axelor.apps.base.service.administration.SequenceService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderManagementRepository;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderComputeService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderService;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderWorkflowService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportSaleOrder {
|
||||
|
||||
@Inject SaleOrderManagementRepository saleOrderRepo;
|
||||
|
||||
protected SaleOrderService saleOrderService;
|
||||
protected SaleOrderComputeService saleOrderComputeService;
|
||||
protected SaleOrderWorkflowService saleOrderWorkflowService;
|
||||
protected SequenceService sequenceService;
|
||||
|
||||
@Inject
|
||||
public ImportSaleOrder(
|
||||
SaleOrderService saleOrderService,
|
||||
SaleOrderComputeService saleOrderComputeService,
|
||||
SaleOrderWorkflowService saleOrderWorkflowService,
|
||||
SequenceService sequenceService) {
|
||||
this.saleOrderService = saleOrderService;
|
||||
this.saleOrderComputeService = saleOrderComputeService;
|
||||
this.saleOrderWorkflowService = saleOrderWorkflowService;
|
||||
this.sequenceService = sequenceService;
|
||||
}
|
||||
|
||||
public Object importSaleOrder(Object bean, Map<String, Object> values) throws AxelorException {
|
||||
assert bean instanceof SaleOrder;
|
||||
|
||||
SaleOrder saleOrder = (SaleOrder) bean;
|
||||
|
||||
saleOrderService.computeAddressStr(saleOrder);
|
||||
|
||||
saleOrder = saleOrderComputeService.computeSaleOrder(saleOrder);
|
||||
|
||||
if (saleOrder.getStatusSelect() == 1) {
|
||||
saleOrder.setSaleOrderSeq(sequenceService.getDraftSequenceNumber(saleOrder));
|
||||
saleOrderRepo.computeFullName(saleOrder);
|
||||
} else {
|
||||
saleOrderWorkflowService.finalizeQuotation(saleOrder);
|
||||
}
|
||||
|
||||
return saleOrder;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.csv.script;
|
||||
|
||||
import com.axelor.apps.sale.db.SaleOrderLine;
|
||||
import com.axelor.apps.sale.service.saleorder.SaleOrderLineService;
|
||||
import com.axelor.exception.AxelorException;
|
||||
import com.axelor.inject.Beans;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImportSaleOrderLine {
|
||||
|
||||
public Object importSaleOrderLine(Object bean, Map<String, Object> values)
|
||||
throws AxelorException {
|
||||
assert bean instanceof SaleOrderLine;
|
||||
|
||||
SaleOrderLine saleOrderLine = (SaleOrderLine) bean;
|
||||
SaleOrderLineService saleOrderLineService = Beans.get(SaleOrderLineService.class);
|
||||
saleOrderLine.setTaxLine(
|
||||
saleOrderLineService.getTaxLine(saleOrderLine.getSaleOrder(), saleOrderLine));
|
||||
saleOrderLineService.computeValues(saleOrderLine.getSaleOrder(), saleOrderLine);
|
||||
|
||||
return saleOrderLine;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Axelor Business Solutions
|
||||
*
|
||||
* Copyright (C) 2019 Axelor (<http://axelor.com>).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.axelor.web;
|
||||
|
||||
import com.axelor.apps.base.db.Country;
|
||||
import com.axelor.apps.base.service.MapService;
|
||||
import com.axelor.apps.sale.db.SaleOrder;
|
||||
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.inject.Inject;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/map")
|
||||
public class MapRestSale {
|
||||
|
||||
@Inject MapService mapService;
|
||||
|
||||
@Inject private SaleOrderRepository saleOrderRepo;
|
||||
|
||||
@Path("/geomap/turnover")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public JsonNode getGeoMapData() {
|
||||
|
||||
Map<String, BigDecimal> data = new HashMap<String, BigDecimal>();
|
||||
List<? extends SaleOrder> orders = saleOrderRepo.all().filter("self.statusSelect=?", 3).fetch();
|
||||
JsonNodeFactory factory = JsonNodeFactory.instance;
|
||||
ObjectNode mainNode = factory.objectNode();
|
||||
ArrayNode arrayNode = factory.arrayNode();
|
||||
|
||||
ArrayNode labelNode = factory.arrayNode();
|
||||
labelNode.add("Country");
|
||||
labelNode.add("Turnover");
|
||||
arrayNode.add(labelNode);
|
||||
|
||||
for (SaleOrder so : orders) {
|
||||
|
||||
Country country = so.getMainInvoicingAddress().getAddressL7Country();
|
||||
BigDecimal value = so.getExTaxTotal();
|
||||
|
||||
if (country != null) {
|
||||
String key = country.getName();
|
||||
|
||||
if (data.containsKey(key)) {
|
||||
BigDecimal oldValue = data.get(key);
|
||||
oldValue = oldValue.add(value);
|
||||
data.put(key, oldValue);
|
||||
} else {
|
||||
data.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<String> keys = data.keySet().iterator();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
ArrayNode dataNode = factory.arrayNode();
|
||||
dataNode.add(key);
|
||||
dataNode.add(data.get(key));
|
||||
arrayNode.add(dataNode);
|
||||
}
|
||||
|
||||
mainNode.put("status", 0);
|
||||
mainNode.set("data", arrayNode);
|
||||
|
||||
return mainNode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,235 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><%
|
||||
configuratorCreatorRepository = 'com.axelor.apps.sale.db.repo.ConfiguratorCreatorRepository'
|
||||
def cc = { com.axelor.inject.Beans.get(configuratorCreatorRepository as Class).find(it) }
|
||||
|
||||
def attributeMethod = {
|
||||
attribute = ""
|
||||
cc(it).attributes?.each( { att ->
|
||||
attribute += """
|
||||
<attribute>
|
||||
<name>${att.name}</name>
|
||||
<title>"""
|
||||
if (att.title != null) {
|
||||
attribute += "${att.title}"
|
||||
}
|
||||
attribute += """</title>
|
||||
<type>${att.type}</type>
|
||||
<defaultValue>"""
|
||||
if (att.defaultValue != null) {
|
||||
attribute += "${att.defaultValue}"
|
||||
}
|
||||
attribute += """</defaultValue>
|
||||
<model>${att.model}</model>
|
||||
<modelField>${att.modelField}</modelField>
|
||||
<jsonModelCode>"""
|
||||
if (att.jsonModel?.code != null) {
|
||||
attribute += "${att.jsonModel?.code}"
|
||||
}
|
||||
attribute += """</jsonModelCode>
|
||||
<selection>"""
|
||||
if (att.selection != null) {
|
||||
attribute += "${att.selection}"
|
||||
}
|
||||
attribute += """</selection>
|
||||
<widget>"""
|
||||
if (att.widget != null) {
|
||||
attribute += "${att.widget}"
|
||||
}
|
||||
attribute += """</widget>
|
||||
<help>"""
|
||||
if (att.help != null) {
|
||||
attribute += "${att.help}"
|
||||
}
|
||||
attribute += """</help>
|
||||
<showIf>"""
|
||||
if (att.showIf != null) {
|
||||
attribute += "${att.showIf}"
|
||||
}
|
||||
attribute += """</showIf>
|
||||
<hideIf>"""
|
||||
if (att.hideIf != null) {
|
||||
attribute += "${att.hideIf}"
|
||||
}
|
||||
attribute += """</hideIf>
|
||||
<requiredIf>"""
|
||||
if (att.requiredIf != null) {
|
||||
attribute += "${att.requiredIf}"
|
||||
}
|
||||
attribute += """</requiredIf>
|
||||
<hidden>${att.hidden}</hidden>
|
||||
<required>${att.required}</required>
|
||||
<nameField>${att.nameField}</nameField>
|
||||
<minSize>${att.minSize}</minSize>
|
||||
<maxSize>${att.maxSize}</maxSize>
|
||||
<precision>${att.precision}</precision>
|
||||
<scale>${att.scale}</scale>
|
||||
<regex>"""
|
||||
if (att.regex != null) {
|
||||
attribute += "${att.regex}"
|
||||
}
|
||||
attribute += """</regex>
|
||||
<targetModel>"""
|
||||
if (att.targetModel != null) {
|
||||
attribute += "${att.targetModel}"
|
||||
}
|
||||
attribute += """</targetModel>
|
||||
<enumType>"""
|
||||
if (att.enumType != null) {
|
||||
attribute += "${att.enumType}"
|
||||
}
|
||||
attribute += """</enumType>
|
||||
<formView>"""
|
||||
if (att.formView != null) {
|
||||
attribute += "${att.formView}"
|
||||
}
|
||||
attribute += """</formView>
|
||||
<gridView>"""
|
||||
if (att.gridView != null) {
|
||||
attribute += "${att.gridView}"
|
||||
}
|
||||
attribute += """</gridView>
|
||||
<domain>"""
|
||||
if (att.domain) {
|
||||
attribute += "${att.domain}"
|
||||
}
|
||||
attribute += """</domain>
|
||||
<targetJsonModelCode>"""
|
||||
if (att.targetJsonModel?.code != null) {
|
||||
attribute += "${att.targetJsonModel?.code}"
|
||||
}
|
||||
attribute += """</targetJsonModelCode>
|
||||
<sequence>${att.sequence}</sequence>
|
||||
<onChange>"""
|
||||
if (att.onChange != null) {
|
||||
attribute += "${att.onChange}"
|
||||
}
|
||||
attribute += """</onChange>
|
||||
<onClick>"""
|
||||
if (att.onClick != null) {
|
||||
attribute += "${att.onClick}"
|
||||
}
|
||||
attribute += """</onClick>
|
||||
<widgetAttrs>"""
|
||||
if (att.widgetAttrs != null) {
|
||||
attribute += "${att.widgetAttrs}"
|
||||
}
|
||||
attribute += """</widgetAttrs>
|
||||
<contextField>"""
|
||||
if (att.contextField != null) {
|
||||
attribute += "${att.contextField}"
|
||||
}
|
||||
attribute += """</contextField>
|
||||
<contextFieldTarget>"""
|
||||
if (att.contextFieldTarget != null) {
|
||||
attribute += "${att.contextFieldTarget}"
|
||||
}
|
||||
attribute += """</contextFieldTarget>
|
||||
<contextFieldTargetName>"""
|
||||
if (att.contextFieldTargetName != null) {
|
||||
attribute += "${att.contextFieldTargetName}"
|
||||
}
|
||||
attribute += """</contextFieldTargetName>
|
||||
<contextFieldValue>"""
|
||||
if (att.contextFieldValue != null) {
|
||||
attribute += "${att.contextFieldValue}"
|
||||
}
|
||||
attribute += """</contextFieldValue>
|
||||
</attribute>
|
||||
"""
|
||||
})
|
||||
return attribute
|
||||
}
|
||||
|
||||
def configuratorProductFormulaMethod = {
|
||||
configuratorProductFormula = ""
|
||||
cc(it).configuratorProductFormulaList?.each( { cf ->
|
||||
configuratorProductFormula += """
|
||||
<configuratorProductFormula>
|
||||
"""
|
||||
if (cf.metaField?.id != null) {
|
||||
configuratorProductFormula += "<metaField>${cf.metaField?.name}</metaField>"
|
||||
}
|
||||
configuratorProductFormula += """
|
||||
<formula>"""
|
||||
if (cf.formula != null) {
|
||||
configuratorProductFormula += "${cf.formula}"
|
||||
}
|
||||
configuratorProductFormula += """</formula>
|
||||
<showOnConfigurator>${cf.showOnConfigurator}</showOnConfigurator>
|
||||
<configuratorCreatorImportId>${cf.productCreator?.id}</configuratorCreatorImportId>
|
||||
</configuratorProductFormula>
|
||||
"""
|
||||
})
|
||||
return configuratorProductFormula
|
||||
}
|
||||
|
||||
def configuratorSOLineFormulaMethod = {
|
||||
configuratorSOLineFormula = ""
|
||||
cc(it).configuratorSOLineFormulaList?.each( { cf ->
|
||||
configuratorSOLineFormula += """
|
||||
<configuratorSOLineFormula>
|
||||
"""
|
||||
if (cf.metaField?.id != null) {
|
||||
configuratorSOLineFormula += "<metaField>${ cf.metaField?.name}</metaField>"
|
||||
}
|
||||
configuratorSOLineFormula += """
|
||||
<formula>"""
|
||||
if (cf.formula != null) {
|
||||
configuratorSOLineFormula += "${cf.formula}"
|
||||
}
|
||||
configuratorSOLineFormula += """</formula>
|
||||
<showOnConfigurator>${cf.showOnConfigurator}</showOnConfigurator>
|
||||
<updateFromSelect>${cf.updateFromSelect}</updateFromSelect>
|
||||
<configuratorCreatorImportId>${cf.soLineCreator?.id}</configuratorCreatorImportId>
|
||||
</configuratorSOLineFormula>
|
||||
"""
|
||||
})
|
||||
return configuratorSOLineFormula
|
||||
}
|
||||
|
||||
def authorizedUserMethod = {
|
||||
authorizedUser = ""
|
||||
cc(it).authorizedUserSet?.each( { au ->
|
||||
authorizedUser += """
|
||||
<authorizedUser>${au.code}</authorizedUser>"""
|
||||
})
|
||||
return authorizedUser
|
||||
}
|
||||
|
||||
def authorizedGroupMethod = {
|
||||
authorizedGroup = ""
|
||||
cc(it).authorizedGroupSet?.each( { ag ->
|
||||
authorizedGroup += """
|
||||
<authorizedGroup>${ag.code}</authorizedGroup>"""
|
||||
})
|
||||
return authorizedGroup
|
||||
}
|
||||
%>
|
||||
|
||||
<configurator-export>
|
||||
|
||||
<configurator-creators><% __ids__.each( { it -> %>
|
||||
|
||||
<configurator-creator>
|
||||
<importId><% out << cc(it).id %></importId>
|
||||
<name><% out << cc(it).name %></name>
|
||||
<attributes><% out << attributeMethod(it) %></attributes>
|
||||
|
||||
<configuratorProductFormulaList><% out << configuratorProductFormulaMethod(it) %></configuratorProductFormulaList>
|
||||
|
||||
<configuratorSOLineFormulaList><% out << configuratorSOLineFormulaMethod(it) %></configuratorSOLineFormulaList>
|
||||
|
||||
<authorizedUserSet><% out << authorizedUserMethod(it) %>
|
||||
</authorizedUserSet>
|
||||
|
||||
<authorizedGroupSet><% out << authorizedGroupMethod(it) %>
|
||||
</authorizedGroupSet>
|
||||
|
||||
<generateProduct><% out << cc(it).generateProduct %></generateProduct>
|
||||
<isActive><% out << cc(it).isActive %></isActive>
|
||||
|
||||
</configurator-creator><% })%>
|
||||
|
||||
</configurator-creators>
|
||||
|
||||
</configurator-export>
|
||||
@ -0,0 +1,195 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xml-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
|
||||
<input file="configurator-creator.xml" root="configurator-creators">
|
||||
|
||||
<!-- Import configuratorBOMs -->
|
||||
|
||||
<bind node="configurator-boms/configurator-bom" type="com.axelor.apps.production.db.ConfiguratorBOM" update="true" search="self.importId = :importId">
|
||||
<bind node="name" to="name"/>
|
||||
<bind node="importId" to="importId"/>
|
||||
<bind node="companyCode" to="company" search="self.code = :companyCode" if="companyCode">
|
||||
<bind node="text()" to="code" alias="companyCode"/>
|
||||
</bind>
|
||||
<bind node="statusSelect" to="statusSelect"/>
|
||||
<bind node="nameFormula" to="nameFormula"/>
|
||||
<bind node="defNameAsFormula" to="defNameAsFormula"/>
|
||||
<bind node="productCode" to="product" search="self.code = :productCode" if="productCode">
|
||||
<bind node="text()" to="code" alias="productCode"/>
|
||||
</bind>
|
||||
<bind node="productFormula" to="productFormula"/>
|
||||
<bind node="defProductAsFormula" to="defProductAsFormula"/>
|
||||
<bind node="defProductFromConfigurator" to="defProductFromConfigurator"/>
|
||||
<bind node="qty" to="qty"/>
|
||||
<bind node="qtyFormula" to="qtyFormula"/>
|
||||
<bind node="defQtyAsFormula" to="defQtyAsFormula"/>
|
||||
<bind node="unitId" to="unit" search="self.id = :unitId" if="unitId">
|
||||
<bind node="text()" to="id" alias="unitId"/>
|
||||
</bind>
|
||||
<bind node="unitFormula" to="unitFormula"/>
|
||||
<bind node="defUnitAsFormula" to="defUnitAsFormula"/>
|
||||
<bind node="prodProcessCode" to="prodProcess" search="self.code = :prodProcessCode" if="prodProcessCode">
|
||||
<bind node="text()" to="code" alias="prodProcessCode"/>
|
||||
</bind>
|
||||
|
||||
<bind node="configuratorProdProcess" to="configuratorProdProcess" update="true" search="self.importId = :importId" if="configProdProcessName">
|
||||
<bind node="importId" to="importId"/>
|
||||
<bind node="statusSelect" to="statusSelect"/>
|
||||
<bind node="name" to="name" alias="configProdProcessName"/>
|
||||
<bind node="code" to="code"/>
|
||||
<bind node="codeFormula" to="codeFormula"/>
|
||||
<bind node="defCodeAsFormula" to="defCodeAsFormula"/>
|
||||
<bind node="companyCode" to="company" search="self.code = :companyCode" if="companyCode">
|
||||
<bind node="text()" to="code" alias="companyCode"/>
|
||||
</bind>
|
||||
<bind node="stockLocation" to="stockLocation" search="self.id = :stockLocationId" if="stockLocationId">
|
||||
<bind node="text()" to="id" alias="stockLocationId"/>
|
||||
</bind>
|
||||
<bind node="stockLocationFormula" to="stockLocationFormula"/>
|
||||
<bind node="defStockLocationAsFormula" to="defStockLocationAsFormula"/>
|
||||
<bind node="producedProductStockLocation" to="producedProductStockLocation" search="self.id = :producedProductStockLocationId" if="producedProductStockLocationId">
|
||||
<bind node="text()" to="id" alias="producedProductStockLocationId"/>
|
||||
</bind>
|
||||
<bind node="producedProductStockLocationFormula" to="producedProductStockLocationFormula"/>
|
||||
<bind node="defProducedProductStockLocationAsFormula" to="defProducedProductStockLocationAsFormula"/>
|
||||
<bind node="workshopStockLocation" to="workshopStockLocation" search="self.id = :workshopStockLocationId" if="workshopStockLocationId">
|
||||
<bind node="text()" to="id" alias="workshopStockLocationId"/>
|
||||
</bind>
|
||||
<bind node="workshopStockLocationFormula" to="workshopStockLocationFormula"/>
|
||||
<bind node="defWorkshopStockLocationAsFormula" to="defWorkshopStockLocationAsFormula"/>
|
||||
<bind node="configuratorProdProcessLineList/configuratorProdProcessLine" to="configuratorProdProcessLineList">
|
||||
<bind node="name" to="name"/>
|
||||
<bind node="priority" to="priority"/>
|
||||
<bind node="workCenter" to="workCenter" search="self.id = :workCenterId" if="workCenterId">
|
||||
<bind node="text()" to="id" alias="workCenterId"/>
|
||||
</bind>
|
||||
<bind node="stockLocation" to="stockLocation" search="self.id = :stockLocationId" if="stockLocationId">
|
||||
<bind node="text()" to="id" alias="stockLocationId"/>
|
||||
</bind>
|
||||
<bind node="outsourcing" to="outsourcing"/>
|
||||
<bind node="description" to="description"/>
|
||||
<bind node="minCapacityPerCycle" to="minCapacityPerCycle"/>
|
||||
<bind node="minCapacityPerCycleFormula" to="minCapacityPerCycleFormula"/>
|
||||
<bind node="defMinCapacityFormula" to="defMinCapacityFormula"/>
|
||||
<bind node="maxCapacityPerCycle" to="maxCapacityPerCycle"/>
|
||||
<bind node="maxCapacityPerCycleFormula" to="maxCapacityPerCycleFormula"/>
|
||||
<bind node="defMaxCapacityFormula" to="defMaxCapacityFormula"/>
|
||||
<bind node="durationPerCycle" to="durationPerCycle"/>
|
||||
<bind node="durationPerCycleFormula" to="durationPerCycleFormula"/>
|
||||
<bind node="defDurationFormula" to="defDurationFormula"/>
|
||||
</bind>
|
||||
</bind>
|
||||
|
||||
<bind node="prodProcessFormula" to="prodProcessFormula"/>
|
||||
<bind node="defProdProcessAsFormula" to="defProdProcessAsFormula"/>
|
||||
<bind node="defProdProcessAsConfigurator" to="defProdProcessAsConfigurator"/>
|
||||
|
||||
<bind node="parentConfiguratorBOMId" to="parentConfiguratorBOM" update="true" search="self.importId = :parentConfigBOMImportId" create="false">
|
||||
<bind node="text()" to="importId" alias="parentConfigBOMImportId"/>
|
||||
</bind>
|
||||
|
||||
<bind node="useCondition" to="useCondition"/>
|
||||
<bind node="billOfMaterialId" to="billOfMaterialId"/>
|
||||
|
||||
</bind>
|
||||
|
||||
|
||||
<!-- Import configurator creators -->
|
||||
|
||||
<bind node="configurator-creators/configurator-creator" type="com.axelor.apps.sale.db.ConfiguratorCreator" update="true" search="self.importId = :importId">
|
||||
|
||||
<bind node="importId" to="importId"/>
|
||||
<bind node="name" to="name"/>
|
||||
<bind node="generateProduct" to="generateProduct"/>
|
||||
<bind node="isActive" to="isActive"/>
|
||||
|
||||
<bind node="attributes/attribute" to="attributes">
|
||||
<bind node="name" to="name"/>
|
||||
<bind node="title" to="title"/>
|
||||
<bind node="type" to="type"/>
|
||||
<bind node="defaultValue" to="defaultValue"/>
|
||||
<bind node="model" to="model"/>
|
||||
<bind node="modelField" to="modelField"/>
|
||||
<bind node="selection" to="selection"/>
|
||||
<bind node="widget" to="widget"/>
|
||||
<bind node="help" to="help"/>
|
||||
<bind node="showIf" to="showIf"/>
|
||||
<bind node="hideIf" to="hideIf"/>
|
||||
<bind node="requiredIf" to="requiredIf"/>
|
||||
<bind node="readonlyIf" to="readonlyIf"/>
|
||||
<bind node="hidden" to="hidden"/>
|
||||
<bind node="required" to="required"/>
|
||||
<bind node="nameField" to="nameField"/>
|
||||
<bind node="minSize" to="minSize"/>
|
||||
<bind node="maxSize" to="maxSize"/>
|
||||
<bind node="precision" to="precision"/>
|
||||
<bind node="scale" to="scale"/>
|
||||
<bind node="regex" to="regex"/>
|
||||
<bind node="targetModel" to="targetModel"/>
|
||||
<bind node="enumType" to="enumType"/>
|
||||
<bind node="formView" to="formView"/>
|
||||
<bind node="gridView" to="gridView"/>
|
||||
<bind node="domain" to="domain"/>
|
||||
<bind node="sequence" to="sequence"/>
|
||||
<bind node="onChange" to="onChange"/>
|
||||
<bind node="onClick" to="onClick"/>
|
||||
<bind node="widgetAttrs" to="widgetAttrs"/>
|
||||
<bind node="contextField" to="contextField"/>
|
||||
<bind node="contextFieldTarget" to="contextFieldTarget"/>
|
||||
<bind node="contextFieldTargetName" to="contextFieldTargetName"/>
|
||||
<bind node="contextFieldValue" to="contextFieldValue"/>
|
||||
</bind>
|
||||
|
||||
<bind node="configuratorProductFormulaList/configuratorProductFormula" to="configuratorProductFormulaList">
|
||||
<bind node="metaField" to="metaField" search="self.name = :metaField and self.metaModel.name = 'Product'" />
|
||||
<bind node="formula" to="formula"/>
|
||||
<bind node="showOnConfigurator" to="showOnConfigurator"/>
|
||||
<bind node="configuratorCreatorImportId" to="productCreator" search="self.importId = :configCreatorImportId" create="false">
|
||||
<bind node="text()" to="importId" alias="configCreatorImportId"/>
|
||||
</bind>
|
||||
</bind>
|
||||
|
||||
<bind node="configuratorSOLineFormulaList/configuratorSOLineFormula" to="configuratorSOLineFormulaList">
|
||||
<bind node="metaField" to="metaField" search="self.name = :metaField and self.metaModel.name = 'SaleOrderLine'"/>
|
||||
<bind node="formula" to="formula"/>
|
||||
<bind node="showOnConfigurator" to="showOnConfigurator"/>
|
||||
<bind node="updateFromSelect" to="updateFromSelect"/>
|
||||
<bind node="configuratorCreatorImportId" to="soLineCreator" search="self.importId = :configCreatorImportId" create="false">
|
||||
<bind node="text()" to="importId" alias="configCreatorImportId"/>
|
||||
</bind>
|
||||
</bind>
|
||||
|
||||
<bind node="authorizedUserSet/authorizedUser" to="authorizedUserSet" search="self.code = :userCode">
|
||||
<bind node="text()" to="code" alias="userCode"/>
|
||||
</bind>
|
||||
<bind node="authorizedGroupSet/authorizedGroup" to="authorizedGroupSet" search="self.code = :groupCode">
|
||||
<bind node="text()" to="code" alias="groupCode"/>
|
||||
</bind>
|
||||
|
||||
<!-- TODO : The link between the configurator creator and its configurator BOM is not created. Check three following lines below. -->
|
||||
<bind node="configuratorBomImportId" to="configuratorBom" search="self.importId = :configBomId" create="false">
|
||||
<bind node="text()" to="importId" alias="configBomId"/>
|
||||
</bind>
|
||||
|
||||
</bind>
|
||||
|
||||
</input>
|
||||
|
||||
|
||||
<!-- TODO : Build arborescence of configuratorBOMs. It's not working for now on. -->
|
||||
|
||||
<!-- <input file="configurator-creator.xml" root="configurator-creators">
|
||||
<bind node="configurator-boms/configurator-bom" type="com.axelor.apps.production.db.ConfiguratorBOM" search="self.id = :configBomId" create="false">
|
||||
<bind node="id" to="id">
|
||||
<bind node="text()" to="id" alias="configBomId" />
|
||||
</bind>
|
||||
<bind node="parentConfiguratorBOMId" to="parentConfiguratorBOM" search="self.id = :parentConfigBOMId">
|
||||
<bind node="text()" to="id" alias="parentConfigBOMId" />
|
||||
</bind>
|
||||
</bind>
|
||||
</input> -->
|
||||
|
||||
</xml-inputs>
|
||||
@ -0,0 +1,2 @@
|
||||
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
|
||||
2;"saleOrder";"Sale order / Customer Order";1;4;"SO";;1;0
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
|
||||
2;"saleOrder";"Devis / commandes clients";1;4;"SO";;1;0
|
||||
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
<input file="base_sequence.csv" separator=";" type="com.axelor.apps.base.db.Sequence" search="self.importId = :importId">
|
||||
<bind to="yearlyResetOk" column="yearlyResetOk" eval="yearlyResetOk == '1' ? true : false"/>
|
||||
<bind to="nextNum" column="nextNum" eval="nextNum?.empty ? '1' : nextNum"/>
|
||||
<bind to="padding" column="padding" eval="padding?.empty ? '1' : padding"/>
|
||||
<bind to="toBeAdded" column="toBeAdded" eval="toBeAdded?.empty ? '1' : toBeAdded"/>
|
||||
<bind to="resetDate" eval="call:com.axelor.apps.base.service.app.AppBaseService:getTodayDate()" />
|
||||
</input>
|
||||
|
||||
</csv-inputs>
|
||||
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
<input file="auth_permission.csv" separator=";" type="com.axelor.auth.db.Permission" search="self.name = :name" call="com.axelor.csv.script.ImportPermission:importPermissionToRole">
|
||||
<bind to="canRead" eval="can_read == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canWrite" eval="can_write == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canCreate" eval="can_create == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canRemove" eval="can_remove == 'x' ? 'true' : 'false'"/>
|
||||
<bind to="canExport" eval="can_export == 'x' ? 'true' : 'false'"/>
|
||||
</input>
|
||||
|
||||
<input file="base_appSale.csv" separator=";" type="com.axelor.apps.base.db.AppSale" call="com.axelor.csv.script.ImportApp:importApp">
|
||||
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
|
||||
</input>
|
||||
|
||||
<input file="base_objectDataConfig.csv" separator=";" type="com.axelor.apps.base.db.DataConfigLine">
|
||||
<bind to="objectDataConfig" search="self.modelSelect = :modelSelect">
|
||||
<bind to="title" column="title"/>
|
||||
<bind to="modelSelect" column="modelSelect" />
|
||||
<bind to="statusSelect" column="status"/>
|
||||
</bind>
|
||||
<bind to="metaModel" column="metaModel" search="self.name = :metaModel" />
|
||||
<bind to="toExportMetaFieldSet" column="fields" search="self.name in :fields and self.metaModel.name = :metaModel" eval="fields.split(',') as List" />
|
||||
<bind to="toDeleteMetaFieldSet" column="toDeleteFields" search="self.name in :toDeleteFields and self.metaModel.name = :metaModel" eval="toDeleteFields.split(',') as List" />
|
||||
</input>
|
||||
|
||||
<input file="meta_helpEN.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
|
||||
<bind to="language" eval="'en'" />
|
||||
<bind to="type" eval="'tooltip'" />
|
||||
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
|
||||
</input>
|
||||
|
||||
<input file="meta_helpFR.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
|
||||
<bind to="language" eval="'fr'" />
|
||||
<bind to="type" eval="'tooltip'" />
|
||||
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
|
||||
</input>
|
||||
|
||||
<input file="meta_metaMenu.csv" separator=";" type="com.axelor.meta.db.MetaMenu" search="self.name = :name" update="true" />
|
||||
|
||||
</csv-inputs>
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
|
||||
"perm.sale.all";"com.axelor.apps.sale.db.*";"x";"x";"x";"x";"x";;;"Admin"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
|
||||
5;"Sale";"sale";4;"Sales configuration";"app-sale.png";"axelor-sale";"crm";2
|
||||
|
@ -0,0 +1,2 @@
|
||||
"title";"modelSelect";"status";"metaModel";"typeSelect";"tabName";"path";"fields";"toDeleteFields"
|
||||
"Partner data config";"com.axelor.apps.base.db.Partner";1;"SaleOrder";0;"SaleOrder";"clientPartner";"saleorderSeq,orderDate,statusSelect,contactPartner,mainInvoicingAddress,mainInvoicingAddressStr,deliveryAddress,deliveryAddressStr";"contactPartner,mainInvoicingAddress,mainInvoicingAddressStr,deliveryAddress,deliveryAddressStr"
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 641 B |
@ -0,0 +1,18 @@
|
||||
"module";"object";"view";"field";"help"
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageSalesUnits";"This function is useful when the units of purchase of a product from a supplier are different from your sales units (for example, if you buy a product per tonne and sell it to the kilo). In this way, when ordering, the selected product will be directly indicated with its sales unit, thus avoiding conversion."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageSaleOrderVersion";"This option enables the ability to create multiple versions of a quotation, allowing you to edit a quotation to create a new versions and track the history."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"productPackMgt";"This option allows you to create pack products in product form, a pack being a product composed of other products. Note that you can then modify the composition of a pack directly from a quotation/sale order line."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"enableConfigurator";"Allows to activate the business configurators on the quotations/sale orders.
|
||||
"
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"allowPendingOrderModification";"This option authorizes the modification of a confirmed sale order. When an order is being modified, the generation of invoices and customer delivery is blocked. If a customer delivery is in the planned status, it remains stuck at this status as long as the order is being modified."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageMultipleSaleQuantity";"This is an option that allows you to define a product's multiples sale quantities on the product form. You can in this way define a product that can only be sold in a certain quantity (for example for a quantity of 5 and 10). If the quantity selected on a sale order line is different than defined multiples, the line can not be validated, and a message will appear, displaying which are the authorized quantities .
|
||||
"
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"printingConfigPerSaleOrder";"If this option is not enabled, the print settings by default for quotations/orders will be those of the company. By enabling this option, you will be able to configure specific print parameters by quotation/order."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"closeOpportunityUponSaleOrderConfirmation";"When an order is linked to an opportunity, by enabling this option, the opportunity will be automatically closed when the order is confirmed."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderInAtiSelect";"It is possible to choose if in sale quotations/orders the prices are always in WT, always in ATI, by default in WT or by default in ATI. The choices ""by default in WT"" and ""by default in ATI"" leave the possibility of modifying the type of price applied on a case by case basis (via a check box)."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"displaySalemanOnPrinting";"If the box is checked, the name of the salesman that is indicated on a quotation/order will appear on the printouts."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"displayDelCondOnPrinting";"On a quotation/order, in the tab ""Delivery Information"", there is a text field ""Delivery conditions"". If enabled, the contents of this field will appear on the printings.
|
||||
"
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderClientBox";"It offers the possibility to personalize a customer box with information of your choice on the printing of quotations/orders thanks to a field html."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderLegalNote";"You can enter here legal note that will appear on the prints of your orders."
|
||||
"axelor-sale";"SaleOrder";"sale-order-form";"versionNumber";"Version number for the order. It increases when clicking on the ""new version"" button of a ""Requested"" order. "
|
||||
|
@ -0,0 +1,18 @@
|
||||
"module";"object";"view";"field";"help"
|
||||
"axelor-sale";"AdvancePayment";"advance-payment-form";"amountRemainingToUse";"??? le montant restant à utiliser est égal au montant rentré."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageSalesUnits";"Cette fonction est utile quand les unités d'achat d'un produit à un fournisseur sont différentes de vos unités de vente (par exemple si vous achetez un produit à la tonne et que vous le revendez au kilo). Ainsi lors des commandes clients, le produit sélectionné sera directement indiqué avec son unité de vente, évitant ainsi de faire des conversions."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageSaleOrderVersion";"Cette option active la possibilité de créer plusieurs versions d'un devis, permettant ainsi de modifier un devis pour en créer une nouvelle version et d'en suivre l'historique."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"productPackMgt";"Cette option permet à partir d'une fiche produit de créer des produits de type kit, composés d'autres produits. A noter que vous pouvez ensuite modifier la composition d'un kit directement depuis une ligne de commande/devis."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"enableConfigurator";"Permet d'activer les configurateurs commerciaux sur les devis/commandes."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"allowPendingOrderModification";"Cette option autorise la modification d'une commande confirmée. Quand une commande est en cours de modification, la génération de facture et de bons de livraison est bloquée. Si un bon de livraison est au statut planifié, il restera bloqué à ce statut tant que la commande sera en cours de modification."
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"manageMultipleSaleQuantity";"Cette option permet de définir sur la fiche d'un produit des quantités multiples de ventes. Vous pourrez ainsi définir qu'un produit ne peut être vendu que sous certains multiples (par exemple que pour une quantité de 5 et 10). Si la quantité sélectionnée sur une ligne de commande est différente, cette ligne ne pourra être validée et un message apparaitra, indiquant quelles sont les quantités autorisées.
|
||||
"
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"printingConfigPerSaleOrder";"Si cette option n'est pas activée, les paramètres d'impression pour les devis/commandes par défaut seront ceux de la société. En activant cette option, vous pourrez configurer des paramètres d'impressions spécifiques par devis/commande. "
|
||||
"axelor-sale";"AppSale";"app-sale-config-form";"closeOpportunityUponSaleOrderConfirmation";"Lorsqu'une commande est liée à une opportunité, en activant cette option, l'opportunité sera automatiquement fermée quand la commande est confirmée."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderInAtiSelect";"Il est possible de choisir si dans les devis/commandes les prix sont toujours en H.T., toujours en T.T.C, par défaut en H.T. ou par défaut en T.T.C. Les choix ""par défaut en H.T."" et ""par défaut en T.T.C."" laissent la possibilité de modifier au cas par cas (via une case à cocher) le type de prix pratiqué.
|
||||
"
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"displaySalemanOnPrinting";"Si la case est cochée, le nom du commercial qui est indiqué sur un devis/commande apparaitra sur les impressions."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"displayDelCondOnPrinting";"Sur un devis/commande, dans l'onglet ""Informations de livraison"", il y a un champ texte ""Conditions de livraison"". Si l'option est activée, le contenu de ce champ apparaitra sur les impressions."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderClientBox";"Cela vous offre la possibilité de personnaliser un encadré client avec des informations de votre choix sur les impressions de devis/commandes grâce à un champ html."
|
||||
"axelor-sale";"SaleConfig";"sale-config-form";"saleOrderLegalNote";"Vous pouvez entrer ici des mentions légales qui apparaitront sur les impressions de vos commandes."
|
||||
"axelor-sale";"SaleOrder";"sale-order-form";"versionNumber";"Numéro de version de la commande. Celui-ci s'incrémente lorsque l'on utilise le bouton ""nouvelle version"" sur une commande au statut "" demandé """
|
||||
|
@ -0,0 +1,34 @@
|
||||
"name";"roles.name"
|
||||
"sc-root-sale";"Admin"
|
||||
"sc-root-sale-customers";"Admin"
|
||||
"sc-root-sale-contacts";"Admin"
|
||||
"sc-root-sale-products";"Admin"
|
||||
"sc-root-sale-quotations";"Admin"
|
||||
"sc-root-sale-orders";"Admin"
|
||||
"sc-root-sale-historical";"Admin"
|
||||
"sc-historical-completed-sale-orders";"Admin"
|
||||
"sc-historical-canceled-sale-orders";"Admin"
|
||||
"sc-root-sale-templates";"Admin"
|
||||
"sc-root-sale-report";"Admin"
|
||||
"sc-root-sale-maps";"Admin"
|
||||
"sale-maps-partner-customers";"Admin"
|
||||
"sale-maps-partner-prospects";"Admin"
|
||||
"sc-root-sale-conf";"Admin"
|
||||
"sale-conf-cancelreason";"Admin"
|
||||
"sale-conf-duration";"Admin"
|
||||
"sale-conf-shipping-costs";"Admin"
|
||||
"sc-root-sale-conf-partner-price-list";"Admin"
|
||||
"sc-root-sale-conf-price-list";"Admin"
|
||||
"sale.conf.tax";"Admin"
|
||||
"sale-conf-configurators";"Admin"
|
||||
"sale-conf-configurators-creator";"Admin"
|
||||
"sale-conf-configurators-configurators";"Admin"
|
||||
"admin-root-batch-sale";"Admin"
|
||||
"top-menu-sales";"Admin"
|
||||
"top-menu-sales-quotations";"Admin"
|
||||
"top-menu-sales-sale-orders";"Admin"
|
||||
"menu-saleman-dashboard-sample";"Admin"
|
||||
"menu-sale-manager-dashboard-sample";"Admin"
|
||||
"menu-sale-dashboard-1";"Admin"
|
||||
"menu-sale-dashboard-2";"Admin"
|
||||
"menu-dashboards-customers";"Admin"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"code";"manageSalesUnits";"manageSaleOrderVersion";"productPackMgt";"printingOnSOFinalization"
|
||||
"sale";"true";"true";"true";"true"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"company.importId"
|
||||
2;1
|
||||
|
@ -0,0 +1,26 @@
|
||||
"name";"metaModel.name";"language.importId";"target";"birtTemplate.name";"filePath";"isDefault";"templateContext";"subject";"content";"toRecipients";"ccRecipients";"bccRecipients";"mediaTypeSelect"
|
||||
"Envoi Devis Client";"SaleOrder";2;;;;"true";;"Votre Devis N° $SaleOrder.saleOrderSeq$";"<p>Bonjour,</p>
|
||||
|
||||
<p>Suite à nos précédents échanges, je vous prie de bien vouloir trouver ci-joint le devis $SaleOrder.saleOrderSeq$.</p>
|
||||
|
||||
<p>Voici la liste des articles que vous avez commandés :</p>
|
||||
<ul>
|
||||
$SaleOrder.saleOrderLineList:{ line | <li>$line.productName$ x$line.qty$ pour $line.exTaxTotal$ $SaleOrder.currency.symbol$ </li>}$
|
||||
</ul>
|
||||
|
||||
<p>Je reste à votre disposition pour des informations complémentaires sur ce devis.</p>
|
||||
";"$SaleOrder.clientPartner.emailAddress.address$";;;2
|
||||
"Sale order";"SaleOrder";1;;;;"true";;"Quote N° $SaleOrder.saleOrderSeq$";"<p>Good morning,</p>
|
||||
|
||||
<p>Following our conversations, please find attached our quote $SaleOrder.saleOrderSeq$.</p>
|
||||
|
||||
<p>Here is the list of items you ordered:</p>
|
||||
<ul>
|
||||
$SaleOrder.saleOrderLineList:{ line | <li>$line.productName$ x$line.qty$ for $line.exTaxTotal$ $SaleOrder.currency.symbol$ </li>}$
|
||||
</ul>
|
||||
|
||||
<p>Please do not hesitate to contact me for any further explanation regarding the quote.</p>
|
||||
|
||||
<p>Best regards<br/>
|
||||
$SaleOrder.user.partner.firstName$ $SaleOrder.user.partner.name$</p>
|
||||
";"$SaleOrder.clientPartner.emailAddress.address$";;;2
|
||||
|
@ -0,0 +1,5 @@
|
||||
"name";"freeText"
|
||||
"Amount too high";"false"
|
||||
"Abandoned project";"false"
|
||||
"Do not meet the need";"false"
|
||||
"Other";"true"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"code";"actionSelect";"company.importId";"description"
|
||||
"AX_GEN_SUBSC_INV";1;1;"Permet de générer les factures d'abonnement à partir des devis."
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"name";"code";"company.importId";"acceptedCredit";"defaultValidityDuration.importId";"displaySalemanOnPrinting";"displayDelCondOnPrinting";"displayProductCodeOnPrinting";"displayTaxDetailOnPrinting";"displayEstimDelivDateOnPrinting";"displayCustomerCodeOnPrinting";"displayProductPictureOnPrinting";"saleOrderClientBox";"saleOrderLegalNote"
|
||||
"1";"Axelor";"AXE";1;"100000";"1";"true";"true";"true";"true";"true";"true";"true";"We wish you good reception of your order and we thank you for choosing Axelor.";"Any late payment will result in late penalties at a rate calculated on the basis of three times the legal interest rate in force in France and a lump sum indemnity of 40 euros for recovery costs, due by law, without a reminder being necessary."
|
||||
|
@ -0,0 +1,5 @@
|
||||
"importId";"externalReference";"clientPartner.importId";"contactPartner.importId";"mainInvoicingAddress.importId";"deliveryAddress.importId";"salemanUser.importId";"team.importId";"paymentMode.importId";"paymentCondition.importId";"currency.code";"creationDate";"statusSelect";"company.importId";"confirmedByUser.importId";"confirmationDateTime";"shipmentDate";"printingSettings.importId"
|
||||
6;"CF-2014-342";88;89;590;590;9;1;6;2;"EUR";"TODAY[-1M-7d]";2;1;;;;1
|
||||
8;;132;133;940;940;8;3;6;2;"EUR";"TODAY[-14d]";2;1;;;;1
|
||||
9;;136;139;950;950;8;3;6;2;"EUR";"TODAY[-7d]";2;1;;;;1
|
||||
11;;224;225;10010;10010;13;3;8;5;"USD";"TODAY";1;1;;;;
|
||||
|
@ -0,0 +1,13 @@
|
||||
"importId";"saleOrder.importId";"sequence";"product.importId";"productName";"qty";"unit.importId";"price";"inTaxPrice";"exTaxTotal";"hasToCreateTask";"isOrdered";"saleSupplySelect"
|
||||
14;6;1;411;"Consultant";15;103;800;960;"12000";"true";"true";3
|
||||
18;8;1;410;"Project Manager";10;103;1000;1200;"10000";"true";"true";3
|
||||
19;8;2;111;"High Performance Server";2;1;2500;3000;"5000";"true";"true";1
|
||||
20;8;3;110;"Classic server";1;1;1500;1800;"1500";"true";"true";1
|
||||
21;8;4;121;"Laser Printer";4;1;429;"514.8";"1716";"true";"true";1
|
||||
22;8;5;2;"Annual Maintenance";1;106;2000;2400;"2000";"true";"true";1
|
||||
23;9;1;400;"Project Study";1;1;8000;9600;"8000";"true";"true";3
|
||||
24;9;2;410;"Project Manager";20;103;1000;1200;"20000";"true";"true";3
|
||||
27;11;1;120;"InkJet Printer";5;1;329;"394.8";"1645";"true";"true";1
|
||||
28;11;2;302;"Ink Cartridge";15;1;"32.9";"39.48";"493.5";"true";"true";1
|
||||
29;11;3;121;"Laser Printer";3;1;429;"514.8";"1287";"true";"true";1
|
||||
30;11;4;303;"Laser Cartridge";6;1;"84.5";"101.4";"507";"true";"true";1
|
||||
|
@ -0,0 +1,2 @@
|
||||
"code";"manageSalesUnits";"manageSaleOrderVersion";"productPackMgt";"printingOnSOFinalization"
|
||||
"sale";"true";"true";"true";"true"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"company.importId"
|
||||
2;1
|
||||
|
@ -0,0 +1,26 @@
|
||||
"name";"metaModel.name";"language.importId";"target";"birtTemplate.name";"filePath";"isDefault";"templateContext";"subject";"content";"toRecipients";"ccRecipients";"bccRecipients";"mediaTypeSelect"
|
||||
"Envoi Devis Client";"SaleOrder";2;;;;"true";;"Votre Devis N° $SaleOrder.saleOrderSeq$";"<p>Bonjour,</p>
|
||||
|
||||
<p>Suite à nos précédents échanges, je vous prie de bien vouloir trouver ci-joint le devis $SaleOrder.saleOrderSeq$.</p>
|
||||
|
||||
<p>Voici la liste des articles que vous avez commandés :</p>
|
||||
<ul>
|
||||
$SaleOrder.saleOrderLineList:{ line | <li>$line.productName$ x$line.qty$ pour $line.exTaxTotal$ $SaleOrder.currency.symbol$ </li>}$
|
||||
</ul>
|
||||
|
||||
<p>Je reste à votre disposition pour des informations complémentaires sur ce devis.</p>
|
||||
";"$SaleOrder.clientPartner.emailAddress.address$";;;2
|
||||
"Sale order";"SaleOrder";1;;;;"true";;"Quote N° $SaleOrder.saleOrderSeq$";"<p>Good morning,</p>
|
||||
|
||||
<p>Following our conversations, please find attached our quote $SaleOrder.saleOrderSeq$.</p>
|
||||
|
||||
<p>Here is the list of items you ordered:</p>
|
||||
<ul>
|
||||
$SaleOrder.saleOrderLineList:{ line | <li>$line.productName$ x$line.qty$ for $line.exTaxTotal$ $SaleOrder.currency.symbol$ </li>}$
|
||||
</ul>
|
||||
|
||||
<p>Please do not hesitate to contact me for any further explanation regarding the quote.</p>
|
||||
|
||||
<p>Best regards<br/>
|
||||
$SaleOrder.user.partner.firstName$ $SaleOrder.user.partner.name$</p>
|
||||
";"$SaleOrder.clientPartner.emailAddress.address$";;;2
|
||||
|
@ -0,0 +1,5 @@
|
||||
"name";"freeText"
|
||||
"Montant trop élevé";"false"
|
||||
"Abandon du projet";"false"
|
||||
"Ne répond pas au besoin";"false"
|
||||
"Autre";"true"
|
||||
|
@ -0,0 +1,2 @@
|
||||
"code";"actionSelect";"company.importId";"description"
|
||||
"AX_GEN_SUBSC_INV";1;1;"Permet de générer les factures d'abonnement à partir des devis."
|
||||
|
@ -0,0 +1,2 @@
|
||||
"importId";"name";"code";"company.importId";"acceptedCredit";"defaultValidityDuration.importId";"displaySalemanOnPrinting";"displayDelCondOnPrinting";"displayProductCodeOnPrinting";"displayTaxDetailOnPrinting";"displayEstimDelivDateOnPrinting";"displayCustomerCodeOnPrinting";"displayProductPictureOnPrinting";"saleOrderClientBox";"saleOrderLegalNote"
|
||||
"1";"Axelor";"AXE";1;"100000";"1";"true";"true";"true";"true";"true";"true";"true";"Nous vous souhaitons bonne réception de votre commande et nous vous remercions d'avoir choisi Axelor.";"Tout retard de paiement entraînera l’exigibilité de pénalités de retard à un taux calculé sur la base de trois fois le taux d'intérêt légal en vigueur en France et d’une indemnité forfaitaire minimale de 40 euros pour frais de recouvrement, dues de plein droit, sans qu'un rappel soit nécessaire."
|
||||
|
@ -0,0 +1,5 @@
|
||||
"importId";"externalReference";"clientPartner.importId";"contactPartner.importId";"mainInvoicingAddress.importId";"deliveryAddress.importId";"salemanUser.importId";"team.importId";"paymentMode.importId";"paymentCondition.importId";"currency.code";"creationDate";"statusSelect";"company.importId";"confirmedByUser.importId";"confirmationDateTime";"shipmentDate";"printingSettings.importId"
|
||||
6;"CF-2014-342";88;89;590;590;9;1;6;2;"EUR";"TODAY[-1M-7d]";2;1;;;;1
|
||||
8;;132;133;940;940;8;3;6;2;"EUR";"TODAY[-14d]";2;1;;;;1
|
||||
9;;136;139;950;950;8;3;6;2;"EUR";"TODAY[-7d]";2;1;;;;1
|
||||
11;;224;225;10010;10010;13;3;8;5;"USD";"TODAY";1;1;;;;
|
||||
|
@ -0,0 +1,13 @@
|
||||
"importId";"saleOrder.importId";"sequence";"product.importId";"productName";"qty";"unit.importId";"price";"inTaxPrice";"exTaxTotal";"hasToCreateTask";"isOrdered";"saleSupplySelect"
|
||||
14;6;1;411;"Consultant";15;103;800;960;"12000";"true";"true";3
|
||||
18;8;1;410;"Chef de projet";10;103;1000;1200;"10000";"true";"true";3
|
||||
19;8;2;111;"Serveur haute performance";2;1;2500;3000;"5000";"true";"true";1
|
||||
20;8;3;110;"Serveur classique";1;1;1500;1800;"1500";"true";"true";1
|
||||
21;8;4;121;"Imprimante Laser";4;1;429;"514.8";"1716";"true";"true";1
|
||||
22;8;5;2;"Maintenance annuelle";1;106;2000;2400;"2000";"true";"true";1
|
||||
23;9;1;400;"Etude";1;1;8000;9600;"8000";"true";"true";3
|
||||
24;9;2;410;"Chef de projet";20;103;1000;1200;"20000";"true";"true";3
|
||||
27;11;1;120;"Imprimante Jet d'encre";5;1;329;394;"1645";"true";"true";1
|
||||
28;11;2;302;"Cartouche jet d'encre";15;1;"32.9";"39.48";"493.5";"true";"true";1
|
||||
29;11;3;121;"Imprimante Laser";3;1;429;"514.8";"1287";"true";"true";1
|
||||
30;11;4;303;"Cartouche encre laser";6;1;"84.5";"101.4";"507";"true";"true";1
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
|
||||
|
||||
<input file="base_appSale.csv" separator=";" type="com.axelor.apps.base.db.AppSale" search="self.code = :code" update="true" />
|
||||
|
||||
<input file="base_sequence.csv" separator=";" type="com.axelor.apps.base.db.Sequence" search="self.importId = :importId" call="com.axelor.csv.script.SequenceScript:computeFullname">
|
||||
<bind to="yearlyResetOk" column="yearlyResetOk" eval="yearlyResetOk == '1' ? true : false"/>
|
||||
<bind to="nextNum" column="nextNum" eval="nextNum?.empty ? '1' : nextNum"/>
|
||||
<bind to="padding" column="padding" eval="padding?.empty ? '1' : padding"/>
|
||||
<bind to="toBeAdded" column="toBeAdded" eval="toBeAdded?.empty ? '1' : toBeAdded"/>
|
||||
<bind to="resetDate" eval="call:com.axelor.apps.base.service.app.AppBaseService:getTodayDate()" />
|
||||
</input>
|
||||
|
||||
<input file="sale_cancelReason.csv" separator=";" type="com.axelor.apps.base.db.CancelReason">
|
||||
<bind to="freeText" eval="freeText == 'true' ? true : false" />
|
||||
<bind to="applicationType" eval="'com.axelor.apps.sale.db.SaleOrder'"/>
|
||||
</input>
|
||||
|
||||
<input file="sale_saleBatch.csv" separator=";" type="com.axelor.apps.sale.db.SaleBatch" />
|
||||
|
||||
<input file="sale_saleConfig.csv" separator=";" type="com.axelor.apps.sale.db.SaleConfig" search="self.importId = :importId"/>
|
||||
|
||||
<input file="sale_saleOrder.csv" separator=";" search="self.importId = :importId" type="com.axelor.apps.sale.db.SaleOrder">
|
||||
<bind to="creationDate" eval="call:com.axelor.csv.script.ImportDateTime:importDate(creationDate)" column="creationDate"/>
|
||||
<bind to="confirmationDateTime" eval="call:com.axelor.csv.script.ImportDateTime:importDateTime(confirmationDateTime)" column="confirmationDateTime"/>
|
||||
<bind to="shipmentDate" eval="call:com.axelor.csv.script.ImportDateTime:importDate(shipmentDate)" column="shipmentDate"/>
|
||||
</input>
|
||||
|
||||
<input file="sale_saleOrderLine.csv" separator=";" type="com.axelor.apps.sale.db.SaleOrderLine" search="self.importId = :importId" call="com.axelor.csv.script.ImportSaleOrderLine:importSaleOrderLine">
|
||||
<bind to="priceDiscounted" eval="price" />
|
||||
</input>
|
||||
|
||||
<input file="sale_saleOrder.csv" separator=";" search="self.importId = :importId" type="com.axelor.apps.sale.db.SaleOrder" call="com.axelor.csv.script.ImportSaleOrder:importSaleOrder">
|
||||
<bind to="creationDate" eval="call:com.axelor.csv.script.ImportDateTime:importDate(creationDate)" column="creationDate"/>
|
||||
<bind to="confirmationDateTime" eval="call:com.axelor.csv.script.ImportDateTime:importDateTime(confirmationDateTime)" column="confirmationDateTime"/>
|
||||
<bind to="shipmentDate" eval="call:com.axelor.csv.script.ImportDateTime:importDate(shipmentDate)" column="shipmentDate"/>
|
||||
</input>
|
||||
|
||||
<input file="base_template.csv" separator=";" type="com.axelor.apps.message.db.Template" search="self.name = :name" >
|
||||
<bind to="language" search="self.code = :languageCode"/>
|
||||
</input>
|
||||
|
||||
</csv-inputs>
|
||||
@ -0,0 +1,15 @@
|
||||
<?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="ABCAnalysis" lang="java">
|
||||
|
||||
<date name="startDate" title="Start date"/>
|
||||
<date name="endDate" title="End date"/>
|
||||
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,37 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="AdvancePayment" lang="java">
|
||||
|
||||
<decimal name="amount" title="Amount" required="true" default="0"/>
|
||||
|
||||
<date name="advancePaymentDate" title="Date" required="true"/>
|
||||
|
||||
<many-to-one name="saleOrder" ref="com.axelor.apps.sale.db.SaleOrder"/>
|
||||
|
||||
<many-to-one name="currency" ref="com.axelor.apps.base.db.Currency" title="Currency" required="true"/>
|
||||
<integer name="statusSelect" title="Status" selection="advance.payment.status.select"/>
|
||||
|
||||
<extra-code>
|
||||
<![CDATA[
|
||||
public static final int STATUS_DRAFT = 0;
|
||||
public static final int STATUS_VALIDATED = 1;
|
||||
public static final int STATUS_CANCELED = 2;
|
||||
]]>
|
||||
</extra-code>
|
||||
|
||||
<track>
|
||||
<field name="amount" on="UPDATE"/>
|
||||
<field name="advancePaymentDate" on="UPDATE"/>
|
||||
<field name="saleOrder" on="UPDATE"/>
|
||||
<field name="currency" on="UPDATE"/>
|
||||
<field name="statusSelect" on="UPDATE"/>
|
||||
</track>
|
||||
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,40 @@
|
||||
<?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="AppSale" lang="java" extends="App">
|
||||
<boolean name="manageSaleOrderVersion" title="Manage sale order versions" default="false"/>
|
||||
<boolean name="printingOnSOFinalization" title="Generate the pdf printing during sale order finalization" default="false"/>
|
||||
<boolean name="manageSalesUnits" title="Manage sales unit on products"/>
|
||||
<boolean name="productPackMgt" title="Product Pack Management" />
|
||||
<boolean name="enableConfigurator" title="Enable business configurator"/>
|
||||
<boolean name="allowPendingOrderModification" />
|
||||
<boolean name="manageMultipleSaleQuantity" title="Manage multiple sale quantity"/>
|
||||
<boolean name="printingConfigPerSaleOrder" title="Printing config per Sale Order"/>
|
||||
<boolean name="closeOpportunityUponSaleOrderConfirmation"
|
||||
title="Close opportunity when one of the linked sale orders is confirmed"/><boolean name="isEnabledProductDescriptionCopy" title="Enable product description copy"/>
|
||||
<integer name="salemanSelect" title="User to fill saleman" selection="sale.order.fill.saleman.select" default="1"/>
|
||||
<boolean name="enableCustomerCatalogMgt" title="Enable customer catalog management"/>
|
||||
<boolean name="isDisplaySaleOrderLineNumber" title="Display sale order line number"/>
|
||||
|
||||
<track>
|
||||
<field name="manageSaleOrderVersion" on="UPDATE"/>
|
||||
<field name="printingOnSOFinalization" on="UPDATE"/>
|
||||
<field name="manageSalesUnits" on="UPDATE"/>
|
||||
<field name="productPackMgt" on="UPDATE"/>
|
||||
<field name="enableConfigurator" on="UPDATE"/>
|
||||
<field name="allowPendingOrderModification" on="UPDATE"/>
|
||||
<field name="manageMultipleSaleQuantity" on="UPDATE"/>
|
||||
<field name="printingConfigPerSaleOrder" on="UPDATE"/>
|
||||
<field name="closeOpportunityUponSaleOrderConfirmation" on="UPDATE"/>
|
||||
<field name="salemanSelect" on="UPDATE"/>
|
||||
<field name="enableCustomerCatalogMgt" on="UPDATE"/>
|
||||
<field name="isDisplaySaleOrderLineNumber" on="UPDATE"/>
|
||||
<field name="isEnabledProductDescriptionCopy" on="UPDATE"/>
|
||||
</track>
|
||||
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,15 @@
|
||||
<?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="Batch" lang="java" sequential="true">
|
||||
|
||||
<!-- NOT DISPLAY -->
|
||||
<many-to-one name="saleBatch" ref="com.axelor.apps.sale.db.SaleBatch"/>
|
||||
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,17 @@
|
||||
<?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="Blocking">
|
||||
<decimal name="maxAmount" title="Total A.T.I." scale="2" precision="20"/>
|
||||
<extra-code>
|
||||
<![CDATA[
|
||||
public static final Integer SALE_BLOCKING = 5;
|
||||
]]>
|
||||
</extra-code>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,14 @@
|
||||
<?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="Company" lang="java" cacheable="true">
|
||||
|
||||
<one-to-one name="saleConfig" ref="com.axelor.apps.sale.db.SaleConfig" title="Sale config" mappedBy="company"/>
|
||||
<string name="orderBloquedMessage" multiline="true"/>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,24 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="Configurator" lang="java">
|
||||
|
||||
<many-to-one name="configuratorCreator" ref="com.axelor.apps.sale.db.ConfiguratorCreator" title="Configurator type" required="true" />
|
||||
<string name="configuratorCreatorName" hidden="true" namecolumn="true" />
|
||||
<string name="attributes" title="Attributes" json="true" />
|
||||
<string name="indicators" title="Indicators" json="true" />
|
||||
<one-to-one name="product" ref="com.axelor.apps.base.db.Product" title="Product"/>
|
||||
|
||||
|
||||
<extra-code><![CDATA[
|
||||
//update sale order line
|
||||
public static final int UPDATE_FROM_PRODUCT = 0;
|
||||
public static final int UPDATE_FROM_CONFIGURATOR = 1;
|
||||
]]></extra-code>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,28 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="ConfiguratorCreator" lang="java">
|
||||
|
||||
<string name="name" title="Name" required="true"/>
|
||||
<one-to-many name="attributes" ref="com.axelor.meta.db.MetaJsonField" title="Attributes" orphanRemoval="true" />
|
||||
<one-to-many name="indicators" ref="com.axelor.meta.db.MetaJsonField" title="Indicators" orphanRemoval="true" />
|
||||
<one-to-many name="configuratorProductFormulaList" mappedBy="productCreator"
|
||||
title="Configurator formula list"
|
||||
ref="com.axelor.apps.sale.db.ConfiguratorProductFormula"/>
|
||||
<one-to-many name="configuratorSOLineFormulaList" mappedBy="soLineCreator"
|
||||
title="Configurator formula list"
|
||||
ref="com.axelor.apps.sale.db.ConfiguratorSOLineFormula"/>
|
||||
<many-to-many name="authorizedUserSet" title="Authorized users"
|
||||
ref="com.axelor.auth.db.User"/>
|
||||
<many-to-many name="authorizedGroupSet" title="Authorized groups"
|
||||
ref="com.axelor.auth.db.Group"/>
|
||||
<boolean name="generateProduct" title="Generate product" default="true"/>
|
||||
|
||||
<boolean name="isActive" title="Is Active" default="false"/>
|
||||
|
||||
</entity>
|
||||
</domain-models>
|
||||
@ -0,0 +1,11 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
<entity name="ConfiguratorFormula" lang="java" strategy="JOINED">
|
||||
<many-to-one name="metaField" ref="com.axelor.meta.db.MetaField"/>
|
||||
<string name="formula" title="Formula" large="true"/>
|
||||
<boolean name="showOnConfigurator" title="Show on configurator"/>
|
||||
</entity>
|
||||
</domain-models>
|
||||
@ -0,0 +1,12 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="ConfiguratorProductFormula" extends="ConfiguratorFormula">
|
||||
<many-to-one name="productCreator" ref="com.axelor.apps.sale.db.ConfiguratorCreator"/>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="ConfiguratorSOLineFormula" extends="ConfiguratorFormula">
|
||||
<integer name="updateFromSelect" title="Update from" default="0" selection="configurator.update.sale.order.line"/>
|
||||
<many-to-one name="soLineCreator" ref="com.axelor.apps.sale.db.ConfiguratorCreator"/>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -0,0 +1,16 @@
|
||||
<?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="sale" package="com.axelor.apps.sale.db"/>
|
||||
|
||||
<entity name="CustomerCatalog" lang="java">
|
||||
|
||||
<many-to-one name="product" ref="com.axelor.apps.base.db.Product" title="Product" required="true"/>
|
||||
<many-to-one name="customerPartner" ref="com.axelor.apps.base.db.Partner" required="true" title="Customer"/>
|
||||
<string name="productCustomerName" title="Product name on catalog"/>
|
||||
<string name="productCustomerCode" title="Product code on catalog"/>
|
||||
</entity>
|
||||
|
||||
</domain-models>
|
||||
@ -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="account" package="com.axelor.apps.account.db"/>
|
||||
|
||||
<entity name="FiscalPosition" lang="java">
|
||||
|
||||
<boolean name="customerSpecificNote" title="Customer specific note"/>
|
||||
|
||||
</entity>
|
||||
</domain-models>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user