First commit waiting for Budget Alert

This commit is contained in:
2025-09-04 13:37:35 +01:00
commit 2d681f27f5
4563 changed files with 1061534 additions and 0 deletions

View File

@ -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.production.db.repo;
import com.axelor.apps.production.db.BillOfMaterial;
import java.math.BigDecimal;
public class BillOfMaterialManagementRepository extends BillOfMaterialRepository {
@Override
public BillOfMaterial save(BillOfMaterial billOfMaterial) {
if (billOfMaterial.getVersionNumber() != null && billOfMaterial.getVersionNumber() > 1) {
billOfMaterial.setFullName(
billOfMaterial.getName() + " - v" + billOfMaterial.getVersionNumber());
} else {
billOfMaterial.setFullName(billOfMaterial.getName());
}
return super.save(billOfMaterial);
}
@Override
public BillOfMaterial copy(BillOfMaterial entity, boolean deep) {
BillOfMaterial copy = super.copy(entity, deep);
copy.setStatusSelect(STATUS_DRAFT);
copy.setVersionNumber(1);
copy.setOriginalBillOfMaterial(null);
copy.setCostPrice(BigDecimal.ZERO);
copy.clearCostSheetList();
return copy;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.production.db.repo;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import javax.persistence.PersistenceException;
public class ManufOrderManagementRepository extends ManufOrderRepository {
@Inject OperationOrderService operationOrderService;
@Override
public ManufOrder copy(ManufOrder entity, boolean deep) {
entity.setStatusSelect(ManufOrderRepository.STATUS_DRAFT);
entity.setManufOrderSeq(null);
entity.setPlannedStartDateT(null);
entity.setPlannedEndDateT(null);
entity.setRealStartDateT(null);
entity.setRealEndDateT(null);
entity.setInStockMoveList(null);
entity.setOutStockMoveList(null);
entity.setWasteStockMove(null);
entity.setToConsumeProdProductList(null);
entity.setConsumedStockMoveLineList(null);
entity.setDiffConsumeProdProductList(null);
entity.setToProduceProdProductList(null);
entity.setProducedStockMoveLineList(null);
entity.setWasteProdProductList(null);
entity.setOperationOrderList(null);
entity.setCostSheetList(null);
entity.setCostPrice(null);
return super.copy(entity, deep);
}
@Override
public ManufOrder save(ManufOrder entity) {
entity = super.save(entity);
try {
if (Strings.isNullOrEmpty(entity.getManufOrderSeq())
&& entity.getStatusSelect() == ManufOrderRepository.STATUS_DRAFT) {
entity.setManufOrderSeq(Beans.get(SequenceService.class).getDraftSequenceNumber(entity));
}
} catch (AxelorException e) {
throw new PersistenceException(e);
}
for (OperationOrder operationOrder : entity.getOperationOrderList()) {
if (operationOrder.getBarCode() == null) {
operationOrderService.createBarcode(operationOrder);
}
}
return super.save(entity);
}
@Override
public void remove(ManufOrder entity) {
Integer status = entity.getStatusSelect();
if (status == ManufOrderRepository.STATUS_PLANNED
|| status == ManufOrderRepository.STATUS_STANDBY
|| status == ManufOrderRepository.STATUS_IN_PROGRESS) {
throw new PersistenceException(I18n.get(IExceptionMessage.ORDER_REMOVE_NOT_OK));
} else if (status == ManufOrderRepository.STATUS_FINISHED) {
entity.setArchived(true);
} else {
super.remove(entity);
}
}
}

View File

@ -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.production.db.repo;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.google.inject.Inject;
public class OperationOrderManagementRepository extends OperationOrderRepository {
@Inject OperationOrderService operationOrderService;
@Override
public OperationOrder save(OperationOrder entity) {
if (entity.getBarCode() == null) {
entity = super.save(entity);
operationOrderService.createBarcode(entity);
}
return super.save(entity);
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.production.db.repo;
import com.axelor.apps.production.db.ProdProcess;
public class ProdProcessManagementRepository extends ProdProcessRepository {
@Override
public ProdProcess save(ProdProcess prodProcess) {
if (prodProcess.getVersionNumber() != null && prodProcess.getVersionNumber() > 1)
prodProcess.setFullName(
prodProcess.getName() + " - v" + String.valueOf(prodProcess.getVersionNumber()));
else prodProcess.setFullName(prodProcess.getName());
return super.save(prodProcess);
}
@Override
public ProdProcess copy(ProdProcess entity, boolean deep) {
ProdProcess copy = super.copy(entity, deep);
copy.setStatusSelect(STATUS_DRAFT);
copy.setVersionNumber(1);
copy.setOriginalProdProcess(null);
return copy;
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.production.db.repo;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.stock.db.repo.ProductStockRepository;
import java.math.BigDecimal;
public class ProductProductionRepository extends ProductStockRepository {
@Override
public Product copy(Product product, boolean deep) {
Product copy = super.copy(product, deep);
copy.setDefaultBillOfMaterial(null);
copy.setLastProductionPrice(BigDecimal.ZERO);
return copy;
}
}

View File

@ -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.production.db.repo;
import com.axelor.apps.production.db.ProductionBatch;
public class ProductionBatchManagementRepository extends ProductionBatchRepository {
@Override
public ProductionBatch copy(ProductionBatch entity, boolean deep) {
ProductionBatch copy = super.copy(entity, deep);
copy.setBatchList(null);
return copy;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.production.db.repo;
import com.axelor.apps.production.db.RawMaterialRequirement;
import com.axelor.apps.production.service.RawMaterialRequirementService;
import com.axelor.db.JPA;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import javax.persistence.PersistenceException;
public class RawMaterialRequirementProductionRepository extends RawMaterialRequirementRepository {
@Override
public RawMaterialRequirement save(RawMaterialRequirement rawMaterialRequirement) {
try {
if (rawMaterialRequirement.getCode() == null) {
RawMaterialRequirementService rawMaterialRequirementService =
Beans.get(RawMaterialRequirementService.class);
String seq = rawMaterialRequirementService.getSequence(rawMaterialRequirement);
rawMaterialRequirement.setCode(seq);
}
return super.save(rawMaterialRequirement);
} catch (Exception e) {
JPA.em().getTransaction().rollback();
JPA.runInTransaction(() -> TraceBackService.trace(e));
JPA.em().getTransaction().begin();
throw new PersistenceException(e.getLocalizedMessage());
}
}
}

View File

@ -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.production.db.repo;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.supplychain.db.repo.StockMoveLineSupplychainRepository;
public class StockMoveLineProductionRepository extends StockMoveLineSupplychainRepository {
@Override
public StockMoveLine copy(StockMoveLine entity, boolean deep) {
StockMoveLine copy = super.copy(entity, deep);
if (!deep) {
copy.setProducedManufOrder(null);
copy.setConsumedManufOrder(null);
copy.setConsumedOperationOrder(null);
}
return copy;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.production.db.repo;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.common.base.Strings;
import javax.persistence.PersistenceException;
public class UnitCostCalculationManagementRepository extends UnitCostCalculationRepository {
@Override
public UnitCostCalculation save(UnitCostCalculation entity) {
try {
if (Strings.isNullOrEmpty(entity.getUnitCostCalcSeq())
&& entity.getStatusSelect() == UnitCostCalculationRepository.STATUS_DRAFT) {
entity.setUnitCostCalcSeq(Beans.get(SequenceService.class).getDraftSequenceNumber(entity));
}
} catch (AxelorException e) {
throw new PersistenceException(e);
}
return super.save(entity);
}
@Override
public UnitCostCalculation copy(UnitCostCalculation entity, boolean deep) {
entity.setStatusSelect(UnitCostCalculationRepository.STATUS_DRAFT);
entity.setUnitCostCalcSeq(null);
entity.setCalculationDate(null);
entity.setUpdateCostDate(null);
entity.setUnitCostCalcLineList(null);
return super.copy(entity, deep);
}
}

View File

@ -0,0 +1,152 @@
/*
* 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.production.exceptions;
/**
* Interface of Exceptions.
*
* @author dubaux
*/
public interface IExceptionMessage {
/** Production order service */
static final String PRODUCTION_ORDER_SEQ = /*$$(*/
"There's no configured sequence for production's orders" /*)*/;
/** Production order sale order service */
static final String PRODUCTION_ORDER_SALES_ORDER_NO_BOM = /*$$(*/
"There no's defined nomenclature for product %s (%s)" /*)*/;
/** Manuf order service */
static final String MANUF_ORDER_SEQ = /*$$(*/
"There's no configured sequence for fabrication's orders" /*)*/;
static final String CANNOT_DELETE_REALIZED_STOCK_MOVE_LINES = /*$$(*/
"You cannot delete realized stock move lines." /*)*/;
/** Batch Compute work in progress valuation */
static final String BATCH_COMPUTE_VALUATION = /*$$(*/ "Computed work in progress valuation" /*)*/;
static final String IN_OR_OUT_INVALID_ARG = /*$$(*/ "inOrOut is invalid" /*)*/;
/** Bill of Material Service */
static final String BOM_1 = /*$$(*/ "Personalized" /*)*/;
/** Production Order Wizard and controller */
static final String PRODUCTION_ORDER_1 = /*$$(*/ "Production's order created" /*)*/;
static final String PRODUCTION_ORDER_2 = /*$$(*/
"Error during production's order's creation" /*)*/;
static final String PRODUCTION_ORDER_3 = /*$$(*/ "You must add a positive quantity" /*)*/;
static final String PRODUCTION_ORDER_4 = /*$$(*/ "You must select a nomenclature" /*)*/;
static final String PRODUCTION_ORDER_5 = /*$$(*/ "Invalid date" /*)*/;
/** Production Config Service */
static final String PRODUCTION_CONFIG_1 = /*$$(*/
"You must configure a production for company %s" /*)*/;
static final String PRODUCTION_CONFIG_2 = /*$$(*/
"You must configure a production virtual stock location for company %s" /*)*/;
static final String PRODUCTION_CONFIG_3 = /*$$(*/
"You must configure a waste stock location for company %s." /*)*/;
static final String PRODUCTION_CONFIG_4 = /*$$(*/
"You must configure a finished products default stock location for company %s." /*)*/;
static final String PRODUCTION_CONFIG_5 = /*$$(*/
"You must configure a component default stock location for company %s." /*)*/;
static final String PRODUCTION_CONFIG_MISSING_MANUF_ORDER_SEQ = /*$$(*/
"You must configure a sequence for manufacturing order for company %s" /*)*/;
/** Manuf Order Controller */
static final String MANUF_ORDER_1 = /*$$(*/
"Please select the Manufacturing order(s) to print." /*)*/;
/** Operation Order Controller */
static final String OPERATION_ORDER_1 = /*$$(*/
"Please select the Operation order(s) to print." /*)*/;
/** Sale order line Controller */
static final String SALE_ORDER_LINE_1 = /*$$(*/ "Personalized nomenclature created" /*)*/;
/** Production Order Controller */
static final String PRODUCTION_ORDER_NO_GENERATION = /*$$(*/
"No production order could be generated. Make sure that everything has been configured correctly. Reminder: check that the order lines that should be produced have their supply method set to 'produce' and that the chosen BoM has a production process associated to it." /*)*/;
/** ProdProcess service */
static final String PROD_PROCESS_USELESS_PRODUCT = /*$$(*/
"The product %s is not in the bill of material related to this production process" /*)*/;
static final String PROD_PROCESS_MISS_PRODUCT = /*$$(*/
"Not enough quantity in products to consume for: %s" /*)*/;
static final String CHARGE_MACHINE_DAYS = /*$$(*/ "Too many days" /*)*/;
/** Bill of material service */
static final String COST_TYPE_CANNOT_BE_CHANGED = /*$$(*/
"The product cost cannot be changed because the product cost type is not manual" /*)*/;
static final String MAX_DEPTH_REACHED = /*$$(*/ "Max depth reached when copying BOM." /*)*/;
/** Configurator Controller */
String BILL_OF_MATERIAL_GENERATED = /*$$(*/ "The bill of material %s has been generated" /*)*/;
/** Configurator Bom Service */
String CONFIGURATOR_BOM_TOO_MANY_CALLS = /*$$(*/
"Too many recursive calls to create the bill of material." /*)*/;
String CONFIGURATOR_BOM_IMPORT_TOO_MANY_CALLS = /*$$(*/
"Too many recursive calls to import the bill of material configurator." /*)*/;
/** Stock move line production controller */
String STOCK_MOVE_LINE_UNKNOWN_PARENT_CONTEXT = /*$$(*/ "Unknown parent context class." /*)*/;
/** Production Order Controller */
static final String MANUF_ORDER_NO_GENERATION = /*$$(*/
"Cannot add a manufacturing order without a production process. Please check that your chosen bill of material has a valid production process." /*)*/;
static final String MANUF_ORDER_MISSING_TEMPLATE = /*$$(*/
"The template to send message for manufacturing order is missing." /*)*/;
/** Operation Order Workflow Service */
String WORKCENTER_NO_MACHINE = /*$$(*/ "Please fill the machine in the workcenter %s." /*)*/;
/** Raw Material RequirementService */
String RAW_MATERIAL_REQUIREMENT_NO_SEQUENCE = /*$$(*/
"Error : You must configure a raw material requirement reporting sequence for the company %s" /*)*/;
static final String ORDER_REMOVE_NOT_OK = /*$$(*/ "You can't remove this record" /*)*/;
static final String MANUF_ORDER_CANCEL = /*$$(*/ "The manufacturing order was canceled." /*)*/;
static final String MANUF_ORDER_CANCEL_REASON_ERROR = /*$$(*/
"A cancel reason must be selected" /*)*/;
static final String MANUF_ORDER_EMAIL_NOT_SENT = /*$$(*/
"Automatic email was not sent because no default email account and/or no valid email account was found : please create one." /*)*/;
static final String MANUF_STOCK_MOVE_ERROR_1 = /*$$(*/ "All products has been consumed" /*)*/;
static final String UNIT_COST_CALCULATION_IMPORT_FAIL_ERROR = /*$$(*/ "Data import failed" /*)*/;
static final String UNIT_COST_CALCULATION_IMPORT_CSV_ERROR = /*$$(*/
"Uploaded file is not a CSV file" /*)*/;
static final String UNIT_COST_CALCULATION_NO_PRODUCT = /*$$(*/
"Please select an element (a product, a product category or a product family) to run calculation" /*)*/;
static final String STOCK_MOVE_NOT_VALIDATED = /*$$(*/ "Stock move not validated by SCH" /*)*/;
}

View File

@ -0,0 +1,136 @@
/*
* 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.production.module;
import com.axelor.app.AxelorModule;
import com.axelor.apps.production.db.repo.BillOfMaterialManagementRepository;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.ManufOrderManagementRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.OperationOrderManagementRepository;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.db.repo.ProdProcessManagementRepository;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.production.db.repo.ProdProductRepository;
import com.axelor.apps.production.db.repo.ProductProductionRepository;
import com.axelor.apps.production.db.repo.ProductionBatchManagementRepository;
import com.axelor.apps.production.db.repo.ProductionBatchRepository;
import com.axelor.apps.production.db.repo.RawMaterialRequirementProductionRepository;
import com.axelor.apps.production.db.repo.RawMaterialRequirementRepository;
import com.axelor.apps.production.db.repo.StockMoveLineProductionRepository;
import com.axelor.apps.production.db.repo.UnitCostCalculationManagementRepository;
import com.axelor.apps.production.db.repo.UnitCostCalculationRepository;
import com.axelor.apps.production.service.BillOfMaterialService;
import com.axelor.apps.production.service.BillOfMaterialServiceImpl;
import com.axelor.apps.production.service.MrpLineServiceProductionImpl;
import com.axelor.apps.production.service.MrpServiceProductionImpl;
import com.axelor.apps.production.service.ProdProcessLineService;
import com.axelor.apps.production.service.ProdProcessLineServiceImpl;
import com.axelor.apps.production.service.ProdProductProductionRepository;
import com.axelor.apps.production.service.ProductionProductStockLocationServiceImpl;
import com.axelor.apps.production.service.RawMaterialRequirementService;
import com.axelor.apps.production.service.RawMaterialRequirementServiceImpl;
import com.axelor.apps.production.service.SaleOrderWorkflowServiceProductionImpl;
import com.axelor.apps.production.service.StockMoveProductionServiceImpl;
import com.axelor.apps.production.service.StockRulesServiceProductionImpl;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.app.AppProductionServiceImpl;
import com.axelor.apps.production.service.app.ConfiguratorServiceProductionImpl;
import com.axelor.apps.production.service.config.StockConfigProductionService;
import com.axelor.apps.production.service.configurator.ConfiguratorBomService;
import com.axelor.apps.production.service.configurator.ConfiguratorBomServiceImpl;
import com.axelor.apps.production.service.configurator.ConfiguratorCreatorImportServiceProductionImpl;
import com.axelor.apps.production.service.configurator.ConfiguratorProdProcessLineService;
import com.axelor.apps.production.service.configurator.ConfiguratorProdProcessLineServiceImpl;
import com.axelor.apps.production.service.configurator.ConfiguratorProdProcessService;
import com.axelor.apps.production.service.configurator.ConfiguratorProdProcessServiceImpl;
import com.axelor.apps.production.service.costsheet.CostSheetLineService;
import com.axelor.apps.production.service.costsheet.CostSheetLineServiceImpl;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.apps.production.service.costsheet.CostSheetServiceImpl;
import com.axelor.apps.production.service.costsheet.UnitCostCalcLineService;
import com.axelor.apps.production.service.costsheet.UnitCostCalcLineServiceImpl;
import com.axelor.apps.production.service.costsheet.UnitCostCalculationService;
import com.axelor.apps.production.service.costsheet.UnitCostCalculationServiceImpl;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.manuforder.ManufOrderServiceImpl;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.axelor.apps.production.service.operationorder.OperationOrderServiceImpl;
import com.axelor.apps.production.service.productionorder.ProductionOrderSaleOrderService;
import com.axelor.apps.production.service.productionorder.ProductionOrderSaleOrderServiceImpl;
import com.axelor.apps.production.service.productionorder.ProductionOrderService;
import com.axelor.apps.production.service.productionorder.ProductionOrderServiceImpl;
import com.axelor.apps.production.service.productionorder.ProductionOrderWizardService;
import com.axelor.apps.production.service.productionorder.ProductionOrderWizardServiceImpl;
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportServiceImpl;
import com.axelor.apps.sale.service.configurator.ConfiguratorServiceImpl;
import com.axelor.apps.stock.db.repo.ProductStockRepository;
import com.axelor.apps.stock.service.config.StockConfigService;
import com.axelor.apps.supplychain.db.repo.StockMoveLineSupplychainRepository;
import com.axelor.apps.supplychain.service.MrpLineServiceImpl;
import com.axelor.apps.supplychain.service.MrpServiceImpl;
import com.axelor.apps.supplychain.service.ProductStockLocationServiceImpl;
import com.axelor.apps.supplychain.service.SaleOrderWorkflowServiceSupplychainImpl;
import com.axelor.apps.supplychain.service.StockMoveServiceSupplychainImpl;
import com.axelor.apps.supplychain.service.StockRulesServiceSupplychainImpl;
public class ProductionModule extends AxelorModule {
@Override
protected void configure() {
bind(ManufOrderRepository.class).to(ManufOrderManagementRepository.class);
bind(OperationOrderRepository.class).to(OperationOrderManagementRepository.class);
bind(ProductionOrderService.class).to(ProductionOrderServiceImpl.class);
bind(BillOfMaterialService.class).to(BillOfMaterialServiceImpl.class);
bind(ManufOrderService.class).to(ManufOrderServiceImpl.class);
bind(OperationOrderService.class).to(OperationOrderServiceImpl.class);
bind(ProductionOrderService.class).to(ProductionOrderServiceImpl.class);
bind(ProductionOrderWizardService.class).to(ProductionOrderWizardServiceImpl.class);
bind(ProductionOrderSaleOrderService.class).to(ProductionOrderSaleOrderServiceImpl.class);
bind(MrpLineServiceImpl.class).to(MrpLineServiceProductionImpl.class);
bind(MrpServiceImpl.class).to(MrpServiceProductionImpl.class);
bind(CostSheetService.class).to(CostSheetServiceImpl.class);
bind(CostSheetLineService.class).to(CostSheetLineServiceImpl.class);
bind(SaleOrderWorkflowServiceSupplychainImpl.class)
.to(SaleOrderWorkflowServiceProductionImpl.class);
bind(StockRulesServiceSupplychainImpl.class).to(StockRulesServiceProductionImpl.class);
bind(BillOfMaterialRepository.class).to(BillOfMaterialManagementRepository.class);
bind(StockConfigService.class).to(StockConfigProductionService.class);
bind(ConfiguratorBomService.class).to(ConfiguratorBomServiceImpl.class);
bind(ConfiguratorProdProcessService.class).to(ConfiguratorProdProcessServiceImpl.class);
bind(ConfiguratorProdProcessLineService.class).to(ConfiguratorProdProcessLineServiceImpl.class);
bind(ConfiguratorServiceImpl.class).to(ConfiguratorServiceProductionImpl.class);
bind(AppProductionService.class).to(AppProductionServiceImpl.class);
bind(ProdProcessRepository.class).to(ProdProcessManagementRepository.class);
bind(StockMoveLineSupplychainRepository.class).to(StockMoveLineProductionRepository.class);
bind(ProdProcessLineService.class).to(ProdProcessLineServiceImpl.class);
bind(StockMoveServiceSupplychainImpl.class).to(StockMoveProductionServiceImpl.class);
bind(ProdProductRepository.class).to(ProdProductProductionRepository.class);
bind(RawMaterialRequirementService.class).to(RawMaterialRequirementServiceImpl.class);
bind(RawMaterialRequirementRepository.class)
.to(RawMaterialRequirementProductionRepository.class);
bind(ProductionBatchRepository.class).to(ProductionBatchManagementRepository.class);
bind(UnitCostCalculationRepository.class).to(UnitCostCalculationManagementRepository.class);
bind(UnitCostCalculationService.class).to(UnitCostCalculationServiceImpl.class);
bind(UnitCostCalcLineService.class).to(UnitCostCalcLineServiceImpl.class);
bind(ProductStockRepository.class).to(ProductProductionRepository.class);
bind(ConfiguratorCreatorImportServiceImpl.class)
.to(ConfiguratorCreatorImportServiceProductionImpl.class);
bind(ProductStockLocationServiceImpl.class).to(ProductionProductStockLocationServiceImpl.class);
}
}

View File

@ -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.production.report;
public interface IReport {
public static final String MANUF_ORDER = "ManufOrder.rptdesign";
public static final String OPERATION_ORDER = "OperationOrder.rptdesign";
public static final String PROD_PROCESS = "ProdProcess.rptdesign";
public static final String BILL_OF_MATERIAL = "BillOfMaterial.rptdesign";
public static final String RAW_MATERIAL_REQUIREMENT = "RawMaterialRequirement.rptdesign";
public static final String COST_SHEET = "CostSheet.rptdesign";
public static final String WORK_IN_PROGRESS_VALUATION = "WorkInProgressValuation.rptdesign";
}

View File

@ -0,0 +1,212 @@
/*
* 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.production.report;
public interface ITranslation {
public static final String OPERATION_ORDER_TITLE = /*$$(*/ "OperationOrder.title"; /*)*/
public static final String OPERATION_ORDER_OPERATION_NAME = /*$$(*/
"OperationOrder.operationName"; /*)*/
public static final String OPERATION_ORDER_RELATED_TO_PRODUCT = /*$$(*/
"OperationOrder.relatedToProduct"; /*)*/
public static final String OPERATION_ORDER_ASSOCIATED_BOM = /*$$(*/
"OperationOrder.associatedBOM"; /*)*/
public static final String OPERATION_ORDER_DATES = /*$$(*/ "OperationOrder.dates"; /*)*/
public static final String OPERATION_ORDER_PLANNED_START_DATE = /*$$(*/
"OperationOrder.plannedStartDate"; /*)*/
public static final String OPERATION_ORDER_PLANNED_END_DATE = /*$$(*/
"OperationOrder.plannedEndDate"; /*)*/
public static final String OPERATION_ORDER_REAL_START_DATE = /*$$(*/
"OperationOrder.realStartDate"; /*)*/
public static final String OPERATION_ORDER_REAL_END_DATE = /*$$(*/
"OperationOrder.realEndDate"; /*)*/
public static final String OPERATION_ORDER_OPERATION_REQUIREMENT = /*$$(*/
"OperationOrder.operationRequirement"; /*)*/
public static final String OPERATION_ORDER_MACHINE = /*$$(*/ "OperationOrder.machine"; /*)*/
public static final String OPERATION_ORDER_CYCLE_DURATION = /*$$(*/
"OperationOrder.cycleDuration"; /*)*/
public static final String OPERATION_ORDER_START_TIME = /*$$(*/ "OperationOrder.startTime"; /*)*/
public static final String OPERATION_ORDER_STOP_TIME = /*$$(*/ "OperationOrder.stopTime"; /*)*/
public static final String OPERATION_ORDER_DELAY_BTW_CYCLE = /*$$(*/
"OperationOrder.delayBtwCycle"; /*)*/
public static final String OPERATION_ORDER_OPERATION_DETAILS = /*$$(*/
"OperationOrder.operationDetails"; /*)*/
public static final String OPERATION_ORDER_PRODUCT_PREPARE = /*$$(*/
"OperationOrder.productToPrepare"; /*)*/
public static final String OPERATION_ORDER_PRODUCT_BARCODE = /*$$(*/
"OperationOrder.productBarcode"; /*)*/
public static final String OPERATION_ORDER_PRODUCT_NAME = /*$$(*/
"OperationOrder.productName"; /*)*/
public static final String OPERATION_ORDER_QTY = /*$$(*/ "OperationOrder.qty"; /*)*/
public static final String OPERATION_ORDER_TRACKING_NUMBER = /*$$(*/
"OperationOrder.trackingNumber"; /*)*/
public static final String OPERATION_ORDER_NUM = /*$$(*/ "OperationOrder.num"; /*)*/
public static final String WORK_IN_PROGRESS_VALUATION_TITLE = /*$$(*/
"WorkInProgressValuation.title"; /*)*/
public static final String MANUF_ORDER_COMPANY = /*$$(*/ "ManufOrder.company"; /*)*/
public static final String MANUF_ORDER_TITLE = /*$$(*/ "ManufOrder.title"; /*)*/
public static final String MANUF_ORDER_PLANNED_START_DATE = /*$$(*/
"ManufOrder.plannedStartDate"; /*)*/
public static final String MANUF_ORDER_PLANNED_END_DATE = /*$$(*/
"ManufOrder.plannedEndDate"; /*)*/
public static final String MANUF_ORDER_PRODUCT = /*$$(*/ "ManufOrder.product"; /*)*/
public static final String MANUF_ORDER_QUANTITY = /*$$(*/ "ManufOrder.quantity"; /*)*/
public static final String MANUF_ORDER_ORIGIN = /*$$(*/ "ManufOrder.origin"; /*)*/
public static final String MANUF_ORDER_BILL_OF_MATERIAL = /*$$(*/
"ManufOrder.billOfMaterial"; /*)*/
public static final String MANUF_ORDER_PRODUCTION_PROCESS = /*$$(*/
"ManufOrder.productionProcess"; /*)*/
public static final String MANUF_ORDER_OPERATIONS = /*$$(*/ "ManufOrder.operations"; /*)*/
public static final String MANUF_ORDER_OPERATION_DETAILS = /*$$(*/
"ManufOrder.operationDetails"; /*)*/
public static final String MANUF_ORDER_FORECAST = /*$$(*/ "ManufOrder.forecast"; /*)*/
public static final String MANUF_ORDER_EFFECTIVE = /*$$(*/ "ManufOrder.effective"; /*)*/
public static final String MANUF_ORDER_NUM = /*$$(*/ "ManufOrder.num"; /*)*/
public static final String MANUF_ORDER_SEQ = /*$$(*/ "ManufOrder.seq"; /*)*/
public static final String MANUF_ORDER_MACHINE = /*$$(*/ "ManufOrder.machine"; /*)*/
public static final String MANUF_ORDER_OPERATION = /*$$(*/ "ManufOrder.operation"; /*)*/
public static final String MANUF_ORDER_START_DATE = /*$$(*/ "ManufOrder.startDate"; /*)*/
public static final String MANUF_ORDER_END_DATE = /*$$(*/ "ManufOrder.endDate"; /*)*/
public static final String MANUF_ORDER_EDITION_DATE = /*$$(*/ "ManufOrder.editionDate"; /*)*/
public static final String MANUF_ORDER_STATUS = /*$$(*/ "ManufOrder.status"; /*)*/
public static final String MANUF_ORDER_PRODUCTS_TO_PREPARE = /*$$(*/
"ManufOrder.productsToPrepare"; /*)*/
public static final String MANUF_ORDER_PRODUCT_CODE = /*$$(*/ "ManufOrder.productCode"; /*)*/
public static final String MANUF_ORDER_PRODUCT_NAME = /*$$(*/ "ManufOrder.productName"; /*)*/
public static final String MANUF_ORDER_PRODUCT_BARCODE = /*$$(*/
"ManufOrder.productBarcode"; /*)*/
public static final String MANUF_ORDER_TRACKING_NUMBER = /*$$(*/
"ManufOrder.trackingNumber"; /*)*/
public static final String MANUF_ORDER_FROM = /*$$(*/ "ManufOrder.from"; /*)*/
public static final String MANUF_ORDER_TO = /*$$(*/ "ManufOrder.to"; /*)*/
public static final String MANUF_ORDER_SERIAL = /*$$(*/ "ManufOrder.serial"; /*)*/
public static final String MANUF_ORDER_STOCK_LOCATION = /*$$(*/ "ManufOrder.stockLocation"; /*)*/
public static final String MANUF_ORDER_NO_LOCATION = /*$$(*/ "ManufOrder.noLocation"; /*)*/
public static final String MANUF_ORDER_RACK = /*$$(*/ "ManufOrder.rack"; /*)*/
public static final String MANUF_ORDER_WORK_CENTER = /*$$(*/ "ManufOrder.workCenter"; /*)*/
public static final String MANUF_COST_PRICE = /*$$(*/ "ManufOrder.costPrice"; /*)*/
public static final String MANUF_ORDER_IN_PROGRESS = /*$$(*/ "ManufOrder.inProgress"; /*)*/
public static final String MANUF_ORDER_STANDBY = /*$$(*/ "ManufOrder.standBy"; /*)*/
public static final String MANUF_ORDER_COMPANY_TOTAL = /*$$(*/ "ManufOrder.total"; /*)*/
public static final String PRODUCTION_PROCESS_LABEL = /*$$(*/ "ProdProcess.label"; /*)*/
public static final String PRODUCTION_PROCESS_NAME = /*$$(*/ "ProdProcess.name"; /*)*/
public static final String PRODUCTION_PROCESS_STOCK_LOCATION = /*$$(*/
"ProdProcess.stockLocation"; /*)*/
public static final String PRODUCTION_PROCESS_FINAL_STOCK_LOCATION = /*$$(*/
"ProdProcess.producedProductStockLocation"; /*)*/
public static final String PRODUCTION_PROCESS_PRODUCT = /*$$(*/ "ProdProcess.product"; /*)*/
public static final String PRODUCTION_PROCESS_ISOPERATION = /*$$(*/
"ProdProcess.isOperation"; /*)*/
public static final String PRODUCTION_PROCESS_ISOUTSOURCING = /*$$(*/
"ProdProcess.isOutsourcing"; /*)*/
public static final String PRODUCTION_PROCESS_COMPANY = /*$$(*/ "ProdProcess.company"; /*)*/
public static final String PRODUCTION_PROCESS_PHASES = /*$$(*/ "ProdProcess.phases"; /*)*/
public static final String PRODUCTION_PROCESS_PRIORITY = /*$$(*/ "ProdProcess.priority"; /*)*/
public static final String PRODUCTION_PROCESS_WORK_CENTER = /*$$(*/
"ProdProcess.workcenter"; /*)*/
public static final String PRODUCTION_PROCESS_OUTSOURCING = /*$$(*/
"ProdProcess.outsourcing"; /*)*/
public static final String PRODUCTION_PROCESS_DESCRIPTION = /*$$(*/
"ProdProcess.description"; /*)*/
public static final String PRODUCTION_PROCESS_QTY = /*$$(*/ "ProdProcess.qty"; /*)*/
public static final String PRODUCTION_PROCESS_UNIT = /*$$(*/ "ProdProcess.unit"; /*)*/
public static final String PRODUCTION_PROCESS_CONSUME_PRODUCT = /*$$(*/
"ProdProcess.consumeProduct"; /*)*/
public static final String BILL_OF_MATERIAL_TITLE = /*$$(*/ "BOM.title"; /*)*/
public static final String BILL_OF_MATERIAL_COMPANY = /*$$(*/ "BOM.company"; /*)*/
public static final String BILL_OF_MATERIAL_UNIT = /*$$(*/ "BOM.unit"; /*)*/
public static final String BILL_OF_MATERIAL_QTY = /*$$(*/ "BOM.qty"; /*)*/
public static final String BILL_OF_MATERIAL_VERSION_NUMBER = /*$$(*/ "BOM.versionNumber"; /*)*/
public static final String BILL_OF_MATERIAL_COMPONENTS = /*$$(*/ "BOM.billOfMaterialList"; /*)*/
public static final String BILL_OF_MATERIAL_PRIORITY = /*$$(*/ "BOM.priority"; /*)*/
public static final String BILL_OF_MATERIAL_DEFINE_SUB_BOM = /*$$(*/
"BOM.defineSubBillOfMaterial"; /*)*/
public static final String BILL_OF_MATERIAL_HAS_NO_MANAGE_STOCK = /*$$(*/
"BOM.hasNoManageStock"; /*)*/
String RAW_MATERIAL_REQUIREMENT_TITLE = /*$$(*/ "RawMaterialRequirement.title"; /*)*/
String RAW_MATERIAL_REQUIREMENT_COMPANY = /*$$(*/ "RawMaterialRequirement.company"; /*)*/
String RAW_MATERIAL_REQUIREMENT_FROM = /*$$(*/ "RawMaterialRequirement.from"; /*)*/
String RAW_MATERIAL_REQUIREMENT_TO = /*$$(*/ "RawMaterialRequirement.to"; /*)*/
String RAW_MATERIAL_REQUIREMENT_PRODUCT = /*$$(*/ "RawMaterialRequirement.product"; /*)*/
String RAW_MATERIAL_REQUIREMENT_MANUF_ORDER = /*$$(*/ "RawMaterialRequirement.manufOrder"; /*)*/
String RAW_MATERIAL_REQUIREMENT_STOCK_LOCATION = /*$$(*/
"RawMaterialRequirement.stockLocation"; /*)*/
String RAW_MATERIAL_REQUIREMENT_STOCK_LOCATIONS = /*$$(*/
"RawMaterialRequirement.stockLocations"; /*)*/
String RAW_MATERIAL_REQUIREMENT_NEEDED_QUANTITY = /*$$(*/
"RawMaterialRequirement.neededQuantity"; /*)*/
String RAW_MATERIAL_REQUIREMENT_MISSING_QUANTITY = /*$$(*/
"RawMaterialRequirement.missingQuantity"; /*)*/
String RAW_MATERIAL_REQUIREMENT_AVAILABLE = /*$$(*/ "RawMaterialRequirement.available"; /*)*/
public static final String COST_SHEET_TITLE = /*$$(*/ "CostSheet.title"; /*)*/
public static final String COST_SHEET_COST_PRICE = /*$$(*/ "CostSheet.costPrice"; /*)*/
public static final String COST_SHEET_COMPANY = /*$$(*/ "CostSheet.company"; /*)*/
public static final String COST_SHEET_MANUF_ORDER = /*$$(*/ "CostSheet.manufOrder"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINES = /*$$(*/ "CostSheet.costSheetLines"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_CODE = /*$$(*/
"CostSheet.costSheetLineCode"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_NAME = /*$$(*/
"CostSheet.costSheetLineName"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_BOM_LEVEL = /*$$(*/
"CostSheet.costSheetLineBomLevel"; /*)*/
public static final String COST_SHEET_GROUP = /*$$(*/
"CostSheet.costSheetLineCostSheetGroup"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_CONSUMPTION_QTY = /*$$(*/
"CostSheet.costSheetLineConsumptionQty"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_UNIT = /*$$(*/
"CostSheet.costSheetLineUnit"; /*)*/
public static final String COST_SHEET_CREATED_ON = /*$$(*/ "CostSheet.createdOn"; /*)*/
public static final String COST_SHEET_CREATED_BY = /*$$(*/ "CostSheet.createdBy"; /*)*/
public static final String COST_SHEET_DATE_OF_EDITION = /*$$(*/ "CostSheet.dateOfEdition"; /*)*/
public static final String COST_SHEET_MO_NUMBER = /*$$(*/ "CostSheet.ManufOrderNumber"; /*)*/
public static final String COST_SHEET_BILL_OF_MATERIAL = /*$$(*/ "CostSheet.billOfMaterial"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE = /*$$(*/ "CostSheet.lineType"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_PRODUCE_PRODUCT = /*$$(*/
"CostSheet.lineTypeProduceProduct"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_CONSUME_PRODUCT = /*$$(*/
"CostSheet.lineTypeConsumeProduct"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_WORK_CENTER = /*$$(*/
"CostSheet.lineTypeWorkCenter"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_INDIRECT_COST = /*$$(*/
"CostSheet.lineTypeIndirectCost"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_CONSUME_PRODUCT_WASTE = /*$$(*/
"CostSheet.lineTypeConsumeProductWaste"; /*)*/
public static final String COST_SHEET_COST_SHEET_LINE_TYPE_HUMAN = /*$$(*/
"CostSheet.lineTypeHuman"; /*)*/
public static final String COST_SHEET_CURRENCY = /*$$(*/ "CostSheet.currency"; /*)*/
public static final String COST_SHEET_LEGEND = /*$$(*/ "CostSheet.legend"; /*)*/
public static final String COST_SHEET_CALCULATION_TYPE = /*$$(*/
"CostSheet.calculationType"; /*)*/
public static final String COST_SHEET_CALCULATION_TYPE_PARTIAL_END_OF_PRODUCTION = /*$$(*/
"CostSheet.calculationTypePartialEndOfProduction"; /*)*/
public static final String COST_SHEET_CALCULATION_TYPE_END_OF_PRODUCTION = /*$$(*/
"CostSheet.calculationTypeEndOfProduction"; /*)*/
public static final String COST_SHEET_CALCULATION_TYPE_WORK_IN_PROGRESS = /*$$(*/
"CostSheet.calculationTypeWorkInProgress"; /*)*/
public static final String COST_SHEET_CALCULATION_TYPE_BILL_OF_MATERIAL = /*$$(*/
"CostSheet.calculationTypeBillOfMaterial"; /*)*/
public static final String COST_SHEET_CALCULATION_DATE = /*$$(*/
"CostSheet.calculationDate"; /*)*/
}

View File

@ -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.production.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.TempBomTree;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
public interface BillOfMaterialService {
static final String UNIT_MIN_CODE = "MIN";
static final String UNIT_DAY_CODE = "JR";
public List<BillOfMaterial> getBillOfMaterialSet(Product product);
@Transactional(rollbackOn = {Exception.class})
public void updateProductCostPrice(BillOfMaterial billOfMaterial) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public BillOfMaterial customizeBillOfMaterial(SaleOrderLine saleOrderLine) throws AxelorException;
public BillOfMaterial generateNewVersion(BillOfMaterial billOfMaterial);
public String getFileName(BillOfMaterial billOfMaterial);
public String getReportLink(
BillOfMaterial billOfMaterial, String name, String language, String format)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public TempBomTree generateTree(BillOfMaterial billOfMaterial);
@Transactional
public void setBillOfMaterialAsDefault(BillOfMaterial billOfMaterial);
@Transactional(rollbackOn = {Exception.class})
BillOfMaterial customizeBillOfMaterial(BillOfMaterial billOfMaterial) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
BillOfMaterial customizeBillOfMaterial(BillOfMaterial billOfMaterial, int depth)
throws AxelorException;
String computeName(BillOfMaterial bom);
void addRawMaterials(
long billOfMaterialId, ArrayList<LinkedHashMap<String, Object>> rawMaterials);
}

View File

@ -0,0 +1,441 @@
/*
* 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.production.service;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.ProductService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.BillOfMaterialConsumption;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.TempBomTree;
import com.axelor.apps.production.db.repo.BillOfMaterialConsumptionRepository;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.TempBomTreeRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.sale.db.SaleOrderLine;
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.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BillOfMaterialServiceImpl implements BillOfMaterialService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Inject protected BillOfMaterialRepository billOfMaterialRepo;
@Inject protected BillOfMaterialConsumptionRepository billOfMaterialConsumptionRepository;
@Inject private TempBomTreeRepository tempBomTreeRepo;
@Inject private ProductRepository productRepo;
private List<Long> processedBom;
@Override
public List<BillOfMaterial> getBillOfMaterialSet(Product product) {
return billOfMaterialRepo.all().filter("self.product = ?1", product).fetch();
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateProductCostPrice(BillOfMaterial billOfMaterial) throws AxelorException {
Product product = billOfMaterial.getProduct();
if (product.getCostTypeSelect() != ProductRepository.COST_TYPE_STANDARD) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.COST_TYPE_CANNOT_BE_CHANGED));
}
product.setCostPrice(
billOfMaterial
.getCostPrice()
.divide(
billOfMaterial.getQty(),
Beans.get(AppBaseService.class).getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_UP));
Beans.get(ProductService.class).updateSalePrice(product);
billOfMaterialRepo.save(billOfMaterial);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BillOfMaterial customizeBillOfMaterial(SaleOrderLine saleOrderLine)
throws AxelorException {
BillOfMaterial billOfMaterial = saleOrderLine.getBillOfMaterial();
return customizeBillOfMaterial(billOfMaterial);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BillOfMaterial customizeBillOfMaterial(BillOfMaterial billOfMaterial)
throws AxelorException {
return customizeBillOfMaterial(billOfMaterial, 0);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BillOfMaterial customizeBillOfMaterial(BillOfMaterial billOfMaterial, int depth)
throws AxelorException {
if (depth > 1000) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.MAX_DEPTH_REACHED));
}
if (billOfMaterial != null) {
BillOfMaterial personalizedBOM = JPA.copy(billOfMaterial, true);
billOfMaterialRepo.save(personalizedBOM);
personalizedBOM.setName(
personalizedBOM.getName()
+ " ("
+ I18n.get(IExceptionMessage.BOM_1)
+ " "
+ personalizedBOM.getId()
+ ")");
personalizedBOM.setPersonalized(true);
Set<BillOfMaterial> personalizedBOMSet = new HashSet<BillOfMaterial>();
for (BillOfMaterial childBillOfMaterial : billOfMaterial.getBillOfMaterialSet()) {
personalizedBOMSet.add(customizeBillOfMaterial(childBillOfMaterial, depth + 1));
}
personalizedBOM.setBillOfMaterialSet(personalizedBOMSet);
return personalizedBOM;
}
return null;
}
@Override
@Transactional
public BillOfMaterial generateNewVersion(BillOfMaterial billOfMaterial) {
BillOfMaterial copy = billOfMaterialRepo.copy(billOfMaterial, true);
copy.setOriginalBillOfMaterial(billOfMaterial);
copy.clearCostSheetList();
copy.setCostPrice(BigDecimal.ZERO);
copy.setOriginalBillOfMaterial(billOfMaterial);
copy.setVersionNumber(
this.getLatestBillOfMaterialVersion(billOfMaterial, billOfMaterial.getVersionNumber(), true)
+ 1);
return billOfMaterialRepo.save(copy);
}
public int getLatestBillOfMaterialVersion(
BillOfMaterial billOfMaterial, int latestVersion, boolean deep) {
List<BillOfMaterial> billOfMaterialSet;
BillOfMaterial up = billOfMaterial;
Long previousId = 0L;
do {
billOfMaterialSet =
billOfMaterialRepo
.all()
.filter("self.originalBillOfMaterial = :origin AND self.id != :id")
.bind("origin", up)
.bind("id", previousId)
.order("-versionNumber")
.fetch();
if (!billOfMaterialSet.isEmpty()) {
latestVersion =
(billOfMaterialSet.get(0).getVersionNumber() > latestVersion)
? billOfMaterialSet.get(0).getVersionNumber()
: latestVersion;
for (BillOfMaterial billOfMaterialIterator : billOfMaterialSet) {
int search =
this.getLatestBillOfMaterialVersion(billOfMaterialIterator, latestVersion, false);
latestVersion = (search > latestVersion) ? search : latestVersion;
}
}
previousId = up.getId();
up = up.getOriginalBillOfMaterial();
} while (up != null && deep);
return latestVersion;
}
@Override
public String getFileName(BillOfMaterial billOfMaterial) {
return I18n.get("Bill of Material")
+ "-"
+ billOfMaterial.getName()
+ ((billOfMaterial.getVersionNumber() > 1) ? "-V" + billOfMaterial.getVersionNumber() : "");
}
@Override
public String getReportLink(
BillOfMaterial billOfMaterial, String name, String language, String format)
throws AxelorException {
return ReportFactory.createReport(IReport.BILL_OF_MATERIAL, name + "-${date}")
.addParam("Locale", language)
.addParam("BillOfMaterialId", billOfMaterial.getId())
.addFormat(format)
.generate()
.getFileLink();
}
@Override
public TempBomTree generateTree(BillOfMaterial billOfMaterial) {
processedBom = new ArrayList<>();
return getBomTree(billOfMaterial, null, null);
}
@Transactional
public TempBomTree getBomTree(BillOfMaterial bom, BillOfMaterial parentBom, TempBomTree parent) {
TempBomTree bomTree;
if (parentBom == null) {
bomTree =
tempBomTreeRepo.all().filter("self.bom = ?1 and self.parentBom = null", bom).fetchOne();
} else {
bomTree =
tempBomTreeRepo
.all()
.filter("self.bom = ?1 and self.parentBom = ?2", bom, parentBom)
.fetchOne();
}
if (bomTree == null) {
bomTree = new TempBomTree();
}
bomTree.setProdProcess(bom.getProdProcess());
bomTree.setProduct(bom.getProduct());
bomTree.setQty(bom.getQty());
bomTree.setUnit(bom.getUnit());
bomTree.setParentBom(parentBom);
bomTree.setParent(parent);
bomTree.setBom(bom);
bomTree = tempBomTreeRepo.save(bomTree);
processedBom.add(bom.getId());
List<Long> validBomIds = processChildBom(bom, bomTree);
validBomIds.add(0L);
removeInvalidTree(validBomIds, bom);
return bomTree;
}
private List<Long> processChildBom(BillOfMaterial bom, TempBomTree bomTree) {
List<Long> validBomIds = new ArrayList<Long>();
for (BillOfMaterial childBom : bom.getBillOfMaterialSet()) {
if (!processedBom.contains(childBom.getId())) {
getBomTree(childBom, bom, bomTree);
} else {
log.debug("Already processed: {}", childBom.getId());
}
validBomIds.add(childBom.getId());
}
return validBomIds;
}
@Transactional
public void removeInvalidTree(List<Long> validBomIds, BillOfMaterial bom) {
List<TempBomTree> invalidBomTrees =
tempBomTreeRepo
.all()
.filter("self.bom.id not in (?1) and self.parentBom = ?2", validBomIds, bom)
.fetch();
log.debug("Invalid bom trees: {}", invalidBomTrees);
if (!invalidBomTrees.isEmpty()) {
List<TempBomTree> childBomTrees =
tempBomTreeRepo.all().filter("self.parent in (?1)", invalidBomTrees).fetch();
for (TempBomTree childBomTree : childBomTrees) {
childBomTree.setParent(null);
tempBomTreeRepo.save(childBomTree);
}
}
for (TempBomTree invalidBomTree : invalidBomTrees) {
tempBomTreeRepo.remove(invalidBomTree);
}
}
@Override
@Transactional
public void setBillOfMaterialAsDefault(BillOfMaterial billOfMaterial) {
billOfMaterial.getProduct().setDefaultBillOfMaterial(billOfMaterial);
}
@Override
public String computeName(BillOfMaterial bom) {
Integer nbDecimalDigitForBomQty =
Beans.get(AppProductionService.class).getAppProduction().getNbDecimalDigitForBomQty();
return bom.getProduct().getName()
+ " - "
+ bom.getQty().setScale(nbDecimalDigitForBomQty, RoundingMode.HALF_EVEN)
+ " "
+ bom.getUnit().getName()
+ " - "
+ bom.getId();
}
@Override
@Transactional
public void addRawMaterials(
long billOfMaterialId, ArrayList<LinkedHashMap<String, Object>> rawMaterials) {
if (rawMaterials != null && !rawMaterials.isEmpty()) {
BillOfMaterial billOfMaterial = billOfMaterialRepo.find(billOfMaterialId);
int priority = 0;
if (billOfMaterial.getBillOfMaterialSet() != null
&& !billOfMaterial.getBillOfMaterialSet().isEmpty()) {
priority =
Collections.max(
billOfMaterial
.getBillOfMaterialSet()
.stream()
.map(it -> it.getPriority())
.collect(Collectors.toSet()));
}
for (LinkedHashMap<String, Object> rawMaterial : rawMaterials) {
priority += 10;
BillOfMaterial newComponent =
createBomFromRawMaterial(Long.valueOf((int) rawMaterial.get("id")), priority);
billOfMaterial.getBillOfMaterialSet().add(newComponent);
}
} else {
return;
}
}
@Transactional
protected BillOfMaterial createBomFromRawMaterial(long productId, int priority) {
BillOfMaterial newBom = new BillOfMaterial();
Product rawMaterial = productRepo.find(productId);
newBom.setDefineSubBillOfMaterial(false);
newBom.setPriority(priority);
newBom.setProduct(rawMaterial);
newBom.setQty(new BigDecimal(1));
newBom.setUnit(rawMaterial.getUnit());
newBom.setWasteRate(BigDecimal.ZERO);
newBom.setHasNoManageStock(false);
billOfMaterialRepo.save(newBom);
String name = this.computeName(newBom); // need to save first cuz computeName uses the id.
newBom.setName(name);
newBom.setFullName(name);
return newBom;
}
@Transactional
public BillOfMaterialConsumption createBomConsumptionFromRawMaterial(
BillOfMaterial bom, ManufOrder manufOrder) {
BillOfMaterialConsumption newBom = new BillOfMaterialConsumption();
newBom.setDefineSubBillOfMaterial(false);
newBom.setPriority(bom.getPriority());
newBom.setProduct(bom.getProduct());
newBom.setQty(bom.getQty());
newBom.setRealQty(bom.getQty());
newBom.setUnit(bom.getUnit());
newBom.setName(bom.getName());
newBom.setFullName(bom.getFullName());
newBom.setManufOrder(manufOrder);
return newBom;
}
@Transactional
public void createBomAndAttachToManufOrder(ManufOrder manufOrder) {
if (manufOrder.getBillOfMaterial() != null
&& manufOrder.getBillOfMaterialConsumptionList().size() == 0) {
for (BillOfMaterial bom : manufOrder.getBillOfMaterial().getBillOfMaterialSet()) {
BillOfMaterialConsumption newBom =
Beans.get(BillOfMaterialServiceImpl.class)
.createBomConsumptionFromRawMaterial(bom, manufOrder);
manufOrder.addBillOfMaterialConsumptionListItem(newBom);
}
log.debug(
"Bill of Material Consumption List size: {}",
manufOrder.getBillOfMaterialConsumptionList().size());
Beans.get(ManufOrderRepository.class).save(manufOrder);
}
}
/**
* Splits the Bill of Material consumption line into two lines.
*
* @param manufOrder
* @param billConsumptionList
* @param splitQty
*/
@Transactional
public void splitBillOfMaterialConsumption(
ManufOrder manufOrder,
List<BillOfMaterialConsumption> billConsumptionList,
BigDecimal splitQty) {
for (BillOfMaterialConsumption billOfMaterialConsumption : billConsumptionList) {
BigDecimal totalQty = billOfMaterialConsumption.getQty();
totalQty = totalQty.subtract(splitQty);
BillOfMaterialConsumption newLine =
Beans.get(BillOfMaterialConsumptionRepository.class)
.copy(billOfMaterialConsumption, true);
newLine.setProduct(billOfMaterialConsumption.getProduct());
newLine.setRealQty(splitQty);
newLine.setQty(splitQty);
newLine.setUnit(billOfMaterialConsumption.getUnit());
newLine.setName(billOfMaterialConsumption.getName());
newLine.setTrackingNumber(billOfMaterialConsumption.getTrackingNumber());
billOfMaterialConsumption.setQty(totalQty);
manufOrder.addBillOfMaterialConsumptionListItem(newLine);
}
}
}

View File

@ -0,0 +1,139 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.repo.ProductionOrderRepository;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.manuforder.ManufOrderServiceImpl;
import com.axelor.apps.purchase.db.PurchaseOrder;
import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository;
import com.axelor.apps.purchase.service.PurchaseOrderLineService;
import com.axelor.apps.stock.service.StockRulesService;
import com.axelor.apps.supplychain.db.MrpLine;
import com.axelor.apps.supplychain.db.repo.MrpLineTypeRepository;
import com.axelor.apps.supplychain.service.MrpLineServiceImpl;
import com.axelor.apps.supplychain.service.PurchaseOrderServiceSupplychainImpl;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.time.LocalDate;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
public class MrpLineServiceProductionImpl extends MrpLineServiceImpl {
protected ManufOrderService manufOrderService;
@Inject
public MrpLineServiceProductionImpl(
AppProductionService appProductionService,
PurchaseOrderServiceSupplychainImpl purchaseOrderServiceSupplychainImpl,
PurchaseOrderLineService purchaseOrderLineService,
PurchaseOrderRepository purchaseOrderRepo,
ManufOrderService manufOrderService,
ProductionOrderRepository productionOrderRepo,
StockRulesService stockRulesService) {
super(
appProductionService,
purchaseOrderServiceSupplychainImpl,
purchaseOrderLineService,
purchaseOrderRepo,
stockRulesService);
this.manufOrderService = manufOrderService;
}
@Override
public void generateProposal(
MrpLine mrpLine,
Map<Pair<Partner, LocalDate>, PurchaseOrder> purchaseOrders,
Map<Partner, PurchaseOrder> purchaseOrdersPerSupplier,
boolean isProposalsPerSupplier)
throws AxelorException {
super.generateProposal(
mrpLine, purchaseOrders, purchaseOrdersPerSupplier, isProposalsPerSupplier);
if (mrpLine.getMrpLineType().getElementSelect()
== MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL) {
this.generateManufacturingProposal(mrpLine);
}
}
@Transactional(rollbackOn = {Exception.class})
protected void generateManufacturingProposal(MrpLine mrpLine) throws AxelorException {
Product product = mrpLine.getProduct();
ManufOrder manufOrder =
manufOrderService.generateManufOrder(
product,
mrpLine.getQty(),
ManufOrderService.DEFAULT_PRIORITY,
ManufOrderService.IS_TO_INVOICE,
null,
mrpLine.getMaturityDate().atStartOfDay(),
null,
ManufOrderServiceImpl
.ORIGIN_TYPE_MRP); // TODO compute the time to produce to put the manuf order at the
// correct day
linkToOrder(mrpLine, manufOrder);
}
@Override
protected String computeRelatedName(Model model) {
if (model instanceof ManufOrder) {
return ((ManufOrder) model).getManufOrderSeq();
} else if (model instanceof OperationOrder) {
return ((OperationOrder) model).getName();
} else {
return super.computeRelatedName(model);
}
}
@Override
protected Partner getPartner(Model model) {
if (model instanceof ManufOrder) {
return ((ManufOrder) model).getClientPartner();
} else if (model instanceof OperationOrder) {
return ((OperationOrder) model).getManufOrder().getClientPartner();
} else {
return super.getPartner(model);
}
}
}

View File

@ -0,0 +1,703 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.purchase.db.repo.PurchaseOrderLineRepository;
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockRules;
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
import com.axelor.apps.stock.db.repo.StockLocationRepository;
import com.axelor.apps.stock.db.repo.StockRulesRepository;
import com.axelor.apps.stock.service.StockLocationService;
import com.axelor.apps.stock.service.StockLocationServiceImpl;
import com.axelor.apps.stock.service.StockRulesService;
import com.axelor.apps.supplychain.db.Mrp;
import com.axelor.apps.supplychain.db.MrpForecast;
import com.axelor.apps.supplychain.db.MrpLine;
import com.axelor.apps.supplychain.db.MrpLineOrigin;
import com.axelor.apps.supplychain.db.MrpLineSophal;
import com.axelor.apps.supplychain.db.MrpLineType;
import com.axelor.apps.supplychain.db.ProductionMasterPlan;
import com.axelor.apps.supplychain.db.repo.MrpForecastRepository;
import com.axelor.apps.supplychain.db.repo.MrpLineRepository;
import com.axelor.apps.supplychain.db.repo.MrpLineSophalRepository;
import com.axelor.apps.supplychain.db.repo.MrpLineTypeRepository;
import com.axelor.apps.supplychain.db.repo.MrpRepository;
import com.axelor.apps.supplychain.db.repo.ProductionMasterPlanRepository;
import com.axelor.apps.supplychain.service.MrpLineService;
import com.axelor.apps.supplychain.service.MrpServiceImpl;
import com.axelor.apps.supplychain.service.ProductStockLocationServiceImpl;
import com.axelor.apps.tool.StringTool;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MrpServiceProductionImpl extends MrpServiceImpl {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ManufOrderRepository manufOrderRepository;
@Inject
public MrpServiceProductionImpl(
AppProductionService appProductionService,
MrpRepository mrpRepository,
StockLocationRepository stockLocationRepository,
ProductRepository productRepository,
StockLocationLineRepository stockLocationLineRepository,
MrpLineTypeRepository mrpLineTypeRepository,
PurchaseOrderLineRepository purchaseOrderLineRepository,
SaleOrderLineRepository saleOrderLineRepository,
MrpLineRepository mrpLineRepository,
StockRulesService stockRulesService,
MrpLineService mrpLineService,
MrpForecastRepository mrpForecastRepository,
ManufOrderRepository manufOrderRepository,
StockLocationService stockLocationService) {
super(
appProductionService,
mrpRepository,
stockLocationRepository,
productRepository,
stockLocationLineRepository,
mrpLineTypeRepository,
purchaseOrderLineRepository,
saleOrderLineRepository,
mrpLineRepository,
stockRulesService,
mrpLineService,
mrpForecastRepository,
stockLocationService);
this.manufOrderRepository = manufOrderRepository;
}
@Override
protected void completeMrp(Mrp mrp) throws AxelorException {
super.completeMrp(mrp);
this.createManufOrderMrpLines();
}
// Manufacturing order AND manufacturing order need
protected void createManufOrderMrpLines() throws AxelorException {
MrpLineType manufOrderMrpLineType =
this.getMrpLineType(MrpLineTypeRepository.ELEMENT_MANUFACTURING_ORDER);
MrpLineType manufOrderNeedMrpLineType =
this.getMrpLineType(MrpLineTypeRepository.ELEMENT_MANUFACTURING_ORDER_NEED);
String statusSelect = manufOrderMrpLineType.getStatusSelect();
List<Integer> statusList = StringTool.getIntegerList(statusSelect);
if (statusList.isEmpty()) {
statusList.add(ManufOrderRepository.STATUS_FINISHED);
}
List<ManufOrder> manufOrderList =
manufOrderRepository
.all()
.filter(
"self.product.id in (?1) AND self.prodProcess.stockLocation in (?2) "
+ "AND self.statusSelect IN (?3)",
this.productMap.keySet(),
this.stockLocationList,
statusList)
.fetch();
for (ManufOrder manufOrder : manufOrderList) {
this.createManufOrderMrpLines(
mrpRepository.find(mrp.getId()),
manufOrderRepository.find(manufOrder.getId()),
mrpLineTypeRepository.find(manufOrderMrpLineType.getId()),
mrpLineTypeRepository.find(manufOrderNeedMrpLineType.getId()));
JPA.clear();
}
}
@Transactional(rollbackOn = {Exception.class})
protected void createManufOrderMrpLines(
Mrp mrp,
ManufOrder manufOrder,
MrpLineType manufOrderMrpLineType,
MrpLineType manufOrderNeedMrpLineType)
throws AxelorException {
StockLocation stockLocation = manufOrder.getProdProcess().getStockLocation();
LocalDate maturityDate = null;
if (manufOrder.getPlannedEndDateT() != null) {
maturityDate = manufOrder.getPlannedEndDateT().toLocalDate();
} else {
maturityDate = manufOrder.getPlannedStartDateT().toLocalDate();
}
maturityDate = this.computeMaturityDate(maturityDate, manufOrderMrpLineType);
for (ProdProduct prodProduct : manufOrder.getToProduceProdProductList()) {
Product product = prodProduct.getProduct();
if (this.isBeforeEndDate(maturityDate) && this.isMrpProduct(product)) {
MrpLine mrpLine =
this.createMrpLine(
mrp,
product,
manufOrderMrpLineType,
prodProduct.getQty(),
maturityDate,
BigDecimal.ZERO,
stockLocation,
manufOrder);
if (mrpLine != null) {
mrpLineRepository.save(mrpLine);
}
}
}
if (manufOrder.getIsConsProOnOperation()) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
for (ProdProduct prodProduct : operationOrder.getToConsumeProdProductList()) {
Product product = prodProduct.getProduct();
if (this.isMrpProduct(product)) {
maturityDate = null;
if (operationOrder.getPlannedEndDateT() != null) {
maturityDate = operationOrder.getPlannedEndDateT().toLocalDate();
} else {
maturityDate = operationOrder.getPlannedStartDateT().toLocalDate();
}
maturityDate = this.computeMaturityDate(maturityDate, manufOrderNeedMrpLineType);
MrpLine mrpLine =
this.createMrpLine(
mrp,
prodProduct.getProduct(),
manufOrderNeedMrpLineType,
prodProduct.getQty(),
maturityDate,
BigDecimal.ZERO,
stockLocation,
operationOrder);
if (mrpLine != null) {
mrpLineRepository.save(mrpLine);
}
}
}
}
} else {
for (ProdProduct prodProduct : manufOrder.getToConsumeProdProductList()) {
Product product = prodProduct.getProduct();
if (this.isMrpProduct(product)) {
// A component of a manuf order that is not loaded on MRP because there is no default
// BOM or
// because the component of manuf order is not a component of the bill of material, we
// add it with the level of manuf order product + 1.
if (!this.productMap.containsKey(product.getId())) {
this.assignProductAndLevel(product, manufOrder.getProduct());
this.createAvailableStockMrpLine(
product, manufOrder.getProdProcess().getStockLocation());
}
MrpLine mrpLine =
this.createMrpLine(
mrp,
product,
manufOrderNeedMrpLineType,
prodProduct.getQty(),
maturityDate,
BigDecimal.ZERO,
stockLocation,
manufOrder);
if (mrpLine != null) {
mrpLineRepository.save(mrpLine);
}
}
}
}
}
@Override
protected void createProposalMrpLine(
Mrp mrp,
Product product,
MrpLineType mrpLineType,
BigDecimal reorderQty,
StockLocation stockLocation,
LocalDate maturityDate,
List<MrpLineOrigin> mrpLineOriginList,
String relatedToSelectName)
throws AxelorException {
super.createProposalMrpLine(
mrp,
product,
mrpLineType,
reorderQty,
stockLocation,
maturityDate,
mrpLineOriginList,
relatedToSelectName);
BillOfMaterial defaultBillOfMaterial = product.getDefaultBillOfMaterial();
if (mrpLineType.getElementSelect() == MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL
&& defaultBillOfMaterial != null) {
MrpLineType manufProposalNeedMrpLineType =
this.getMrpLineType(MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL_NEED);
for (BillOfMaterial billOfMaterial : defaultBillOfMaterial.getBillOfMaterialSet()) {
Product subProduct = billOfMaterial.getProduct();
if (this.isMrpProduct(subProduct)) {
// TODO take the time to do the Manuf order (use machine planning)
super.createProposalMrpLine(
mrp,
subProduct,
manufProposalNeedMrpLineType,
reorderQty.multiply(billOfMaterial.getQty()),
stockLocation,
maturityDate,
mrpLineOriginList,
relatedToSelectName);
}
}
}
}
/**
* Returns the type of an mrp proposal.
*
* <p>First checks for a stock rule, then if there isn't one, checks for the
* procurementMethodSelect field of the product. If the product can be both bought and produced, a
* manufacturing order is generated.
*/
@Override
protected MrpLineType getMrpLineTypeForProposal(StockRules stockRules, Product product)
throws AxelorException {
if (stockRules != null) {
if (stockRules.getOrderAlertSelect() == StockRulesRepository.ORDER_ALERT_PRODUCTION_ORDER) {
return this.getMrpLineType(MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL);
} else {
return this.getMrpLineType(MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL);
}
}
if (product.getProcurementMethodSelect().equals(ProductRepository.PROCUREMENT_METHOD_BUY)) {
return this.getMrpLineType(MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL);
} else {
return this.getMrpLineType(MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL);
}
}
@Override
protected boolean isProposalElement(MrpLineType mrpLineType) {
if (mrpLineType.getElementSelect() == MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL
|| mrpLineType.getElementSelect() == MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL
|| mrpLineType.getElementSelect()
== MrpLineTypeRepository.ELEMENT_MANUFACTURING_PROPOSAL_NEED) {
return true;
}
return false;
}
@Override
protected void assignProductAndLevel(Product product) {
log.debug("Add of the product : {}", product.getFullName());
this.productMap.put(product.getId(), this.getMaxLevel(product, 0));
if (product.getDefaultBillOfMaterial() != null) {
this.assignProductLevel(product.getDefaultBillOfMaterial(), 0);
}
}
public int getMaxLevel(Product product, int level) {
if (this.productMap.containsKey(product.getId())) {
return Math.max(level, this.productMap.get(product.getId()));
}
return level;
}
/**
* Update the level of Bill of material. The highest for each product (0: product with parent, 1:
* product with a parent, 2: product with a parent that have a parent, ...)
*
* @param billOfMaterial
* @param level
*/
protected void assignProductLevel(BillOfMaterial billOfMaterial, int level) {
if (billOfMaterial.getBillOfMaterialSet() == null
|| billOfMaterial.getBillOfMaterialSet().isEmpty()
|| level > 100) {
Product subProduct = billOfMaterial.getProduct();
log.debug("Add of the sub product : {} for the level : {} ", subProduct.getFullName(), level);
this.productMap.put(subProduct.getId(), this.getMaxLevel(subProduct, level));
} else {
level = level + 1;
for (BillOfMaterial subBillOfMaterial : billOfMaterial.getBillOfMaterialSet()) {
Product subProduct = subBillOfMaterial.getProduct();
if (this.isMrpProduct(subProduct)) {
this.assignProductLevel(subBillOfMaterial, level);
if (subProduct.getDefaultBillOfMaterial() != null) {
this.assignProductLevel(subProduct.getDefaultBillOfMaterial(), level);
}
}
}
}
}
/**
* Add a component product of a manuf order where the component product is not contained on the
* default bill of material of the produced product.
*
* @param manufOrderComponentProduct
* @param manufOrderProducedProduct
*/
protected void assignProductAndLevel(
Product manufOrderComponentProduct, Product manufOrderProducedProduct) {
log.debug("Add of the product : {}", manufOrderComponentProduct.getFullName());
this.productMap.put(
manufOrderComponentProduct.getId(), this.getMaxLevel(manufOrderProducedProduct, 0) + 1);
}
protected void createAvailableStockMrpLine(Product product, StockLocation stockLocation)
throws AxelorException {
MrpLineType availableStockMrpLineType =
this.getMrpLineType(MrpLineTypeRepository.ELEMENT_AVAILABLE_STOCK);
mrpLineRepository.save(
this.createAvailableStockMrpLine(
mrpRepository.find(mrp.getId()),
productRepository.find(product.getId()),
stockLocation,
availableStockMrpLineType));
}
@Override
protected Mrp completeProjectedStock(
Mrp mrp, Product product, Company company, StockLocation stockLocation)
throws AxelorException {
super.completeProjectedStock(mrp, product, company, stockLocation);
this.createManufOrderMrpLines();
return mrp;
}
@Override
public void createAvailableMrpLineSophal(Mrp mrp) throws AxelorException {
log.debug("MRP PROD >>>>");
super.createAvailableMrpLineSophal(mrp);
}
@Transactional
@Override
public void createMrpLineSophal(
Mrp mrp,
Product product,
Unit unit,
BigDecimal qty,
List<MrpForecast> mrpForecastList,
Map<Long, Double> productQty)
throws AxelorException {
log.debug("****** FORTHUPPER ******** {} {}", product, qty);
if (mrp.getIncludeBOM()) {
BillOfMaterial defaultBillOfMaterial = product.getDefaultBillOfMaterial();
if (defaultBillOfMaterial != null) {
super.createdProductionMasterPlan(product, mrp, qty, defaultBillOfMaterial.getQty());
for (BillOfMaterial billOfMaterial : defaultBillOfMaterial.getBillOfMaterialSet()) {
List<ProductionMasterPlan> productionMasterPlans =
Beans.get(ProductionMasterPlanRepository.class)
.all()
.fetchStream()
.filter(t -> t.getProduct() == product && t.getMrp() == mrp)
.collect(Collectors.toList());
Product subProduct = billOfMaterial.getProduct();
if (this.isMrpProduct(subProduct)) {
Double prodQty =
productQty.get(subProduct.getId()) == null
? Double.parseDouble("0")
: productQty.get(subProduct.getId());
BigDecimal reelQty = new BigDecimal(prodQty);
createMrpLineSophalProd(
mrp,
subProduct,
product,
reelQty,
billOfMaterial,
defaultBillOfMaterial.getQty(),
productionMasterPlans);
}
}
}
} else {
super.createMrpLineSophal(mrp, product, unit, qty, mrpForecastList, productQty);
}
}
@Transactional
public void createMrpLineSophalProd(
Mrp mrp,
Product product,
Product parentProduct,
BigDecimal qty,
BillOfMaterial billOfMaterial,
BigDecimal defaultQty,
List<ProductionMasterPlan> productionMasterPlans)
throws AxelorException {
MrpLineSophal mrpLineSophal = new MrpLineSophal();
log.debug("****** FORTH ******** {} {}", product, qty);
BigDecimal qtyReqPerBatch = billOfMaterial.getQty();
int currentMonth = LocalDate.now().getMonth().getValue();
BigDecimal currentProductionPlan = BigDecimal.ZERO;
BigDecimal remaining = BigDecimal.ZERO;
BigDecimal decreasingQty = qty;
BigDecimal totalQtyUsed = BigDecimal.ZERO;
BigDecimal futureQty = BigDecimal.ZERO;
BigDecimal purchaseQty = BigDecimal.ZERO;
if (mrp.getIncludeFutureQty()) {
futureQty =
Beans.get(StockLocationServiceImpl.class)
.getFutureQty(product.getId(), mrp.getStockLocation().getCompany().getId(), 0L);
decreasingQty = decreasingQty.add(futureQty);
}
if (mrp.getIncludePurchaseQty()) {
purchaseQty =
Beans.get(ProductStockLocationServiceImpl.class)
.getPurchaseOrderQty(product, mrp.getStockLocation().getCompany(), null);
decreasingQty = decreasingQty.add(purchaseQty);
}
BigDecimal initialQty = decreasingQty;
for (int index = currentMonth; index < 13; index++) {
currentProductionPlan = getCurrentProductionPlan(parentProduct, mrp, index);
BigDecimal qtyReqForProd = currentProductionPlan.multiply(qtyReqPerBatch);
if (mrp.getIncludeBomWaste()) {
qtyReqForProd =
qtyReqForProd.add(
qtyReqForProd.multiply(
billOfMaterial
.getWasteRate()
.divide(new BigDecimal("100"), 0, RoundingMode.HALF_UP)));
}
totalQtyUsed.add(qtyReqForProd);
log.debug("totalQtyUsed**************** {}", totalQtyUsed);
if (decreasingQty.compareTo(qtyReqForProd) > 0) {
remaining = decreasingQty.subtract(qtyReqForProd);
decreasingQty = BigDecimal.ZERO;
} else {
remaining = BigDecimal.ZERO;
decreasingQty = qtyReqForProd.subtract(decreasingQty);
if (mrp.getIncludeStockRule()) {
StockRules stockRules =
stockRulesService.getStockRules(
product,
null,
StockRulesRepository.TYPE_FUTURE,
StockRulesRepository.USE_CASE_USED_FOR_MRP);
if (stockRules != null && stockRules.getReOrderQty() != null) {
decreasingQty = stockRules.getReOrderQty().max(decreasingQty);
}
}
}
switch (index) {
case 1:
mrpLineSophal.setJanuary(decreasingQty);
break;
case 2:
mrpLineSophal.setFebruary(decreasingQty);
break;
case 3:
mrpLineSophal.setMarch(decreasingQty);
break;
case 4:
mrpLineSophal.setApril(decreasingQty);
case 5:
mrpLineSophal.setMay(decreasingQty);
case 6:
mrpLineSophal.setJuin(decreasingQty);
break;
case 7:
mrpLineSophal.setJuly(decreasingQty);
break;
case 8:
mrpLineSophal.setAugust(decreasingQty);
break;
case 9:
mrpLineSophal.setSeptember(decreasingQty);
break;
case 10:
mrpLineSophal.setOctober(decreasingQty);
break;
case 11:
mrpLineSophal.setNovember(decreasingQty);
break;
case 12:
mrpLineSophal.setDecember(decreasingQty);
break;
default:
break;
}
if (remaining.compareTo(BigDecimal.ZERO) > 0) {
decreasingQty = decreasingQty.add(remaining);
} else {
decreasingQty = BigDecimal.ZERO;
}
}
mrpLineSophal.setQty(qty);
mrpLineSophal.setTotalQtyUsed(totalQtyUsed);
mrpLineSophal.setInitialQty(initialQty);
mrpLineSophal.setPurchaseOrderQty(purchaseQty);
mrpLineSophal.setFutureQty(futureQty);
mrpLineSophal.setMrp(mrp);
mrpLineSophal.setProduct(product);
mrpLineSophal.setUnit(product.getPurchasesUnit());
mrpLineSophal.setProductOrigin(parentProduct);
Beans.get(MrpLineSophalRepository.class).save(mrpLineSophal);
}
private BigDecimal getCurrentProductionPlan(Product product, Mrp mrp, int monthSelect) {
ProductionMasterPlan productionMasterPlan =
Beans.get(ProductionMasterPlanRepository.class)
.all()
.fetchStream()
.filter(t -> t.getProduct() == product && t.getMrp() == mrp)
.findFirst()
.orElse(null);
log.debug("productionMasterPlan >>>>>>>>>>>> {}", productionMasterPlan);
if (productionMasterPlan != null) {
switch (monthSelect) {
case 1:
return productionMasterPlan.getJanuaryBatchQty();
case 2:
return productionMasterPlan.getFebruaryBatchQty();
case 3:
return productionMasterPlan.getMarchBatchQty();
case 4:
return productionMasterPlan.getAprilBatchQty();
case 5:
return productionMasterPlan.getMayBatchQty();
case 6:
return productionMasterPlan.getJuinBatchQty();
case 7:
return productionMasterPlan.getJulyBatchQty();
case 8:
return productionMasterPlan.getAugustBatchQty();
case 9:
return productionMasterPlan.getSeptemberBatchQty();
case 10:
return productionMasterPlan.getOctoberBatchQty();
case 11:
return productionMasterPlan.getNovemberBatchQty();
case 12:
return productionMasterPlan.getDecemberBatchQty();
default:
return BigDecimal.ZERO;
}
}
return BigDecimal.ZERO;
}
}

View File

@ -0,0 +1,34 @@
package com.axelor.apps.production.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Previous {
public static void main(String[] args) throws IOException {
String s = null;
Process p =
Runtime.getRuntime()
.exec(
"python /Users\\Bachir.souldi.SOPHAL\\Desktop\\dev\\whatsapp_erp\\index.py hello");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
System.exit(0);
}
}

View File

@ -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.production.service;
import com.axelor.apps.production.db.WorkCenter;
import java.math.BigDecimal;
public interface ProdProcessLineService {
public Long getProdProcessLineDurationFromWorkCenter(WorkCenter workCenter);
public BigDecimal getProdProcessLineMinCapacityPerCycleFromWorkCenter(WorkCenter workCenter);
public BigDecimal getProdProcessLineMaxCapacityPerCycleFromWorkCenter(WorkCenter workCenter);
}

View File

@ -0,0 +1,77 @@
/*
* 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.production.service;
import com.axelor.apps.production.db.ProdHumanResource;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.db.repo.ProdProcessLineRepository;
import com.axelor.apps.production.db.repo.WorkCenterRepository;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
public class ProdProcessLineServiceImpl implements ProdProcessLineService {
protected ProdProcessLineRepository prodProcessLineRepo;
@Inject
public ProdProcessLineServiceImpl(ProdProcessLineRepository prodProcessLineRepo) {
this.prodProcessLineRepo = prodProcessLineRepo;
}
public Long getProdProcessLineDurationFromWorkCenter(WorkCenter workCenter) {
List<Long> durations = new ArrayList<Long>();
if (workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
durations.add(workCenter.getDurationPerCycle());
}
if (workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_HUMAN
|| workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
if (workCenter.getProdHumanResourceList() != null) {
for (ProdHumanResource prodHumanResource : workCenter.getProdHumanResourceList()) {
durations.add(prodHumanResource.getDuration());
}
}
}
return !CollectionUtils.isEmpty(durations) ? Collections.max(durations) : new Long(0);
}
public BigDecimal getProdProcessLineMinCapacityPerCycleFromWorkCenter(WorkCenter workCenter) {
if (workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
return workCenter.getMinCapacityPerCycle();
} else {
return new BigDecimal(1);
}
}
public BigDecimal getProdProcessLineMaxCapacityPerCycleFromWorkCenter(WorkCenter workCenter) {
if (workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenter.getWorkCenterTypeSelect() == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
return workCenter.getMaxCapacityPerCycle();
} else {
return new BigDecimal(1);
}
}
}

View File

@ -0,0 +1,172 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ProdProcessService {
protected ProdProcessRepository prodProcessRepo;
@Inject
public ProdProcessService(ProdProcessRepository prodProcessRepo) {
this.prodProcessRepo = prodProcessRepo;
}
public void validateProdProcess(ProdProcess prodProcess, BillOfMaterial bom)
throws AxelorException {
Map<Product, BigDecimal> bomMap = new HashMap<Product, BigDecimal>();
Set<BillOfMaterial> setBoM =
MoreObjects.firstNonNull(bom.getBillOfMaterialSet(), Collections.emptySet());
for (BillOfMaterial bomIt : setBoM) {
bomMap.put(bomIt.getProduct(), bomIt.getQty());
}
List<ProdProcessLine> listPPL =
MoreObjects.firstNonNull(prodProcess.getProdProcessLineList(), Collections.emptyList());
for (ProdProcessLine prodProcessLine : listPPL) {
List<ProdProduct> listPP =
MoreObjects.firstNonNull(
prodProcessLine.getToConsumeProdProductList(), Collections.emptyList());
for (ProdProduct prodProduct : listPP) {
if (!bomMap.containsKey(prodProduct.getProduct())) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PROD_PROCESS_USELESS_PRODUCT),
prodProduct.getProduct().getName());
}
bomMap.put(
prodProduct.getProduct(),
bomMap.get(prodProduct.getProduct()).subtract(prodProduct.getQty()));
}
}
Set<Product> keyList = bomMap.keySet();
Map<Product, BigDecimal> copyMap = new HashMap<Product, BigDecimal>();
List<String> nameProductList = new ArrayList<String>();
for (Product product : keyList) {
if (bomMap.get(product).compareTo(BigDecimal.ZERO) > 0) {
copyMap.put(product, bomMap.get(product));
nameProductList.add(product.getName());
}
}
if (!copyMap.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PROD_PROCESS_MISS_PRODUCT),
Joiner.on(",").join(nameProductList));
}
}
public ProdProcess changeProdProcessListOutsourcing(ProdProcess prodProcess) {
for (ProdProcessLine prodProcessLine : prodProcess.getProdProcessLineList()) {
prodProcessLine.setOutsourcing(prodProcess.getOutsourcing());
}
return prodProcess;
}
public String getLanguageToPrinting(ProdProcess prodProcess) {
User user = AuthUtils.getUser();
String language = "en";
if (user != null && !Strings.isNullOrEmpty(user.getLanguage())) {
return user.getLanguage();
}
if (prodProcess == null) {
return language;
}
Company company = prodProcess.getCompany();
if (company != null && company.getPartner() != null) {
language = ReportSettings.getPrintingLocale(company.getPartner());
}
return language;
}
@Transactional
public ProdProcess generateNewVersion(ProdProcess prodProcess) {
ProdProcess copy = prodProcessRepo.copy(prodProcess, true);
copy.getProdProcessLineList().forEach(list -> list.setProdProcess(copy));
copy.setOriginalProdProcess(prodProcess);
copy.setVersionNumber(
this.getLatestProdProcessVersion(prodProcess, prodProcess.getVersionNumber(), true) + 1);
return prodProcessRepo.save(copy);
}
public int getLatestProdProcessVersion(ProdProcess prodProcess, int latestVersion, boolean deep) {
List<ProdProcess> prodProcessSet = Lists.newArrayList();
ProdProcess up = prodProcess;
Long previousId = Long.valueOf(0);
do {
prodProcessSet =
prodProcessRepo
.all()
.filter("self.originalProdProcess = :origin AND self.id != :id")
.bind("origin", up)
.bind("id", previousId)
.order("-versionNumber")
.fetch();
if (!prodProcessSet.isEmpty()) {
latestVersion =
(prodProcessSet.get(0).getVersionNumber() > latestVersion)
? prodProcessSet.get(0).getVersionNumber()
: latestVersion;
for (ProdProcess prodProcessIterator : prodProcessSet) {
int search = this.getLatestProdProcessVersion(prodProcessIterator, latestVersion, false);
latestVersion = (search > latestVersion) ? search : latestVersion;
}
}
previousId = up.getId();
up = up.getOriginalProdProcess();
} while (up != null && deep);
return latestVersion;
}
}

View File

@ -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.production.service;
import com.axelor.apps.production.db.repo.ProdProductRepository;
import com.axelor.db.JPA;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ProdProductProductionRepository extends ProdProductRepository {
@Override
public Map<String, Object> populate(Map<String, Object> json, Map<String, Object> context) {
Object productFromView = json.get("product");
Object qtyFromView = json.get("qty");
Object toProduceManufOrderIdFromView;
if (context == null || context.isEmpty()) {
toProduceManufOrderIdFromView =
json.get("toConsumeManufOrder") == null
? null
: ((HashMap<String, Object>) json.get("toConsumeManufOrder")).get("id");
} else {
toProduceManufOrderIdFromView = context.get("id");
}
if (productFromView == null || qtyFromView == null || toProduceManufOrderIdFromView == null) {
return super.populate(json, context);
} else {
Long productId = (Long) ((HashMap<String, Object>) productFromView).get("id");
Long toProduceManufOrderId = (Long) toProduceManufOrderIdFromView;
json.put(
"$missingQty",
computeMissingQty(productId, (BigDecimal) qtyFromView, toProduceManufOrderId));
}
return super.populate(json, context);
}
protected BigDecimal computeMissingQty(
Long productId, BigDecimal qty, Long toProduceManufOrderId) {
if (productId == null || qty == null || toProduceManufOrderId == null) {
return BigDecimal.ZERO;
}
List<BigDecimal> queryResult =
JPA.em()
.createQuery(
"SELECT locationLine.currentQty "
+ "FROM ManufOrder manufOrder "
+ "LEFT JOIN StockLocationLine locationLine "
+ "ON locationLine.stockLocation.id = manufOrder.prodProcess.stockLocation.id "
+ "WHERE locationLine.product.id = :productId "
+ "AND manufOrder.id = :manufOrderId",
BigDecimal.class)
.setParameter("productId", productId)
.setParameter("manufOrderId", toProduceManufOrderId)
.getResultList();
BigDecimal availableQty;
if (queryResult.isEmpty()) {
availableQty = BigDecimal.ZERO;
} else {
availableQty = queryResult.get(0);
}
return BigDecimal.ZERO.max(qty.subtract(availableQty));
}
}

View File

@ -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.production.service;
import com.axelor.apps.production.db.ProdProduct;
import java.math.BigDecimal;
import java.util.List;
public class ProdProductService {
// TODO add conversion unit
public BigDecimal computeQuantity(List<ProdProduct> prodProductList) {
BigDecimal qty = BigDecimal.ZERO;
if (prodProductList != null) {
for (ProdProduct prodProduct : prodProductList) {
qty = qty.add(prodProduct.getQty());
}
}
return qty;
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
import com.axelor.apps.stock.db.repo.StockLocationRepository;
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
import com.axelor.apps.stock.service.StockLocationLineService;
import com.axelor.apps.stock.service.StockLocationService;
import com.axelor.apps.supplychain.service.ProductStockLocationServiceImpl;
import com.axelor.apps.supplychain.service.StockLocationServiceSupplychain;
import com.axelor.apps.supplychain.service.app.AppSupplychainService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
public class ProductionProductStockLocationServiceImpl extends ProductStockLocationServiceImpl {
protected AppProductionService appProductionService;
protected ManufOrderService manufOrderService;
protected StockMoveLineRepository stockMoveLineRepository;
@Inject
public ProductionProductStockLocationServiceImpl(
UnitConversionService unitConversionService,
AppSupplychainService appSupplychainService,
ProductRepository productRepository,
CompanyRepository companyRepository,
StockLocationRepository stockLocationRepository,
StockLocationService stockLocationService,
StockLocationServiceSupplychain stockLocationServiceSupplychain,
StockLocationLineService stockLocationLineService,
StockLocationLineRepository stockLocationLineRepository,
AppProductionService appProductionService,
ManufOrderService manufOrderService,
StockMoveLineRepository stockMoveLineRepository) {
super(
unitConversionService,
appSupplychainService,
productRepository,
companyRepository,
stockLocationRepository,
stockLocationService,
stockLocationServiceSupplychain,
stockLocationLineService,
stockLocationLineRepository);
this.appProductionService = appProductionService;
this.manufOrderService = manufOrderService;
this.stockMoveLineRepository = stockMoveLineRepository;
}
@Override
public Map<String, Object> computeIndicators(Long productId, Long companyId, Long stockLocationId)
throws AxelorException {
Map<String, Object> map = super.computeIndicators(productId, companyId, stockLocationId);
Product product = productRepository.find(productId);
Company company = companyRepository.find(companyId);
StockLocation stockLocation = stockLocationRepository.find(stockLocationId);
BigDecimal consumeManufOrderQty =
this.getConsumeManufOrderQty(product, company, stockLocation).setScale(2);
BigDecimal availableQty =
(BigDecimal) map.getOrDefault("$availableQty", BigDecimal.ZERO.setScale(2));
map.put("$buildingQty", this.getBuildingQty(product, company, stockLocation).setScale(2));
map.put("$consumeManufOrderQty", consumeManufOrderQty);
map.put(
"$missingManufOrderQty",
BigDecimal.ZERO.max(consumeManufOrderQty.subtract(availableQty)).setScale(2));
return map;
}
protected BigDecimal getBuildingQty(Product product, Company company, StockLocation stockLocation)
throws AxelorException {
if (product == null || product.getUnit() == null) {
return BigDecimal.ZERO;
}
Long companyId = 0L;
Long stockLocationId = 0L;
if (company != null) {
companyId = company.getId();
if (stockLocation != null) {
stockLocationId = stockLocation.getId();
}
}
String query =
manufOrderService.getBuildingQtyForAProduct(product.getId(), companyId, stockLocationId);
List<StockMoveLine> stockMoveLineList = stockMoveLineRepository.all().filter(query).fetch();
BigDecimal sumBuildingQty = BigDecimal.ZERO;
if (!stockMoveLineList.isEmpty()) {
Unit unitConversion = product.getUnit();
for (StockMoveLine stockMoveLine : stockMoveLineList) {
BigDecimal productBuildingQty = stockMoveLine.getRealQty();
unitConversionService.convert(
stockMoveLine.getUnit(),
unitConversion,
productBuildingQty,
productBuildingQty.scale(),
product);
sumBuildingQty = sumBuildingQty.add(productBuildingQty);
}
}
return sumBuildingQty;
}
protected BigDecimal getConsumeManufOrderQty(
Product product, Company company, StockLocation stockLocation) throws AxelorException {
if (product == null || product.getUnit() == null) {
return BigDecimal.ZERO;
}
Long companyId = 0L;
Long stockLocationId = 0L;
if (company != null) {
companyId = company.getId();
if (stockLocation != null) {
stockLocationId = stockLocation.getId();
}
}
String query =
manufOrderService.getConsumeAndMissingQtyForAProduct(
product.getId(), companyId, stockLocationId);
List<StockMoveLine> stockMoveLineList = stockMoveLineRepository.all().filter(query).fetch();
BigDecimal sumConsumeManufOrderQty = BigDecimal.ZERO;
if (!stockMoveLineList.isEmpty()) {
Unit unitConversion = product.getUnit();
for (StockMoveLine stockMoveLine : stockMoveLineList) {
BigDecimal productConsumeManufOrderQty = stockMoveLine.getRealQty();
unitConversionService.convert(
stockMoveLine.getUnit(),
unitConversion,
productConsumeManufOrderQty,
productConsumeManufOrderQty.scale(),
product);
sumConsumeManufOrderQty = sumConsumeManufOrderQty.add(productConsumeManufOrderQty);
}
}
return sumConsumeManufOrderQty;
}
protected BigDecimal getMissingManufOrderQty(
Product product, Company company, StockLocation stockLocation) throws AxelorException {
if (product == null || product.getUnit() == null) {
return BigDecimal.ZERO;
}
Long companyId = 0L;
Long stockLocationId = 0L;
if (company != null) {
companyId = company.getId();
if (stockLocation != null) {
stockLocationId = stockLocation.getId();
}
}
String query =
manufOrderService.getConsumeAndMissingQtyForAProduct(
product.getId(), companyId, stockLocationId);
List<StockMoveLine> stockMoveLineList = stockMoveLineRepository.all().filter(query).fetch();
BigDecimal sumMissingManufOrderQty = BigDecimal.ZERO;
if (!stockMoveLineList.isEmpty()) {
Unit unitConversion = product.getUnit();
for (StockMoveLine stockMoveLine : stockMoveLineList) {
BigDecimal productMissingManufOrderQty = getMissingQtyOfStockMoveLine(stockMoveLine);
unitConversionService.convert(
stockMoveLine.getUnit(),
unitConversion,
productMissingManufOrderQty,
productMissingManufOrderQty.scale(),
product);
sumMissingManufOrderQty = sumMissingManufOrderQty.add(productMissingManufOrderQty);
}
}
return sumMissingManufOrderQty;
}
protected BigDecimal getMissingQtyOfStockMoveLine(StockMoveLine stockMoveLine) {
if (stockMoveLine.getProduct() != null) {
BigDecimal availableQty = stockMoveLine.getAvailableQty();
BigDecimal availableQtyForProduct = stockMoveLine.getAvailableQtyForProduct();
BigDecimal realQty = stockMoveLine.getRealQty();
if (availableQty.compareTo(realQty) >= 0 || !stockMoveLine.getProduct().getStockManaged()) {
return BigDecimal.ZERO;
} else if (availableQtyForProduct.compareTo(realQty) >= 0) {
return BigDecimal.ZERO;
} else if (availableQty.compareTo(realQty) < 0
&& availableQtyForProduct.compareTo(realQty) < 0) {
if (stockMoveLine.getProduct().getTrackingNumberConfiguration() != null) {
return availableQtyForProduct.subtract(realQty);
} else {
return availableQty.subtract(realQty);
}
}
}
return BigDecimal.ZERO;
}
}

View File

@ -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.production.service;
import com.axelor.apps.production.db.RawMaterialRequirement;
import com.axelor.exception.AxelorException;
public interface RawMaterialRequirementService {
/**
* Print the raw material requirement report.
*
* @param rawMaterialRequirement the user defined parameter of the report.
* @return URL to the printed report.
*/
String print(RawMaterialRequirement rawMaterialRequirement) throws AxelorException;
/**
* Fetch next value for the sequence linked to the given raw material requirement.
*
* @param rawMaterialRequirement the report needing a sequence.
* @return a string containing the value from the sequence.
* @throws AxelorException if there is no sequence found.
*/
String getSequence(RawMaterialRequirement rawMaterialRequirement) throws AxelorException;
}

View File

@ -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.production.service;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.production.db.RawMaterialRequirement;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
public class RawMaterialRequirementServiceImpl implements RawMaterialRequirementService {
/** The title of the report. */
public static final String RAW_MATERIAL_REPORT_TITLE = "Raw material requirement";
@Override
public String print(RawMaterialRequirement rawMaterialRequirement) throws AxelorException {
String name = String.format("%s - ${date}", I18n.get(RAW_MATERIAL_REPORT_TITLE));
ReportSettings reportSetting =
ReportFactory.createReport(IReport.RAW_MATERIAL_REQUIREMENT, name);
String locale = ReportSettings.getPrintingLocale(null);
return reportSetting
.addParam("RawMaterialRequirementId", rawMaterialRequirement.getId())
.addParam("Locale", locale)
.generate()
.getFileLink();
}
@Override
public String getSequence(RawMaterialRequirement rawMaterialRequirement) throws AxelorException {
String seq =
Beans.get(SequenceService.class)
.getSequenceNumber(
SequenceRepository.RAW_MATERIAL_REQUIREMENT, rawMaterialRequirement.getCompany());
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.RAW_MATERIAL_REQUIREMENT_NO_SEQUENCE),
rawMaterialRequirement.getCompany().getName());
}
return seq;
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.repo.PartnerRepository;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.productionorder.ProductionOrderSaleOrderService;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
import com.axelor.apps.sale.service.app.AppSaleService;
import com.axelor.apps.supplychain.service.AccountingSituationSupplychainService;
import com.axelor.apps.supplychain.service.SaleOrderPurchaseService;
import com.axelor.apps.supplychain.service.SaleOrderStockService;
import com.axelor.apps.supplychain.service.SaleOrderWorkflowServiceSupplychainImpl;
import com.axelor.apps.supplychain.service.app.AppSupplychainService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class SaleOrderWorkflowServiceProductionImpl
extends SaleOrderWorkflowServiceSupplychainImpl {
protected ProductionOrderSaleOrderService productionOrderSaleOrderService;
protected AppProductionService appProductionService;
@Inject
public SaleOrderWorkflowServiceProductionImpl(
SequenceService sequenceService,
PartnerRepository partnerRepo,
SaleOrderRepository saleOrderRepo,
AppSaleService appSaleService,
UserService userService,
SaleOrderStockService saleOrderStockService,
SaleOrderPurchaseService saleOrderPurchaseService,
AppSupplychainService appSupplychainService,
AccountingSituationSupplychainService accountingSituationSupplychainService,
ProductionOrderSaleOrderService productionOrderSaleOrderService,
AppProductionService appProductionService) {
super(
sequenceService,
partnerRepo,
saleOrderRepo,
appSaleService,
userService,
saleOrderStockService,
saleOrderPurchaseService,
appSupplychainService,
accountingSituationSupplychainService);
this.productionOrderSaleOrderService = productionOrderSaleOrderService;
this.appProductionService = appProductionService;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void confirmSaleOrder(SaleOrder saleOrder) throws AxelorException {
super.confirmSaleOrder(saleOrder);
if (appProductionService.getAppProduction().getProductionOrderGenerationAuto()) {
productionOrderSaleOrderService.generateProductionOrder(saleOrder);
}
}
}

View File

@ -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.production.service;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository;
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.PartnerProductQualityRatingService;
import com.axelor.apps.stock.service.StockMoveLineService;
import com.axelor.apps.stock.service.StockMoveToolService;
import com.axelor.apps.supplychain.service.ReservedQtyService;
import com.axelor.apps.supplychain.service.StockMoveServiceSupplychainImpl;
import com.axelor.apps.supplychain.service.app.AppSupplychainService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
public class StockMoveProductionServiceImpl extends StockMoveServiceSupplychainImpl {
@Inject
public StockMoveProductionServiceImpl(
StockMoveLineService stockMoveLineService,
StockMoveToolService stockMoveToolService,
StockMoveLineRepository stockMoveLineRepository,
AppBaseService appBaseService,
StockMoveRepository stockMoveRepository,
PartnerProductQualityRatingService partnerProductQualityRatingService,
AppSupplychainService appSupplyChainService,
PurchaseOrderRepository purchaseOrderRepo,
SaleOrderRepository saleOrderRepo,
UnitConversionService unitConversionService,
ReservedQtyService reservedQtyService,
ProductRepository productRepository) {
super(
stockMoveLineService,
stockMoveToolService,
stockMoveLineRepository,
appBaseService,
stockMoveRepository,
partnerProductQualityRatingService,
appSupplyChainService,
purchaseOrderRepo,
saleOrderRepo,
unitConversionService,
reservedQtyService,
productRepository);
}
@Override
public void checkExpirationDates(StockMove stockMove) throws AxelorException {
// if (stockMove.getInManufOrder() != null) {
// stockMoveLineService.checkExpirationDates(stockMove);
// } else {
super.checkExpirationDates(stockMove);
// }
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.production.service;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.apps.message.db.repo.TemplateRepository;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.apps.production.service.productionorder.ProductionOrderService;
import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository;
import com.axelor.apps.purchase.service.PurchaseOrderLineService;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockLocationLine;
import com.axelor.apps.stock.db.StockRules;
import com.axelor.apps.stock.db.repo.StockConfigRepository;
import com.axelor.apps.stock.db.repo.StockRulesRepository;
import com.axelor.apps.supplychain.service.StockRulesServiceSupplychainImpl;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class StockRulesServiceProductionImpl extends StockRulesServiceSupplychainImpl {
@Inject
public StockRulesServiceProductionImpl(
StockRulesRepository stockRuleRepo,
PurchaseOrderLineService purchaseOrderLineService,
PurchaseOrderRepository purchaseOrderRepo,
TemplateRepository templateRepo,
TemplateMessageService templateMessageService,
MessageRepository messageRepo,
StockConfigRepository stockConfigRepo) {
super(
stockRuleRepo,
purchaseOrderLineService,
purchaseOrderRepo,
templateRepo,
templateMessageService,
messageRepo,
stockConfigRepo);
}
public void generateOrder(
Product product, BigDecimal qty, StockLocationLine stockLocationLine, int type)
throws AxelorException {
StockLocation stockLocation = stockLocationLine.getStockLocation();
if (stockLocation == null) {
return;
}
StockRules stockRules =
this.getStockRules(
product, stockLocation, type, StockRulesRepository.USE_CASE_STOCK_CONTROL);
if (stockRules == null) {
return;
}
if (stockRules.getOrderAlertSelect() == StockRulesRepository.ORDER_ALERT_PRODUCTION_ORDER) {
this.generateProductionOrder(product, qty, stockLocationLine, type, stockRules);
} else {
this.generatePurchaseOrder(product, qty, stockLocationLine, type);
}
}
public void generateProductionOrder(
Product product,
BigDecimal qty,
StockLocationLine stockLocationLine,
int type,
StockRules stockRules)
throws AxelorException {
if (this.useMinStockRules(
stockLocationLine,
this.getStockRules(
product,
stockLocationLine.getStockLocation(),
type,
StockRulesRepository.USE_CASE_STOCK_CONTROL),
qty,
type)) {
BigDecimal qtyToProduce = this.getQtyToOrder(qty, stockLocationLine, type, stockRules);
Beans.get(ProductionOrderService.class)
.generateProductionOrder(product, null, qtyToProduce, LocalDateTime.now());
}
}
}

View File

@ -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.production.service.app;
import com.axelor.apps.base.db.AppProduction;
import com.axelor.apps.base.service.app.AppBaseService;
public interface AppProductionService extends AppBaseService {
public AppProduction getAppProduction();
public void generateProductionConfigurations();
public int getNbDecimalDigitForBomQty();
}

View File

@ -0,0 +1,66 @@
/*
* 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.production.service.app;
import com.axelor.apps.base.db.AppProduction;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.service.app.AppBaseServiceImpl;
import com.axelor.apps.production.db.ProductionConfig;
import com.axelor.apps.production.db.repo.ProductionConfigRepository;
import com.axelor.db.Query;
import com.axelor.inject.Beans;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.util.List;
@Singleton
public class AppProductionServiceImpl extends AppBaseServiceImpl implements AppProductionService {
public static final int DEFAULT_NB_DECIMAL_DIGITS = 2;
@Override
public AppProduction getAppProduction() {
return Query.of(AppProduction.class).cacheable().fetchOne();
}
@Override
@Transactional
public void generateProductionConfigurations() {
List<Company> companies =
Query.of(Company.class).filter("self.productionConfig is null").fetch();
for (Company company : companies) {
ProductionConfig productionConfig = new ProductionConfig();
productionConfig.setCompany(company);
Beans.get(ProductionConfigRepository.class).save(productionConfig);
}
}
@Override
public int getNbDecimalDigitForBomQty() {
AppProduction appProduction = getAppProduction();
if (appProduction != null) {
return appProduction.getNbDecimalDigitForBomQty();
}
return DEFAULT_NB_DECIMAL_DIGITS;
}
}

View File

@ -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.production.service.app;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ConfiguratorBOM;
import com.axelor.apps.production.service.configurator.ConfiguratorBomService;
import com.axelor.apps.sale.db.Configurator;
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
import com.axelor.apps.sale.service.configurator.ConfiguratorServiceImpl;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.axelor.rpc.JsonContext;
import com.google.inject.persist.Transactional;
public class ConfiguratorServiceProductionImpl extends ConfiguratorServiceImpl {
/**
* In this implementation, we also create a bill of material.
*
* @param configurator
* @param jsonAttributes
* @param jsonIndicators
* @throws AxelorException
*/
@Override
@Transactional(rollbackOn = {Exception.class})
public void generate(
Configurator configurator, JsonContext jsonAttributes, JsonContext jsonIndicators)
throws AxelorException {
super.generate(configurator, jsonAttributes, jsonIndicators);
ConfiguratorBOM configuratorBOM = configurator.getConfiguratorCreator().getConfiguratorBom();
if (configuratorBOM != null && checkConditions(configuratorBOM, jsonAttributes)) {
Product generatedProduct = configurator.getProduct();
BillOfMaterial generatedBom =
Beans.get(ConfiguratorBomService.class)
.generateBillOfMaterial(configuratorBOM, jsonAttributes, 0, generatedProduct);
generatedProduct.setDefaultBillOfMaterial(generatedBom);
}
}
protected boolean checkConditions(ConfiguratorBOM configuratorBOM, JsonContext jsonAttributes)
throws AxelorException {
String condition = configuratorBOM.getUseCondition();
// no condition = we always generate the bill of material
if (condition == null) {
return true;
}
return (boolean) Beans.get(ConfiguratorService.class).computeFormula(condition, jsonAttributes);
}
}

View File

@ -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.production.service.batch;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.service.administration.AbstractBatch;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.ProductionBatch;
import com.axelor.apps.production.db.repo.CostSheetRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.db.JPA;
import com.axelor.db.Query;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BatchComputeWorkInProgressValuation extends AbstractBatch {
protected CostSheetService costSheetService;
protected ManufOrderRepository manufOrderRepository;
protected static final int FETCH_LIMIT = 1;
@Inject
public BatchComputeWorkInProgressValuation(
CostSheetService costSheetService, ManufOrderRepository manufOrderRepository) {
this.costSheetService = costSheetService;
this.manufOrderRepository = manufOrderRepository;
}
@Override
protected void process() {
ProductionBatch productionBatch = batch.getProductionBatch();
Company company = productionBatch.getCompany();
StockLocation workshopStockLocation = productionBatch.getWorkshopStockLocation();
if (productionBatch.getValuationDate() == null) {
productionBatch.setValuationDate(Beans.get(AppBaseService.class).getTodayDate());
}
LocalDate valuationDate = productionBatch.getValuationDate();
List<ManufOrder> manufOrderList = null;
Map<String, Object> bindValues = new HashMap<String, Object>();
String domain =
"(self.statusSelect = :statusSelectInProgress or self.statusSelect = :statusSelectStandBy)";
bindValues.put("statusSelectInProgress", ManufOrderRepository.STATUS_IN_PROGRESS);
bindValues.put("statusSelectStandBy", ManufOrderRepository.STATUS_STANDBY);
if (company != null && workshopStockLocation == null) {
domain += " and self.company.id = :companyId";
bindValues.put("companyId", company.getId());
} else if (company == null && workshopStockLocation != null) {
domain += " and self.workshopStockLocation.id = :stockLocationId";
bindValues.put("stockLocationId", workshopStockLocation.getId());
} else if (company != null && workshopStockLocation != null) {
domain +=
" and (self.company.id = :companyId and self.workshopStockLocation.id = :stockLocationId)";
bindValues.put("companyId", company.getId());
bindValues.put("stockLocationId", workshopStockLocation.getId());
}
Query<ManufOrder> manufOrderQuery =
Beans.get(ManufOrderRepository.class).all().filter(domain).bind(bindValues);
int offset = 0;
while (!(manufOrderList = manufOrderQuery.order("id").fetch(FETCH_LIMIT, offset)).isEmpty()) {
for (ManufOrder manufOrder : manufOrderList) {
++offset;
try {
costSheetService.computeCostPrice(
manufOrderRepository.find(manufOrder.getId()),
CostSheetRepository.CALCULATION_WORK_IN_PROGRESS,
valuationDate);
incrementDone();
JPA.clear();
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(e, IExceptionMessage.MANUF_ORDER_NO_GENERATION, batch.getId());
}
}
}
}
@Override
protected void stop() {
String comment =
String.format(
"\t* %s " + I18n.get(IExceptionMessage.BATCH_COMPUTE_VALUATION) + "\n",
batch.getDone());
comment +=
String.format(
"\t" + I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.ALARM_ENGINE_BATCH_4),
batch.getAnomaly());
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.production.service.batch;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.administration.AbstractBatchService;
import com.axelor.apps.production.db.ProductionBatch;
import com.axelor.apps.production.db.repo.ProductionBatchRepository;
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;
public class ProductionBatchService extends AbstractBatchService {
@Override
protected Class<? extends Model> getModelClass() {
return ProductionBatch.class;
}
@Override
public Batch run(Model model) throws AxelorException {
Batch batch;
ProductionBatch productionBatch = (ProductionBatch) model;
switch (productionBatch.getActionSelect()) {
case ProductionBatchRepository.ACTION_COMPUTE_WORK_IN_PROGRESS_VALUATION:
batch = computeValuation(productionBatch);
break;
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BASE_BATCH_1),
productionBatch.getActionSelect(),
productionBatch.getCode());
}
return batch;
}
public Batch computeValuation(ProductionBatch productionBatch) {
return Beans.get(BatchComputeWorkInProgressValuation.class).run(productionBatch);
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.production.service.config;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.production.db.ProductionConfig;
import com.axelor.apps.production.db.WorkshopSequenceConfigLine;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
public class ProductionConfigService {
public ProductionConfig getProductionConfig(Company company) throws AxelorException {
ProductionConfig productionConfig = company.getProductionConfig();
if (productionConfig == null) {
throw new AxelorException(
company,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_1),
company.getName());
}
return productionConfig;
}
/**
* Find the configured sequence for a manufacturing order.
*
* <p>A company can have a different sequence per workshop. If no sequence is found per workshop,
* then return {@link ProductionConfig#manufOrderSequence}.
*
* @param productionConfig the config corresponding to the company.
* @param workshop the workshop of the manufacturing order.
* @return the found sequence.
* @throws AxelorException if no sequence is found for the given workshop, and if no default
* sequence is filled.
*/
public Sequence getManufOrderSequence(ProductionConfig productionConfig, StockLocation workshop)
throws AxelorException {
Sequence sequence = null;
if (productionConfig.getWorkshopSequenceConfigLineList() != null) {
sequence =
productionConfig
.getWorkshopSequenceConfigLineList()
.stream()
.filter(
workshopSequenceConfigLine ->
workshopSequenceConfigLine.getWorkshopStockLocation().equals(workshop))
.map(WorkshopSequenceConfigLine::getSequence)
.findFirst()
.orElseGet(productionConfig::getManufOrderSequence);
}
if (sequence == null) {
throw new AxelorException(
productionConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_MISSING_MANUF_ORDER_SEQ),
productionConfig.getCompany().getName());
}
return sequence;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.production.service.config;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.stock.db.StockConfig;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.service.config.StockConfigService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
public class StockConfigProductionService extends StockConfigService {
public StockLocation getProductionVirtualStockLocation(StockConfig stockConfig)
throws AxelorException {
if (stockConfig.getProductionVirtualStockLocation() == null) {
throw new AxelorException(
stockConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_2),
stockConfig.getCompany().getName());
}
return stockConfig.getProductionVirtualStockLocation();
}
public StockLocation getWasteStockLocation(StockConfig stockConfig) throws AxelorException {
if (stockConfig.getWasteStockLocation() == null) {
throw new AxelorException(
stockConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_3),
stockConfig.getCompany().getName());
}
return stockConfig.getWasteStockLocation();
}
public StockLocation getFinishedProductsDefaultStockLocation(StockConfig stockConfig)
throws AxelorException {
if (stockConfig.getFinishedProductsDefaultStockLocation() == null) {
throw new AxelorException(
stockConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_4),
stockConfig.getCompany().getName());
}
return stockConfig.getFinishedProductsDefaultStockLocation();
}
public StockLocation getComponentDefaultStockLocation(StockConfig stockConfig)
throws AxelorException {
if (stockConfig.getComponentDefaultStockLocation() == null) {
throw new AxelorException(
stockConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_CONFIG_5),
stockConfig.getCompany().getName());
}
return stockConfig.getComponentDefaultStockLocation();
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.production.service.configurator;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ConfiguratorBOM;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.JsonContext;
public interface ConfiguratorBomService {
/**
* Generate a bill of material from a configurator BOM and a JsonContext holding the custom values
*
* @param configuratorBOM
* @param attributes
* @param level
* @param generatedProduct
*/
BillOfMaterial generateBillOfMaterial(
ConfiguratorBOM configuratorBOM, JsonContext attributes, int level, Product generatedProduct)
throws AxelorException;
}

View File

@ -0,0 +1,153 @@
/*
* 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.production.service.configurator;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.UnitRepository;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ConfiguratorBOM;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.ConfiguratorBOMRepository;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.rpc.JsonContext;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
public class ConfiguratorBomServiceImpl implements ConfiguratorBomService {
private static final int MAX_LEVEL = 10;
protected ConfiguratorBOMRepository configuratorBOMRepo;
protected ConfiguratorService configuratorService;
protected BillOfMaterialRepository billOfMaterialRepository;
protected ConfiguratorProdProcessService confProdProcessService;
@Inject
ConfiguratorBomServiceImpl(
ConfiguratorBOMRepository configuratorBOMRepo,
ConfiguratorService configuratorService,
BillOfMaterialRepository billOfMaterialRepository,
ConfiguratorProdProcessService confProdProcessService) {
this.configuratorBOMRepo = configuratorBOMRepo;
this.configuratorService = configuratorService;
this.billOfMaterialRepository = billOfMaterialRepository;
this.confProdProcessService = confProdProcessService;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public BillOfMaterial generateBillOfMaterial(
ConfiguratorBOM configuratorBOM, JsonContext attributes, int level, Product generatedProduct)
throws AxelorException {
level++;
if (level > MAX_LEVEL) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CONFIGURATOR_BOM_TOO_MANY_CALLS));
}
String name;
Product product;
BigDecimal qty;
Unit unit;
ProdProcess prodProcess;
if (configuratorBOM.getDefNameAsFormula()) {
name =
(String) configuratorService.computeFormula(configuratorBOM.getNameFormula(), attributes);
} else {
name = configuratorBOM.getName();
}
if (configuratorBOM.getDefProductFromConfigurator()) {
product = generatedProduct;
} else if (configuratorBOM.getDefProductAsFormula()) {
product =
(Product)
configuratorService.computeFormula(configuratorBOM.getProductFormula(), attributes);
if (product != null) {
product = Beans.get(ProductRepository.class).find(product.getId());
}
} else {
product = configuratorBOM.getProduct();
}
if (configuratorBOM.getDefQtyAsFormula()) {
qty =
new BigDecimal(
configuratorService
.computeFormula(configuratorBOM.getQtyFormula(), attributes)
.toString());
} else {
qty = configuratorBOM.getQty();
}
if (configuratorBOM.getDefUnitAsFormula()) {
unit =
(Unit) configuratorService.computeFormula(configuratorBOM.getUnitFormula(), attributes);
if (unit != null) {
unit = Beans.get(UnitRepository.class).find(unit.getId());
}
} else {
unit = configuratorBOM.getUnit();
}
if (configuratorBOM.getDefProdProcessAsFormula()) {
prodProcess =
(ProdProcess)
configuratorService.computeFormula(
configuratorBOM.getProdProcessFormula(), attributes);
if (prodProcess != null) {
prodProcess = Beans.get(ProdProcessRepository.class).find(prodProcess.getId());
}
} else if (configuratorBOM.getDefProdProcessAsConfigurator()) {
prodProcess =
confProdProcessService.generateProdProcessService(
configuratorBOM.getConfiguratorProdProcess(), attributes, product);
} else {
prodProcess = configuratorBOM.getProdProcess();
}
BillOfMaterial billOfMaterial = new BillOfMaterial();
billOfMaterial.setCompany(configuratorBOM.getCompany());
billOfMaterial.setName(name);
billOfMaterial.setProduct(product);
billOfMaterial.setQty(qty);
billOfMaterial.setUnit(unit);
billOfMaterial.setProdProcess(prodProcess);
billOfMaterial.setStatusSelect(configuratorBOM.getStatusSelect());
if (configuratorBOM.getConfiguratorBomList() != null) {
for (ConfiguratorBOM confBomChild : configuratorBOM.getConfiguratorBomList()) {
BillOfMaterial childBom =
generateBillOfMaterial(confBomChild, attributes, level, generatedProduct);
billOfMaterial.addBillOfMaterialSetItem(childBom);
}
}
billOfMaterial = billOfMaterialRepository.save(billOfMaterial);
configuratorBOM.setBillOfMaterialId(billOfMaterial.getId());
configuratorBOMRepo.save(configuratorBOM);
return billOfMaterial;
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.production.service.configurator;
import com.axelor.apps.production.db.ConfiguratorBOM;
import com.axelor.apps.production.db.ConfiguratorProdProcess;
import com.axelor.apps.production.db.ConfiguratorProdProcessLine;
import com.axelor.apps.production.db.repo.ConfiguratorBOMRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.sale.db.ConfiguratorCreator;
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportServiceImpl;
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorService;
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.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ConfiguratorCreatorImportServiceProductionImpl
extends ConfiguratorCreatorImportServiceImpl {
private static int MAX_DEPTH = 50;
@Inject
public ConfiguratorCreatorImportServiceProductionImpl(
ConfiguratorCreatorService configuratorCreatorService) {
super(configuratorCreatorService);
}
/**
* Update the changed attribute in all formula O2M. This implementation also update formulas in
* configurator BOM and configurator prod process.
*
* @param creator
* @param oldName
* @param newName
* @throws AxelorException
*/
@Override
protected void updateAttributeNameInFormulas(
ConfiguratorCreator creator, String oldName, String newName) throws AxelorException {
super.updateAttributeNameInFormulas(creator, oldName, newName);
ConfiguratorBOM configuratorBom = creator.getConfiguratorBom();
if (configuratorBom != null) {
updateAttributeNameInFormulas(configuratorBom, oldName, newName, 0);
}
}
/**
* Update attribute name in formulas for a configurator bom.
*
* @param configuratorBom
* @param oldName
* @param newName
* @param counter used to count the recursive call.
* @throws AxelorException if we got too many recursive call.
*/
protected void updateAttributeNameInFormulas(
ConfiguratorBOM configuratorBom, String oldName, String newName, int counter)
throws AxelorException {
if (counter > MAX_DEPTH) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.CONFIGURATOR_BOM_IMPORT_TOO_MANY_CALLS));
}
updateAllFormulaFields(configuratorBom, oldName, newName);
ConfiguratorProdProcess configuratorProdProcess = configuratorBom.getConfiguratorProdProcess();
if (configuratorProdProcess != null) {
updateAttributeNameInFormulas(configuratorBom.getConfiguratorProdProcess(), oldName, newName);
}
// recursive call for child BOMs
List<ConfiguratorBOM> childConfiguratorBomList =
Beans.get(ConfiguratorBOMRepository.class)
.all()
.filter("self.parentConfiguratorBOM.id = :parentId")
.bind("parentId", configuratorBom.getId())
.fetch();
if (childConfiguratorBomList != null) {
for (ConfiguratorBOM childConfiguratorBom : childConfiguratorBomList) {
updateAttributeNameInFormulas(childConfiguratorBom, oldName, newName, counter + 1);
}
}
}
/**
* Update attribute name in formulas for a configurator prod process.
*
* @param configuratorBom
* @param oldName
* @param newName
* @throws AxelorException
*/
protected void updateAttributeNameInFormulas(
ConfiguratorProdProcess configuratorProdProcess, String oldName, String newName)
throws AxelorException {
List<ConfiguratorProdProcessLine> configuratorProdProcessLines =
configuratorProdProcess.getConfiguratorProdProcessLineList();
if (configuratorProdProcessLines == null) {
return;
}
for (ConfiguratorProdProcessLine configuratorProdProcessLine : configuratorProdProcessLines) {
updateAllFormulaFields(configuratorProdProcessLine, oldName, newName);
}
}
/**
* Replace oldName by newName in all string fields of the given object.
*
* @param obj
* @param oldName
* @param newName
* @throws AxelorException
*/
protected void updateAllFormulaFields(Object obj, String oldName, String newName)
throws AxelorException {
List<Field> formulaFields =
Arrays.stream(obj.getClass().getDeclaredFields())
.filter(field -> field.getType().equals(String.class))
.collect(Collectors.toList());
for (Field field : formulaFields) {
try {
// call getter of the string field
Object strFormula =
new PropertyDescriptor(field.getName(), obj.getClass()).getReadMethod().invoke(obj);
if (strFormula != null) {
new PropertyDescriptor(field.getName(), obj.getClass())
.getWriteMethod()
.invoke(obj, ((String) strFormula).replace(oldName, newName));
}
} catch (IntrospectionException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
// should not happen since we fetched fields from the class
throw new AxelorException(e, TraceBackRepository.CATEGORY_INCONSISTENCY);
}
}
}
}

View File

@ -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.production.service.configurator;
import com.axelor.apps.production.db.ConfiguratorProdProcessLine;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.JsonContext;
public interface ConfiguratorProdProcessLineService {
/**
* Generate a prod process line from a configurator prod process line and a JsonContext holding
* the custom values
*
* @param confProdProcessLine
* @param attributes
* @return
*/
ProdProcessLine generateProdProcessLine(
ConfiguratorProdProcessLine confProdProcessLine, JsonContext attributes)
throws AxelorException;
}

View File

@ -0,0 +1,90 @@
/*
* 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.production.service.configurator;
import com.axelor.apps.production.db.ConfiguratorProdProcessLine;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.JsonContext;
import com.google.inject.Inject;
import java.math.BigDecimal;
public class ConfiguratorProdProcessLineServiceImpl implements ConfiguratorProdProcessLineService {
protected ConfiguratorService configuratorService;
@Inject
ConfiguratorProdProcessLineServiceImpl(ConfiguratorService configuratorService) {
this.configuratorService = configuratorService;
}
@Override
public ProdProcessLine generateProdProcessLine(
ConfiguratorProdProcessLine confProdProcessLine, JsonContext attributes)
throws AxelorException {
if (confProdProcessLine == null) {
return null;
}
ProdProcessLine prodProcessLine = new ProdProcessLine();
BigDecimal minCapacityPerCycle;
BigDecimal maxCapacityPerCycle;
long durationPerCycle;
if (confProdProcessLine.getDefMinCapacityFormula()) {
minCapacityPerCycle =
new BigDecimal(
configuratorService
.computeFormula(confProdProcessLine.getMinCapacityPerCycleFormula(), attributes)
.toString());
} else {
minCapacityPerCycle = confProdProcessLine.getMinCapacityPerCycle();
}
if (confProdProcessLine.getDefMaxCapacityFormula()) {
maxCapacityPerCycle =
new BigDecimal(
configuratorService
.computeFormula(confProdProcessLine.getMaxCapacityPerCycleFormula(), attributes)
.toString());
} else {
maxCapacityPerCycle = confProdProcessLine.getMaxCapacityPerCycle();
}
if (confProdProcessLine.getDefDurationFormula()) {
durationPerCycle =
Long.decode(
configuratorService
.computeFormula(confProdProcessLine.getDurationPerCycleFormula(), attributes)
.toString());
} else {
durationPerCycle = confProdProcessLine.getDurationPerCycle();
}
prodProcessLine.setName(confProdProcessLine.getName());
prodProcessLine.setPriority(confProdProcessLine.getPriority());
prodProcessLine.setWorkCenter(confProdProcessLine.getWorkCenter());
prodProcessLine.setOutsourcing(confProdProcessLine.getOutsourcing());
prodProcessLine.setStockLocation(confProdProcessLine.getStockLocation());
prodProcessLine.setDescription(confProdProcessLine.getDescription());
prodProcessLine.setMinCapacityPerCycle(minCapacityPerCycle);
prodProcessLine.setMaxCapacityPerCycle(maxCapacityPerCycle);
prodProcessLine.setDurationPerCycle(durationPerCycle);
return prodProcessLine;
}
}

View File

@ -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.apps.production.service.configurator;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.ConfiguratorProdProcess;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.JsonContext;
public interface ConfiguratorProdProcessService {
/**
* Generate a prod process from a configurator prod process and a JsonContext holding the custom
* values
*
* @param confProdProcess
* @param attributes
* @param product the generated product in configurator BOM.
* @return
*/
ProdProcess generateProdProcessService(
ConfiguratorProdProcess confProdProcess, JsonContext attributes, Product product)
throws AxelorException;
}

View File

@ -0,0 +1,131 @@
/*
* 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.production.service.configurator;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.ConfiguratorProdProcess;
import com.axelor.apps.production.db.ConfiguratorProdProcessLine;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.sale.service.configurator.ConfiguratorService;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.JsonContext;
import com.google.inject.Inject;
import java.util.List;
public class ConfiguratorProdProcessServiceImpl implements ConfiguratorProdProcessService {
protected ConfiguratorProdProcessLineService confProdProcessLineService;
protected ConfiguratorService configuratorService;
protected ProdProcessRepository prodProcessRepository;
@Inject
ConfiguratorProdProcessServiceImpl(
ConfiguratorProdProcessLineService confProdProcessLineService,
ConfiguratorService configuratorService,
ProdProcessRepository prodProcessRepository) {
this.confProdProcessLineService = confProdProcessLineService;
this.configuratorService = configuratorService;
this.prodProcessRepository = prodProcessRepository;
}
@Override
public ProdProcess generateProdProcessService(
ConfiguratorProdProcess confProdProcess, JsonContext attributes, Product product)
throws AxelorException {
if (confProdProcess == null) {
return null;
}
String code;
StockLocation stockLocation;
StockLocation producedProductStockLocation;
StockLocation workshopStockLocation;
if (confProdProcess.getDefCodeAsFormula()) {
code =
String.valueOf(
configuratorService.computeFormula(confProdProcess.getCodeFormula(), attributes));
} else {
code = confProdProcess.getCode();
}
if (confProdProcess.getDefStockLocationAsFormula()) {
stockLocation =
(StockLocation)
configuratorService.computeFormula(
confProdProcess.getStockLocationFormula(), attributes);
} else {
stockLocation = confProdProcess.getStockLocation();
}
if (confProdProcess.getDefProducedProductStockLocationAsFormula()) {
producedProductStockLocation =
(StockLocation)
configuratorService.computeFormula(
confProdProcess.getProducedProductStockLocationFormula(), attributes);
} else {
producedProductStockLocation = confProdProcess.getProducedProductStockLocation();
}
if (confProdProcess.getDefWorkshopStockLocationAsFormula()) {
workshopStockLocation =
(StockLocation)
configuratorService.computeFormula(
confProdProcess.getWorkshopStockLocationFormula(), attributes);
} else {
workshopStockLocation = confProdProcess.getWorkshopStockLocation();
}
ProdProcess prodProcess =
createProdProcessHeader(
confProdProcess,
code,
stockLocation,
producedProductStockLocation,
workshopStockLocation,
product);
List<ConfiguratorProdProcessLine> confLines =
confProdProcess.getConfiguratorProdProcessLineList();
if (confLines != null) {
for (ConfiguratorProdProcessLine confLine : confLines) {
prodProcess.addProdProcessLineListItem(
confProdProcessLineService.generateProdProcessLine(confLine, attributes));
}
}
return prodProcess;
}
/** Instantiate a new prod process and set the right attributes. */
protected ProdProcess createProdProcessHeader(
ConfiguratorProdProcess confProdProcess,
String code,
StockLocation stockLocation,
StockLocation producedProductStockLocation,
StockLocation workshopStockLocation,
Product product) {
ProdProcess prodProcess = new ProdProcess();
prodProcess.setName(confProdProcess.getName());
prodProcess.setCompany(confProdProcess.getCompany());
prodProcess.setStatusSelect(confProdProcess.getStatusSelect());
prodProcess.setCode(code);
prodProcess.setStockLocation(stockLocation);
prodProcess.setProducedProductStockLocation(producedProductStockLocation);
prodProcess.setWorkshopStockLocation(workshopStockLocation);
prodProcess.setProduct(product);
return prodProcessRepository.save(prodProcess);
}
}

View File

@ -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.apps.production.service.costsheet;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.production.db.CostSheetGroup;
import com.axelor.apps.production.db.CostSheetLine;
import com.axelor.apps.production.db.ProdHumanResource;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.exception.AxelorException;
import java.math.BigDecimal;
public interface CostSheetLineService {
public CostSheetLine createCostSheetLine(
String name,
String code,
int bomLevel,
BigDecimal consumptionQty,
BigDecimal costPrice,
CostSheetGroup costSheetGroup,
Product product,
int typeSelect,
int typeSelectIcon,
Unit unit,
WorkCenter workCenter,
CostSheetLine parentCostSheetLine);
public CostSheetLine createProducedProductCostSheetLine(
Product product, Unit unit, BigDecimal consumptionQty);
public CostSheetLine createResidualProductCostSheetLine(
Product product, Unit unit, BigDecimal consumptionQty) throws AxelorException;
public CostSheetLine createConsumedProductCostSheetLine(
Company company,
Product product,
Unit unit,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException;
public CostSheetLine createConsumedProductWasteCostSheetLine(
Company company,
Product product,
Unit unit,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal wasteRate,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException;
public CostSheetLine createWorkCenterHRCostSheetLine(
WorkCenter workCenter,
ProdHumanResource prodHumanResource,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal costPrice,
Unit unit);
public CostSheetLine createWorkCenterMachineCostSheetLine(
WorkCenter workCenter,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal costPrice,
Unit unit);
}

View File

@ -0,0 +1,568 @@
/*
* 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.production.service.costsheet;
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.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.db.repo.UnitRepository;
import com.axelor.apps.base.service.CurrencyService;
import com.axelor.apps.base.service.ShippingCoefService;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.CostSheetGroup;
import com.axelor.apps.production.db.CostSheetLine;
import com.axelor.apps.production.db.ProdHumanResource;
import com.axelor.apps.production.db.UnitCostCalcLine;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.db.repo.CostSheetGroupRepository;
import com.axelor.apps.production.db.repo.CostSheetLineRepository;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.purchase.db.SupplierCatalog;
import com.axelor.apps.stock.service.WeightedAveragePriceService;
import com.axelor.exception.AxelorException;
import com.google.common.collect.Lists;
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.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CostSheetLineServiceImpl implements CostSheetLineService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected AppProductionService appProductionService;
protected CostSheetGroupRepository costSheetGroupRepository;
protected UnitConversionService unitConversionService;
protected UnitRepository unitRepo;
protected WeightedAveragePriceService weightedAveragePriceService;
protected UnitCostCalcLineServiceImpl unitCostCalcLineServiceImpl;
protected CurrencyService currencyService;
protected ShippingCoefService shippingCoefService;
@Inject
public CostSheetLineServiceImpl(
AppProductionService appProductionService,
CostSheetGroupRepository costSheetGroupRepository,
UnitConversionService unitConversionService,
UnitRepository unitRepo,
WeightedAveragePriceService weightedAveragePriceService,
UnitCostCalcLineServiceImpl unitCostCalcLineServiceImpl,
CurrencyService currencyService,
ShippingCoefService shippingCoefService) {
this.appProductionService = appProductionService;
this.costSheetGroupRepository = costSheetGroupRepository;
this.unitConversionService = unitConversionService;
this.unitRepo = unitRepo;
this.weightedAveragePriceService = weightedAveragePriceService;
this.unitCostCalcLineServiceImpl = unitCostCalcLineServiceImpl;
this.currencyService = currencyService;
this.shippingCoefService = shippingCoefService;
}
public CostSheetLine createCostSheetLine(
String name,
String code,
int bomLevel,
BigDecimal consumptionQty,
BigDecimal costPrice,
CostSheetGroup costSheetGroup,
Product product,
int typeSelect,
int typeSelectIcon,
Unit unit,
WorkCenter workCenter,
CostSheetLine parentCostSheetLine) {
logger.debug(
"Add a new line of cost sheet ({} - {} - BOM level {} - cost price : {})",
code,
name,
bomLevel,
costPrice);
CostSheetLine costSheetLine = new CostSheetLine(code, name);
costSheetLine.setBomLevel(bomLevel);
costSheetLine.setConsumptionQty(consumptionQty);
costSheetLine.setCostSheetGroup(costSheetGroup);
costSheetLine.setProduct(product);
costSheetLine.setTypeSelect(typeSelect);
costSheetLine.setTypeSelectIcon(typeSelectIcon);
if (unit != null) {
costSheetLine.setUnit(unitRepo.find(unit.getId()));
}
costSheetLine.setWorkCenter(workCenter);
if (costPrice == null) {
costPrice = BigDecimal.ZERO;
}
costSheetLine.setCostPrice(
costPrice.setScale(
appProductionService.getNbDecimalDigitForUnitPrice(), BigDecimal.ROUND_HALF_EVEN));
if (parentCostSheetLine != null) {
parentCostSheetLine.addCostSheetLineListItem(costSheetLine);
this.createIndirectCostSheetGroups(
costSheetGroup, parentCostSheetLine, costSheetLine.getCostPrice());
}
return costSheetLine;
}
public CostSheetLine createProducedProductCostSheetLine(
Product product, Unit unit, BigDecimal consumptionQty) {
return this.createCostSheetLine(
product.getName(),
product.getCode(),
0,
consumptionQty,
null,
product.getCostSheetGroup(),
product,
CostSheetLineRepository.TYPE_PRODUCED_PRODUCT,
CostSheetLineRepository.TYPE_PRODUCED_PRODUCT,
unit,
null,
null);
}
public CostSheetLine createResidualProductCostSheetLine(
Product product, Unit unit, BigDecimal consumptionQty) throws AxelorException {
if (appProductionService.getAppProduction().getSubtractProdResidualOnCostSheet()) {
consumptionQty = consumptionQty.negate();
}
BigDecimal costPrice =
unitConversionService
.convert(
unit,
product.getUnit(),
product.getCostPrice(),
appProductionService.getNbDecimalDigitForUnitPrice(),
product)
.multiply(consumptionQty);
return this.createCostSheetLine(
product.getName(),
product.getCode(),
0,
consumptionQty,
costPrice,
product.getCostSheetGroup(),
product,
CostSheetLineRepository.TYPE_PRODUCED_PRODUCT,
CostSheetLineRepository.TYPE_PRODUCED_PRODUCT,
unit,
null,
null);
}
public CostSheetLine createConsumedProductCostSheetLine(
Company company,
Product product,
Unit unit,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException {
Product parentProduct = parentCostSheetLine.getProduct();
BigDecimal costPrice = null;
switch (origin) {
case CostSheetService.ORIGIN_MANUF_ORDER:
costPrice =
this.getComponentCostPrice(
product, parentProduct.getManufOrderCompValuMethodSelect(), company);
break;
case CostSheetService.ORIGIN_BULK_UNIT_COST_CALCULATION:
BillOfMaterial componentDefaultBillOfMaterial = product.getDefaultBillOfMaterial();
if (componentDefaultBillOfMaterial != null) {
UnitCostCalcLine unitCostCalcLine =
unitCostCalcLineServiceImpl.getUnitCostCalcLine(unitCostCalculation, product);
if (unitCostCalcLine != null) {
costPrice = unitCostCalcLine.getComputedCost();
break;
}
}
// If we didn't have a computed price in cost calculation session, so we compute the price
// from its bill of material
case CostSheetService.ORIGIN_BILL_OF_MATERIAL:
costPrice =
this.getComponentCostPrice(
product, parentProduct.getBomCompValuMethodSelect(), company);
break;
default:
costPrice = BigDecimal.ZERO;
}
costPrice = costPrice.multiply(consumptionQty);
costPrice =
unitConversionService.convert(
unit,
product.getUnit(),
costPrice,
appProductionService.getNbDecimalDigitForUnitPrice(),
product);
List<CostSheetLine> costSheetLineList =
parentCostSheetLine.getCostSheetLineList() != null
? parentCostSheetLine.getCostSheetLineList()
: new ArrayList<CostSheetLine>();
for (CostSheetLine costSheetLine : costSheetLineList) {
if (product.equals(costSheetLine.getProduct()) && unit.equals(costSheetLine.getUnit())) {
BigDecimal qty = costSheetLine.getConsumptionQty().add(consumptionQty);
costSheetLine.setConsumptionQty(qty);
costSheetLine.setCostPrice(
costPrice
.add(costSheetLine.getCostPrice())
.setScale(
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN));
return costSheetLine;
}
}
return this.createCostSheetLine(
product.getName(),
product.getCode(),
bomLevel,
consumptionQty,
costPrice,
product.getCostSheetGroup(),
product,
CostSheetLineRepository.TYPE_CONSUMED_PRODUCT,
CostSheetLineRepository.TYPE_CONSUMED_PRODUCT,
unit,
null,
parentCostSheetLine);
}
protected BigDecimal getComponentCostPrice(
Product product, int componentsValuationMethod, Company company) throws AxelorException {
BigDecimal price = null;
Currency companyCurrency = company.getCurrency();
if (componentsValuationMethod == ProductRepository.COMPONENTS_VALUATION_METHOD_AVERAGE) {
price = weightedAveragePriceService.computeAvgPriceForCompany(product, company);
if (price == null || price.compareTo(BigDecimal.ZERO) == 0) {
price = product.getCostPrice();
}
} else if (componentsValuationMethod == ProductRepository.COMPONENTS_VALUATION_METHOD_COST) {
price = product.getCostPrice();
if (price == null || price.compareTo(BigDecimal.ZERO) == 0) {
price = weightedAveragePriceService.computeAvgPriceForCompany(product, company);
}
}
if (price == null || price.compareTo(BigDecimal.ZERO) == 0) {
price = product.getPurchasePrice();
BigDecimal shippingCoef =
shippingCoefService.getShippingCoef(
product, product.getDefaultSupplierPartner(), company, new BigDecimal(9999999));
price = product.getPurchasePrice().multiply(shippingCoef);
price =
currencyService.getAmountCurrencyConvertedAtDate(
product.getPurchaseCurrency(),
companyCurrency,
price,
appProductionService.getTodayDate());
if (price == null || price.compareTo(BigDecimal.ZERO) == 0) {
for (SupplierCatalog supplierCatalog : product.getSupplierCatalogList()) {
if (BigDecimal.ZERO.compareTo(supplierCatalog.getPrice()) < 0) {
price = supplierCatalog.getPrice();
Partner supplierPartner = supplierCatalog.getSupplierPartner();
if (supplierPartner != null) {
shippingCoef =
shippingCoefService.getShippingCoef(
product, supplierPartner, company, new BigDecimal(9999999));
price = price.multiply(shippingCoef);
price =
currencyService.getAmountCurrencyConvertedAtDate(
supplierPartner.getCurrency(),
companyCurrency,
price,
appProductionService.getTodayDate());
}
break;
}
}
}
}
return price;
}
public CostSheetLine createConsumedProductWasteCostSheetLine(
Company company,
Product product,
Unit unit,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal wasteRate,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException {
Product parentProduct = parentCostSheetLine.getProduct();
BigDecimal qty =
consumptionQty
.multiply(wasteRate)
.divide(
new BigDecimal("100"),
appProductionService.getNbDecimalDigitForBomQty(),
BigDecimal.ROUND_HALF_EVEN);
BigDecimal costPrice = null;
switch (origin) {
case CostSheetService.ORIGIN_BULK_UNIT_COST_CALCULATION:
BillOfMaterial componentDefaultBillOfMaterial = product.getDefaultBillOfMaterial();
if (componentDefaultBillOfMaterial != null) {
UnitCostCalcLine unitCostCalcLine =
unitCostCalcLineServiceImpl.getUnitCostCalcLine(unitCostCalculation, product);
if (unitCostCalcLine != null) {
costPrice = unitCostCalcLine.getComputedCost();
break;
}
}
case CostSheetService.ORIGIN_BILL_OF_MATERIAL:
costPrice =
this.getComponentCostPrice(
product, parentProduct.getBomCompValuMethodSelect(), company);
break;
default:
costPrice = BigDecimal.ZERO;
}
costPrice =
unitConversionService
.convert(
unit,
product.getUnit(),
costPrice,
appProductionService.getNbDecimalDigitForUnitPrice(),
product)
.multiply(qty);
return this.createCostSheetLine(
product.getName(),
product.getCode(),
bomLevel,
qty.setScale(appProductionService.getNbDecimalDigitForBomQty(), RoundingMode.HALF_EVEN),
costPrice.setScale(
appProductionService.getNbDecimalDigitForUnitPrice(), RoundingMode.HALF_EVEN),
product.getCostSheetGroup(),
product,
CostSheetLineRepository.TYPE_CONSUMED_PRODUCT_WASTE,
CostSheetLineRepository.TYPE_CONSUMED_PRODUCT_WASTE,
unit,
null,
parentCostSheetLine);
}
public CostSheetLine createWorkCenterHRCostSheetLine(
WorkCenter workCenter,
ProdHumanResource prodHumanResource,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal costPrice,
Unit unit) {
return this.createWorkCenterCostSheetLine(
workCenter,
priority,
bomLevel,
parentCostSheetLine,
consumptionQty,
costPrice,
unit,
null,
CostSheetLineRepository.TYPE_HUMAN);
}
public CostSheetLine createWorkCenterMachineCostSheetLine(
WorkCenter workCenter,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal costPrice,
Unit unit) {
return this.createWorkCenterCostSheetLine(
workCenter,
priority,
bomLevel,
parentCostSheetLine,
consumptionQty,
costPrice,
unit,
workCenter.getCostSheetGroup(),
CostSheetLineRepository.TYPE_WORK_CENTER);
}
protected CostSheetLine createWorkCenterCostSheetLine(
WorkCenter workCenter,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
BigDecimal consumptionQty,
BigDecimal costPrice,
Unit unit,
CostSheetGroup costSheetGroup,
int typeSelectIcon) {
return this.createCostSheetLine(
workCenter.getName(),
priority + " - " + workCenter.getCode(),
bomLevel,
consumptionQty,
costPrice,
costSheetGroup,
null,
CostSheetLineRepository.TYPE_WORK_CENTER,
typeSelectIcon,
unit,
workCenter,
parentCostSheetLine);
}
protected List<CostSheetGroup> getIndirectCostSheetGroups(CostSheetGroup costSheetGroup) {
if (costSheetGroup == null) {
return Lists.newArrayList();
}
return costSheetGroupRepository
.all()
.filter(
"?1 member of self.costSheetGroupSet AND self.costTypeSelect = ?2",
costSheetGroup,
CostSheetGroupRepository.COST_TYPE_INDIRECT)
.fetch();
}
protected void createIndirectCostSheetGroups(
CostSheetGroup costSheetGroup, CostSheetLine parentCostSheetLine, BigDecimal costPrice) {
if (costSheetGroup == null) {
return;
}
for (CostSheetGroup indirectCostSheetGroup : this.getIndirectCostSheetGroups(costSheetGroup)) {
this.createIndirectCostSheetLine(parentCostSheetLine, indirectCostSheetGroup, costPrice);
}
}
protected CostSheetLine createIndirectCostSheetLine(
CostSheetLine parentCostSheetLine, CostSheetGroup costSheetGroup, BigDecimal costPrice) {
CostSheetLine indirectCostSheetLine =
this.getCostSheetLine(costSheetGroup, parentCostSheetLine);
if (indirectCostSheetLine == null) {
indirectCostSheetLine =
this.createCostSheetLine(
costSheetGroup.getCode(),
costSheetGroup.getName(),
parentCostSheetLine.getBomLevel() + 1,
BigDecimal.ONE,
null,
costSheetGroup,
null,
CostSheetLineRepository.TYPE_INDIRECT_COST,
CostSheetLineRepository.TYPE_INDIRECT_COST,
null,
null,
parentCostSheetLine);
parentCostSheetLine.addCostSheetLineListItem(indirectCostSheetLine);
}
indirectCostSheetLine.setCostPrice(
indirectCostSheetLine
.getCostPrice()
.add(this.getIndirectCostPrice(costSheetGroup, costPrice)));
return indirectCostSheetLine;
}
protected BigDecimal getIndirectCostPrice(CostSheetGroup costSheetGroup, BigDecimal costPrice) {
BigDecimal indirectCostPrice = BigDecimal.ZERO;
indirectCostPrice =
costPrice
.multiply(costSheetGroup.getRate())
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_EVEN);
if (costSheetGroup.getRateTypeSelect() == CostSheetGroupRepository.COST_TYPE_SURCHARGE) {
indirectCostPrice = indirectCostPrice.add(costPrice);
}
return indirectCostPrice;
}
protected CostSheetLine getCostSheetLine(
CostSheetGroup indirectCostSheetGroup, CostSheetLine parentCostSheetLine) {
for (CostSheetLine costSheetLine : parentCostSheetLine.getCostSheetLineList()) {
CostSheetGroup costSheetGroup = costSheetLine.getCostSheetGroup();
if (costSheetGroup != null
&& costSheetGroup.getCostTypeSelect() == CostSheetGroupRepository.COST_TYPE_INDIRECT
&& costSheetGroup.equals(indirectCostSheetGroup)) {
return costSheetLine;
}
}
return null;
}
}

View File

@ -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.production.service.costsheet;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.time.LocalDate;
public interface CostSheetService {
public static final int ORIGIN_BILL_OF_MATERIAL = 0;
public static final int ORIGIN_MANUF_ORDER = 1;
public static final int ORIGIN_BULK_UNIT_COST_CALCULATION = 2;
/**
* @param billOfMaterial
* @param origin 0 : ORIGIN_BILL_OF_MATERIAL 1 : ORIGIN_MANUF_ORDER 2 :
* ORIGIN_BULK_UNIT_COST_CALCULATION
* @param unitCostCalculation Required if origin = ORIGIN_BULK_UNIT_COST_CALCULATION
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public CostSheet computeCostPrice(
BillOfMaterial billOfMaterial, int origin, UnitCostCalculation unitCostCalculation)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public CostSheet computeCostPrice(
ManufOrder manufOrder, int calculationTypeSelect, LocalDate calculationDate)
throws AxelorException;
}

View File

@ -0,0 +1,966 @@
/*
* 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.production.service.costsheet;
import com.axelor.apps.base.db.AppProduction;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.CostSheetLine;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdHumanResource;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.ProdResidualProduct;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.CostSheetRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.WorkCenterRepository;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.tool.date.DurationTool;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CostSheetServiceImpl implements CostSheetService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final int QTY_MAX_SCALE = 10;
protected UnitConversionService unitConversionService;
protected CostSheetLineService costSheetLineService;
protected BillOfMaterialRepository billOfMaterialRepo;
protected AppProductionService appProductionService;
protected Unit hourUnit;
protected Unit cycleUnit;
protected boolean manageResidualProductOnBom;
protected CostSheet costSheet;
@Inject
public CostSheetServiceImpl(
AppProductionService appProductionService,
UnitConversionService unitConversionService,
CostSheetLineService costSheetLineService,
BillOfMaterialRepository billOfMaterialRepo) {
this.appProductionService = appProductionService;
this.unitConversionService = unitConversionService;
this.costSheetLineService = costSheetLineService;
this.billOfMaterialRepo = billOfMaterialRepo;
}
protected void init() {
AppProduction appProduction = appProductionService.getAppProduction();
this.hourUnit = appProductionService.getAppBase().getUnitHours();
this.cycleUnit = appProduction.getCycleUnit();
this.manageResidualProductOnBom = appProduction.getManageResidualProductOnBom();
costSheet = new CostSheet();
}
@Override
@Transactional(rollbackOn = {Exception.class})
public CostSheet computeCostPrice(
BillOfMaterial billOfMaterial, int origin, UnitCostCalculation unitCostCalculation)
throws AxelorException {
this.init();
billOfMaterial.addCostSheetListItem(costSheet);
CostSheetLine producedCostSheetLine =
costSheetLineService.createProducedProductCostSheetLine(
billOfMaterial.getProduct(), billOfMaterial.getUnit(), billOfMaterial.getQty());
costSheet.addCostSheetLineListItem(producedCostSheetLine);
costSheet.setCalculationTypeSelect(CostSheetRepository.CALCULATION_BILL_OF_MATERIAL);
costSheet.setCalculationDate(Beans.get(AppBaseService.class).getTodayDate());
Company company = billOfMaterial.getCompany();
if (company != null && company.getCurrency() != null) {
costSheet.setCurrency(company.getCurrency());
}
this._computeCostPrice(
billOfMaterial.getCompany(),
billOfMaterial,
0,
producedCostSheetLine,
origin,
unitCostCalculation);
this.computeResidualProduct(billOfMaterial);
billOfMaterial.setCostPrice(this.computeCostPrice(costSheet));
billOfMaterialRepo.save(billOfMaterial);
return costSheet;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public CostSheet computeCostPrice(
ManufOrder manufOrder, int calculationTypeSelect, LocalDate calculationDate)
throws AxelorException {
this.init();
List<CostSheet> costSheetList = manufOrder.getCostSheetList();
LocalDate previousCostSheetDate = null;
for (CostSheet costSheet : costSheetList) {
if ((costSheet.getCalculationTypeSelect() == CostSheetRepository.CALCULATION_END_OF_PRODUCTION
|| costSheet.getCalculationTypeSelect()
== CostSheetRepository.CALCULATION_PARTIAL_END_OF_PRODUCTION)
&& costSheet.getCalculationDate() != null) {
if (previousCostSheetDate == null) {
previousCostSheetDate = costSheet.getCalculationDate();
} else if (costSheet.getCalculationDate().isAfter(previousCostSheetDate)) {
previousCostSheetDate = costSheet.getCalculationDate();
}
}
}
manufOrder.addCostSheetListItem(costSheet);
costSheet.setCalculationTypeSelect(calculationTypeSelect);
costSheet.setCalculationDate(
calculationDate != null ? calculationDate : Beans.get(AppBaseService.class).getTodayDate());
BigDecimal producedQty =
computeTotalProducedQty(
manufOrder.getProduct(),
manufOrder.getProducedStockMoveLineList(),
costSheet.getCalculationDate(),
previousCostSheetDate,
costSheet.getCalculationTypeSelect());
CostSheetLine producedCostSheetLine =
costSheetLineService.createProducedProductCostSheetLine(
manufOrder.getProduct(), manufOrder.getUnit(), producedQty);
costSheet.addCostSheetLineListItem(producedCostSheetLine);
Company company = manufOrder.getCompany();
if (company != null && company.getCurrency() != null) {
costSheet.setCurrency(company.getCurrency());
}
BigDecimal totalToProduceQty = getTotalToProduceQty(manufOrder);
BigDecimal ratio = BigDecimal.ZERO;
if (totalToProduceQty.compareTo(BigDecimal.ZERO) != 0) {
ratio = producedQty.divide(totalToProduceQty, 5, RoundingMode.HALF_UP);
}
costSheet.setManufOrderProducedRatio(ratio);
this.computeRealCostPrice(manufOrder, 0, producedCostSheetLine, previousCostSheetDate);
this.computeRealResidualProduct(manufOrder);
this.computeCostPrice(costSheet);
manufOrder.setCostPrice(costSheet.getCostPrice());
Beans.get(ManufOrderRepository.class).save(manufOrder);
return costSheet;
}
protected void computeResidualProduct(BillOfMaterial billOfMaterial) throws AxelorException {
if (this.manageResidualProductOnBom && billOfMaterial.getProdResidualProductList() != null) {
for (ProdResidualProduct prodResidualProduct : billOfMaterial.getProdResidualProductList()) {
CostSheetLine costSheetLine =
costSheetLineService.createResidualProductCostSheetLine(
prodResidualProduct.getProduct(),
prodResidualProduct.getUnit(),
prodResidualProduct.getQty());
costSheet.addCostSheetLineListItem(costSheetLine);
}
}
}
protected BigDecimal computeCostPrice(CostSheet costSheet) {
BigDecimal costPrice = BigDecimal.ZERO;
if (costSheet.getCostSheetLineList() != null) {
for (CostSheetLine costSheetLine : costSheet.getCostSheetLineList()) {
if (costSheetLine.getCostSheetLineList() != null
&& !costSheetLine.getCostSheetLineList().isEmpty()) {
costPrice = costPrice.add(this.computeCostPrice(costSheetLine));
} else {
costPrice = costPrice.add(costSheetLine.getCostPrice());
}
}
}
costSheet.setCostPrice(costPrice);
return costPrice;
}
protected BigDecimal computeCostPrice(CostSheetLine parentCostSheetLine) {
BigDecimal costPrice = BigDecimal.ZERO;
if (parentCostSheetLine.getCostSheetLineList() != null) {
for (CostSheetLine costSheetLine : parentCostSheetLine.getCostSheetLineList()) {
if (costSheetLine.getCostSheetLineList() != null
&& !costSheetLine.getCostSheetLineList().isEmpty()) {
costPrice = costPrice.add(this.computeCostPrice(costSheetLine));
} else {
costPrice = costPrice.add(costSheetLine.getCostPrice());
}
}
}
parentCostSheetLine.setCostPrice(costPrice);
return costPrice;
}
protected void _computeCostPrice(
Company company,
BillOfMaterial billOfMaterial,
int bomLevel,
CostSheetLine parentCostSheetLine,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException {
bomLevel++;
// Cout des composants
this._computeToConsumeProduct(
company, billOfMaterial, bomLevel, parentCostSheetLine, origin, unitCostCalculation);
// Cout des operations
this._computeProcess(
billOfMaterial.getProdProcess(),
billOfMaterial.getQty(),
billOfMaterial.getProduct().getUnit(),
bomLevel,
parentCostSheetLine);
}
protected void _computeToConsumeProduct(
Company company,
BillOfMaterial billOfMaterial,
int bomLevel,
CostSheetLine parentCostSheetLine,
int origin,
UnitCostCalculation unitCostCalculation)
throws AxelorException {
if (billOfMaterial.getBillOfMaterialSet() != null) {
for (BillOfMaterial billOfMaterialLine : billOfMaterial.getBillOfMaterialSet()) {
Product product = billOfMaterialLine.getProduct();
if (product != null) {
CostSheetLine costSheetLine =
costSheetLineService.createConsumedProductCostSheetLine(
company,
product,
billOfMaterialLine.getUnit(),
bomLevel,
parentCostSheetLine,
billOfMaterialLine.getQty(),
origin,
unitCostCalculation);
BigDecimal wasteRate = billOfMaterialLine.getWasteRate();
if (wasteRate != null && wasteRate.compareTo(BigDecimal.ZERO) > 0) {
costSheetLineService.createConsumedProductWasteCostSheetLine(
company,
product,
billOfMaterialLine.getUnit(),
bomLevel,
parentCostSheetLine,
billOfMaterialLine.getQty(),
wasteRate,
origin,
unitCostCalculation);
}
if (billOfMaterialLine.getDefineSubBillOfMaterial()) {
this._computeCostPrice(
company, billOfMaterialLine, bomLevel, costSheetLine, origin, unitCostCalculation);
}
}
}
}
}
protected void _computeProcess(
ProdProcess prodProcess,
BigDecimal producedQty,
Unit pieceUnit,
int bomLevel,
CostSheetLine parentCostSheetLine)
throws AxelorException {
if (prodProcess != null && prodProcess.getProdProcessLineList() != null) {
for (ProdProcessLine prodProcessLine : prodProcess.getProdProcessLineList()) {
WorkCenter workCenter = prodProcessLine.getWorkCenter();
if (workCenter != null) {
int workCenterTypeSelect = workCenter.getWorkCenterTypeSelect();
if (workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_HUMAN
|| workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
this._computeHumanResourceCost(
workCenter, prodProcessLine.getPriority(), bomLevel, parentCostSheetLine);
}
if (workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
this._computeMachineCost(
prodProcessLine, producedQty, pieceUnit, bomLevel, parentCostSheetLine);
}
}
}
}
}
protected void _computeHumanResourceCost(
WorkCenter workCenter, int priority, int bomLevel, CostSheetLine parentCostSheetLine)
throws AxelorException {
if (workCenter.getProdHumanResourceList() != null) {
for (ProdHumanResource prodHumanResource : workCenter.getProdHumanResourceList()) {
this._computeHumanResourceCost(prodHumanResource, priority, bomLevel, parentCostSheetLine);
}
}
}
protected void _computeHumanResourceCost(
ProdHumanResource prodHumanResource,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine)
throws AxelorException {
BigDecimal costPerHour = BigDecimal.ZERO;
if (prodHumanResource.getProduct() != null) {
Product product = prodHumanResource.getProduct();
costPerHour =
unitConversionService.convert(
hourUnit,
product.getUnit(),
product.getCostPrice(),
appProductionService.getNbDecimalDigitForUnitPrice(),
product);
}
BigDecimal durationHours =
BigDecimal.valueOf(prodHumanResource.getDuration())
.divide(
BigDecimal.valueOf(3600),
appProductionService.getNbDecimalDigitForUnitPrice(),
RoundingMode.HALF_EVEN);
costSheetLineService.createWorkCenterHRCostSheetLine(
prodHumanResource.getWorkCenter(),
prodHumanResource,
priority,
bomLevel,
parentCostSheetLine,
durationHours,
costPerHour.multiply(durationHours),
hourUnit);
}
protected void _computeMachineCost(
ProdProcessLine prodProcessLine,
BigDecimal producedQty,
Unit pieceUnit,
int bomLevel,
CostSheetLine parentCostSheetLine) {
WorkCenter workCenter = prodProcessLine.getWorkCenter();
int costType = workCenter.getCostTypeSelect();
if (costType == WorkCenterRepository.COST_TYPE_PER_CYCLE) {
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
prodProcessLine.getPriority(),
bomLevel,
parentCostSheetLine,
this.getNbCycle(producedQty, prodProcessLine.getMaxCapacityPerCycle()),
workCenter.getCostAmount(),
cycleUnit);
} else if (costType == WorkCenterRepository.COST_TYPE_PER_HOUR) {
BigDecimal qty =
new BigDecimal(prodProcessLine.getDurationPerCycle())
.divide(
new BigDecimal(3600),
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN)
.multiply(this.getNbCycle(producedQty, prodProcessLine.getMaxCapacityPerCycle()));
qty = qty.setScale(QTY_MAX_SCALE, BigDecimal.ROUND_HALF_EVEN);
BigDecimal costPrice = workCenter.getCostAmount().multiply(qty);
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
prodProcessLine.getPriority(),
bomLevel,
parentCostSheetLine,
qty,
costPrice,
hourUnit);
} else if (costType == WorkCenterRepository.COST_TYPE_PER_PIECE) {
BigDecimal costPrice = workCenter.getCostAmount().multiply(producedQty);
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
prodProcessLine.getPriority(),
bomLevel,
parentCostSheetLine,
producedQty,
costPrice,
pieceUnit);
}
}
protected BigDecimal getNbCycle(BigDecimal producedQty, BigDecimal capacityPerCycle) {
if (capacityPerCycle.compareTo(BigDecimal.ZERO) == 0) {
return producedQty;
}
return producedQty.divide(capacityPerCycle, RoundingMode.CEILING);
}
protected void computeRealResidualProduct(ManufOrder manufOrder) throws AxelorException {
for (StockMoveLine stockMoveLine : manufOrder.getProducedStockMoveLineList()) {
if (stockMoveLine.getProduct() != null
&& manufOrder.getProduct() != null
&& (!stockMoveLine.getProduct().equals(manufOrder.getProduct()))) {
CostSheetLine costSheetLine =
costSheetLineService.createResidualProductCostSheetLine(
stockMoveLine.getProduct(), stockMoveLine.getUnit(), stockMoveLine.getRealQty());
costSheet.addCostSheetLineListItem(costSheetLine);
}
}
}
protected void computeRealCostPrice(
ManufOrder manufOrder,
int bomLevel,
CostSheetLine parentCostSheetLine,
LocalDate previousCostSheetDate)
throws AxelorException {
bomLevel++;
this.computeConsumedProduct(manufOrder, bomLevel, parentCostSheetLine, previousCostSheetDate);
BigDecimal producedQty = parentCostSheetLine.getConsumptionQty();
this.computeRealProcess(
manufOrder.getOperationOrderList(),
manufOrder.getProduct().getUnit(),
producedQty,
bomLevel,
parentCostSheetLine,
previousCostSheetDate);
}
protected void computeConsumedProduct(
ManufOrder manufOrder,
int bomLevel,
CostSheetLine parentCostSheetLine,
LocalDate previousCostSheetDate)
throws AxelorException {
BigDecimal ratio = costSheet.getManufOrderProducedRatio();
if (manufOrder.getIsConsProOnOperation()) {
for (OperationOrder operation : manufOrder.getOperationOrderList()) {
this.computeConsumedProduct(
bomLevel,
previousCostSheetDate,
parentCostSheetLine,
operation.getConsumedStockMoveLineList(),
operation.getToConsumeProdProductList(),
ratio);
}
} else {
this.computeConsumedProduct(
bomLevel,
previousCostSheetDate,
parentCostSheetLine,
manufOrder.getConsumedStockMoveLineList(),
manufOrder.getToConsumeProdProductList(),
ratio);
}
}
protected void computeConsumedProduct(
int bomLevel,
LocalDate previousCostSheetDate,
CostSheetLine parentCostSheetLine,
List<StockMoveLine> consumedStockMoveLineList,
List<ProdProduct> toConsumeProdProductList,
BigDecimal ratio)
throws AxelorException {
CostSheet parentCostSheet = parentCostSheetLine.getCostSheet();
int calculationTypeSelect = parentCostSheet.getCalculationTypeSelect();
LocalDate calculationDate = parentCostSheet.getCalculationDate();
Map<List<Object>, BigDecimal> consumedStockMoveLinePerProductAndUnit =
getTotalQtyPerProductAndUnit(
consumedStockMoveLineList,
calculationDate,
previousCostSheetDate,
calculationTypeSelect);
for (List<Object> keys : consumedStockMoveLinePerProductAndUnit.keySet()) {
Iterator<Object> iterator = keys.iterator();
Product product = (Product) iterator.next();
Unit unit = (Unit) iterator.next();
BigDecimal realQty = consumedStockMoveLinePerProductAndUnit.get(keys);
if (product == null) {
continue;
}
BigDecimal valuationQty = BigDecimal.ZERO;
if (calculationTypeSelect == CostSheetRepository.CALCULATION_WORK_IN_PROGRESS) {
BigDecimal plannedConsumeQty =
computeTotalQtyPerUnit(toConsumeProdProductList, product, unit);
valuationQty = realQty.subtract(plannedConsumeQty.multiply(ratio));
}
valuationQty =
valuationQty.setScale(
appProductionService.getNbDecimalDigitForBomQty(), RoundingMode.HALF_UP);
if (valuationQty.compareTo(BigDecimal.ZERO) == 0) {
continue;
}
costSheetLineService.createConsumedProductCostSheetLine(
parentCostSheet.getManufOrder().getCompany(),
product,
unit,
bomLevel,
parentCostSheetLine,
valuationQty,
CostSheetService.ORIGIN_MANUF_ORDER,
null);
}
}
protected BigDecimal computeTotalProducedQty(
Product producedProduct,
List<StockMoveLine> producedStockMoveLineList,
LocalDate calculationDate,
LocalDate previousCostSheetDate,
int calculationTypeSelect)
throws AxelorException {
BigDecimal totalQty = BigDecimal.ZERO;
Map<List<Object>, BigDecimal> producedStockMoveLinePerProductAndUnit =
getTotalQtyPerProductAndUnit(
producedStockMoveLineList,
calculationDate,
previousCostSheetDate,
calculationTypeSelect);
for (List<Object> keys : producedStockMoveLinePerProductAndUnit.keySet()) {
Iterator<Object> iterator = keys.iterator();
Product product = (Product) iterator.next();
Unit unit = (Unit) iterator.next();
BigDecimal realQty = producedStockMoveLinePerProductAndUnit.get(keys);
if (product == null || !product.equals(producedProduct)) {
continue;
}
totalQty =
totalQty.add(
unitConversionService.convert(
unit,
costSheet.getManufOrder().getUnit(),
realQty,
appProductionService.getNbDecimalDigitForBomQty(),
product));
}
return totalQty;
}
protected BigDecimal computeTotalQtyPerUnit(
List<ProdProduct> prodProductList, Product product, Unit unit) {
BigDecimal totalQty = BigDecimal.ZERO;
for (ProdProduct prodProduct : prodProductList) {
if (product.equals(prodProduct.getProduct()) && unit.equals(prodProduct.getUnit())) {
totalQty = totalQty.add(prodProduct.getQty());
}
}
return totalQty;
}
protected Map<List<Object>, BigDecimal> getTotalQtyPerProductAndUnit(
List<StockMoveLine> stockMoveLineList,
LocalDate calculationDate,
LocalDate previousCostSheetDate,
int calculationType) {
Map<List<Object>, BigDecimal> stockMoveLinePerProductAndUnitMap = new HashMap<>();
if (stockMoveLineList == null) {
return stockMoveLinePerProductAndUnitMap;
}
for (StockMoveLine stockMoveLine : stockMoveLineList) {
StockMove stockMove = stockMoveLine.getStockMove();
if (stockMove == null
|| StockMoveRepository.STATUS_REALIZED
!= stockMoveLine.getStockMove().getStatusSelect()) {
continue;
}
if ((calculationType == CostSheetRepository.CALCULATION_PARTIAL_END_OF_PRODUCTION
|| calculationType == CostSheetRepository.CALCULATION_END_OF_PRODUCTION)
&& previousCostSheetDate != null
&& !previousCostSheetDate.isBefore(stockMove.getRealDate().toLocalDate())) {
continue;
} else if (calculationType == CostSheetRepository.CALCULATION_WORK_IN_PROGRESS
&& calculationDate.isBefore(stockMove.getRealDate().toLocalDate())) {
continue;
}
Product productKey = stockMoveLine.getProduct();
Unit unitKey = stockMoveLine.getUnit();
List<Object> keys = new ArrayList<Object>();
keys.add(productKey);
keys.add(unitKey);
BigDecimal qty = stockMoveLinePerProductAndUnitMap.get(keys);
if (qty == null) {
qty = BigDecimal.ZERO;
}
stockMoveLinePerProductAndUnitMap.put(keys, qty.add(stockMoveLine.getRealQty()));
}
return stockMoveLinePerProductAndUnitMap;
}
protected void computeRealProcess(
List<OperationOrder> operationOrders,
Unit pieceUnit,
BigDecimal producedQty,
int bomLevel,
CostSheetLine parentCostSheetLine,
LocalDate previousCostSheetDate)
throws AxelorException {
for (OperationOrder operationOrder : operationOrders) {
WorkCenter workCenter = operationOrder.getMachineWorkCenter();
if (workCenter == null) {
workCenter = operationOrder.getWorkCenter();
}
if (workCenter == null) {
continue;
}
int workCenterTypeSelect = workCenter.getWorkCenterTypeSelect();
if (workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_HUMAN
|| workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
this.computeRealHumanResourceCost(
operationOrder,
operationOrder.getPriority(),
bomLevel,
parentCostSheetLine,
previousCostSheetDate);
}
if (workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
this.computeRealMachineCost(
operationOrder,
workCenter,
producedQty,
pieceUnit,
bomLevel,
parentCostSheetLine,
previousCostSheetDate);
}
}
}
protected void computeRealHumanResourceCost(
OperationOrder operationOrder,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
LocalDate previousCostSheetDate)
throws AxelorException {
if (operationOrder.getProdHumanResourceList() != null) {
Long duration = 0L;
if (parentCostSheetLine.getCostSheet().getCalculationTypeSelect()
== CostSheetRepository.CALCULATION_END_OF_PRODUCTION
|| parentCostSheetLine.getCostSheet().getCalculationTypeSelect()
== CostSheetRepository.CALCULATION_PARTIAL_END_OF_PRODUCTION) {
Period period =
previousCostSheetDate != null
? Period.between(
parentCostSheetLine.getCostSheet().getCalculationDate(), previousCostSheetDate)
: null;
duration =
period != null ? Long.valueOf(period.getDays() * 24) : operationOrder.getRealDuration();
} else if (parentCostSheetLine.getCostSheet().getCalculationTypeSelect()
== CostSheetRepository.CALCULATION_WORK_IN_PROGRESS) {
BigDecimal ratio = costSheet.getManufOrderProducedRatio();
Long plannedDuration =
DurationTool.getSecondsDuration(
Duration.between(
operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT()))
* ratio.longValue();
Long totalPlannedDuration = 0L;
for (OperationOrder manufOperationOrder :
operationOrder.getManufOrder().getOperationOrderList()) {
if (manufOperationOrder.getId() == operationOrder.getId()) {
totalPlannedDuration += manufOperationOrder.getPlannedDuration();
}
}
duration = Math.abs(totalPlannedDuration - plannedDuration);
}
for (ProdHumanResource prodHumanResource : operationOrder.getProdHumanResourceList()) {
this.computeRealHumanResourceCost(
prodHumanResource,
operationOrder.getWorkCenter(),
priority,
bomLevel,
parentCostSheetLine,
duration);
}
}
}
protected void computeRealHumanResourceCost(
ProdHumanResource prodHumanResource,
WorkCenter workCenter,
int priority,
int bomLevel,
CostSheetLine parentCostSheetLine,
Long realDuration)
throws AxelorException {
BigDecimal costPerHour = BigDecimal.ZERO;
// if (prodHumanResource.getProduct() != null) {
// Product product = prodHumanResource.getProduct();
// costPerHour =
// unitConversionService.convert(
// hourUnit,
// product.getUnit(),
// product.getCostPrice(),
// appProductionService.getNbDecimalDigitForUnitPrice(),
// product);
// }
costPerHour = workCenter.getCostAmount();
BigDecimal durationHours =
new BigDecimal(realDuration)
.divide(
new BigDecimal(3600),
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN);
costSheetLineService.createWorkCenterHRCostSheetLine(
workCenter,
prodHumanResource,
priority,
bomLevel,
parentCostSheetLine,
durationHours,
costPerHour.multiply(durationHours),
hourUnit);
}
protected void computeRealMachineCost(
OperationOrder operationOrder,
WorkCenter workCenter,
BigDecimal producedQty,
Unit pieceUnit,
int bomLevel,
CostSheetLine parentCostSheetLine,
LocalDate previousCostSheetDate) {
int costType = workCenter.getCostTypeSelect();
if (costType == WorkCenterRepository.COST_TYPE_PER_CYCLE) {
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
operationOrder.getPriority(),
bomLevel,
parentCostSheetLine,
this.getNbCycle(producedQty, workCenter.getMaxCapacityPerCycle()),
workCenter.getCostAmount(),
cycleUnit);
} else if (costType == WorkCenterRepository.COST_TYPE_PER_HOUR) {
BigDecimal qty = BigDecimal.ZERO;
if (workCenter.getIsRevaluationAtActualPrices()) {
qty =
new BigDecimal(operationOrder.getRealDuration())
.divide(
new BigDecimal(3600),
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN);
} else {
BigDecimal manufOrderQty = operationOrder.getManufOrder().getQty();
BigDecimal durationPerCycle =
new BigDecimal(workCenter.getDurationPerCycle())
.divide(
new BigDecimal(3600),
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN);
if (manufOrderQty.compareTo(workCenter.getMinCapacityPerCycle()) == 1) {
BigDecimal maxCapacityPerCycle =
workCenter.getMaxCapacityPerCycle().compareTo(BigDecimal.ZERO) == 0
? BigDecimal.ONE
: workCenter.getMaxCapacityPerCycle();
qty =
manufOrderQty
.divide(
maxCapacityPerCycle,
appProductionService.getNbDecimalDigitForUnitPrice(),
BigDecimal.ROUND_HALF_EVEN)
.multiply(durationPerCycle)
.setScale(QTY_MAX_SCALE, BigDecimal.ROUND_HALF_EVEN);
} else {
qty = durationPerCycle;
}
}
BigDecimal costPrice = workCenter.getCostAmount().multiply(qty);
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
operationOrder.getPriority(),
bomLevel,
parentCostSheetLine,
qty,
costPrice,
hourUnit);
} else if (costType == WorkCenterRepository.COST_TYPE_PER_PIECE) {
BigDecimal costPrice = workCenter.getCostAmount().multiply(producedQty);
costSheetLineService.createWorkCenterMachineCostSheetLine(
workCenter,
operationOrder.getPriority(),
bomLevel,
parentCostSheetLine,
producedQty,
costPrice,
pieceUnit);
}
}
protected BigDecimal getTotalToProduceQty(ManufOrder manufOrder) throws AxelorException {
BigDecimal totalProducedQty = BigDecimal.ZERO;
for (StockMoveLine stockMoveLine : manufOrder.getProducedStockMoveLineList()) {
if (stockMoveLine.getUnit().equals(manufOrder.getUnit())
&& (stockMoveLine.getStockMove().getStatusSelect() == StockMoveRepository.STATUS_PLANNED
|| stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_REALIZED)) {
Product product = stockMoveLine.getProduct();
totalProducedQty =
totalProducedQty.add(
unitConversionService.convert(
stockMoveLine.getUnit(),
costSheet.getManufOrder().getUnit(),
stockMoveLine.getQty(),
appProductionService.getNbDecimalDigitForBomQty(),
product));
}
}
return totalProducedQty;
}
}

View File

@ -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.production.service.costsheet;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.UnitCostCalcLine;
public interface UnitCostCalcLineService {
public UnitCostCalcLine createUnitCostCalcLine(
Product product, Company company, int maxLevel, CostSheet costSheet);
}

View File

@ -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.production.service.costsheet;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.UnitCostCalcLine;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.repo.UnitCostCalcLineRepository;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UnitCostCalcLineServiceImpl implements UnitCostCalcLineService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ProductRepository productRepository;
protected UnitCostCalcLineRepository unitCostCalcLineRepository;
@Inject
public UnitCostCalcLineServiceImpl(UnitCostCalcLineRepository unitCostCalcLineRepository) {
this.unitCostCalcLineRepository = unitCostCalcLineRepository;
}
public UnitCostCalcLine createUnitCostCalcLine(
Product product, Company company, int maxLevel, CostSheet costSheet) {
UnitCostCalcLine unitCostCalcLine = new UnitCostCalcLine();
unitCostCalcLine.setProduct(product);
unitCostCalcLine.setCompany(company);
unitCostCalcLine.setPreviousCost(product.getCostPrice());
unitCostCalcLine.setCostSheet(costSheet);
unitCostCalcLine.setComputedCost(costSheet.getCostPrice());
unitCostCalcLine.setCostToApply(costSheet.getCostPrice());
unitCostCalcLine.setMaxLevel(maxLevel);
return unitCostCalcLine;
}
public UnitCostCalcLine getUnitCostCalcLine(
UnitCostCalculation unitCostCalculation, Product product) {
return unitCostCalcLineRepository
.all()
.filter("self.unitCostCalculation = ?1 AND self.product = ?2", unitCostCalculation, product)
.fetchOne();
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.production.service.costsheet;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.exception.AxelorException;
import com.axelor.meta.db.MetaFile;
import java.io.IOException;
public interface UnitCostCalculationService {
public MetaFile exportUnitCostCalc(UnitCostCalculation unitCostCalculation, String fileName)
throws IOException;
public void importUnitCostCalc(MetaFile dataFile, UnitCostCalculation unitCostCalculation)
throws IOException;
public void runUnitCostCalc(UnitCostCalculation unitCostCalculation) throws AxelorException;
public void updateUnitCosts(UnitCostCalculation unitCostCalculation);
}

View File

@ -0,0 +1,473 @@
/*
* 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.production.service.costsheet;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.ProductService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.UnitCostCalcLine;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.UnitCostCalcLineRepository;
import com.axelor.apps.production.db.repo.UnitCostCalculationRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.tool.StringTool;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.data.csv.CSVImporter;
import com.axelor.db.JPA;
import com.axelor.dms.db.DMSFile;
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.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.ValidationException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UnitCostCalculationServiceImpl implements UnitCostCalculationService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ProductRepository productRepository;
protected UnitCostCalculationRepository unitCostCalculationRepository;
protected UnitCostCalcLineService unitCostCalcLineService;
protected CostSheetService costSheetService;
protected UnitCostCalcLineRepository unitCostCalcLineRepository;
protected AppProductionService appProductionService;
protected ProductService productService;
protected Map<Long, Integer> productMap;
@Inject
public UnitCostCalculationServiceImpl(
ProductRepository productRepository,
UnitCostCalculationRepository unitCostCalculationRepository,
UnitCostCalcLineService unitCostCalcLineService,
CostSheetService costSheetService,
UnitCostCalcLineRepository unitCostCalcLineRepository,
AppProductionService appProductionService,
ProductService productService) {
this.productRepository = productRepository;
this.unitCostCalculationRepository = unitCostCalculationRepository;
this.unitCostCalcLineService = unitCostCalcLineService;
this.costSheetService = costSheetService;
this.unitCostCalcLineRepository = unitCostCalcLineRepository;
this.appProductionService = appProductionService;
this.productService = productService;
}
@Override
public MetaFile exportUnitCostCalc(UnitCostCalculation unitCostCalculation, String fileName)
throws IOException {
List<String[]> list = new ArrayList<>();
List<UnitCostCalcLine> unitCostCalcLineList = unitCostCalculation.getUnitCostCalcLineList();
Collections.sort(
unitCostCalcLineList,
new Comparator<UnitCostCalcLine>() {
@Override
public int compare(
UnitCostCalcLine unitCostCalcLine1, UnitCostCalcLine unitCostCalcLine2) {
return unitCostCalcLine1
.getProduct()
.getCode()
.compareTo(unitCostCalcLine2.getProduct().getCode());
}
});
for (UnitCostCalcLine unitCostCalcLine : unitCostCalcLineList) {
String[] item = new String[4];
item[0] =
unitCostCalcLine.getProduct() == null ? "" : unitCostCalcLine.getProduct().getCode();
item[1] =
unitCostCalcLine.getProduct() == null ? "" : unitCostCalcLine.getProduct().getName();
item[2] = unitCostCalcLine.getComputedCost().toString();
item[3] = unitCostCalcLine.getCostToApply().toString();
list.add(item);
}
String filePath = AppSettings.get().get("file.upload.dir");
Path path = Paths.get(filePath, fileName);
File file = path.toFile();
log.debug("File located at: {}", path);
String[] headers = {
I18n.get("Product_code"),
I18n.get("Product_name"),
I18n.get("Product_currency"),
I18n.get("Computed_cost"),
I18n.get("Cost_to_apply")
};
CsvTool.csvWriter(filePath, fileName, ';', '"', headers, list);
try (InputStream is = new FileInputStream(file)) {
DMSFile dmsFile = Beans.get(MetaFiles.class).attach(is, fileName, unitCostCalculation);
return dmsFile.getMetaFile();
}
}
@Override
public void importUnitCostCalc(MetaFile dataFile, UnitCostCalculation unitCostCalculation)
throws IOException {
File tempDir = Files.createTempDir();
File csvFile = new File(tempDir, "unitcostcalc.csv");
Files.copy(MetaFiles.getPath(dataFile).toFile(), csvFile);
File configXmlFile = this.getConfigXmlFile();
CSVImporter csvImporter =
new CSVImporter(configXmlFile.getAbsolutePath(), tempDir.getAbsolutePath());
Map<String, Object> context = new HashMap<>();
context.put("_unitCostCalculation", unitCostCalculation.getId());
csvImporter.setContext(context);
csvImporter.run();
}
protected File getConfigXmlFile() {
File configFile = null;
try {
configFile = File.createTempFile("input-config", ".xml");
InputStream bindFileInputStream =
this.getClass().getResourceAsStream("/import-configs/" + "csv-config.xml");
if (bindFileInputStream == null) {
throw new ValidationException(IExceptionMessage.UNIT_COST_CALCULATION_IMPORT_FAIL_ERROR);
}
FileOutputStream outputStream = new FileOutputStream(configFile);
IOUtils.copy(bindFileInputStream, outputStream);
} catch (Exception e) {
TraceBackService.trace(e);
}
return configFile;
}
@Override
public void runUnitCostCalc(UnitCostCalculation unitCostCalculation) throws AxelorException {
if (!unitCostCalculation.getUnitCostCalcLineList().isEmpty()) {
clear(unitCostCalculation);
}
unitCostCalculation = unitCostCalculationRepository.find(unitCostCalculation.getId());
this.assignProductAndLevel(this.getProductList(unitCostCalculation));
calculationProcess(unitCostCalculation);
updateStatusToComputed(unitCostCalculationRepository.find(unitCostCalculation.getId()));
}
@Transactional
protected void clear(UnitCostCalculation unitCostCalculation) {
unitCostCalculation.clearUnitCostCalcLineList();
unitCostCalculationRepository.save(unitCostCalculation);
}
@Transactional
protected void updateStatusToComputed(UnitCostCalculation unitCostCalculation) {
unitCostCalculation.setCalculationDate(appProductionService.getTodayDate());
unitCostCalculation.setStatusSelect(UnitCostCalculationRepository.STATUS_COSTS_COMPUTED);
unitCostCalculationRepository.save(unitCostCalculation);
}
protected void calculationProcess(UnitCostCalculation unitCostCalculation)
throws AxelorException {
for (int level = this.getMaxLevel(); level >= 0; level--) {
for (Product product : this.getProductList(level)) {
this.calculationProductProcess(
unitCostCalculationRepository.find(unitCostCalculation.getId()),
productRepository.find(product.getId()));
JPA.clear();
}
}
}
@Transactional(rollbackOn = {Exception.class})
protected void calculationProductProcess(UnitCostCalculation unitCostCalculation, Product product)
throws AxelorException {
int level = this.productMap.get(product.getId()).intValue();
log.debug("Unit cost price calculation for product : {}, level : {}", product.getCode(), level);
int origin =
unitCostCalculation.getAllBomLevels()
? CostSheetService.ORIGIN_BULK_UNIT_COST_CALCULATION
: CostSheetService.ORIGIN_BILL_OF_MATERIAL;
BillOfMaterial billOfMaterial = product.getDefaultBillOfMaterial();
CostSheet costSheet =
costSheetService.computeCostPrice(billOfMaterial, origin, unitCostCalculation);
UnitCostCalcLine unitCostCalcLine =
unitCostCalcLineService.createUnitCostCalcLine(
product, billOfMaterial.getCompany(), level, costSheet);
unitCostCalculation.addUnitCostCalcLineListItem(unitCostCalcLine);
unitCostCalculationRepository.save(unitCostCalculation);
}
protected Set<Product> getProductList(UnitCostCalculation unitCostCalculation)
throws AxelorException {
Set<Product> productSet = Sets.newHashSet();
if (!unitCostCalculation.getProductSet().isEmpty()) {
productSet.addAll(unitCostCalculation.getProductSet());
}
List<Integer> productSubTypeSelects =
StringTool.getIntegerList(unitCostCalculation.getProductSubTypeSelect());
if (!unitCostCalculation.getProductCategorySet().isEmpty()) {
productSet.addAll(
productRepository
.all()
.filter(
"self.productCategory in (?1) AND self.productTypeSelect = ?2 AND self.productSubTypeSelect in (?3)"
+ " AND self.defaultBillOfMaterial.company in (?4) AND self.procurementMethodSelect in (?5, ?6)",
unitCostCalculation.getProductCategorySet(),
ProductRepository.PRODUCT_TYPE_STORABLE,
productSubTypeSelects,
unitCostCalculation.getCompanySet(),
ProductRepository.PROCUREMENT_METHOD_PRODUCE,
ProductRepository.PROCUREMENT_METHOD_BUYANDPRODUCE)
.fetch());
}
if (!unitCostCalculation.getProductFamilySet().isEmpty()) {
productSet.addAll(
productRepository
.all()
.filter(
"self.productFamily in (?1) AND self.productTypeSelect = ?2 AND self.productSubTypeSelect in (?3)"
+ " AND self.defaultBillOfMaterial.company in (?4) AND self.procurementMethodSelect in (?5, ?6)",
unitCostCalculation.getProductFamilySet(),
ProductRepository.PRODUCT_TYPE_STORABLE,
productSubTypeSelects,
unitCostCalculation.getCompanySet(),
ProductRepository.PROCUREMENT_METHOD_PRODUCE,
ProductRepository.PROCUREMENT_METHOD_BUYANDPRODUCE)
.fetch());
}
if (productSet.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.UNIT_COST_CALCULATION_NO_PRODUCT));
}
return productSet;
}
/**
* Get the list of product for a level
*
* @param level
* @return
*/
protected List<Product> getProductList(int level) {
List<Product> productList = Lists.newArrayList();
for (Long productId : this.productMap.keySet()) {
if (this.productMap.get(productId) == level) {
productList.add(productRepository.find(productId));
}
}
return productList;
}
protected void assignProductAndLevel(Set<Product> productList) {
productMap = Maps.newHashMap();
for (Product product : productList) {
this.assignProductAndLevel(product);
}
}
protected boolean hasValidBillOfMaterial(Product product) {
BillOfMaterial defaultBillOfMaterial = product.getDefaultBillOfMaterial();
if (defaultBillOfMaterial != null
&& (defaultBillOfMaterial.getStatusSelect() == BillOfMaterialRepository.STATUS_VALIDATED
|| defaultBillOfMaterial.getStatusSelect()
== BillOfMaterialRepository.STATUS_APPLICABLE)
&& (product.getProcurementMethodSelect()
== ProductRepository.PROCUREMENT_METHOD_BUYANDPRODUCE
|| product.getProcurementMethodSelect()
== ProductRepository.PROCUREMENT_METHOD_PRODUCE)) {
return true;
}
return false;
}
protected void assignProductAndLevel(Product product) {
log.debug("Add of the product : {}", product.getFullName());
this.productMap.put(product.getId(), this.getMaxLevel(product, 0));
if (hasValidBillOfMaterial(product)) {
this.assignProductLevel(product.getDefaultBillOfMaterial(), 0);
}
}
protected int getMaxLevel(Product product, int level) {
if (this.productMap.containsKey(product.getId())) {
return Math.max(level, this.productMap.get(product.getId()));
}
return level;
}
protected int getMaxLevel() {
int maxLevel = 0;
for (int level : this.productMap.values()) {
if (level > maxLevel) {
maxLevel = level;
}
}
return maxLevel;
}
/**
* Update the level of Bill of material. The highest for each product (0: product with parent, 1:
* product with a parent, 2: product with a parent that have a parent, ...)
*
* @param billOfMaterial
* @param level
*/
protected void assignProductLevel(BillOfMaterial billOfMaterial, int level) {
if (billOfMaterial.getBillOfMaterialSet() == null
|| billOfMaterial.getBillOfMaterialSet().isEmpty()
|| level > 100) {
Product subProduct = billOfMaterial.getProduct();
log.debug("Add of the sub product : {} for the level : {} ", subProduct.getFullName(), level);
this.productMap.put(subProduct.getId(), this.getMaxLevel(subProduct, level));
} else {
level = level + 1;
for (BillOfMaterial subBillOfMaterial : billOfMaterial.getBillOfMaterialSet()) {
Product subProduct = subBillOfMaterial.getProduct();
if (this.productMap.containsKey(subProduct.getId())) {
this.assignProductLevel(subBillOfMaterial, level);
if (hasValidBillOfMaterial(subProduct)) {
this.assignProductLevel(subProduct.getDefaultBillOfMaterial(), level);
}
}
}
}
}
public void updateUnitCosts(UnitCostCalculation unitCostCalculation) {
for (UnitCostCalcLine unitCostCalcLine : unitCostCalculation.getUnitCostCalcLineList()) {
updateUnitCosts(unitCostCalcLineRepository.find(unitCostCalcLine.getId()));
JPA.clear();
}
updateStatusProductCostPriceUpdated(
unitCostCalculationRepository.find(unitCostCalculation.getId()));
}
@Transactional
protected void updateUnitCosts(UnitCostCalcLine unitCostCalcLine) {
Product product = unitCostCalcLine.getProduct();
product.setCostPrice(
unitCostCalcLine
.getCostToApply()
.setScale(
appProductionService.getNbDecimalDigitForUnitPrice(), BigDecimal.ROUND_HALF_UP));
productService.updateSalePrice(product);
}
@Transactional
protected void updateStatusProductCostPriceUpdated(UnitCostCalculation unitCostCalculation) {
unitCostCalculation.setUpdateCostDate(appProductionService.getTodayDate());
unitCostCalculation.setStatusSelect(UnitCostCalculationRepository.STATUS_COSTS_UPDATED);
unitCostCalculationRepository.save(unitCostCalculation);
}
}

View File

@ -0,0 +1,249 @@
/*
* 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.production.service.manuforder;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public interface ManufOrderService {
public static int DEFAULT_PRIORITY = 2;
public static int DEFAULT_PRIORITY_INTERVAL = 10;
public static boolean IS_TO_INVOICE = false;
public static int ORIGIN_TYPE_MRP = 1;
public static int ORIGIN_TYPE_SALE_ORDER = 2;
public static int ORIGIN_TYPE_OTHER = 3;
/**
* @param product
* @param qtyRequested
* @param priority
* @param isToInvoice
* @param billOfMaterial
* @param plannedStartDateT
* @param originType
* <li>1 : MRP
* <li>2 : Sale order
* <li>3 : Other
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public ManufOrder generateManufOrder(
Product product,
BigDecimal qtyRequested,
int priority,
boolean isToInvoice,
BillOfMaterial billOfMaterial,
LocalDateTime plannedStartDateT,
LocalDateTime plannedEndDateT,
int originType)
throws AxelorException;
public void createToConsumeProdProductList(ManufOrder manufOrder);
@Transactional(rollbackOn = {Exception.class})
public StockMove createToConsumeProdProductList(ManufOrder manufOrder, int size)
throws AxelorException;
/**
* Compute the quantity on generated prod product line. If the quantity of the bill of material is
* equal to the quantity of manuf order then the prod product line will have the same quantity as
* configured line.
*
* @param bomQty quantity of the bill of material.
* @param manufOrderQty quantity configured of the manuf order.
* @param lineQty quantity of the line.
* @return the quantity for the prod product line.
*/
BigDecimal computeToConsumeProdProductLineQuantity(
BigDecimal bomQty, BigDecimal manufOrderQty, BigDecimal lineQty);
public void createToProduceProdProductList(ManufOrder manufOrder);
public ManufOrder createManufOrder(
Product product,
BigDecimal qty,
int priority,
boolean isToInvoice,
Company company,
BillOfMaterial billOfMaterial,
LocalDateTime plannedStartDateT,
LocalDateTime plannedEndDateT)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void preFillOperations(ManufOrder manufOrder) throws AxelorException;
public String getManufOrderSeq(ManufOrder manufOrder) throws AxelorException;
public boolean isManagedConsumedProduct(BillOfMaterial billOfMaterial);
/**
* Generate waste stock move.
*
* @param manufOrder
* @return wasteStockMove
*/
public StockMove generateWasteStockMove(ManufOrder manufOrder) throws AxelorException;
/**
* Update planned qty in {@link ManufOrder#toConsumeProdProductList} and {@link
* ManufOrder#toProduceProdProductList} then update quantity in stock move lines to match the new
* planned qty.
*
* @param manufOrder
*/
void updatePlannedQty(ManufOrder manufOrder) throws AxelorException;
/**
* Update real qty in {@link ManufOrder#consumedStockMoveLineList} and {@link
* ManufOrder#producedStockMoveLineList}
*
* @param manufOrder
* @param qtyToUpdate
*/
void updateRealQty(ManufOrder manufOrder, BigDecimal qtyToUpdate) throws AxelorException;
/**
* Updates the diff prod product list.
*
* @param manufOrder
* @return the updated manufOrder
* @throws AxelorException
*/
ManufOrder updateDiffProdProductList(ManufOrder manufOrder) throws AxelorException;
/**
* Compute the difference between the two lists for the given manuf order.
*
* @param manufOrder
* @param prodProductList
* @param stockMoveLineList
* @return a list of ProdProduct
* @throws AxelorException
*/
List<ProdProduct> createDiffProdProductList(
ManufOrder manufOrder,
List<ProdProduct> prodProductList,
List<StockMoveLine> stockMoveLineList)
throws AxelorException;
/**
* Compute the difference between the two lists.
*
* @param prodProductList
* @param stockMoveLineList
* @return a list of ProdProduct
* @throws AxelorException
*/
List<ProdProduct> createDiffProdProductList(
List<ProdProduct> prodProductList, List<StockMoveLine> stockMoveLineList)
throws AxelorException;
/**
* On changing {@link ManufOrder#consumedStockMoveLineList}, we also update the stock move.
*
* @param manufOrder
*/
void updateConsumedStockMoveFromManufOrder(ManufOrder manufOrder) throws AxelorException;
/**
* On changing {@link ManufOrder#producedStockMoveLineList}, we also update the stock move.
*
* @param manufOrder
* @throws AxelorException
*/
void updateProducedStockMoveFromManufOrder(ManufOrder manufOrder) throws AxelorException;
/**
* Check the realized consumed stock move lines in manuf order has not changed.
*
* @param manufOrder a manuf order from context.
* @param oldManufOrder a manuf order from database.
* @throws AxelorException if the check fails.
*/
void checkConsumedStockMoveLineList(ManufOrder manufOrder, ManufOrder oldManufOrder)
throws AxelorException;
/**
* Check the realized produced stock move lines in manuf order has not changed.
*
* @param manufOrder a manuf order from context.
* @param oldManufOrder a manuf order from database.
* @throws AxelorException if the check fails.
*/
void checkProducedStockMoveLineList(ManufOrder manufOrder, ManufOrder oldManufOrder)
throws AxelorException;
/**
* Check between a new and an old stock move line list whether a realized stock move line has been
* deleted.
*
* @param stockMoveLineList a stock move line list from view context.
* @param oldStockMoveLineList a stock move line list from database.
* @throws AxelorException if the check fails.
*/
void checkRealizedStockMoveLineList(
List<StockMoveLine> stockMoveLineList, List<StockMoveLine> oldStockMoveLineList)
throws AxelorException;
/**
* Compute {@link ManufOrder#diffConsumeProdProductList}, then add and remove lines to the stock
* move to match the stock move line list. The list can be from manuf order or operation order.
*
* @param stockMoveLineList
* @param stockMove
* @throws AxelorException
*/
void updateStockMoveFromManufOrder(List<StockMoveLine> stockMoveLineList, StockMove stockMove)
throws AxelorException;
/**
* Create a query to find product's consume and missing qty of a specific/all company and a
* specific/all stock location in a Manuf Order
*
* @param productId, companyId and stockLocationId
* @return the query.
*/
public String getConsumeAndMissingQtyForAProduct(
Long productId, Long companyId, Long stockLocationId);
/**
* Create a query to find product's building qty of a specific/all company and a specific/all
* stock location in a Manuf Order
*
* @param productId, companyId and stockLocationId
* @return the query.
*/
public String getBuildingQtyForAProduct(Long productId, Long companyId, Long stockLocationId);
@Transactional(rollbackOn = {Exception.class})
public StockMove createOutgoinfStockMove(ManufOrder manufOrder) throws AxelorException;
}

View File

@ -0,0 +1,977 @@
/*
* 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.production.service.manuforder;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.service.ProductVariantService;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.BillOfMaterialConsumption;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.ProdResidualProduct;
import com.axelor.apps.production.db.ProductionConfig;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.ProdProductRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.BillOfMaterialServiceImpl;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.config.ProductionConfigService;
import com.axelor.apps.production.service.config.StockConfigProductionService;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.axelor.apps.production.service.operationorder.OperationOrderStockMoveService;
import com.axelor.apps.stock.db.StockConfig;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockLocationRepository;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.StockLocationService;
import com.axelor.apps.stock.service.StockMoveLineService;
import com.axelor.apps.stock.service.StockMoveService;
import com.axelor.apps.tool.StringTool;
import com.axelor.common.StringUtils;
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.MoreObjects;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ManufOrderServiceImpl implements ManufOrderService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected SequenceService sequenceService;
protected OperationOrderService operationOrderService;
protected ManufOrderWorkflowService manufOrderWorkflowService;
protected ProductVariantService productVariantService;
protected AppProductionService appProductionService;
protected ManufOrderRepository manufOrderRepo;
protected ProdProductRepository prodProductRepo;
@Inject
public ManufOrderServiceImpl(
SequenceService sequenceService,
OperationOrderService operationOrderService,
ManufOrderWorkflowService manufOrderWorkflowService,
ProductVariantService productVariantService,
AppProductionService appProductionService,
ManufOrderRepository manufOrderRepo,
ProdProductRepository prodProductRepo) {
this.sequenceService = sequenceService;
this.operationOrderService = operationOrderService;
this.manufOrderWorkflowService = manufOrderWorkflowService;
this.productVariantService = productVariantService;
this.appProductionService = appProductionService;
this.manufOrderRepo = manufOrderRepo;
this.prodProductRepo = prodProductRepo;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public ManufOrder generateManufOrder(
Product product,
BigDecimal qtyRequested,
int priority,
boolean isToInvoice,
BillOfMaterial billOfMaterial,
LocalDateTime plannedStartDateT,
LocalDateTime plannedEndDateT,
int originType)
throws AxelorException {
if (billOfMaterial == null) {
billOfMaterial = this.getBillOfMaterial(product);
}
Company company = billOfMaterial.getCompany();
// BigDecimal bomQty = billOfMaterial.getQty();
// BigDecimal qty = qtyRequested.divide(bomQty, 0, RoundingMode.CEILING).multiply(bomQty);
ManufOrder manufOrder =
this.createManufOrder(
product,
qtyRequested,
priority,
IS_TO_INVOICE,
company,
billOfMaterial,
plannedStartDateT,
plannedEndDateT);
if (originType == ORIGIN_TYPE_SALE_ORDER
&& appProductionService.getAppProduction().getAutoPlanManufOrderFromSO()
|| originType == ORIGIN_TYPE_MRP
|| originType == ORIGIN_TYPE_OTHER) {
// manufOrder = manufOrderWorkflowService.plan(manufOrder);
}
return manufOrderRepo.save(manufOrder);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public StockMove createToConsumeProdProductList(ManufOrder manufOrder, int times)
throws AxelorException {
BigDecimal manufOrderQty = manufOrder.getQty();
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
BigDecimal bomQty = billOfMaterial.getQty();
StockMove stockMove =
Beans.get(ManufOrderStockMoveService.class)
._createToConsumeStockMove(manufOrder, manufOrder.getCompany());
if (billOfMaterial.getBillOfMaterialSet() != null) {
for (BillOfMaterial billOfMaterialLine :
getSortedBillsOfMaterials(billOfMaterial.getBillOfMaterialSet())) {
if (!billOfMaterialLine.getHasNoManageStock()) {
Product product =
productVariantService.getProductVariant(
manufOrder.getProduct(), billOfMaterialLine.getProduct());
BigDecimal qty =
computeToConsumeProdProductLineQuantity(
bomQty, manufOrderQty, billOfMaterialLine.getQty())
.multiply(new BigDecimal(times));
ProdProduct prodProduct = new ProdProduct(product, qty, billOfMaterialLine.getUnit());
StockMoveLine stockMoveline =
Beans.get(ManufOrderStockMoveService.class)
._createStockMoveLine(
prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS);
stockMove.addStockMoveLineListItem(stockMoveline);
}
}
}
return Beans.get(StockMoveRepository.class).save(stockMove);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public StockMove createOutgoinfStockMove(ManufOrder manufOrder) throws AxelorException {
BigDecimal manufOrderQty = manufOrder.getQty();
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
BigDecimal bomQty = billOfMaterial.getQty();
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(manufOrder.getCompany());
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
StockMove stockMove =
Beans.get(ManufOrderStockMoveService.class)
._createToConsumeStockMove(manufOrder, manufOrder.getCompany());
stockMove.setPartner(manufOrder.getCompany().getPartner());
stockMove.setTypeSelect(StockMoveRepository.TYPE_OUTGOING);
stockMove.setFromStockLocation(manufOrder.getWorkshopStockLocation());
stockMove.setToStockLocation(virtualStockLocation);
if (billOfMaterial.getBillOfMaterialSet() != null) {
for (BillOfMaterial billOfMaterialLine :
getSortedBillsOfMaterials(billOfMaterial.getBillOfMaterialSet())) {
if (!billOfMaterialLine.getHasNoManageStock()) {
Product product =
productVariantService.getProductVariant(
manufOrder.getProduct(), billOfMaterialLine.getProduct());
BigDecimal qty =
computeToConsumeProdProductLineQuantity(
bomQty, manufOrderQty, billOfMaterialLine.getQty());
ProdProduct prodProduct = new ProdProduct(product, qty, billOfMaterialLine.getUnit());
StockMoveLine stockMoveline =
Beans.get(ManufOrderStockMoveService.class)
._createStockMoveLine(
prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS);
stockMove.addStockMoveLineListItem(stockMoveline);
manufOrder.addConsumedStockMoveLineListItem(stockMoveline);
}
}
}
return Beans.get(StockMoveRepository.class).save(stockMove);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void createToConsumeProdProductList(ManufOrder manufOrder) {
BigDecimal manufOrderQty = manufOrder.getQty();
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
BigDecimal bomQty = billOfMaterial.getQty();
if (billOfMaterial.getBillOfMaterialSet() != null) {
for (BillOfMaterial billOfMaterialLine :
getSortedBillsOfMaterials(billOfMaterial.getBillOfMaterialSet())) {
if (!billOfMaterialLine.getHasNoManageStock()) {
Product product =
productVariantService.getProductVariant(
manufOrder.getProduct(), billOfMaterialLine.getProduct());
BigDecimal qty =
computeToConsumeProdProductLineQuantity(
bomQty, manufOrderQty, billOfMaterialLine.getQty());
ProdProduct prodProduct = new ProdProduct(product, qty, billOfMaterialLine.getUnit());
manufOrder.addToConsumeProdProductListItem(prodProduct);
prodProductRepo.persist(prodProduct); // id by order of creation
}
}
}
}
public void createToConsumeProdProductListFromSelectedManufOrder(
List<ManufOrder> manufOrderList) {
BigDecimal manufOrderQty =
manufOrderList.stream().map(ManufOrder::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
BillOfMaterial billOfMaterial = manufOrderList.get(0).getBillOfMaterial();
BigDecimal bomQty = billOfMaterial.getQty();
if (billOfMaterial.getBillOfMaterialSet() != null) {
for (BillOfMaterial billOfMaterialLine :
getSortedBillsOfMaterials(billOfMaterial.getBillOfMaterialSet())) {
if (!billOfMaterialLine.getHasNoManageStock()) {
Product product =
productVariantService.getProductVariant(
manufOrderList.get(0).getProduct(), billOfMaterialLine.getProduct());
BigDecimal qty =
computeToConsumeProdProductLineQuantity(
bomQty, manufOrderQty, billOfMaterialLine.getQty());
ProdProduct prodProduct = new ProdProduct(product, qty, billOfMaterialLine.getUnit());
manufOrderList.get(0).addToConsumeProdProductListItem(prodProduct);
prodProductRepo.persist(prodProduct); // id by order of creation
}
}
}
}
@Override
public BigDecimal computeToConsumeProdProductLineQuantity(
BigDecimal bomQty, BigDecimal manufOrderQty, BigDecimal lineQty) {
BigDecimal qty = BigDecimal.ZERO;
if (bomQty.signum() != 0) {
qty =
manufOrderQty
.multiply(lineQty)
.divide(
bomQty,
Beans.get(AppProductionService.class).getNbDecimalDigitForBomQty(),
RoundingMode.HALF_EVEN);
}
return qty;
}
private List<BillOfMaterial> getSortedBillsOfMaterials(
Collection<BillOfMaterial> billsOfMaterials) {
billsOfMaterials = MoreObjects.firstNonNull(billsOfMaterials, Collections.emptyList());
return billsOfMaterials
.stream()
.sorted(
Comparator.comparing(BillOfMaterial::getPriority)
.thenComparing(Comparator.comparing(BillOfMaterial::getId)))
.collect(Collectors.toList());
}
@Override
public void createToProduceProdProductList(ManufOrder manufOrder) {
BigDecimal manufOrderQty = manufOrder.getQty();
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
BigDecimal bomQty = billOfMaterial.getQty();
// add the produced product
manufOrder.addToProduceProdProductListItem(
new ProdProduct(manufOrder.getProduct(), manufOrderQty, billOfMaterial.getUnit()));
// Add the residual products
if (appProductionService.getAppProduction().getManageResidualProductOnBom()
&& billOfMaterial.getProdResidualProductList() != null) {
for (ProdResidualProduct prodResidualProduct : billOfMaterial.getProdResidualProductList()) {
Product product =
productVariantService.getProductVariant(
manufOrder.getProduct(), prodResidualProduct.getProduct());
BigDecimal qty =
bomQty.signum() != 0
? prodResidualProduct
.getQty()
.multiply(manufOrderQty)
.divide(
bomQty,
appProductionService.getNbDecimalDigitForBomQty(),
RoundingMode.HALF_EVEN)
: BigDecimal.ZERO;
manufOrder.addToProduceProdProductListItem(
new ProdProduct(product, qty, prodResidualProduct.getUnit()));
}
}
}
@Override
public ManufOrder createManufOrder(
Product product,
BigDecimal qty,
int priority,
boolean isToInvoice,
Company company,
BillOfMaterial billOfMaterial,
LocalDateTime plannedStartDateT,
LocalDateTime plannedEndDateT)
throws AxelorException {
logger.debug("Création d'un OF {}", priority);
ProdProcess prodProcess = billOfMaterial.getProdProcess();
ManufOrder manufOrder =
new ManufOrder(
qty,
company,
null,
priority,
this.isManagedConsumedProduct(billOfMaterial),
billOfMaterial,
product,
prodProcess,
plannedStartDateT,
plannedEndDateT,
ManufOrderRepository.STATUS_DRAFT);
if (appProductionService.getAppProduction().getManageWorkshop()) {
manufOrder.setWorkshopStockLocation(billOfMaterial.getWorkshopStockLocation());
}
if (prodProcess != null && prodProcess.getProdProcessLineList() != null) {
for (ProdProcessLine prodProcessLine :
this._sortProdProcessLineByPriority(prodProcess.getProdProcessLineList())) {
manufOrder.addOperationOrderListItem(
operationOrderService.createOperationOrder(manufOrder, prodProcessLine));
}
}
if (!manufOrder.getIsConsProOnOperation()) {
// bachir temp
// this.createToConsumeProdProductList(manufOrder);
}
// bachir temp
// this.createToProduceProdProductList(manufOrder);
return manufOrder;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void preFillOperations(ManufOrder manufOrder) throws AxelorException {
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
if (manufOrder.getProdProcess() == null) {
manufOrder.setProdProcess(billOfMaterial.getProdProcess());
}
ProdProcess prodProcess = manufOrder.getProdProcess();
if (manufOrder.getPlannedStartDateT() == null) {
manufOrder.setPlannedStartDateT(appProductionService.getTodayDateTime().toLocalDateTime());
}
if (prodProcess != null && prodProcess.getProdProcessLineList() != null) {
for (ProdProcessLine prodProcessLine :
this._sortProdProcessLineByPriority(prodProcess.getProdProcessLineList())) {
manufOrder.addOperationOrderListItem(
operationOrderService.createOperationOrder(manufOrder, prodProcessLine));
}
}
if (manufOrder.getBillOfMaterial() != null) {
for (BillOfMaterial bom : manufOrder.getBillOfMaterial().getBillOfMaterialSet()) {
BillOfMaterialConsumption newBom =
Beans.get(BillOfMaterialServiceImpl.class)
.createBomConsumptionFromRawMaterial(bom, manufOrder);
manufOrder.addBillOfMaterialConsumptionListItem(newBom);
}
}
manufOrderRepo.save(manufOrder);
manufOrder.setPlannedEndDateT(manufOrderWorkflowService.computePlannedEndDateT(manufOrder));
System.out.println("***************************");
System.out.println("yessssssssssssssssss");
System.out.println("***************************");
manufOrderRepo.save(manufOrder);
}
/**
* Trier une liste de ligne de règle de template
*
* @param templateRuleLine
*/
public List<ProdProcessLine> _sortProdProcessLineByPriority(
List<ProdProcessLine> prodProcessLineList) {
Collections.sort(
prodProcessLineList,
new Comparator<ProdProcessLine>() {
@Override
public int compare(ProdProcessLine ppl1, ProdProcessLine ppl2) {
return ppl1.getPriority().compareTo(ppl2.getPriority());
}
});
return prodProcessLineList;
}
@Override
public String getManufOrderSeq(ManufOrder manufOrder) throws AxelorException {
ProductionConfigService productionConfigService = Beans.get(ProductionConfigService.class);
ProductionConfig productionConfig =
productionConfigService.getProductionConfig(manufOrder.getCompany());
Sequence sequence =
productionConfigService.getManufOrderSequence(
productionConfig, manufOrder.getWorkshopStockLocation());
if (manufOrder.getStypeSelect() == ManufOrderRepository.STYPE_PACKAGING_ORDER) {
sequence = productionConfig.getPackagingOrderSequence();
}
String seq = sequenceService.getSequenceNumber(sequence);
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.MANUF_ORDER_SEQ));
}
return seq;
}
@Override
public boolean isManagedConsumedProduct(BillOfMaterial billOfMaterial) {
if (billOfMaterial != null
&& billOfMaterial.getProdProcess() != null
&& billOfMaterial.getProdProcess().getProdProcessLineList() != null) {
for (ProdProcessLine prodProcessLine :
billOfMaterial.getProdProcess().getProdProcessLineList()) {
if ((prodProcessLine.getToConsumeProdProductList() != null
&& !prodProcessLine.getToConsumeProdProductList().isEmpty())) {
return true;
}
}
}
return false;
}
public BillOfMaterial getBillOfMaterial(Product product) throws AxelorException {
BillOfMaterial billOfMaterial = product.getDefaultBillOfMaterial();
if (billOfMaterial == null && product.getParentProduct() != null) {
billOfMaterial = product.getParentProduct().getDefaultBillOfMaterial();
}
if (billOfMaterial == null) {
throw new AxelorException(
product,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_ORDER_SALES_ORDER_NO_BOM),
product.getName(),
product.getCode());
}
return billOfMaterial;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public StockMove generateWasteStockMove(ManufOrder manufOrder) throws AxelorException {
StockMove wasteStockMove = null;
Company company = manufOrder.getCompany();
if (manufOrder.getWasteProdProductList() == null
|| company == null
|| manufOrder.getWasteProdProductList().isEmpty()) {
return wasteStockMove;
}
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockMoveService stockMoveService = Beans.get(StockMoveService.class);
StockMoveLineService stockMoveLineService = Beans.get(StockMoveLineService.class);
AppBaseService appBaseService = Beans.get(AppBaseService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
StockLocation wasteStockLocation = stockConfigService.getWasteStockLocation(stockConfig);
wasteStockMove =
stockMoveService.createStockMove(
virtualStockLocation.getAddress(),
wasteStockLocation.getAddress(),
company,
virtualStockLocation,
wasteStockLocation,
null,
appBaseService.getTodayDate(),
manufOrder.getWasteProdDescription(),
StockMoveRepository.TYPE_INTERNAL);
for (ProdProduct prodProduct : manufOrder.getWasteProdProductList()) {
stockMoveLineService.createStockMoveLine(
prodProduct.getProduct(),
prodProduct.getProduct().getName(),
prodProduct.getProduct().getDescription(),
prodProduct.getQty(),
prodProduct.getProduct().getCostPrice(),
prodProduct.getProduct().getCostPrice(),
prodProduct.getUnit(),
wasteStockMove,
StockMoveLineService.TYPE_WASTE_PRODUCTIONS,
false,
BigDecimal.ZERO);
}
stockMoveService.validate(wasteStockMove);
manufOrder.setWasteStockMove(wasteStockMove);
return wasteStockMove;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updatePlannedQty(ManufOrder manufOrder) throws AxelorException {
manufOrder.clearToConsumeProdProductList();
manufOrder.clearToProduceProdProductList();
this.createToConsumeProdProductList(manufOrder);
this.createToProduceProdProductList(manufOrder);
updateRealQty(manufOrder, manufOrder.getQty());
LocalDateTime plannedStartDateT = manufOrder.getPlannedStartDateT();
manufOrderWorkflowService.updatePlannedDates(
manufOrder,
plannedStartDateT != null
? plannedStartDateT
: appProductionService.getTodayDateTime().toLocalDateTime());
manufOrderRepo.save(manufOrder);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateRealQty(ManufOrder manufOrder, BigDecimal qtyToUpdate) throws AxelorException {
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
if (!manufOrder.getIsConsProOnOperation()) {
manufOrderStockMoveService.createNewConsumedStockMoveLineList(manufOrder, qtyToUpdate);
updateDiffProdProductList(manufOrder);
} else {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
Beans.get(OperationOrderStockMoveService.class)
.createNewConsumedStockMoveLineList(operationOrder, qtyToUpdate);
Beans.get(OperationOrderService.class).updateDiffProdProductList(operationOrder);
}
}
manufOrderStockMoveService.createNewProducedStockMoveLineList(manufOrder, qtyToUpdate);
}
@Override
public ManufOrder updateDiffProdProductList(ManufOrder manufOrder) throws AxelorException {
List<ProdProduct> toConsumeList = manufOrder.getToConsumeProdProductList();
List<StockMoveLine> consumedList = manufOrder.getConsumedStockMoveLineList();
if (toConsumeList == null || consumedList == null) {
return manufOrder;
}
List<ProdProduct> diffConsumeList =
createDiffProdProductList(manufOrder, toConsumeList, consumedList);
manufOrder.clearDiffConsumeProdProductList();
diffConsumeList.forEach(manufOrder::addDiffConsumeProdProductListItem);
return manufOrder;
}
@Override
public List<ProdProduct> createDiffProdProductList(
ManufOrder manufOrder,
List<ProdProduct> prodProductList,
List<StockMoveLine> stockMoveLineList)
throws AxelorException {
List<ProdProduct> diffConsumeList =
createDiffProdProductList(prodProductList, stockMoveLineList);
diffConsumeList.forEach(prodProduct -> prodProduct.setDiffConsumeManufOrder(manufOrder));
return diffConsumeList;
}
@Override
public List<ProdProduct> createDiffProdProductList(
List<ProdProduct> prodProductList, List<StockMoveLine> stockMoveLineList)
throws AxelorException {
List<ProdProduct> diffConsumeList = new ArrayList<>();
for (ProdProduct prodProduct : prodProductList) {
Product product = prodProduct.getProduct();
Unit newUnit = prodProduct.getUnit();
List<StockMoveLine> stockMoveLineProductList =
stockMoveLineList
.stream()
.filter(stockMoveLine1 -> stockMoveLine1.getProduct() != null)
.filter(stockMoveLine1 -> stockMoveLine1.getProduct().equals(product))
.collect(Collectors.toList());
if (stockMoveLineProductList.isEmpty()) {
StockMoveLine stockMoveLine = new StockMoveLine();
stockMoveLineProductList.add(stockMoveLine);
}
BigDecimal diffQty = computeDiffQty(prodProduct, stockMoveLineProductList, product);
BigDecimal plannedQty = prodProduct.getQty();
BigDecimal realQty = diffQty.add(plannedQty);
if (diffQty.compareTo(BigDecimal.ZERO) != 0) {
ProdProduct diffProdProduct = new ProdProduct();
diffProdProduct.setQty(diffQty);
diffProdProduct.setPlannedQty(plannedQty);
diffProdProduct.setRealQty(realQty);
diffProdProduct.setProduct(product);
diffProdProduct.setUnit(newUnit);
diffConsumeList.add(diffProdProduct);
}
}
// There are stock move lines with products that are not available in
// prod product list. It needs to appear in the prod product list
List<StockMoveLine> stockMoveLineMissingProductList =
stockMoveLineList
.stream()
.filter(stockMoveLine1 -> stockMoveLine1.getProduct() != null)
.filter(
stockMoveLine1 ->
!prodProductList
.stream()
.map(ProdProduct::getProduct)
.collect(Collectors.toList())
.contains(stockMoveLine1.getProduct()))
.collect(Collectors.toList());
for (StockMoveLine stockMoveLine : stockMoveLineMissingProductList) {
if (stockMoveLine.getQty().compareTo(BigDecimal.ZERO) != 0) {
ProdProduct diffProdProduct = new ProdProduct();
diffProdProduct.setQty(stockMoveLine.getQty());
diffProdProduct.setPlannedQty(BigDecimal.ZERO);
diffProdProduct.setRealQty(stockMoveLine.getQty());
diffProdProduct.setProduct(stockMoveLine.getProduct());
diffProdProduct.setUnit(stockMoveLine.getUnit());
diffConsumeList.add(diffProdProduct);
}
}
return diffConsumeList;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateConsumedStockMoveFromManufOrder(ManufOrder manufOrder) throws AxelorException {
this.updateDiffProdProductList(manufOrder);
List<StockMoveLine> consumedStockMoveLineList = manufOrder.getConsumedStockMoveLineList();
if (consumedStockMoveLineList == null) {
return;
}
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
Optional<StockMove> stockMoveOpt =
manufOrderStockMoveService.getPlannedStockMove(manufOrder.getInStockMoveList());
StockMove stockMove;
if (stockMoveOpt.isPresent()) {
stockMove = stockMoveOpt.get();
} else {
// stockMove =
// manufOrderStockMoveService._createToConsumeStockMove(manufOrder,
// manufOrder.getCompany());
// manufOrder.addInStockMoveListItem(stockMove);
// Beans.get(StockMoveService.class).plan(stockMove);
}
// updateStockMoveFromManufOrder(consumedStockMoveLineList, stockMove);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateProducedStockMoveFromManufOrder(ManufOrder manufOrder) throws AxelorException {
List<StockMoveLine> producedStockMoveLineList = manufOrder.getProducedStockMoveLineList();
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
Optional<StockMove> stockMoveOpt =
manufOrderStockMoveService.getPlannedStockMove(manufOrder.getOutStockMoveList());
StockMove stockMove;
if (stockMoveOpt.isPresent()) {
stockMove = stockMoveOpt.get();
} else {
stockMove =
manufOrderStockMoveService._createToProduceStockMove(manufOrder, manufOrder.getCompany());
manufOrder.addOutStockMoveListItem(stockMove);
Beans.get(StockMoveService.class).plan(stockMove);
}
updateStockMoveFromManufOrder(producedStockMoveLineList, stockMove);
}
@Override
public void checkConsumedStockMoveLineList(ManufOrder manufOrder, ManufOrder oldManufOrder)
throws AxelorException {
checkRealizedStockMoveLineList(
manufOrder.getConsumedStockMoveLineList(), oldManufOrder.getConsumedStockMoveLineList());
}
@Override
public void checkProducedStockMoveLineList(ManufOrder manufOrder, ManufOrder oldManufOrder)
throws AxelorException {
checkRealizedStockMoveLineList(
manufOrder.getProducedStockMoveLineList(), oldManufOrder.getProducedStockMoveLineList());
}
@Override
public void checkRealizedStockMoveLineList(
List<StockMoveLine> stockMoveLineList, List<StockMoveLine> oldStockMoveLineList)
throws AxelorException {
List<StockMoveLine> realizedProducedStockMoveLineList =
stockMoveLineList
.stream()
.filter(
stockMoveLine ->
stockMoveLine.getStockMove() != null
&& stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_REALIZED)
.sorted(Comparator.comparingLong(StockMoveLine::getId))
.collect(Collectors.toList());
List<StockMoveLine> oldRealizedProducedStockMoveLineList =
oldStockMoveLineList
.stream()
.filter(
stockMoveLine ->
stockMoveLine.getStockMove() != null
&& stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_REALIZED)
.sorted(Comparator.comparingLong(StockMoveLine::getId))
.collect(Collectors.toList());
// the two lists must be equal
if (!realizedProducedStockMoveLineList.equals(oldRealizedProducedStockMoveLineList)) {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.CANNOT_DELETE_REALIZED_STOCK_MOVE_LINES));
}
}
@Override
public void updateStockMoveFromManufOrder(
List<StockMoveLine> stockMoveLineList, StockMove stockMove) throws AxelorException {
if (stockMoveLineList == null) {
return;
}
// add missing lines in stock move
stockMoveLineList
.stream()
.filter(stockMoveLine -> stockMoveLine.getStockMove() == null)
.forEach(stockMove::addStockMoveLineListItem);
// remove lines in stock move removed in manuf order
if (stockMove.getStockMoveLineList() != null) {
stockMove
.getStockMoveLineList()
.removeIf(stockMoveLine -> !stockMoveLineList.contains(stockMoveLine));
}
StockMoveService stockMoveService = Beans.get(StockMoveService.class);
// update stock location by cancelling then planning stock move.
stockMoveService.cancel(stockMove);
stockMoveService.plan(stockMove);
}
/**
* Compute the difference in qty between a prodProduct and the qty in a list of stock move lines.
*
* @param prodProduct
* @param stockMoveLineList
* @param product
* @return
* @throws AxelorException
*/
protected BigDecimal computeDiffQty(
ProdProduct prodProduct, List<StockMoveLine> stockMoveLineList, Product product)
throws AxelorException {
BigDecimal consumedQty = BigDecimal.ZERO;
for (StockMoveLine stockMoveLine : stockMoveLineList) {
if (stockMoveLine.getUnit() != null && prodProduct.getUnit() != null) {
consumedQty =
consumedQty.add(
Beans.get(UnitConversionService.class)
.convert(
stockMoveLine.getUnit(),
prodProduct.getUnit(),
stockMoveLine.getRealQty(),
stockMoveLine.getRealQty().scale(),
product));
} else {
consumedQty = consumedQty.add(stockMoveLine.getRealQty());
}
}
return consumedQty.subtract(prodProduct.getQty());
}
@Override
public String getConsumeAndMissingQtyForAProduct(
Long productId, Long companyId, Long stockLocationId) {
List<Integer> statusList = getMOFiltersOnProductionConfig();
String statusListQuery =
statusList.stream().map(String::valueOf).collect(Collectors.joining(","));
String query =
"self.product.id = "
+ productId
+ " AND self.stockMove.statusSelect = "
+ StockMoveRepository.STATUS_PLANNED
+ " AND self.stockMove.fromStockLocation.typeSelect != "
+ StockLocationRepository.TYPE_VIRTUAL
+ " AND ( (self.consumedManufOrder IS NOT NULL AND self.consumedManufOrder.statusSelect IN ("
+ statusListQuery
+ "))"
+ " OR (self.consumedOperationOrder IS NOT NULL AND self.consumedOperationOrder.statusSelect IN ( "
+ statusListQuery
+ ") ) ) ";
if (companyId != 0L) {
query += " AND self.stockMove.company.id = " + companyId;
if (stockLocationId != 0L) {
if (stockLocationId != 0L) {
StockLocation stockLocation =
Beans.get(StockLocationRepository.class).find(stockLocationId);
List<StockLocation> stockLocationList =
Beans.get(StockLocationService.class)
.getAllLocationAndSubLocation(stockLocation, false);
if (!stockLocationList.isEmpty() && stockLocation.getCompany().getId() == companyId) {
query +=
" AND self.stockMove.fromStockLocation.id IN ("
+ StringTool.getIdListString(stockLocationList)
+ ") ";
}
}
}
}
return query;
}
@Override
public String getBuildingQtyForAProduct(Long productId, Long companyId, Long stockLocationId) {
List<Integer> statusList = getMOFiltersOnProductionConfig();
String statusListQuery =
statusList.stream().map(String::valueOf).collect(Collectors.joining(","));
String query =
"self.product.id = "
+ productId
+ " AND self.stockMove.statusSelect = "
+ StockMoveRepository.STATUS_PLANNED
+ " AND self.stockMove.toStockLocation.typeSelect != "
+ StockLocationRepository.TYPE_VIRTUAL
+ " AND self.producedManufOrder IS NOT NULL "
+ " AND self.producedManufOrder.statusSelect IN ( "
+ statusListQuery
+ " )";
if (companyId != 0L) {
query += "AND self.stockMove.company.id = " + companyId;
if (stockLocationId != 0L) {
StockLocation stockLocation =
Beans.get(StockLocationRepository.class).find(stockLocationId);
List<StockLocation> stockLocationList =
Beans.get(StockLocationService.class)
.getAllLocationAndSubLocation(stockLocation, false);
if (!stockLocationList.isEmpty() && stockLocation.getCompany().getId() == companyId) {
query +=
" AND self.stockMove.toStockLocation.id IN ("
+ StringTool.getIdListString(stockLocationList)
+ ") ";
}
}
}
return query;
}
private List<Integer> getMOFiltersOnProductionConfig() {
List<Integer> statusList = new ArrayList<>();
statusList.add(ManufOrderRepository.STATUS_IN_PROGRESS);
statusList.add(ManufOrderRepository.STATUS_STANDBY);
String status = appProductionService.getAppProduction().getmOFilterOnStockDetailStatusSelect();
if (!StringUtils.isBlank(status)) {
statusList = StringTool.getIntegerList(status);
}
return statusList;
}
}

View File

@ -0,0 +1,819 @@
/*
* 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.production.service.manuforder;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.production.db.BillOfMaterialConsumption;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.config.StockConfigProductionService;
import com.axelor.apps.production.service.operationorder.OperationOrderStockMoveService;
import com.axelor.apps.stock.db.StockConfig;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.StockMoveLineService;
import com.axelor.apps.stock.service.StockMoveService;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ManufOrderStockMoveService {
public static final int PART_FINISH_IN = 1;
public static final int PART_FINISH_OUT = 2;
public static final int STOCK_LOCATION_IN = 1;
public static final int STOCK_LOCATION_OUT = 2;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected StockMoveService stockMoveService;
protected StockMoveLineService stockMoveLineService;
@Inject
public ManufOrderStockMoveService(
StockMoveService stockMoveService, StockMoveLineService stockMoveLineService) {
this.stockMoveService = stockMoveService;
this.stockMoveLineService = stockMoveLineService;
}
public void createToConsumeStockMove(ManufOrder manufOrder) throws AxelorException {
Company company = manufOrder.getCompany();
if (manufOrder.getToConsumeProdProductList() != null && company != null) {
StockMove stockMove = this._createToConsumeStockMove(manufOrder, company);
for (ProdProduct prodProduct : manufOrder.getToConsumeProdProductList()) {
this._createStockMoveLine(prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS);
}
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
stockMoveService.plan(stockMove);
manufOrder.addInStockMoveListItem(stockMove);
}
// fill here the consumed stock move line list item to manage the
// case where we had to split tracked stock move lines
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
manufOrder.addTransferedStockMoveLineListItem(stockMoveLine);
}
}
}
}
public void createToTransferStockMove(ManufOrder manufOrder) throws AxelorException {
Company company = manufOrder.getCompany();
if (manufOrder.getToConsumeProdProductList() != null && company != null) {
StockMove stockMove = this._createToConsumeStockMove(manufOrder, company);
for (ProdProduct prodProduct : manufOrder.getToConsumeProdProductList()) {
this._createStockMoveLine(prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS);
}
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
stockMoveService.plan(stockMove);
manufOrder.addInStockMoveListItem(stockMove);
}
// fill here the consumed stock move line list item to manage the
// case where we had to split tracked stock move lines
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
manufOrder.addTransferedStockMoveLineListItem(stockMoveLine);
}
}
}
}
public StockMove _createToConsumeStockMove(ManufOrder manufOrder, Company company)
throws AxelorException {
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
StockLocation fromStockLocation =
getDefaultStockLocation(manufOrder, company, STOCK_LOCATION_IN);
StockMove stockMove =
stockMoveService.createStockMove(
null,
null,
company,
manufOrder.getProdProcess().getStockLocation(),
manufOrder.getWorkshopStockLocation(),
null,
manufOrder.getPlannedStartDateT().toLocalDate(),
null,
StockMoveRepository.TYPE_INTERNAL);
stockMove.setOriginId(manufOrder.getId());
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_MANUF_ORDER);
stockMove.setOrigin(manufOrder.getManufOrderSeq());
return stockMove;
}
/**
* Given a manuf order, its company and whether we want to create a in or out stock move,
* determine the default stock location and return it. First search in prodprocess, then in
* company stock configuration.
*
* @param manufOrder a manufacturing order.
* @param company a company with stock config.
* @param inOrOut can be {@link ManufOrderStockMoveService#STOCK_LOCATION_IN} or {@link
* ManufOrderStockMoveService#STOCK_LOCATION_OUT}.
* @return the found stock location, which can be null.
* @throws AxelorException if the stock config is missing for the company.
*/
public StockLocation getDefaultStockLocation(ManufOrder manufOrder, Company company, int inOrOut)
throws AxelorException {
if (inOrOut != STOCK_LOCATION_IN && inOrOut != STOCK_LOCATION_OUT) {
throw new IllegalArgumentException(I18n.get(IExceptionMessage.IN_OR_OUT_INVALID_ARG));
}
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation stockLocation = getDefaultStockLocation(manufOrder.getProdProcess(), inOrOut);
if (stockLocation == null) {
stockLocation =
inOrOut == STOCK_LOCATION_IN
? stockConfigService.getComponentDefaultStockLocation(stockConfig)
: stockConfigService.getFinishedProductsDefaultStockLocation(stockConfig);
}
return stockLocation;
}
/**
* Given a prodprocess and whether we want to create a in or out stock move, determine the stock
* location and return it.
*
* @param prodProcess a production process.
* @param inOrOut can be {@link ManufOrderStockMoveService#STOCK_LOCATION_IN} or {@link
* ManufOrderStockMoveService#STOCK_LOCATION_OUT}.
* @return the found stock location, or null if the prod process is null.
*/
protected StockLocation getDefaultStockLocation(ProdProcess prodProcess, int inOrOut) {
if (inOrOut != STOCK_LOCATION_IN && inOrOut != STOCK_LOCATION_OUT) {
throw new IllegalArgumentException(I18n.get(IExceptionMessage.IN_OR_OUT_INVALID_ARG));
}
if (prodProcess == null) {
return null;
}
if (inOrOut == STOCK_LOCATION_IN) {
return prodProcess.getStockLocation();
} else {
return prodProcess.getProducedProductStockLocation();
}
}
public void createToProduceStockMove(ManufOrder manufOrder) throws AxelorException {
Company company = manufOrder.getCompany();
if (manufOrder.getToProduceProdProductList() != null && company != null) {
StockMove stockMove = this._createToProduceStockMove(manufOrder, company);
for (ProdProduct prodProduct : manufOrder.getToProduceProdProductList()) {
this._createStockMoveLine(
prodProduct,
stockMove,
StockMoveLineService.TYPE_OUT_PRODUCTIONS,
prodProduct.getQty(),
getCostPriceFromProduct(prodProduct));
}
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
stockMoveService.plan(stockMove);
manufOrder.addOutStockMoveListItem(stockMove);
}
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
if (manufOrder.getTrackingNumber() != null) {
stockMoveLine.setTrackingNumber(manufOrder.getTrackingNumber());
}
manufOrder.addProducedStockMoveLineListItem(stockMoveLine);
}
}
}
}
/**
* Consume in stock moves in manuf order.
*
* @param manufOrder
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public void consumeInStockMoves(ManufOrder manufOrder) throws AxelorException {
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(manufOrder.getCompany());
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
for (StockMove stockMove : manufOrder.getInStockMoveList()) {
if (stockMove.getStatusSelect() == StockMoveRepository.STATUS_REALIZED) {
StockMove move = Beans.get(StockMoveRepository.class).copy(stockMove, true);
move.setPartner(manufOrder.getCompany().getPartner());
move.setTypeSelect(StockMoveRepository.TYPE_OUTGOING);
move.setFromStockLocation(manufOrder.getWorkshopStockLocation());
move.setToStockLocation(virtualStockLocation);
// move.setInManufOrder(null);
move.setOutManufOrder(manufOrder);
Beans.get(StockMoveRepository.class).save(move);
if (move.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : move.getStockMoveLineList()) {
stockMoveLine.setTransferedManufOrder(null);
stockMoveLine.setConsumedManufOrder(manufOrder);
manufOrder.addConsumedStockMoveLineListItem(stockMoveLine);
}
}
// stockMoveService.plan(move);
}
finishStockMove(stockMove);
}
}
@Transactional(rollbackOn = {Exception.class})
public void validateOutStockMoves(ManufOrder manufOrder) throws AxelorException {
for (StockMove stockMove : manufOrder.getOutStockMoveList()) {
System.out.println("***************************");
System.out.println(stockMove.getStockMoveSeq());
stockMoveService.plan(stockMove);
finishStockMove(stockMove);
}
}
@Transactional(rollbackOn = {Exception.class})
public void validateInStockMoves(ManufOrder manufOrder) throws AxelorException {
for (StockMove stockMove : manufOrder.getInStockMoveList()) {
System.out.println("***************************");
System.out.println(stockMove.getStockMoveSeq());
stockMoveService.plan(stockMove);
finishStockMove(stockMove);
}
}
/**
* A null safe method to get costPrice from the product in the prod product.
*
* @param prodProduct a nullable prodProduct.
* @return the cost price or 0 if the product is null.
*/
protected BigDecimal getCostPriceFromProduct(ProdProduct prodProduct) {
Optional<Product> product = Optional.ofNullable(prodProduct.getProduct());
return product.map(Product::getCostPrice).orElse(BigDecimal.ZERO);
}
protected StockMove _createToProduceStockMove(ManufOrder manufOrder, Company company)
throws AxelorException {
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
LocalDateTime plannedEndDateT = manufOrder.getPlannedEndDateT();
LocalDate plannedEndDate = plannedEndDateT != null ? plannedEndDateT.toLocalDate() : null;
StockLocation producedProductStockLocation =
manufOrder.getProdProcess().getProducedProductStockLocation();
if (producedProductStockLocation == null) {
producedProductStockLocation =
stockConfigService.getFinishedProductsDefaultStockLocation(stockConfig);
}
StockMove stockMove =
stockMoveService.createStockMove(
null,
null,
company,
virtualStockLocation,
producedProductStockLocation,
null,
plannedEndDate,
null,
StockMoveRepository.TYPE_INTERNAL);
stockMove.setOriginId(manufOrder.getId());
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_MANUF_ORDER);
stockMove.setOrigin(manufOrder.getManufOrderSeq());
return stockMove;
}
public StockMoveLine _createStockMoveLine(
ProdProduct prodProduct, StockMove stockMove, int inOrOutType) throws AxelorException {
return _createStockMoveLine(prodProduct, stockMove, inOrOutType, prodProduct.getQty());
}
public StockMoveLine _createStockMoveLine(
ProdProduct prodProduct, StockMove stockMove, int inOrOutType, BigDecimal qty)
throws AxelorException {
return _createStockMoveLine(
prodProduct, stockMove, inOrOutType, qty, getCostPriceFromProduct(prodProduct));
}
protected StockMoveLine _createStockMoveLine(
ProdProduct prodProduct,
StockMove stockMove,
int inOrOutType,
BigDecimal qty,
BigDecimal costPrice)
throws AxelorException {
return stockMoveLineService.createStockMoveLine(
prodProduct.getProduct(),
prodProduct.getProduct().getName(),
prodProduct.getProduct().getDescription(),
qty,
costPrice,
costPrice,
prodProduct.getUnit(),
stockMove,
inOrOutType,
false,
BigDecimal.ZERO);
}
public void finish(ManufOrder manufOrder) throws AxelorException {
// clear empty stock move
manufOrder
.getInStockMoveList()
.removeIf(stockMove -> CollectionUtils.isEmpty(stockMove.getStockMoveLineList()));
manufOrder
.getOutStockMoveList()
.removeIf(stockMove -> CollectionUtils.isEmpty(stockMove.getStockMoveLineList()));
// finish remaining stock move
for (StockMove stockMove : manufOrder.getInStockMoveList()) {
this.finishStockMove(stockMove);
}
if (manufOrder.getStypeSelect() == ManufOrderRepository.STYPE_PACKAGING_ORDER) {
for (StockMove stockMove : manufOrder.getOutStockMoveList()) {
updateRealPrice(manufOrder, stockMove);
// this.finishStockMove(stockMove);
}
}
}
/**
* Update price in stock move line: if the product price is configured to be real, then we use the
* cost price from costsheet. Else, we do nothing as the planned price is already filled.
*
* @param manufOrder
* @param stockMove
*/
protected void updateRealPrice(ManufOrder manufOrder, StockMove stockMove) {
stockMove
.getStockMoveLineList()
.stream()
.filter(
stockMoveLine ->
stockMoveLine.getProduct() != null
&& stockMoveLine.getProduct().getRealOrEstimatedPriceSelect()
== ProductRepository.PRICE_METHOD_REAL)
.forEach(stockMoveLine -> stockMoveLine.setUnitPriceUntaxed(manufOrder.getCostPrice()));
}
public void finishStockMove(StockMove stockMove) throws AxelorException {
if (stockMove != null && stockMove.getStatusSelect() == StockMoveRepository.STATUS_PLANNED) {
stockMove.setIsWithBackorder(false);
// stockMoveService.copyQtyToRealQty(stockMove);
stockMoveService.realize(stockMove);
}
}
/**
* Call the method to realize in stock move, then the method to realize out stock move for the
* given manufacturing order.
*
* @param manufOrder
*/
@Transactional(rollbackOn = {Exception.class})
public void partialFinish(ManufOrder manufOrder) throws AxelorException {
if (manufOrder.getIsConsProOnOperation()) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_IN_PROGRESS) {
Beans.get(OperationOrderStockMoveService.class).partialFinish(operationOrder);
}
}
} else {
partialFinish(manufOrder, PART_FINISH_IN);
}
partialFinish(manufOrder, PART_FINISH_OUT);
Beans.get(ManufOrderRepository.class).save(manufOrder);
}
/**
* Allows to create and realize in or out stock moves for the given manufacturing order.
*
* @param manufOrder
* @param inOrOut can be {@link ManufOrderStockMoveService#PART_FINISH_IN} or {@link
* ManufOrderStockMoveService#PART_FINISH_OUT}
* @throws AxelorException
*/
protected void partialFinish(ManufOrder manufOrder, int inOrOut) throws AxelorException {
if (inOrOut != PART_FINISH_IN && inOrOut != PART_FINISH_OUT) {
throw new IllegalArgumentException(I18n.get(IExceptionMessage.IN_OR_OUT_INVALID_ARG));
}
Company company = manufOrder.getCompany();
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation fromStockLocation;
StockLocation toStockLocation;
Set<StockMove> stockMoveList;
if (inOrOut == PART_FINISH_IN) {
stockMoveList = manufOrder.getInStockMoveList();
fromStockLocation = getDefaultStockLocation(manufOrder, company, STOCK_LOCATION_IN);
toStockLocation = stockConfigService.getProductionVirtualStockLocation(stockConfig);
} else {
stockMoveList = manufOrder.getOutStockMoveList();
fromStockLocation = stockConfigService.getProductionVirtualStockLocation(stockConfig);
toStockLocation = getDefaultStockLocation(manufOrder, company, STOCK_LOCATION_OUT);
}
// realize current stock move and update the price
Optional<StockMove> stockMoveToRealize = getPlannedStockMove(stockMoveList);
if (stockMoveToRealize.isPresent()) {
updateRealPrice(manufOrder, stockMoveToRealize.get());
finishStockMove(stockMoveToRealize.get());
}
// generate new stock move
StockMove newStockMove =
stockMoveService.createStockMove(
null,
null,
company,
fromStockLocation,
toStockLocation,
null,
manufOrder.getPlannedStartDateT().toLocalDate(),
null,
StockMoveRepository.TYPE_INTERNAL);
newStockMove.setStockMoveLineList(new ArrayList<>());
newStockMove.setOrigin(manufOrder.getManufOrderSeq());
newStockMove.setOriginId(manufOrder.getId());
newStockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_MANUF_ORDER);
createNewStockMoveLines(manufOrder, newStockMove, inOrOut);
if (!newStockMove.getStockMoveLineList().isEmpty()) {
// plan the stockmove
stockMoveService.plan(newStockMove);
if (inOrOut == PART_FINISH_IN) {
manufOrder.addInStockMoveListItem(newStockMove);
newStockMove.getStockMoveLineList().forEach(manufOrder::addConsumedStockMoveLineListItem);
manufOrder.clearDiffConsumeProdProductList();
} else {
manufOrder.addOutStockMoveListItem(newStockMove);
newStockMove.getStockMoveLineList().forEach(manufOrder::addProducedStockMoveLineListItem);
}
}
}
/**
* Get the planned stock move in a stock move list
*
* @param stockMoveList can be {@link ManufOrder#inStockMoveList} or {@link
* ManufOrder#outStockMoveList}
* @return an optional stock move
*/
public Optional<StockMove> getPlannedStockMove(Set<StockMove> stockMoveList) {
return stockMoveList
.stream()
.filter(stockMove -> stockMove.getStatusSelect() == StockMoveRepository.STATUS_PLANNED)
.findFirst();
}
public Optional<StockMove> getPlannedStockMove(List<StockMove> stockMoveList) {
return stockMoveList
.stream()
.filter(stockMove -> stockMove.getStatusSelect() == StockMoveRepository.STATUS_PLANNED)
.findFirst();
}
/**
* Generate stock move lines after a partial finish
*
* @param manufOrder
* @param stockMove
* @param inOrOut can be {@link ManufOrderStockMoveService#PART_FINISH_IN} or {@link
* ManufOrderStockMoveService#PART_FINISH_OUT}
*/
public void createNewStockMoveLines(ManufOrder manufOrder, StockMove stockMove, int inOrOut)
throws AxelorException {
int stockMoveLineType;
List<ProdProduct> diffProdProductList;
if (inOrOut == PART_FINISH_IN) {
stockMoveLineType = StockMoveLineService.TYPE_IN_PRODUCTIONS;
diffProdProductList = new ArrayList<>(manufOrder.getDiffConsumeProdProductList());
} else {
stockMoveLineType = StockMoveLineService.TYPE_OUT_PRODUCTIONS;
// must compute remaining quantities in produced product
List<ProdProduct> outProdProductList = manufOrder.getToProduceProdProductList();
List<StockMoveLine> stockMoveLineList = manufOrder.getProducedStockMoveLineList();
if (outProdProductList == null || stockMoveLineList == null) {
return;
}
diffProdProductList =
Beans.get(ManufOrderService.class)
.createDiffProdProductList(manufOrder, outProdProductList, stockMoveLineList);
}
createNewStockMoveLines(diffProdProductList, stockMove, stockMoveLineType);
}
/**
* Generate stock move lines after a partial finish
*
* @param diffProdProductList
* @param stockMove
* @param stockMoveLineType
* @throws AxelorException
*/
public void createNewStockMoveLines(
List<ProdProduct> diffProdProductList, StockMove stockMove, int stockMoveLineType)
throws AxelorException {
diffProdProductList.forEach(prodProduct -> prodProduct.setQty(prodProduct.getQty().negate()));
for (ProdProduct prodProduct : diffProdProductList) {
if (prodProduct.getQty().signum() >= 0) {
_createStockMoveLine(prodProduct, stockMove, stockMoveLineType);
}
}
}
public void cancel(ManufOrder manufOrder) throws AxelorException {
for (StockMove stockMove : manufOrder.getInStockMoveList()) {
this.cancel(stockMove);
}
for (StockMove stockMove : manufOrder.getOutStockMoveList()) {
this.cancel(stockMove);
}
}
public void cancel(StockMove stockMove) throws AxelorException {
if (stockMove != null) {
stockMoveService.cancel(stockMove);
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
stockMoveLine.setProducedManufOrder(null);
stockMoveLine.setTransferedManufOrder(null);
}
}
}
/**
* Clear the consumed list and create a new one with the right quantity.
*
* @param manufOrder
* @param qtyToUpdate
*/
public void createNewConsumedStockMoveLineList(ManufOrder manufOrder, BigDecimal qtyToUpdate)
throws AxelorException {
// find planned stock move
Optional<StockMove> stockMoveOpt = getPlannedStockMove(manufOrder.getInStockMoveList());
if (!stockMoveOpt.isPresent()) {
return;
}
StockMove stockMove = stockMoveOpt.get();
stockMoveService.cancel(stockMove);
// clear all lists from planned lines
manufOrder
.getConsumedStockMoveLineList()
.removeIf(
stockMoveLine ->
stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_CANCELED);
// clear all lists from planned lines
manufOrder
.getTransferedStockMoveLineList()
.removeIf(
stockMoveLine ->
stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_CANCELED);
stockMove.clearStockMoveLineList();
// create a new list
for (ProdProduct prodProduct : manufOrder.getToConsumeProdProductList()) {
BigDecimal qty = getFractionQty(manufOrder, prodProduct, qtyToUpdate);
_createStockMoveLine(prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS, qty);
// Update consumed StockMoveLineList with created stock move lines
stockMove
.getStockMoveLineList()
.stream()
.filter(
stockMoveLine1 -> !manufOrder.getConsumedStockMoveLineList().contains(stockMoveLine1))
.forEach(manufOrder::addConsumedStockMoveLineListItem);
}
stockMoveService.plan(stockMove);
}
/**
* Clear the produced list and create a new one with the right quantity.
*
* @param manufOrder
* @param qtyToUpdate
*/
public void createNewProducedStockMoveLineList(ManufOrder manufOrder, BigDecimal qtyToUpdate)
throws AxelorException {
Optional<StockMove> stockMoveOpt = getPlannedStockMove(manufOrder.getOutStockMoveList());
if (!stockMoveOpt.isPresent()) {
return;
}
StockMove stockMove = stockMoveOpt.get();
stockMoveService.cancel(stockMove);
// clear all lists
manufOrder
.getProducedStockMoveLineList()
.removeIf(
stockMoveLine ->
stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_CANCELED);
stockMove.clearStockMoveLineList();
// create a new list
for (ProdProduct prodProduct : manufOrder.getToProduceProdProductList()) {
BigDecimal qty = getFractionQty(manufOrder, prodProduct, qtyToUpdate);
_createStockMoveLine(
prodProduct,
stockMove,
StockMoveLineService.TYPE_OUT_PRODUCTIONS,
qty,
getCostPriceFromProduct(prodProduct));
// Update produced StockMoveLineList with created stock move lines
stockMove
.getStockMoveLineList()
.stream()
.filter(
stockMoveLine1 -> !manufOrder.getProducedStockMoveLineList().contains(stockMoveLine1))
.forEach(manufOrder::addProducedStockMoveLineListItem);
}
stockMoveService.plan(stockMove);
}
/**
* Compute the right qty when modifying real quantity in a manuf order
*
* @param manufOrder
* @param prodProduct
* @param qtyToUpdate
* @return
*/
public BigDecimal getFractionQty(
ManufOrder manufOrder, ProdProduct prodProduct, BigDecimal qtyToUpdate) {
BigDecimal manufOrderQty = manufOrder.getQty();
BigDecimal prodProductQty = prodProduct.getQty();
return qtyToUpdate.multiply(prodProductQty).divide(manufOrderQty, 2, RoundingMode.HALF_EVEN);
}
@Transactional
public void createToReturnStockMove(ManufOrder manufOrder) throws AxelorException {
Company company = manufOrder.getCompany();
if (manufOrder.getConsumedStockMoveLineList() != null && company != null) {
StockMove outStockMove = manufOrder.getConsumedStockMoveLineList().get(0).getStockMove();
StockMove stockMove = Beans.get(StockMoveRepository.class).copy(outStockMove, false);
StockLocation fromLocation = manufOrder.getWorkshopStockLocation();
StockLocation toLocation = manufOrder.getProdProcess().getStockLocation();
stockMove.setFromStockLocation(fromLocation);
stockMove.setToStockLocation(toLocation);
stockMove.setPartner(null);
stockMove.setTypeSelect(StockMoveRepository.TYPE_INTERNAL);
for (StockMoveLine stockMoveLine : manufOrder.getConsumedStockMoveLineList()) {
BigDecimal diff = stockMoveLine.getQty().subtract(stockMoveLine.getRealQty());
StockMoveLine stockMoveLine2 =
Beans.get(StockMoveLineRepository.class).copy(stockMoveLine, false);
if (diff.compareTo(BigDecimal.ZERO) > 0) {
stockMoveLine2.setQty(diff);
stockMoveLine2.setRealQty(diff);
stockMoveLine2.setConsumedManufOrder(null);
stockMoveLine2.setReturnedManufOrder(manufOrder);
stockMove.addStockMoveLineListItem(stockMoveLine2);
}
}
stockMoveService.plan(stockMove);
manufOrder.addOutStockMoveListItem(stockMove);
// fill here the consumed stock move line list item to manage the
// case where we had to split tracked stock move lines
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
stockMoveLine.setConsumedManufOrder(null);
manufOrder.addToReturnStockMoveLineListItem(stockMoveLine);
}
}
}
}
@Transactional
public void createTempStockMove(ManufOrder manufOrder) throws AxelorException {
StockMove stockMove = this._createToConsumeStockMove(manufOrder, manufOrder.getCompany());
stockMove.setTypeSelect(StockMoveRepository.TYPE_OUTGOING);
stockMove.setFromStockLocation(manufOrder.getWorkshopStockLocation());
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(manufOrder.getCompany());
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
stockMove.setToStockLocation(virtualStockLocation);
stockMove.setPartner(manufOrder.getCompany().getPartner());
for (BillOfMaterialConsumption billOfMaterialConsumption : manufOrder.getBillOfMaterialConsumptionList()) {
StockMoveLine stockMoveLine = stockMoveLineService.createStockMoveLine(
billOfMaterialConsumption.getProduct(),
billOfMaterialConsumption.getProduct().getName(),
billOfMaterialConsumption.getProduct().getDescription(),
billOfMaterialConsumption.getRealQty(),
BigDecimal.ZERO,
BigDecimal.ZERO,
billOfMaterialConsumption.getProduct().getUnit(),
stockMove,
StockMoveLineService.TYPE_OUT_PRODUCTIONS,
false,
BigDecimal.ZERO);
stockMoveLine.setTrackingNumber(billOfMaterialConsumption.getTrackingNumber());
stockMove.addStockMoveLineListItem(stockMoveLine);
}
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
stockMoveService.plan(stockMove);
manufOrder.addOutStockMoveListItem(stockMove);
}
}
}

View File

@ -0,0 +1,604 @@
/*
* 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.production.service.manuforder;
import com.axelor.apps.base.db.CancelReason;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.ProductService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.message.db.Template;
import com.axelor.apps.message.db.repo.EmailAccountRepository;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.BillOfMaterialConsumption;
import com.axelor.apps.production.db.DocumentationManufOrder;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProductionConfig;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.CostSheetRepository;
import com.axelor.apps.production.db.repo.DocumentationManufOrderRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.production.db.repo.ProductionConfigRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.BillOfMaterialServiceImpl;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.axelor.apps.production.service.operationorder.OperationOrderWorkflowService;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.TrackingNumber;
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
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.MoreObjects;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
public class ManufOrderWorkflowService {
protected OperationOrderWorkflowService operationOrderWorkflowService;
protected OperationOrderRepository operationOrderRepo;
protected ManufOrderStockMoveService manufOrderStockMoveService;
protected ManufOrderRepository manufOrderRepo;
@Inject ProductionConfigRepository productionConfigRepo;
@Inject
public ManufOrderWorkflowService(
OperationOrderWorkflowService operationOrderWorkflowService,
OperationOrderRepository operationOrderRepo,
ManufOrderStockMoveService manufOrderStockMoveService,
ManufOrderRepository manufOrderRepo) {
this.operationOrderWorkflowService = operationOrderWorkflowService;
this.operationOrderRepo = operationOrderRepo;
this.manufOrderStockMoveService = manufOrderStockMoveService;
this.manufOrderRepo = manufOrderRepo;
}
@Transactional(rollbackOn = {Exception.class})
public ManufOrder plan(ManufOrder manufOrder) throws AxelorException {
ManufOrderService manufOrderService = Beans.get(ManufOrderService.class);
if (manufOrder.getBillOfMaterial().getStatusSelect()
!= BillOfMaterialRepository.STATUS_APPLICABLE
&& manufOrder.getProdProcess().getStatusSelect()
!= ProdProcessRepository.STATUS_APPLICABLE) {
throw new AxelorException(
manufOrder,
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get("Bill of material and production process must be applicable"));
}
if (Beans.get(SequenceService.class)
.isEmptyOrDraftSequenceNumber(manufOrder.getManufOrderSeq())) {
manufOrder.setManufOrderSeq(manufOrderService.getManufOrderSeq(manufOrder));
}
if (CollectionUtils.isEmpty(manufOrder.getOperationOrderList())) {
manufOrderService.preFillOperations(manufOrder);
}
if (!manufOrder.getIsConsProOnOperation()
&& CollectionUtils.isEmpty(manufOrder.getToConsumeProdProductList())) {
manufOrderService.createToConsumeProdProductList(manufOrder);
}
System.out.println(
"*********************************************CollectionUtils.isEmpty(manufOrder.getToProduceProdProductList())");
System.out.println(CollectionUtils.isEmpty(manufOrder.getToProduceProdProductList()));
System.out.println(
"*********************************************CollectionUtils.isEmpty(manufOrder.getToProduceProdProductList())");
if (CollectionUtils.isEmpty(manufOrder.getToProduceProdProductList())) {
manufOrderService.createToProduceProdProductList(manufOrder);
}
if (manufOrder.getPlannedStartDateT() == null && manufOrder.getPlannedEndDateT() == null) {
manufOrder.setPlannedStartDateT(
Beans.get(AppProductionService.class).getTodayDateTime().toLocalDateTime());
} else if (manufOrder.getPlannedStartDateT() == null
&& manufOrder.getPlannedEndDateT() != null) {
long duration = 0;
for (OperationOrder order : manufOrder.getOperationOrderList()) {
duration +=
operationOrderWorkflowService.computeEntireCycleDuration(
order, order.getManufOrder().getQty()); // in seconds
}
manufOrder.setPlannedStartDateT(manufOrder.getPlannedEndDateT().minusSeconds(duration));
}
for (OperationOrder operationOrder : getSortedOperationOrderList(manufOrder)) {
operationOrderWorkflowService.plan(operationOrder);
}
if (manufOrder.getPlannedEndDateT() == null) {
manufOrder.setPlannedEndDateT(this.computePlannedEndDateT(manufOrder));
}
if (manufOrder.getBillOfMaterial() != null) {
manufOrder.setUnit(manufOrder.getBillOfMaterial().getUnit());
}
// if (!manufOrder.getIsConsProOnOperation()) {
System.out.println("manufOrder<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
System.out.println();
System.out.println(manufOrder);
System.out.println(manufOrder.getStypeSelect());
System.out.println("manufOrder<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
if (manufOrder.getStypeSelect() == ManufOrderRepository.STYPE_PACKAGING_ORDER) {
manufOrderStockMoveService.createToConsumeStockMove(manufOrder);
}
manufOrderService.createOutgoinfStockMove(manufOrder);
// }
if (manufOrder.getStypeSelect() == ManufOrderRepository.STYPE_PACKAGING_ORDER) {
manufOrderStockMoveService.createToProduceStockMove(manufOrder);
}
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_PLANNED);
manufOrder.setCancelReason(null);
manufOrder.setCancelReasonStr(null);
return manufOrderRepo.save(manufOrder);
}
@Transactional(rollbackOn = {Exception.class})
public void start(ManufOrder manufOrder) throws AxelorException {
manufOrder.setRealStartDateT(
Beans.get(AppProductionService.class).getTodayDateTime().toLocalDateTime());
int beforeOrAfterConfig = manufOrder.getProdProcess().getStockMoveRealizeOrderSelect();
if (beforeOrAfterConfig == ProductionConfigRepository.REALIZE_START) {
for (StockMove stockMove : manufOrder.getInStockMoveList()) {
if (!stockMove.getIsValidatedProduction()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.STOCK_MOVE_NOT_VALIDATED));
}
manufOrderStockMoveService.finishStockMove(stockMove);
}
}
if (manufOrder.getBillOfMaterial() != null) {
for (BillOfMaterial bom : manufOrder.getBillOfMaterial().getBillOfMaterialSet()) {
BillOfMaterialConsumption newBom =
Beans.get(BillOfMaterialServiceImpl.class)
.createBomConsumptionFromRawMaterial(bom, manufOrder);
manufOrder.addBillOfMaterialConsumptionListItem(newBom);
}
}
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_IN_PROGRESS);
manufOrderRepo.save(manufOrder);
}
@Transactional
public void pause(ManufOrder manufOrder) {
if (manufOrder.getOperationOrderList() != null) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_IN_PROGRESS) {
operationOrderWorkflowService.pause(operationOrder);
}
}
}
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_STANDBY);
manufOrderRepo.save(manufOrder);
}
@Transactional
public void resume(ManufOrder manufOrder) {
if (manufOrder.getOperationOrderList() != null) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_STANDBY) {
operationOrderWorkflowService.resume(operationOrder);
}
}
}
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_IN_PROGRESS);
manufOrderRepo.save(manufOrder);
}
@Transactional(rollbackOn = {Exception.class})
public boolean finish(ManufOrder manufOrder) throws AxelorException {
if (manufOrder.getOperationOrderList() != null) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() != OperationOrderRepository.STATUS_FINISHED) {
if (operationOrder.getStatusSelect() != OperationOrderRepository.STATUS_IN_PROGRESS
&& operationOrder.getStatusSelect() != OperationOrderRepository.STATUS_STANDBY) {
operationOrderWorkflowService.start(operationOrder);
}
operationOrderWorkflowService.finish(operationOrder);
}
}
}
// create cost sheet
Beans.get(CostSheetService.class)
.computeCostPrice(
manufOrder,
CostSheetRepository.CALCULATION_END_OF_PRODUCTION,
Beans.get(AppBaseService.class).getTodayDate());
// update price in product
Product product = manufOrder.getProduct();
if (product.getRealOrEstimatedPriceSelect() == ProductRepository.PRICE_METHOD_FORECAST) {
product.setLastProductionPrice(manufOrder.getBillOfMaterial().getCostPrice());
} else if (product.getRealOrEstimatedPriceSelect() == ProductRepository.PRICE_METHOD_REAL) {
BigDecimal costPrice = computeOneUnitProductionPrice(manufOrder);
if (costPrice.signum() != 0) {
product.setLastProductionPrice(costPrice);
}
} else {
// default value is forecast
product.setRealOrEstimatedPriceSelect(ProductRepository.PRICE_METHOD_FORECAST);
product.setLastProductionPrice(manufOrder.getBillOfMaterial().getCostPrice());
}
// update costprice in product
if (product.getCostTypeSelect() == ProductRepository.COST_TYPE_LAST_PRODUCTION_PRICE) {
product.setCostPrice(product.getLastProductionPrice());
if (product.getAutoUpdateSalePrice()) {
Beans.get(ProductService.class).updateSalePrice(product);
}
}
manufOrderStockMoveService.finish(manufOrder);
manufOrder.setRealEndDateT(
Beans.get(AppProductionService.class).getTodayDateTime().toLocalDateTime());
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_FINISHED);
manufOrder.setEndTimeDifference(
new BigDecimal(
ChronoUnit.MINUTES.between(
manufOrder.getPlannedEndDateT(), manufOrder.getRealEndDateT())));
manufOrderRepo.save(manufOrder);
ProductionConfig productionConfig =
manufOrder.getCompany() != null
? productionConfigRepo.findByCompany(manufOrder.getCompany())
: null;
if (productionConfig != null && productionConfig.getFinishMoAutomaticEmail()) {
return this.sendMail(manufOrder, productionConfig.getFinishMoMessageTemplate());
}
return true;
}
/** Return the cost price for one unit in a manufacturing order. */
protected BigDecimal computeOneUnitProductionPrice(ManufOrder manufOrder) {
BigDecimal qty = manufOrder.getQty();
if (qty.signum() != 0) {
int scale = Beans.get(AppProductionService.class).getNbDecimalDigitForUnitPrice();
return manufOrder.getCostPrice().divide(qty, scale, BigDecimal.ROUND_HALF_EVEN);
} else {
return BigDecimal.ZERO;
}
}
/**
* Allows to finish partially a manufacturing order, by realizing current stock move and planning
* the difference with the planned prodproducts.
*
* @param manufOrder
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public boolean partialFinish(ManufOrder manufOrder) throws AxelorException {
if (manufOrder.getIsConsProOnOperation()) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_PLANNED) {
operationOrderWorkflowService.start(operationOrder);
}
}
}
Beans.get(CostSheetService.class)
.computeCostPrice(
manufOrder,
CostSheetRepository.CALCULATION_PARTIAL_END_OF_PRODUCTION,
Beans.get(AppBaseService.class).getTodayDate());
Beans.get(ManufOrderStockMoveService.class).partialFinish(manufOrder);
ProductionConfig productionConfig =
manufOrder.getCompany() != null
? productionConfigRepo.findByCompany(manufOrder.getCompany())
: null;
if (productionConfig != null && productionConfig.getPartFinishMoAutomaticEmail()) {
return this.sendMail(manufOrder, productionConfig.getPartFinishMoMessageTemplate());
}
return true;
}
@Transactional(rollbackOn = {Exception.class})
public void cancel(ManufOrder manufOrder, CancelReason cancelReason, String cancelReasonStr)
throws AxelorException {
if (cancelReason == null
&& manufOrder.getStatusSelect() != ManufOrderRepository.STATUS_DRAFT
&& manufOrder.getStatusSelect() != ManufOrderRepository.STATUS_PLANNED) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.MANUF_ORDER_CANCEL_REASON_ERROR));
}
if (manufOrder.getOperationOrderList() != null) {
for (OperationOrder operationOrder : manufOrder.getOperationOrderList()) {
if (operationOrder.getStatusSelect() != OperationOrderRepository.STATUS_CANCELED) {
operationOrderWorkflowService.cancel(operationOrder);
}
}
}
manufOrderStockMoveService.cancel(manufOrder);
if (manufOrder.getConsumedStockMoveLineList() != null) {
manufOrder
.getConsumedStockMoveLineList()
.forEach(stockMoveLine -> stockMoveLine.setConsumedManufOrder(null));
}
if (manufOrder.getProducedStockMoveLineList() != null) {
manufOrder
.getProducedStockMoveLineList()
.forEach(stockMoveLine -> stockMoveLine.setProducedManufOrder(null));
}
if (manufOrder.getTransferedStockMoveLineList() != null) {
manufOrder
.getTransferedStockMoveLineList()
.forEach(stockMoveLine -> stockMoveLine.setTransferedManufOrder(null));
}
if (manufOrder.getDiffConsumeProdProductList() != null) {
manufOrder.clearDiffConsumeProdProductList();
}
manufOrder.setStatusSelect(ManufOrderRepository.STATUS_CANCELED);
if (cancelReason != null) {
manufOrder.setCancelReason(cancelReason);
if (Strings.isNullOrEmpty(cancelReasonStr)) {
manufOrder.setCancelReasonStr(cancelReason.getName());
} else {
manufOrder.setCancelReasonStr(cancelReasonStr);
}
}
manufOrderRepo.save(manufOrder);
}
public LocalDateTime computePlannedEndDateT(ManufOrder manufOrder) {
OperationOrder lastOperationOrder = getLastOperationOrder(manufOrder);
if (lastOperationOrder != null) {
return lastOperationOrder.getPlannedEndDateT();
}
return manufOrder.getPlannedStartDateT();
}
@Transactional(rollbackOn = {Exception.class})
public void allOpFinished(ManufOrder manufOrder) throws AxelorException {
int count = 0;
List<OperationOrder> operationOrderList = manufOrder.getOperationOrderList();
for (OperationOrder operationOrderIt : operationOrderList) {
if (operationOrderIt.getStatusSelect() == OperationOrderRepository.STATUS_FINISHED) {
count++;
}
}
if (count == operationOrderList.size()) {
this.finish(manufOrder);
}
}
/**
* Returns last operation order (highest priority) of given {@link ManufOrder}
*
* @param manufOrder A manufacturing order
* @return Last operation order of {@code manufOrder}
*/
public OperationOrder getLastOperationOrder(ManufOrder manufOrder) {
return operationOrderRepo
.all()
.filter("self.manufOrder = ? AND self.plannedEndDateT IS NOT NULL", manufOrder)
.order("-plannedEndDateT")
.fetchOne();
}
/**
* Update planned dates.
*
* @param manufOrder
* @param plannedStartDateT
*/
@Transactional(rollbackOn = {Exception.class})
public void updatePlannedDates(ManufOrder manufOrder, LocalDateTime plannedStartDateT)
throws AxelorException {
manufOrder.setPlannedStartDateT(plannedStartDateT);
if (manufOrder.getOperationOrderList() != null) {
List<OperationOrder> operationOrderList = getSortedOperationOrderList(manufOrder);
operationOrderWorkflowService.resetPlannedDates(operationOrderList);
for (OperationOrder operationOrder : operationOrderList) {
operationOrderWorkflowService.replan(operationOrder);
}
}
manufOrder.setPlannedEndDateT(computePlannedEndDateT(manufOrder));
}
/**
* Get a list of operation orders sorted by priority and id from the specified manufacturing
* order.
*
* @param manufOrder
* @return
*/
protected List<OperationOrder> getSortedOperationOrderList(ManufOrder manufOrder) {
List<OperationOrder> operationOrderList =
MoreObjects.firstNonNull(manufOrder.getOperationOrderList(), Collections.emptyList());
Comparator<OperationOrder> byPriority =
Comparator.comparing(
OperationOrder::getPriority, Comparator.nullsFirst(Comparator.naturalOrder()));
Comparator<OperationOrder> byId =
Comparator.comparing(
OperationOrder::getId, Comparator.nullsFirst(Comparator.naturalOrder()));
return operationOrderList
.stream()
.sorted(byPriority.thenComparing(byId))
.collect(Collectors.toList());
}
protected boolean sendMail(ManufOrder manufOrder, Template template) throws AxelorException {
if (template == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.MANUF_ORDER_MISSING_TEMPLATE));
}
if (Beans.get(EmailAccountRepository.class)
.all()
.filter("self.isDefault = true AND self.isValid = true")
.fetchOne()
== null) {
return false;
}
try {
Beans.get(TemplateMessageService.class).generateAndSendMessage(manufOrder, template);
} catch (Exception e) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, e.getMessage(), manufOrder);
}
return true;
}
@Transactional(rollbackOn = {Exception.class})
public Long createDocumentationManufOrder(ManufOrder manufOrder) throws AxelorException {
DocumentationManufOrder documentationManufOrder = new DocumentationManufOrder();
documentationManufOrder.setStatusSelect(1);
documentationManufOrder.setProduct(manufOrder.getProduct());
documentationManufOrder.setTrackingNumber(manufOrder.getTrackingNumber());
documentationManufOrder.setManufOrder(manufOrder);
ProdProcess prodProcess = Beans.get(ProdProcessRepository.class).findByName("DOCUMENTATION");
for (ProdProcessLine prodProcessLine :
Beans.get(ManufOrderServiceImpl.class)
._sortProdProcessLineByPriority(prodProcess.getProdProcessLineList())) {
documentationManufOrder.addOperationOrderListItem(
Beans.get(OperationOrderService.class).createOperationOrder(manufOrder, prodProcessLine));
}
DocumentationManufOrder savedocumentationManufOrder =
Beans.get(DocumentationManufOrderRepository.class).save(documentationManufOrder);
return savedocumentationManufOrder.getId();
}
@Transactional(rollbackOn = {Exception.class})
public Long createPackagingOrder(ManufOrder manufOrder) throws AxelorException {
ManufOrderService manufOrderService = Beans.get(ManufOrderService.class);
// ProdProcess prodProcess = Beans.get(ProdProcessRepository.class).all().filter("self.product =
// ?1 and self.typeSelect = ?2",manufOrder.getProduct(),
// ManufOrderRepository.STYPE_PACKAGING_ORDER).fetchOne();
BillOfMaterial billOfMaterial =
Beans.get(BillOfMaterialRepository.class)
.all()
.filter(
"self.product = ?1 and self.typeSelect = ?2",
manufOrder.getProduct(),
ManufOrderRepository.STYPE_PACKAGING_ORDER)
.fetchOne();
ManufOrder packagingOrder =
manufOrderService.generateManufOrder(
manufOrder.getProduct(),
manufOrder.getQty(),
ManufOrderService.DEFAULT_PRIORITY,
ManufOrderService.IS_TO_INVOICE,
billOfMaterial,
manufOrder.getRealEndDateT(),
null,
ManufOrderService.ORIGIN_TYPE_OTHER);
packagingOrder.setTrackingNumber(manufOrder.getTrackingNumber());
packagingOrder.setStypeSelect(ManufOrderRepository.STYPE_PACKAGING_ORDER);
packagingOrder.setOriginManufOrder(manufOrder);
packagingOrder.setProductionOrder(manufOrder.getProductionOrder());
ManufOrder packOrder = Beans.get(ManufOrderRepository.class).save(packagingOrder);
return packOrder.getId();
}
@Transactional(rollbackOn = {Exception.class})
public void createAndAssignTrackingNumber(OperationOrder operationOrder) throws AxelorException {
ManufOrderService manufOrderService = Beans.get(ManufOrderService.class);
// Optional<OperationOrder> operationOrder1 =
// manufOrder.getOperationOrderList().stream().filter(op
// ->op.getOperationName().equals("MELANGE")).findFirst();
// Optional<OperationOrder> operationOrder2 =
// manufOrder.getOperationOrderList().stream().filter(op
// ->op.getOperationName().equals("GRANULATION")).findFirst();
// ProdProcess prodProcess = Beans.get(ProdProcessRepository.class).all().filter("self.product =
// ?1 and self.typeSelect = ?2",manufOrder.getProduct(),
// ManufOrderRepository.STYPE_PACKAGING_ORDER).fetchOne();
ManufOrder manufOrder = operationOrder.getManufOrder();
BillOfMaterial billOfMaterial = manufOrder.getBillOfMaterial();
Product product = billOfMaterial.getProduct();
Integer lifeTime = billOfMaterial.getProductLifeTimeInMonth();
Integer seq = billOfMaterial.getProducedTrackingNumberSeq();
String workShopName = billOfMaterial.getWorkshopStockLocation().getName();
String lastYear = String.valueOf(LocalDate.now().getYear()).substring(2);
String workShopCode = "";
if (workShopName.length() >= 6) {
workShopCode = Character.toString(workShopName.charAt(5));
}
DecimalFormat df = new DecimalFormat("000");
String formattedseq = df.format(seq);
String sequence = workShopCode + lastYear + formattedseq;
LocalDate perishableDate =
LocalDate.now().plusMonths(lifeTime).with(TemporalAdjusters.lastDayOfMonth());
TrackingNumber trackingNumber = new TrackingNumber();
trackingNumber.setProduct(product);
trackingNumber.setPerishableExpirationDate(perishableDate);
trackingNumber.setTrackingNumberSeq(sequence);
manufOrder.setTrackingNumber(Beans.get(TrackingNumberRepository.class).save(trackingNumber));
Integer nextSeq = billOfMaterial.getProducedTrackingNumberSeq() + 1;
billOfMaterial.setProducedTrackingNumberSeq(nextSeq);
Beans.get(BillOfMaterialRepository.class).save(billOfMaterial);
Beans.get(ManufOrderRepository.class).save(manufOrder);
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.production.service.operationorder;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public interface OperationOrderService {
@Transactional(rollbackOn = {Exception.class})
public OperationOrder createOperationOrder(ManufOrder manufOrder, ProdProcessLine prodProcessLine)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public OperationOrder createOperationOrder(
ManufOrder manufOrder,
int priority,
WorkCenter workCenter,
WorkCenter machineWorkCenter,
ProdProcessLine prodProcessLine)
throws AxelorException;
public String computeName(ManufOrder manufOrder, int priority, String operationName);
/**
* Generate {@link OperationOrder#toConsumeProdProductList} from the prod process line in param.
*
* @param operationOrder
*/
void createToConsumeProdProductList(OperationOrder operationOrder);
/**
* Updates the diff prod product list.
*
* @param operationOrder
* @return the updated operation order
* @throws AxelorException
*/
OperationOrder updateDiffProdProductList(OperationOrder operationOrder) throws AxelorException;
List<Map<String, Object>> chargeByMachineHours(
LocalDateTime fromDateTime, LocalDateTime toDateTime) throws AxelorException;
List<Map<String, Object>> chargeByMachineDays(
LocalDateTime fromDateTime, LocalDateTime toDateTime) throws AxelorException;
/**
* Compute the difference between the two lists for the given operation order.
*
* @param operationOrder
* @param prodProductList
* @param stockMoveLineList
* @return
* @throws AxelorException
*/
List<ProdProduct> createDiffProdProductList(
OperationOrder operationOrder,
List<ProdProduct> prodProductList,
List<StockMoveLine> stockMoveLineList)
throws AxelorException;
/**
* Check the realized consumed stock move lines in operation order has not changed.
*
* @param operationOrder an operation order from context.
* @param oldOperationOrder an operation order from database.
* @throws AxelorException if the check fails.
*/
void checkConsumedStockMoveLineList(
OperationOrder operationOrder, OperationOrder oldOperationOrder) throws AxelorException;
/**
* On changing {@link OperationOrder#consumedStockMoveLineList}, we update {@link
* OperationOrder#diffConsumeProdProductList}, and also the stock move.
*
* @param operationOrder
*/
void updateConsumedStockMoveFromOperationOrder(OperationOrder operationOrder)
throws AxelorException;
public void createBarcode(OperationOrder operationOrder);
}

View File

@ -0,0 +1,489 @@
/*
* 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.production.service.operationorder;
import com.axelor.apps.base.db.DayPlanning;
import com.axelor.apps.base.service.BarcodeGeneratorService;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdHumanResource;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.manuforder.ManufOrderStockMoveService;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.StockMoveService;
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.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OperationOrderServiceImpl implements OperationOrderService {
@Inject private MetaFiles metaFiles;
@Inject protected BarcodeGeneratorService barcodeGeneratorService;
@Inject protected AppProductionService appProductionService;
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy");
private static final DateTimeFormatter DATE_TIME_FORMAT =
DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Transactional(rollbackOn = {Exception.class})
public OperationOrder createOperationOrder(ManufOrder manufOrder, ProdProcessLine prodProcessLine)
throws AxelorException {
OperationOrder operationOrder =
this.createOperationOrder(
manufOrder,
prodProcessLine.getPriority(),
prodProcessLine.getWorkCenter(),
prodProcessLine.getWorkCenter(),
prodProcessLine);
return Beans.get(OperationOrderRepository.class).save(operationOrder);
}
@Transactional(rollbackOn = {Exception.class})
public OperationOrder createOperationOrder(
ManufOrder manufOrder,
int priority,
WorkCenter workCenter,
WorkCenter machineWorkCenter,
ProdProcessLine prodProcessLine)
throws AxelorException {
logger.debug(
"Création d'une opération {} pour l'OF {}", priority, manufOrder.getManufOrderSeq());
String operationName = prodProcessLine.getName();
OperationOrder operationOrder =
new OperationOrder(
priority,
this.computeName(manufOrder, priority, operationName),
operationName,
manufOrder,
workCenter,
machineWorkCenter,
OperationOrderRepository.STATUS_DRAFT,
prodProcessLine);
operationOrder.setProduct(prodProcessLine.getProduct());
operationOrder.setQty(prodProcessLine.getQty());
operationOrder.setUnit(prodProcessLine.getUnit());
this._createHumanResourceList(operationOrder, machineWorkCenter);
return Beans.get(OperationOrderRepository.class).save(operationOrder);
}
protected void _createHumanResourceList(OperationOrder operationOrder, WorkCenter workCenter) {
if (workCenter != null && workCenter.getProdHumanResourceList() != null) {
for (ProdHumanResource prodHumanResource : workCenter.getProdHumanResourceList()) {
operationOrder.addProdHumanResourceListItem(this.copyProdHumanResource(prodHumanResource));
}
}
}
protected ProdHumanResource copyProdHumanResource(ProdHumanResource prodHumanResource) {
return new ProdHumanResource(prodHumanResource.getProduct(), prodHumanResource.getDuration());
}
public String computeName(ManufOrder manufOrder, int priority, String operationName) {
String name = "";
if (manufOrder != null) {
if (manufOrder.getManufOrderSeq() != null) {
name += manufOrder.getManufOrderSeq();
} else {
name += manufOrder.getId();
}
}
name += "-" + priority + "-" + operationName;
return name;
}
@Override
public void createToConsumeProdProductList(OperationOrder operationOrder) {
BigDecimal manufOrderQty = operationOrder.getManufOrder().getQty();
BigDecimal bomQty = operationOrder.getManufOrder().getBillOfMaterial().getQty();
ProdProcessLine prodProcessLine = operationOrder.getProdProcessLine();
if (prodProcessLine.getToConsumeProdProductList() != null) {
for (ProdProduct prodProduct : prodProcessLine.getToConsumeProdProductList()) {
BigDecimal qty =
Beans.get(ManufOrderService.class)
.computeToConsumeProdProductLineQuantity(
bomQty, manufOrderQty, prodProduct.getQty());
operationOrder.addToConsumeProdProductListItem(
new ProdProduct(prodProduct.getProduct(), qty, prodProduct.getUnit()));
}
}
}
@Override
public OperationOrder updateDiffProdProductList(OperationOrder operationOrder)
throws AxelorException {
List<ProdProduct> toConsumeList = operationOrder.getToConsumeProdProductList();
List<StockMoveLine> consumedList = operationOrder.getConsumedStockMoveLineList();
if (toConsumeList == null || consumedList == null) {
return operationOrder;
}
List<ProdProduct> diffConsumeList =
createDiffProdProductList(operationOrder, toConsumeList, consumedList);
operationOrder.clearDiffConsumeProdProductList();
diffConsumeList.forEach(operationOrder::addDiffConsumeProdProductListItem);
return operationOrder;
}
public List<Map<String, Object>> chargeByMachineHours(
LocalDateTime fromDateTime, LocalDateTime toDateTime) throws AxelorException {
List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
LocalDateTime itDateTime =
LocalDateTime.parse(fromDateTime.toString(), DateTimeFormatter.ISO_DATE_TIME);
OperationOrderRepository operationOrderRepo = Beans.get(OperationOrderRepository.class);
if (Duration.between(fromDateTime, toDateTime).toDays() > 20) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CHARGE_MACHINE_DAYS));
}
List<OperationOrder> operationOrderListTemp =
operationOrderRepo
.all()
.filter(
"self.plannedStartDateT <= ?2 AND self.plannedEndDateT >= ?1",
fromDateTime,
toDateTime)
.fetch();
Set<String> machineNameList = new HashSet<String>();
for (OperationOrder operationOrder : operationOrderListTemp) {
if (operationOrder.getWorkCenter() != null
&& operationOrder.getWorkCenter().getMachine() != null) {
if (!machineNameList.contains(operationOrder.getWorkCenter().getMachine().getName())) {
machineNameList.add(operationOrder.getWorkCenter().getMachine().getName());
}
}
}
while (!itDateTime.isAfter(toDateTime)) {
List<OperationOrder> operationOrderList =
operationOrderRepo
.all()
.filter(
"self.plannedStartDateT <= ?2 AND self.plannedEndDateT >= ?1",
itDateTime,
itDateTime.plusHours(1))
.fetch();
Map<String, BigDecimal> map = new HashMap<String, BigDecimal>();
for (OperationOrder operationOrder : operationOrderList) {
if (operationOrder.getWorkCenter() != null
&& operationOrder.getWorkCenter().getMachine() != null) {
String machine = operationOrder.getWorkCenter().getMachine().getName();
long numberOfMinutes = 0;
if (operationOrder.getPlannedStartDateT().isBefore(itDateTime)) {
numberOfMinutes =
Duration.between(itDateTime, operationOrder.getPlannedEndDateT()).toMinutes();
} else if (operationOrder.getPlannedEndDateT().isAfter(itDateTime.plusHours(1))) {
numberOfMinutes =
Duration.between(operationOrder.getPlannedStartDateT(), itDateTime.plusHours(1))
.toMinutes();
} else {
numberOfMinutes =
Duration.between(
operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT())
.toMinutes();
}
if (numberOfMinutes > 60) {
numberOfMinutes = 60;
}
BigDecimal percentage =
new BigDecimal(numberOfMinutes)
.multiply(new BigDecimal(100))
.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
if (map.containsKey(machine)) {
map.put(machine, map.get(machine).add(percentage));
} else {
map.put(machine, percentage);
}
}
}
Set<String> keyList = map.keySet();
for (String key : machineNameList) {
if (keyList.contains(key)) {
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("dateTime", (Object) itDateTime.format(DATE_TIME_FORMAT));
dataMap.put("charge", (Object) map.get(key));
dataMap.put("machine", (Object) key);
dataList.add(dataMap);
} else {
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("dateTime", (Object) itDateTime.format(DATE_TIME_FORMAT));
dataMap.put("charge", (Object) BigDecimal.ZERO);
dataMap.put("machine", (Object) key);
dataList.add(dataMap);
}
}
itDateTime = itDateTime.plusHours(1);
}
return dataList;
}
public List<Map<String, Object>> chargeByMachineDays(
LocalDateTime fromDateTime, LocalDateTime toDateTime) throws AxelorException {
List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
fromDateTime = fromDateTime.withHour(0).withMinute(0);
toDateTime = toDateTime.withHour(23).withMinute(59);
LocalDateTime itDateTime =
LocalDateTime.parse(fromDateTime.toString(), DateTimeFormatter.ISO_DATE_TIME);
if (Duration.between(fromDateTime, toDateTime).toDays() > 500) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.CHARGE_MACHINE_DAYS));
}
List<OperationOrder> operationOrderListTemp =
Beans.get(OperationOrderRepository.class)
.all()
.filter(
"self.plannedStartDateT <= ?2 AND self.plannedEndDateT >= ?1",
fromDateTime,
toDateTime)
.fetch();
Set<String> machineNameList = new HashSet<String>();
for (OperationOrder operationOrder : operationOrderListTemp) {
if (operationOrder.getWorkCenter() != null
&& operationOrder.getWorkCenter().getMachine() != null) {
if (!machineNameList.contains(operationOrder.getWorkCenter().getMachine().getName())) {
machineNameList.add(operationOrder.getWorkCenter().getMachine().getName());
}
}
}
while (!itDateTime.isAfter(toDateTime)) {
List<OperationOrder> operationOrderList =
Beans.get(OperationOrderRepository.class)
.all()
.filter(
"self.plannedStartDateT <= ?2 AND self.plannedEndDateT >= ?1",
itDateTime,
itDateTime.plusHours(1))
.fetch();
Map<String, BigDecimal> map = new HashMap<String, BigDecimal>();
WeeklyPlanningService weeklyPlanningService = Beans.get(WeeklyPlanningService.class);
for (OperationOrder operationOrder : operationOrderList) {
if (operationOrder.getWorkCenter() != null
&& operationOrder.getWorkCenter().getMachine() != null) {
String machine = operationOrder.getWorkCenter().getMachine().getName();
long numberOfMinutes = 0;
if (operationOrder.getPlannedStartDateT().isBefore(itDateTime)) {
numberOfMinutes =
Duration.between(itDateTime, operationOrder.getPlannedEndDateT()).toMinutes();
} else if (operationOrder.getPlannedEndDateT().isAfter(itDateTime.plusHours(1))) {
numberOfMinutes =
Duration.between(operationOrder.getPlannedStartDateT(), itDateTime.plusHours(1))
.toMinutes();
} else {
numberOfMinutes =
Duration.between(
operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT())
.toMinutes();
}
if (numberOfMinutes > 60) {
numberOfMinutes = 60;
}
long numberOfMinutesPerDay = 0;
if (operationOrder.getWorkCenter().getMachine().getWeeklyPlanning() != null) {
DayPlanning dayPlanning =
weeklyPlanningService.findDayPlanning(
operationOrder.getWorkCenter().getMachine().getWeeklyPlanning(),
LocalDateTime.parse(itDateTime.toString(), DateTimeFormatter.ISO_DATE_TIME)
.toLocalDate());
if (dayPlanning != null) {
numberOfMinutesPerDay =
Duration.between(dayPlanning.getMorningFrom(), dayPlanning.getMorningTo())
.toMinutes();
numberOfMinutesPerDay +=
Duration.between(dayPlanning.getAfternoonFrom(), dayPlanning.getAfternoonTo())
.toMinutes();
} else {
numberOfMinutesPerDay = 0;
}
} else {
numberOfMinutesPerDay = 60 * 8;
}
if (numberOfMinutesPerDay != 0) {
BigDecimal percentage =
new BigDecimal(numberOfMinutes)
.multiply(new BigDecimal(100))
.divide(new BigDecimal(numberOfMinutesPerDay), 2, RoundingMode.HALF_UP);
if (map.containsKey(machine)) {
map.put(machine, map.get(machine).add(percentage));
} else {
map.put(machine, percentage);
}
}
}
}
Set<String> keyList = map.keySet();
for (String key : machineNameList) {
if (keyList.contains(key)) {
int found = 0;
for (Map<String, Object> mapIt : dataList) {
if (mapIt.get("dateTime").equals((Object) itDateTime.format(DATE_FORMAT))
&& mapIt.get("machine").equals((Object) key)) {
mapIt.put("charge", new BigDecimal(mapIt.get("charge").toString()).add(map.get(key)));
found = 1;
break;
}
}
if (found == 0) {
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("dateTime", (Object) itDateTime.format(DATE_FORMAT));
dataMap.put("charge", (Object) map.get(key));
dataMap.put("machine", (Object) key);
dataList.add(dataMap);
}
}
}
itDateTime = itDateTime.plusHours(1);
}
return dataList;
}
@Override
public List<ProdProduct> createDiffProdProductList(
OperationOrder operationOrder,
List<ProdProduct> prodProductList,
List<StockMoveLine> stockMoveLineList)
throws AxelorException {
List<ProdProduct> diffConsumeList =
Beans.get(ManufOrderService.class)
.createDiffProdProductList(prodProductList, stockMoveLineList);
diffConsumeList.forEach(
prodProduct -> prodProduct.setDiffConsumeOperationOrder(operationOrder));
return diffConsumeList;
}
@Override
public void checkConsumedStockMoveLineList(
OperationOrder operationOrder, OperationOrder oldOperationOrder) throws AxelorException {
Beans.get(ManufOrderService.class)
.checkRealizedStockMoveLineList(
operationOrder.getConsumedStockMoveLineList(),
oldOperationOrder.getConsumedStockMoveLineList());
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateConsumedStockMoveFromOperationOrder(OperationOrder operationOrder)
throws AxelorException {
this.updateDiffProdProductList(operationOrder);
List<StockMoveLine> consumedStockMoveLineList = operationOrder.getConsumedStockMoveLineList();
if (consumedStockMoveLineList == null) {
return;
}
Optional<StockMove> stockMoveOpt =
operationOrder
.getInStockMoveList()
.stream()
.filter(stockMove -> stockMove.getStatusSelect() == StockMoveRepository.STATUS_PLANNED)
.findFirst();
StockMove stockMove;
if (stockMoveOpt.isPresent()) {
stockMove = stockMoveOpt.get();
} else {
stockMove =
Beans.get(ManufOrderStockMoveService.class)
._createToConsumeStockMove(
operationOrder.getManufOrder(), operationOrder.getManufOrder().getCompany());
operationOrder.addInStockMoveListItem(stockMove);
Beans.get(StockMoveService.class).plan(stockMove);
}
Beans.get(ManufOrderService.class)
.updateStockMoveFromManufOrder(consumedStockMoveLineList, stockMove);
}
@Override
public void createBarcode(OperationOrder operationOrder) {
try {
String stringId = operationOrder.getId().toString();
boolean addPadding = true;
InputStream inStream =
barcodeGeneratorService.createBarCode(
stringId, appProductionService.getAppProduction().getBarcodeTypeConfig(), addPadding);
if (inStream != null) {
MetaFile barcodeFile =
metaFiles.upload(
inStream, String.format("OppOrderBarcode%d.png", operationOrder.getId()));
operationOrder.setBarCode(barcodeFile);
}
} catch (IOException e) {
e.printStackTrace();
} catch (AxelorException e) {
throw new ValidationException(e.getMessage());
}
}
}

View File

@ -0,0 +1,312 @@
/*
* 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.production.service.operationorder;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.ProdProduct;
import com.axelor.apps.production.service.config.StockConfigProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderStockMoveService;
import com.axelor.apps.stock.db.StockConfig;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockLocationRepository;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.StockMoveLineService;
import com.axelor.apps.stock.service.StockMoveService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.commons.collections.CollectionUtils;
public class OperationOrderStockMoveService {
protected StockMoveService stockMoveService;
protected StockMoveLineService stockMoveLineService;
protected StockLocationRepository stockLocationRepo;
@Inject
public OperationOrderStockMoveService(
StockMoveService stockMoveService,
StockMoveLineService stockMoveLineService,
StockLocationRepository stockLocationRepo) {
this.stockMoveService = stockMoveService;
this.stockMoveLineService = stockMoveLineService;
this.stockLocationRepo = stockLocationRepo;
}
public void createToConsumeStockMove(OperationOrder operationOrder) throws AxelorException {
Company company = operationOrder.getManufOrder().getCompany();
if (operationOrder.getToConsumeProdProductList() != null && company != null) {
StockMove stockMove = this._createToConsumeStockMove(operationOrder, company);
stockMove.setOriginId(operationOrder.getId());
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_OPERATION_ORDER);
stockMove.setOrigin(operationOrder.getOperationName());
for (ProdProduct prodProduct : operationOrder.getToConsumeProdProductList()) {
StockMoveLine stockMoveLine = this._createStockMoveLine(prodProduct, stockMove);
}
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
stockMoveService.plan(stockMove);
operationOrder.addInStockMoveListItem(stockMove);
}
// fill here the consumed stock move line list item to manage the
// case where we had to split tracked stock move lines
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
operationOrder.addConsumedStockMoveLineListItem(stockMoveLine);
}
}
}
}
protected StockMove _createToConsumeStockMove(OperationOrder operationOrder, Company company)
throws AxelorException {
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation virtualStockLocation =
stockConfigService.getProductionVirtualStockLocation(stockConfig);
StockLocation fromStockLocation;
ProdProcessLine prodProcessLine = operationOrder.getProdProcessLine();
if (operationOrder.getManufOrder().getIsConsProOnOperation()
&& prodProcessLine != null
&& prodProcessLine.getStockLocation() != null) {
fromStockLocation = prodProcessLine.getStockLocation();
} else if (!operationOrder.getManufOrder().getIsConsProOnOperation()
&& prodProcessLine != null
&& prodProcessLine.getProdProcess() != null
&& prodProcessLine.getProdProcess().getStockLocation() != null) {
fromStockLocation = prodProcessLine.getProdProcess().getStockLocation();
} else {
fromStockLocation = stockConfigService.getComponentDefaultStockLocation(stockConfig);
}
return stockMoveService.createStockMove(
null,
null,
company,
fromStockLocation,
virtualStockLocation,
null,
operationOrder.getPlannedStartDateT().toLocalDate(),
null,
StockMoveRepository.TYPE_INTERNAL);
}
protected StockMoveLine _createStockMoveLine(ProdProduct prodProduct, StockMove stockMove)
throws AxelorException {
return stockMoveLineService.createStockMoveLine(
prodProduct.getProduct(),
prodProduct.getProduct().getName(),
prodProduct.getProduct().getDescription(),
prodProduct.getQty(),
prodProduct.getProduct().getCostPrice(),
prodProduct.getProduct().getCostPrice(),
prodProduct.getUnit(),
stockMove,
StockMoveLineService.TYPE_IN_PRODUCTIONS,
false,
BigDecimal.ZERO);
}
public void finish(OperationOrder operationOrder) throws AxelorException {
List<StockMove> stockMoveList = operationOrder.getInStockMoveList();
if (stockMoveList != null) {
// clear empty stock move
stockMoveList.removeIf(
stockMove -> CollectionUtils.isEmpty(stockMove.getStockMoveLineList()));
for (StockMove stockMove : stockMoveList) {
Beans.get(ManufOrderStockMoveService.class).finishStockMove(stockMove);
}
}
}
/**
* Allows to create and realize in stock moves for the given operation order. This method is used
* during a partial finish.
*
* @param operationOrder
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public void partialFinish(OperationOrder operationOrder) throws AxelorException {
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
ManufOrder manufOrder = operationOrder.getManufOrder();
Company company = manufOrder.getCompany();
StockConfigProductionService stockConfigService = Beans.get(StockConfigProductionService.class);
StockConfig stockConfig = stockConfigService.getStockConfig(company);
StockLocation fromStockLocation;
StockLocation toStockLocation;
List<StockMove> stockMoveList;
stockMoveList = operationOrder.getInStockMoveList();
fromStockLocation =
manufOrderStockMoveService.getDefaultStockLocation(
manufOrder, company, ManufOrderStockMoveService.STOCK_LOCATION_IN);
toStockLocation = stockConfigService.getProductionVirtualStockLocation(stockConfig);
// realize current stock move
Optional<StockMove> stockMoveToRealize =
stockMoveList
.stream()
.filter(
stockMove ->
stockMove.getStatusSelect() == StockMoveRepository.STATUS_PLANNED
&& !CollectionUtils.isEmpty(stockMove.getStockMoveLineList()))
.findFirst();
if (stockMoveToRealize.isPresent()) {
manufOrderStockMoveService.finishStockMove(stockMoveToRealize.get());
}
// generate new stock move
StockMove newStockMove =
stockMoveService.createStockMove(
null,
null,
company,
fromStockLocation,
toStockLocation,
null,
operationOrder.getPlannedStartDateT().toLocalDate(),
null,
StockMoveRepository.TYPE_INTERNAL);
newStockMove.setOrigin(operationOrder.getOperationName());
newStockMove.setOriginId(operationOrder.getId());
newStockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_OPERATION_ORDER);
newStockMove.setStockMoveLineList(new ArrayList<>());
createNewStockMoveLines(operationOrder, newStockMove);
if (!newStockMove.getStockMoveLineList().isEmpty()) {
// plan the stockmove
stockMoveService.plan(newStockMove);
operationOrder.addInStockMoveListItem(newStockMove);
newStockMove.getStockMoveLineList().forEach(operationOrder::addConsumedStockMoveLineListItem);
operationOrder.clearDiffConsumeProdProductList();
}
}
/**
* Generate stock move lines after a partial finish
*
* @param operationOrder
* @param stockMove
*/
public void createNewStockMoveLines(OperationOrder operationOrder, StockMove stockMove)
throws AxelorException {
List<ProdProduct> diffProdProductList;
Beans.get(OperationOrderService.class).updateDiffProdProductList(operationOrder);
diffProdProductList = new ArrayList<>(operationOrder.getDiffConsumeProdProductList());
Beans.get(ManufOrderStockMoveService.class)
.createNewStockMoveLines(
diffProdProductList, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS);
}
public void cancel(OperationOrder operationOrder) throws AxelorException {
List<StockMove> stockMoveList = operationOrder.getInStockMoveList();
if (stockMoveList != null) {
for (StockMove stockMove : stockMoveList) {
stockMoveService.cancel(stockMove);
}
stockMoveList
.stream()
.filter(stockMove -> stockMove.getStockMoveLineList() != null)
.flatMap(stockMove -> stockMove.getStockMoveLineList().stream())
.forEach(stockMoveLine -> stockMoveLine.setConsumedOperationOrder(null));
}
}
/**
* Clear the consumed list and create a new one with the right quantity.
*
* @param operationOrder
* @param qtyToUpdate
*/
public void createNewConsumedStockMoveLineList(
OperationOrder operationOrder, BigDecimal qtyToUpdate) throws AxelorException {
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
// find planned stock move
Optional<StockMove> stockMoveOpt =
manufOrderStockMoveService.getPlannedStockMove(operationOrder.getInStockMoveList());
if (!stockMoveOpt.isPresent()) {
return;
}
StockMove stockMove = stockMoveOpt.get();
stockMoveService.cancel(stockMove);
// clear all lists from planned lines
operationOrder
.getConsumedStockMoveLineList()
.removeIf(
stockMoveLine ->
stockMoveLine.getStockMove().getStatusSelect()
== StockMoveRepository.STATUS_CANCELED);
stockMove.clearStockMoveLineList();
// create a new list
for (ProdProduct prodProduct : operationOrder.getToConsumeProdProductList()) {
BigDecimal qty =
manufOrderStockMoveService.getFractionQty(
operationOrder.getManufOrder(), prodProduct, qtyToUpdate);
manufOrderStockMoveService._createStockMoveLine(
prodProduct, stockMove, StockMoveLineService.TYPE_IN_PRODUCTIONS, qty);
// Update consumed StockMoveLineList with created stock move lines
stockMove
.getStockMoveLineList()
.stream()
.filter(
stockMoveLine1 ->
!operationOrder.getConsumedStockMoveLineList().contains(stockMoveLine1))
.forEach(operationOrder::addConsumedStockMoveLineListItem);
}
stockMoveService.plan(stockMove);
}
}

View File

@ -0,0 +1,498 @@
/*
* 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.production.service.operationorder;
import com.axelor.apps.production.db.Machine;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.OperationOrderDuration;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.OperationOrderDurationRepository;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.db.repo.ProductionConfigRepository;
import com.axelor.apps.production.db.repo.WorkCenterRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderStockMoveService;
import com.axelor.apps.production.service.manuforder.ManufOrderWorkflowService;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.service.StockMoveService;
import com.axelor.apps.tool.date.DurationTool;
import com.axelor.auth.AuthUtils;
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 com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
public class OperationOrderWorkflowService {
protected OperationOrderStockMoveService operationOrderStockMoveService;
protected OperationOrderRepository operationOrderRepo;
protected OperationOrderDurationRepository operationOrderDurationRepo;
protected AppProductionService appProductionService;
@Inject
public OperationOrderWorkflowService(
OperationOrderStockMoveService operationOrderStockMoveService,
OperationOrderRepository operationOrderRepo,
OperationOrderDurationRepository operationOrderDurationRepo,
AppProductionService appProductionService) {
this.operationOrderStockMoveService = operationOrderStockMoveService;
this.operationOrderRepo = operationOrderRepo;
this.operationOrderDurationRepo = operationOrderDurationRepo;
this.appProductionService = appProductionService;
}
/**
* Plan an operation order. For successive calls, must be called by order of operation order
* priority.
*
* @param operationOrder
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public OperationOrder plan(OperationOrder operationOrder) throws AxelorException {
if (CollectionUtils.isEmpty(operationOrder.getToConsumeProdProductList())) {
Beans.get(OperationOrderService.class).createToConsumeProdProductList(operationOrder);
}
// operationOrder.setPlannedStartDateT(this.getLastOperationOrder(operationOrder));
// operationOrder.setPlannedEndDateT(this.computePlannedEndDateT(operationOrder));
// operationOrder.setPlannedDuration(
// DurationTool.getSecondsDuration(
// Duration.between(
// operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT())));
ManufOrder manufOrder = operationOrder.getManufOrder();
if (manufOrder == null || manufOrder.getIsConsProOnOperation()) {
operationOrderStockMoveService.createToConsumeStockMove(operationOrder);
}
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_PLANNED);
return operationOrderRepo.save(operationOrder);
}
/**
* Replan an operation order. For successive calls, must reset planned dates first, then call by
* order of operation order priority.
*
* @param operationOrder
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public OperationOrder replan(OperationOrder operationOrder) throws AxelorException {
operationOrder.setPlannedStartDateT(this.getLastOperationOrder(operationOrder));
operationOrder.setPlannedEndDateT(this.computePlannedEndDateT(operationOrder));
operationOrder.setPlannedDuration(
DurationTool.getSecondsDuration(
Duration.between(
operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT())));
return operationOrderRepo.save(operationOrder);
}
/**
* Reset the planned dates from the specified operation order list.
*
* @param operationOrderList
* @return
*/
@Transactional
public List<OperationOrder> resetPlannedDates(List<OperationOrder> operationOrderList) {
for (OperationOrder operationOrder : operationOrderList) {
operationOrder.setPlannedStartDateT(null);
operationOrder.setPlannedEndDateT(null);
operationOrder.setPlannedDuration(null);
}
return operationOrderList;
}
public LocalDateTime getLastOperationOrder(OperationOrder operationOrder) {
OperationOrder lastOperationOrder =
operationOrderRepo
.all()
.filter(
"self.manufOrder = ?1 AND self.priority <= ?2 AND self.statusSelect >= 3 AND self.statusSelect < 6 AND self.id != ?3",
operationOrder.getManufOrder(),
operationOrder.getPriority(),
operationOrder.getId())
.order("-priority")
.order("-plannedEndDateT")
.fetchOne();
if (lastOperationOrder != null) {
if (lastOperationOrder.getPriority() == operationOrder.getPriority()) {
if (lastOperationOrder.getPlannedStartDateT() != null
&& lastOperationOrder
.getPlannedStartDateT()
.isAfter(operationOrder.getManufOrder().getPlannedStartDateT())) {
if (lastOperationOrder
.getMachineWorkCenter()
.equals(operationOrder.getMachineWorkCenter())) {
return lastOperationOrder.getPlannedEndDateT();
}
return lastOperationOrder.getPlannedStartDateT();
} else {
return operationOrder.getManufOrder().getPlannedStartDateT();
}
} else {
if (lastOperationOrder.getPlannedEndDateT() != null
&& lastOperationOrder
.getPlannedEndDateT()
.isAfter(operationOrder.getManufOrder().getPlannedStartDateT())) {
return lastOperationOrder.getPlannedEndDateT();
} else {
return operationOrder.getManufOrder().getPlannedStartDateT();
}
}
}
return operationOrder.getManufOrder().getPlannedStartDateT();
}
/**
* Starts the given {@link OperationOrder} and sets its starting time
*
* @param operationOrder An operation order
*/
@Transactional(rollbackOn = {Exception.class})
public void start(OperationOrder operationOrder) throws AxelorException {
if (operationOrder.getStatusSelect() != OperationOrderRepository.STATUS_IN_PROGRESS) {
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_IN_PROGRESS);
operationOrder.setRealStartDateT(appProductionService.getTodayDateTime().toLocalDateTime());
startOperationOrderDuration(operationOrder);
if (operationOrder.getManufOrder() != null) {
int beforeOrAfterConfig =
operationOrder.getManufOrder().getProdProcess().getStockMoveRealizeOrderSelect();
if (beforeOrAfterConfig == ProductionConfigRepository.REALIZE_START) {
for (StockMove stockMove : operationOrder.getInStockMoveList()) {
Beans.get(ManufOrderStockMoveService.class).finishStockMove(stockMove);
}
StockMove newStockMove =
operationOrderStockMoveService._createToConsumeStockMove(
operationOrder, operationOrder.getManufOrder().getCompany());
newStockMove.setStockMoveLineList(new ArrayList<>());
Beans.get(StockMoveService.class).plan(newStockMove);
operationOrder.addInStockMoveListItem(newStockMove);
}
}
operationOrderRepo.save(operationOrder);
}
if (operationOrder.getManufOrder().getStatusSelect()
!= ManufOrderRepository.STATUS_IN_PROGRESS) {
Beans.get(ManufOrderWorkflowService.class).start(operationOrder.getManufOrder());
}
}
/**
* Pauses the given {@link OperationOrder} and sets its pausing time
*
* @param operationOrder An operation order
*/
@Transactional
public void pause(OperationOrder operationOrder) {
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_STANDBY);
stopOperationOrderDuration(operationOrder);
operationOrderRepo.save(operationOrder);
}
/**
* Resumes the given {@link OperationOrder} and sets its resuming time
*
* @param operationOrder An operation order
*/
@Transactional
public void resume(OperationOrder operationOrder) {
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_IN_PROGRESS);
startOperationOrderDuration(operationOrder);
operationOrderRepo.save(operationOrder);
}
/**
* Ends the given {@link OperationOrder} and sets its stopping time<br>
* Realizes the linked stock moves
*
* @param operationOrder An operation order
*/
@Transactional(rollbackOn = {Exception.class})
public void finish(OperationOrder operationOrder) throws AxelorException {
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_FINISHED);
operationOrder.setRealEndDateT(appProductionService.getTodayDateTime().toLocalDateTime());
stopOperationOrderDuration(operationOrder);
// operationOrderStockMoveService.finish(operationOrder);
operationOrderRepo.save(operationOrder);
}
@Transactional(rollbackOn = {Exception.class})
public void finishAndAllOpFinished(OperationOrder operationOrder) throws AxelorException {
finish(operationOrder);
Beans.get(ManufOrderWorkflowService.class).allOpFinished(operationOrder.getManufOrder());
}
/**
* Cancels the given {@link OperationOrder} and its linked stock moves And sets its stopping time
*
* @param operationOrder An operation order
*/
@Transactional
public void cancel(OperationOrder operationOrder) throws AxelorException {
int oldStatus = operationOrder.getStatusSelect();
operationOrder.setStatusSelect(OperationOrderRepository.STATUS_CANCELED);
if (oldStatus == OperationOrderRepository.STATUS_IN_PROGRESS) {
stopOperationOrderDuration(operationOrder);
}
if (operationOrder.getConsumedStockMoveLineList() != null) {
operationOrder
.getConsumedStockMoveLineList()
.forEach(stockMoveLine -> stockMoveLine.setConsumedOperationOrder(null));
}
operationOrderStockMoveService.cancel(operationOrder);
operationOrderRepo.save(operationOrder);
}
/**
* Starts an {@link OperationOrderDuration} and links it to the given {@link OperationOrder}
*
* @param operationOrder An operation order
*/
public void startOperationOrderDuration(OperationOrder operationOrder) {
OperationOrderDuration duration = new OperationOrderDuration();
duration.setStartedBy(AuthUtils.getUser());
duration.setStartingDateTime(appProductionService.getTodayDateTime().toLocalDateTime());
operationOrder.addOperationOrderDurationListItem(duration);
}
/**
* Ends the last {@link OperationOrderDuration} and sets the real duration of {@code
* operationOrder}<br>
* Adds the real duration to the {@link Machine} linked to {@code operationOrder}
*
* @param operationOrder An operation order
*/
public void stopOperationOrderDuration(OperationOrder operationOrder) {
OperationOrderDuration duration =
operationOrderDurationRepo
.all()
.filter(
"self.operationOrder.id = ? AND self.stoppedBy IS NULL AND self.stoppingDateTime IS NULL",
operationOrder.getId())
.fetchOne();
duration.setStoppedBy(AuthUtils.getUser());
duration.setStoppingDateTime(appProductionService.getTodayDateTime().toLocalDateTime());
if (operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_FINISHED) {
long durationLong = DurationTool.getSecondsDuration(computeRealDuration(operationOrder));
operationOrder.setRealDuration(durationLong);
WorkCenter machineWorkCenter = operationOrder.getMachineWorkCenter();
Machine machine = null;
if (machineWorkCenter != null) {
machine = machineWorkCenter.getMachine();
} else if (operationOrder.getWorkCenter() != null) {
machine = operationOrder.getWorkCenter().getMachine();
}
if (machine != null) {
machine.setOperatingDuration(machine.getOperatingDuration() + durationLong);
}
}
operationOrderDurationRepo.save(duration);
}
/**
* Compute the duration of operation order, then fill {@link OperationOrder#realDuration} with the
* computed value.
*
* @param operationOrder
*/
public void updateRealDuration(OperationOrder operationOrder) {
long durationLong = DurationTool.getSecondsDuration(computeRealDuration(operationOrder));
operationOrder.setRealDuration(durationLong);
}
/**
* Computes the duration of all the {@link OperationOrderDuration} of {@code operationOrder}
*
* @param operationOrder An operation order
* @return Real duration of {@code operationOrder}
*/
public Duration computeRealDuration(OperationOrder operationOrder) {
Duration totalDuration = Duration.ZERO;
List<OperationOrderDuration> operationOrderDurations =
operationOrder.getOperationOrderDurationList();
if (operationOrderDurations != null) {
for (OperationOrderDuration operationOrderDuration : operationOrderDurations) {
if (operationOrderDuration.getStartingDateTime() != null
&& operationOrderDuration.getStoppingDateTime() != null) {
totalDuration =
totalDuration.plus(
Duration.between(
operationOrderDuration.getStartingDateTime(),
operationOrderDuration.getStoppingDateTime()));
}
}
}
return totalDuration;
}
/**
* Set planned start and end dates.
*
* @param operationOrder
* @param plannedStartDateT
* @param plannedEndDateT
* @return
*/
@Transactional
public OperationOrder setPlannedDates(
OperationOrder operationOrder,
LocalDateTime plannedStartDateT,
LocalDateTime plannedEndDateT) {
operationOrder.setPlannedStartDateT(plannedStartDateT);
operationOrder.setPlannedEndDateT(plannedEndDateT);
return computeDuration(operationOrder);
}
/**
* Set real start and end dates.
*
* @param operationOrder
* @param realStartDateT
* @param realEndDateT
* @return
*/
@Transactional
public OperationOrder setRealDates(
OperationOrder operationOrder, LocalDateTime realStartDateT, LocalDateTime realEndDateT) {
operationOrder.setRealStartDateT(realStartDateT);
operationOrder.setRealEndDateT(realEndDateT);
return computeDuration(operationOrder);
}
@Transactional
public OperationOrder computeDuration(OperationOrder operationOrder) {
Long duration;
if (operationOrder.getPlannedStartDateT() != null
&& operationOrder.getPlannedEndDateT() != null) {
duration =
DurationTool.getSecondsDuration(
Duration.between(
operationOrder.getPlannedStartDateT(), operationOrder.getPlannedEndDateT()));
operationOrder.setPlannedDuration(duration);
}
updateRealDuration(operationOrder);
return operationOrder;
}
public LocalDateTime computePlannedEndDateT(OperationOrder operationOrder)
throws AxelorException {
if (operationOrder.getWorkCenter() != null) {
return operationOrder
.getPlannedStartDateT()
.plusSeconds(
(int)
this.computeEntireCycleDuration(
operationOrder, operationOrder.getManufOrder().getQty()));
}
return operationOrder.getPlannedStartDateT();
}
public long computeEntireCycleDuration(OperationOrder operationOrder, BigDecimal qty)
throws AxelorException {
ProdProcessLine prodProcessLine = operationOrder.getProdProcessLine();
WorkCenter workCenter = prodProcessLine.getWorkCenter();
long duration = 0;
BigDecimal maxCapacityPerCycle = prodProcessLine.getMaxCapacityPerCycle();
BigDecimal nbCycles;
if (maxCapacityPerCycle.compareTo(BigDecimal.ZERO) == 0) {
nbCycles = qty;
} else {
nbCycles = qty.divide(maxCapacityPerCycle, 0, RoundingMode.UP);
}
int workCenterTypeSelect = workCenter.getWorkCenterTypeSelect();
if (workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_MACHINE
|| workCenterTypeSelect == WorkCenterRepository.WORK_CENTER_TYPE_BOTH) {
Machine machine = workCenter.getMachine();
if (machine == null) {
throw new AxelorException(
workCenter,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.WORKCENTER_NO_MACHINE),
workCenter.getName());
}
duration += machine.getStartingDuration();
duration += machine.getEndingDuration();
duration +=
nbCycles
.subtract(new BigDecimal(1))
.multiply(new BigDecimal(machine.getSetupDuration()))
.longValue();
}
BigDecimal durationPerCycle = new BigDecimal(prodProcessLine.getDurationPerCycle());
duration += nbCycles.multiply(durationPerCycle).longValue();
return duration;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.production.service.productionorder;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.exception.AxelorException;
import java.util.List;
public interface ProductionOrderSaleOrderService {
public List<Long> generateProductionOrder(SaleOrder saleOrder) throws AxelorException;
public ProductionOrder generateManufOrder(
ProductionOrder productionOrder, SaleOrderLine saleOrderLine) throws AxelorException;
}

View File

@ -0,0 +1,152 @@
/*
* 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.production.service.productionorder;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.db.repo.ProductionOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductionOrderSaleOrderServiceImpl implements ProductionOrderSaleOrderService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected UnitConversionService unitConversionService;
protected ProductionOrderService productionOrderService;
protected ProductionOrderRepository productionOrderRepo;
protected AppProductionService appProductionService;
@Inject
public ProductionOrderSaleOrderServiceImpl(
UnitConversionService unitConversionService,
ProductionOrderService productionOrderService,
ProductionOrderRepository productionOrderRepo,
AppProductionService appProductionService) {
this.unitConversionService = unitConversionService;
this.productionOrderService = productionOrderService;
this.productionOrderRepo = productionOrderRepo;
this.appProductionService = appProductionService;
}
@Override
public List<Long> generateProductionOrder(SaleOrder saleOrder) throws AxelorException {
boolean oneProdOrderPerSO = appProductionService.getAppProduction().getOneProdOrderPerSO();
List<Long> productionOrderIdList = new ArrayList<>();
if (saleOrder.getSaleOrderLineList() == null) {
return productionOrderIdList;
}
ProductionOrder productionOrder = null;
for (SaleOrderLine saleOrderLine : saleOrder.getSaleOrderLineList()) {
if (productionOrder == null || !oneProdOrderPerSO) {
productionOrder = this.createProductionOrder(saleOrder);
}
productionOrder = this.generateManufOrder(productionOrder, saleOrderLine);
if (productionOrder != null && !productionOrderIdList.contains(productionOrder.getId())) {
productionOrderIdList.add(productionOrder.getId());
}
}
return productionOrderIdList;
}
protected ProductionOrder createProductionOrder(SaleOrder saleOrder) throws AxelorException {
return productionOrderService.createProductionOrder(saleOrder);
}
@Override
public ProductionOrder generateManufOrder(
ProductionOrder productionOrder, SaleOrderLine saleOrderLine) throws AxelorException {
Product product = saleOrderLine.getProduct();
if (saleOrderLine.getSaleSupplySelect() == ProductRepository.SALE_SUPPLY_PRODUCE
&& product != null
&& product.getProductTypeSelect().equals(ProductRepository.PRODUCT_TYPE_STORABLE)) {
BillOfMaterial billOfMaterial = saleOrderLine.getBillOfMaterial();
if (billOfMaterial == null) {
billOfMaterial = product.getDefaultBillOfMaterial();
}
if (billOfMaterial == null && product.getParentProduct() != null) {
billOfMaterial = product.getParentProduct().getDefaultBillOfMaterial();
}
if (billOfMaterial == null) {
throw new AxelorException(
saleOrderLine,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_ORDER_SALES_ORDER_NO_BOM),
product.getName(),
product.getCode());
}
if (billOfMaterial.getProdProcess() == null) {
return null;
}
Unit unit = saleOrderLine.getProduct().getUnit();
BigDecimal qty = saleOrderLine.getQty();
if (unit != null && !unit.equals(saleOrderLine.getUnit())) {
qty =
unitConversionService.convert(
saleOrderLine.getUnit(), unit, qty, qty.scale(), saleOrderLine.getProduct());
}
return productionOrderService.addManufOrder(
productionOrder,
product,
billOfMaterial,
qty,
LocalDateTime.now(),
null,
saleOrderLine.getSaleOrder(),
ManufOrderService.ORIGIN_TYPE_SALE_ORDER);
}
return null;
}
}

View File

@ -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.production.service.productionorder;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public interface ProductionOrderService {
public ProductionOrder createProductionOrder(SaleOrder saleOrder) throws AxelorException;
public String getProductionOrderSeq() throws AxelorException;
/**
* Generate a Production Order
*
* @param product Product must be passed in param because product can be different of bill of
* material product (Product variant)
* @param billOfMaterial
* @param qtyRequested
* @param businessProject
* @param startDate
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public ProductionOrder generateProductionOrder(
Product product,
BillOfMaterial billOfMaterial,
BigDecimal qtyRequested,
LocalDateTime startDate)
throws AxelorException;
/**
* @param productionOrder
* @param product
* @param billOfMaterial
* @param qtyRequested
* @param startDate
* @param saleOrder
* @param originType
* <li>1 : MRP
* <li>2 : Sale order
* <li>3 : Other
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public ProductionOrder addManufOrder(
ProductionOrder productionOrder,
Product product,
BillOfMaterial billOfMaterial,
BigDecimal qtyRequested,
LocalDateTime startDate,
LocalDateTime endDate,
SaleOrder saleOrder,
int originType)
throws AxelorException;
}

View File

@ -0,0 +1,197 @@
/*
* 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.production.service.productionorder;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.SequenceRepository;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.ProductionOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.manuforder.ManufOrderServiceImpl;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.service.StockMoveService;
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 com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductionOrderServiceImpl implements ProductionOrderService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ManufOrderService manufOrderService;
protected SequenceService sequenceService;
protected ProductionOrderRepository productionOrderRepo;
@Inject
public ProductionOrderServiceImpl(
ManufOrderService manufOrderService,
SequenceService sequenceService,
ProductionOrderRepository productionOrderRepo) {
this.manufOrderService = manufOrderService;
this.sequenceService = sequenceService;
this.productionOrderRepo = productionOrderRepo;
}
public ProductionOrder createProductionOrder(SaleOrder saleOrder) throws AxelorException {
ProductionOrder productionOrder = new ProductionOrder(this.getProductionOrderSeq());
if (saleOrder != null) {
productionOrder.setClientPartner(saleOrder.getClientPartner());
productionOrder.setSaleOrder(saleOrder);
}
return productionOrder;
}
public String getProductionOrderSeq() throws AxelorException {
String seq = sequenceService.getSequenceNumber(SequenceRepository.PRODUCTION_ORDER);
if (seq == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_ORDER_SEQ));
}
return seq;
}
/**
* Generate a Production Order
*
* @param product Product must be passed in param because product can be different of bill of
* material product (Product variant)
* @param billOfMaterial
* @param qtyRequested
* @param businessProject
* @return
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public ProductionOrder generateProductionOrder(
Product product,
BillOfMaterial billOfMaterial,
BigDecimal qtyRequested,
LocalDateTime startDate)
throws AxelorException {
ProductionOrder productionOrder = this.createProductionOrder(null);
this.addManufOrder(
productionOrder,
product,
billOfMaterial,
qtyRequested,
startDate,
null,
null,
ManufOrderService.ORIGIN_TYPE_OTHER);
return productionOrderRepo.save(productionOrder);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public ProductionOrder addManufOrder(
ProductionOrder productionOrder,
Product product,
BillOfMaterial billOfMaterial,
BigDecimal qtyRequested,
LocalDateTime startDate,
LocalDateTime endDate,
SaleOrder saleOrder,
int originType)
throws AxelorException {
BigDecimal bomQty = billOfMaterial.getQty();
BigDecimal manufCount = qtyRequested.divide(bomQty, 0, RoundingMode.CEILING);
for (int index = 0; index < manufCount.intValue(); index++) {
ManufOrder manufOrder =
manufOrderService.generateManufOrder(
product,
bomQty,
ManufOrderService.DEFAULT_PRIORITY,
ManufOrderService.IS_TO_INVOICE,
billOfMaterial,
startDate,
endDate,
originType);
if (manufOrder != null) {
if (saleOrder != null) {
manufOrder.setSaleOrder(saleOrder);
manufOrder.setClientPartner(saleOrder.getClientPartner());
}
manufOrder.setStypeSelect(ManufOrderRepository.STYPE_MANUF_ORDER);
productionOrder.addManufOrderListItem(manufOrder);
}
}
return productionOrderRepo.save(productionOrder);
}
@Transactional(rollbackOn = {Exception.class})
public StockMove generateConsumeStockMoveFromSelectedManufOrder(
ProductionOrder productionOrder, List<ManufOrder> manufOrderList) throws AxelorException {
ManufOrder manufOrder = manufOrderList.get(0);
StockMove stockMove =
Beans.get(ManufOrderServiceImpl.class)
.createToConsumeProdProductList(manufOrder, manufOrderList.size());
for (ManufOrder manufOrder2 : manufOrderList) {
if (stockMove.getStockMoveLineList() != null && !stockMove.getStockMoveLineList().isEmpty()) {
Beans.get(ManufOrderServiceImpl.class).createToConsumeProdProductList(manufOrder2);
Beans.get(StockMoveService.class).plan(stockMove);
}
if (stockMove.getStockMoveLineList() != null) {
for (StockMoveLine stockMoveLine : stockMove.getStockMoveLineList()) {
manufOrder2.addTransferedStockMoveLineListItem(stockMoveLine);
}
}
manufOrder2.addInStockMoveListItem(stockMove);
Beans.get(ProductionOrderRepository.class).save(productionOrder);
}
stockMove.setOrigin(productionOrder.getProductionOrderSeq());
stockMove.setOriginId(productionOrder.getId());
stockMove.setOriginTypeSelect(StockMoveRepository.ORIGIN_PRODUCTION_ORDER);
return Beans.get(StockMoveRepository.class).save(stockMove);
}
}

View File

@ -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.production.service.productionorder;
import com.axelor.exception.AxelorException;
import com.axelor.rpc.Context;
public interface ProductionOrderWizardService {
public Long validate(Context context) throws AxelorException;
}

View File

@ -0,0 +1,103 @@
/*
* 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.production.service.productionorder;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.rpc.Context;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductionOrderWizardServiceImpl implements ProductionOrderWizardService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected ProductionOrderService productionOrderService;
protected BillOfMaterialRepository billOfMaterialRepo;
protected ProductRepository productRepo;
protected AppProductionService appProductionService;
@Inject
public ProductionOrderWizardServiceImpl(
ProductionOrderService productionOrderService,
BillOfMaterialRepository billOfMaterialRepo,
ProductRepository productRepo,
AppProductionService appProductionService) {
this.productionOrderService = productionOrderService;
this.billOfMaterialRepo = billOfMaterialRepo;
this.productRepo = productRepo;
this.appProductionService = appProductionService;
}
@Override
@SuppressWarnings("unchecked")
public Long validate(Context context) throws AxelorException {
Map<String, Object> bomContext = (Map<String, Object>) context.get("billOfMaterial");
BillOfMaterial billOfMaterial =
billOfMaterialRepo.find(((Integer) bomContext.get("id")).longValue());
BigDecimal qty = new BigDecimal((String) context.get("qty"));
Product product = null;
if (context.get("product") != null) {
Map<String, Object> productContext = (Map<String, Object>) context.get("product");
product = productRepo.find(((Integer) productContext.get("id")).longValue());
} else {
product = billOfMaterial.getProduct();
}
ZonedDateTime startDateT;
if (context.containsKey("_startDate") && context.get("_startDate") != null) {
startDateT =
ZonedDateTime.parse(
(CharSequence) context.get("_startDate"),
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault()));
} else {
startDateT = appProductionService.getTodayDateTime();
}
ProductionOrder productionOrder =
productionOrderService.generateProductionOrder(
product, billOfMaterial, qty, startDateT.toLocalDateTime());
if (productionOrder != null) {
return productionOrder.getId();
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.PRODUCTION_ORDER_2));
}
}
}

View File

@ -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.production.translation;
public interface ITranslation {
public static final String MANUFACTURING_APP_NAME = /*$$(*/ "value:Manufacturing"; /*)*/
public static final String WORK_IN_PROGRESS_VALUATION = /*$$(*/
"Work in progress valuation"; /*)*/
}

View File

@ -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.production.web;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class AppProductionController {
public void generateProductionConfigurations(ActionRequest request, ActionResponse response) {
Beans.get(AppProductionService.class).generateProductionConfigurations();
response.setReload(true);
}
}

View File

@ -0,0 +1,223 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.TempBomTree;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.service.BillOfMaterialService;
import com.axelor.apps.production.service.ProdProcessService;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.ResponseMessageType;
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.google.common.collect.Lists;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class BillOfMaterialController {
private static final Logger LOG = LoggerFactory.getLogger(BillOfMaterialController.class);
public void computeCostPrice(ActionRequest request, ActionResponse response)
throws AxelorException {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
CostSheet costSheet =
Beans.get(CostSheetService.class)
.computeCostPrice(
Beans.get(BillOfMaterialRepository.class).find(billOfMaterial.getId()),
CostSheetService.ORIGIN_BILL_OF_MATERIAL,
null);
response.setView(
ActionView.define(String.format(I18n.get("Cost sheet - %s"), billOfMaterial.getName()))
.model(CostSheet.class.getName())
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("popup-save", "false")
.add("grid", "cost-sheet-bill-of-material-grid")
.add("form", "cost-sheet-bill-of-material-form")
.context("_showRecord", String.valueOf(costSheet.getId()))
.map());
response.setReload(true);
}
public void updateProductCostPrice(ActionRequest request, ActionResponse response)
throws AxelorException {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
Beans.get(BillOfMaterialService.class)
.updateProductCostPrice(
Beans.get(BillOfMaterialRepository.class).find(billOfMaterial.getId()));
response.setReload(true);
}
public void checkOriginalBillOfMaterial(ActionRequest request, ActionResponse response) {
BillOfMaterialRepository billOfMaterialRepository = Beans.get(BillOfMaterialRepository.class);
BillOfMaterial billOfMaterial =
billOfMaterialRepository.find(request.getContext().asType(BillOfMaterial.class).getId());
List<BillOfMaterial> BillOfMaterialSet = Lists.newArrayList();
BillOfMaterialSet =
billOfMaterialRepository
.all()
.filter("self.originalBillOfMaterial = :origin")
.bind("origin", billOfMaterial)
.fetch();
String message;
if (!BillOfMaterialSet.isEmpty()) {
String existingVersions = "";
for (BillOfMaterial billOfMaterialVersion : BillOfMaterialSet) {
existingVersions += "<li>" + billOfMaterialVersion.getFullName() + "</li>";
}
message =
String.format(
I18n.get(
"This bill of material already has the following versions : <br/><ul> %s </ul>And these versions may also have ones. Do you still wish to create a new one ?"),
existingVersions);
} else {
message = I18n.get("Do you really wish to create a new version of this bill of material ?");
}
response.setAlert(message);
}
public void generateNewVersion(ActionRequest request, ActionResponse response) {
BillOfMaterial billOfMaterial =
Beans.get(BillOfMaterialRepository.class)
.find(request.getContext().asType(BillOfMaterial.class).getId());
BillOfMaterial copy = Beans.get(BillOfMaterialService.class).generateNewVersion(billOfMaterial);
response.setView(
ActionView.define("Bill of material")
.model(BillOfMaterial.class.getName())
.add("form", "bill-of-material-form")
.add("grid", "bill-of-material-grid")
.domain("self.defineSubBillOfMaterial = true AND self.personalized = false")
.context("_showRecord", String.valueOf(copy.getId()))
.map());
}
public void validateProdProcess(ActionRequest request, ActionResponse response) {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
if (billOfMaterial != null && billOfMaterial.getProdProcess() != null) {
if (billOfMaterial.getProdProcess().getIsConsProOnOperation()) {
try {
Beans.get(ProdProcessService.class)
.validateProdProcess(billOfMaterial.getProdProcess(), billOfMaterial);
} catch (AxelorException e) {
TraceBackService.trace(response, e, ResponseMessageType.ERROR);
}
}
}
}
public void print(ActionRequest request, ActionResponse response) throws AxelorException {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
BillOfMaterialService billOfMaterialService = Beans.get(BillOfMaterialService.class);
String language = ReportSettings.getPrintingLocale(null);
String name = billOfMaterialService.getFileName(billOfMaterial);
String fileLink =
billOfMaterialService.getReportLink(
billOfMaterial, name, language, ReportSettings.FORMAT_PDF);
LOG.debug("Printing " + name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
public void openBomTree(ActionRequest request, ActionResponse response) {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
billOfMaterial = Beans.get(BillOfMaterialRepository.class).find(billOfMaterial.getId());
TempBomTree tempBomTree = Beans.get(BillOfMaterialService.class).generateTree(billOfMaterial);
response.setView(
ActionView.define(I18n.get("Bill of material"))
.model(TempBomTree.class.getName())
.add("tree", "bill-of-material-tree")
.context("_tempBomTreeId", tempBomTree.getId())
.map());
}
public void setBillOfMaterialAsDefault(ActionRequest request, ActionResponse response) {
try {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
billOfMaterial = Beans.get(BillOfMaterialRepository.class).find(billOfMaterial.getId());
Beans.get(BillOfMaterialService.class).setBillOfMaterialAsDefault(billOfMaterial);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(e);
}
}
public void computeName(ActionRequest request, ActionResponse response) {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
if (billOfMaterial.getName() == null) {
response.setValue("name", Beans.get(BillOfMaterialService.class).computeName(billOfMaterial));
}
}
public void addRawMaterials(ActionRequest request, ActionResponse response) {
try {
BillOfMaterial billOfMaterial = request.getContext().asType(BillOfMaterial.class);
@SuppressWarnings("unchecked")
ArrayList<LinkedHashMap<String, Object>> rawMaterials =
(ArrayList<LinkedHashMap<String, Object>>) request.getContext().get("rawMaterials");
if (rawMaterials != null && !rawMaterials.isEmpty()) {
Beans.get(BillOfMaterialService.class)
.addRawMaterials(billOfMaterial.getId(), rawMaterials);
response.setReload(true);
}
} catch (Exception e) {
TraceBackService.trace(e);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.production.web;
import com.axelor.app.AppSettings;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.report.engine.ReportSettings;
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.google.inject.Singleton;
@Singleton
public class CostSheetController {
public void printCostSheetLineDetail(ActionRequest request, ActionResponse response) {
try {
CostSheet costSheet = request.getContext().asType(CostSheet.class);
Long costSheetId = costSheet.getId();
String name = I18n.get("Cost sheet");
String fileLink =
ReportFactory.createReport(IReport.COST_SHEET, name + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("CostSheetId", costSheetId)
.addParam(
"manageCostSheetGroup",
Beans.get(AppProductionService.class)
.getAppProduction()
.getManageCostSheetGroup())
.addParam("BaseUrl", AppSettings.get().getBaseURL())
.generate()
.getFileLink();
response.setCanClose(true);
response.setView(ActionView.define(name).add("html", fileLink).map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,787 @@
/*
* 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.production.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.BillOfMaterialConsumption;
import com.axelor.apps.production.db.CostSheet;
import com.axelor.apps.production.db.DocumentationManufOrder;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.repo.BillOfMaterialConsumptionRepository;
import com.axelor.apps.production.db.repo.CostSheetRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.BillOfMaterialServiceImpl;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.manuforder.ManufOrderStockMoveService;
import com.axelor.apps.production.service.manuforder.ManufOrderWorkflowService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.db.mapper.Mapper;
import com.axelor.exception.AxelorException;
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.Context;
import com.google.common.base.Strings;
import com.google.inject.Singleton;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.birt.core.exception.BirtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ManufOrderController {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void start(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
Beans.get(ManufOrderWorkflowService.class).start(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void pause(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
Beans.get(ManufOrderWorkflowService.class).pause(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void resume(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
Beans.get(ManufOrderWorkflowService.class).resume(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void finish(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
if (!Beans.get(ManufOrderWorkflowService.class).finish(manufOrder)) {
response.setNotify(I18n.get(IExceptionMessage.MANUF_ORDER_EMAIL_NOT_SENT));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void partialFinish(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
if (!Beans.get(ManufOrderWorkflowService.class).partialFinish(manufOrder)) {
response.setNotify(I18n.get(IExceptionMessage.MANUF_ORDER_EMAIL_NOT_SENT));
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void cancel(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
ManufOrder manufOrder = context.asType(ManufOrder.class);
Beans.get(ManufOrderWorkflowService.class)
.cancel(
Beans.get(ManufOrderRepository.class).find(manufOrder.getId()),
manufOrder.getCancelReason(),
manufOrder.getCancelReasonStr());
response.setFlash(I18n.get(IExceptionMessage.MANUF_ORDER_CANCEL));
response.setCanClose(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void plan(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter(
"self.id in ?1 and self.statusSelect in (?2,?3)",
context.get("_ids"),
ManufOrderRepository.STATUS_DRAFT,
ManufOrderRepository.STATUS_CANCELED)
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderWorkflowService.class).plan(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void startF(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter("self.id in ?1", context.get("_ids"))
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderWorkflowService.class).start(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void pauseF(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter("self.id in ?1", context.get("_ids"))
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderWorkflowService.class).pause(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void resumeF(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter("self.id in ?1", context.get("_ids"))
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderWorkflowService.class).resume(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void finishF(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter("self.id in ?1", context.get("_ids"))
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderWorkflowService.class).finish(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order form on clicking realize button. Call {@link
* ManufOrderStockMoveService#consumeInStockMoves(ManufOrder)} to consume material used in manuf
* order.
*
* @param request
* @param response
*/
public void consumeStockMove(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderStockMoveService.class).consumeInStockMoves(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order form on clicking realize button. Call {@link
* ManufOrderStockMoveService#consumeInStockMoves(ManufOrder)} to consume material used in manuf
* order.
*
* @param request
* @param response
*/
public void validateOutStockMove(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderStockMoveService.class).validateOutStockMoves(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Method that generate a Pdf file for an manufacturing order
*
* @param request
* @param response
* @return
* @throws BirtException
* @throws IOException
*/
public void print(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
String manufOrderIds = "";
@SuppressWarnings("unchecked")
List<Integer> lstSelectedManufOrder = (List<Integer>) request.getContext().get("_ids");
if (lstSelectedManufOrder != null) {
for (Integer it : lstSelectedManufOrder) {
manufOrderIds += it.toString() + ",";
}
}
if (!manufOrderIds.equals("")) {
manufOrderIds = manufOrderIds.substring(0, manufOrderIds.length() - 1);
manufOrder =
Beans.get(ManufOrderRepository.class).find(new Long(lstSelectedManufOrder.get(0)));
} else if (manufOrder.getId() != null) {
manufOrderIds = manufOrder.getId().toString();
}
if (!manufOrderIds.equals("")) {
String name;
if (lstSelectedManufOrder == null) {
name =
String.format(
"%s %s",
I18n.get("Manufacturing order"),
Strings.nullToEmpty(manufOrder.getManufOrderSeq()));
} else {
name = I18n.get("Manufacturing orders");
}
String fileLink =
ReportFactory.createReport(IReport.MANUF_ORDER, name + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("ManufOrderId", manufOrderIds)
.addParam(
"activateBarCodeGeneration",
Beans.get(AppBaseService.class).getAppBase().getActivateBarCodeGeneration())
.generate()
.getFileLink();
LOG.debug("Printing {}", name);
response.setView(ActionView.define(name).add("html", fileLink).map());
} else {
response.setFlash(I18n.get(IExceptionMessage.MANUF_ORDER_1));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void preFillOperations(ActionRequest request, ActionResponse response)
throws AxelorException {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
ManufOrderService moService = Beans.get(ManufOrderService.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
moService.preFillOperations(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void generateWasteStockMove(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).generateWasteStockMove(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order wizard view. Call {@link
* ManufOrderService#updatePlannedQty(ManufOrder)}
*
* @param request
* @param response
*/
public void updatePlannedQty(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).updatePlannedQty(manufOrder);
response.setReload(true);
response.setCanClose(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order wizard view. Call {@link ManufOrderService#updateRealQty(ManufOrder,
* BigDecimal)}
*
* @param request
* @param response
*/
public void updateRealQty(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
BigDecimal qtyToUpdate = new BigDecimal(request.getContext().get("qtyToUpdate").toString());
Beans.get(ManufOrderService.class).updateRealQty(manufOrder, qtyToUpdate);
response.setReload(true);
response.setCanClose(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void printProdProcess(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
String prodProcessId = manufOrder.getProdProcess().getId().toString();
String prodProcessLable = manufOrder.getProdProcess().getName();
String fileLink =
ReportFactory.createReport(IReport.PROD_PROCESS, prodProcessLable + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("ProdProcessId", prodProcessId)
.generate()
.getFileLink();
response.setView(ActionView.define(prodProcessLable).add("html", fileLink).map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void updatePlannedDates(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrderView = request.getContext().asType(ManufOrder.class);
if (manufOrderView.getStatusSelect() == ManufOrderRepository.STATUS_PLANNED) {
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderView.getId());
if (manufOrderView.getPlannedStartDateT() != null) {
if (!manufOrderView.getPlannedStartDateT().isEqual(manufOrder.getPlannedStartDateT())) {
Beans.get(ManufOrderWorkflowService.class)
.updatePlannedDates(manufOrder, manufOrderView.getPlannedStartDateT());
response.setReload(true);
}
} else {
response.setValue("plannedStartDateT", manufOrder.getPlannedStartDateT());
}
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order form, on produced stock move line change. Call {@link
* ManufOrderService#checkProducedStockMoveLineList(ManufOrder, ManufOrder)}.
*
* @param request
* @param response
*/
public void checkProducedStockMoveLineList(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
ManufOrder oldManufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).checkProducedStockMoveLineList(manufOrder, oldManufOrder);
} catch (Exception e) {
TraceBackService.trace(response, e);
response.setReload(true);
}
}
/**
* Called from manuf order form, on produced stock move line change. Call {@link
* ManufOrderService#updateProducedStockMoveFromManufOrder(ManufOrder)}.
*
* @param request
* @param response
*/
public void updateProducedStockMoveFromManufOrder(
ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).updateProducedStockMoveFromManufOrder(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order form, on consumed stock move line change. Call {@link
* ManufOrderService#checkConsumedStockMoveLineList(ManufOrder, ManufOrder)}.
*
* @param request
* @param response
*/
public void checkConsumedStockMoveLineList(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
ManufOrder oldManufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).checkConsumedStockMoveLineList(manufOrder, oldManufOrder);
} catch (Exception e) {
TraceBackService.trace(response, e);
response.setReload(true);
}
}
/**
* Called from manuf order form, on consumed stock move line change. Call {@link
* ManufOrderService#updateConsumedStockMoveFromManufOrder(ManufOrder)}.
*
* @param request
* @param response
*/
public void updateConsumedStockMoveFromManufOrder(
ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(ManufOrderService.class).updateConsumedStockMoveFromManufOrder(manufOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from manuf order form, on clicking "compute cost price" button. Call {@link
* CostSheetService#computeCostPrice(ManufOrder, int, LocalDate)}.
*
* @param request
* @param response
*/
public void computeCostPrice(ActionRequest request, ActionResponse response) {
try {
ManufOrder manufOrder = request.getContext().asType(ManufOrder.class);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
CostSheet costSheet =
Beans.get(CostSheetService.class)
.computeCostPrice(
manufOrder,
CostSheetRepository.CALCULATION_WORK_IN_PROGRESS,
Beans.get(AppBaseService.class).getTodayDate());
response.setView(
ActionView.define(I18n.get("Cost sheet"))
.model(CostSheet.class.getName())
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("popup-save", "false")
.add("grid", "cost-sheet-bill-of-material-grid")
.add("form", "cost-sheet-bill-of-material-form")
.context("_showRecord", String.valueOf(costSheet.getId()))
.map());
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void createPackagingOrder(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
if (!Beans.get(ManufOrderWorkflowService.class).finish(manufOrder)) {
response.setNotify(I18n.get(IExceptionMessage.MANUF_ORDER_EMAIL_NOT_SENT));
}
// response.setReload(true);
ManufOrder manufOrder2 = Beans.get(ManufOrderRepository.class).find(manufOrderId);
if (manufOrder.getStypeSelect() == ManufOrderRepository.STYPE_MANUF_ORDER) {
Long packagingOrderid =
Beans.get(ManufOrderWorkflowService.class).createPackagingOrder(manufOrder2);
response.setView(
ActionView.define("Pckaging order")
.model(ManufOrder.class.getName())
.add("form", "manuf-order-form")
.add("grid", "manuf-order-grid")
.context("_showRecord", String.valueOf(packagingOrderid))
.domain("self.id = " + packagingOrderid)
.map());
} else if (manufOrder2.getStypeSelect() == ManufOrderRepository.STYPE_PACKAGING_ORDER) {
Long docManufOrderid =
Beans.get(ManufOrderWorkflowService.class).createDocumentationManufOrder(manufOrder2);
response.setView(
ActionView.define("Documentation order")
.model(DocumentationManufOrder.class.getName())
.add("form", "documentation-manuf-order-form")
.add("grid", "documentation-manuf-order-grid")
.context("_showRecord", String.valueOf(docManufOrderid))
.domain("self.id = " + docManufOrderid)
.map());
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void createTrackingNumberAndAssign(ActionRequest request, ActionResponse response)
throws AxelorException {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
Optional<OperationOrder> operationOrder1 =
manufOrder
.getOperationOrderList()
.stream()
.filter(op -> op.getOperationName().equals("MELANGE"))
.findFirst();
Optional<OperationOrder> operationOrder2 =
manufOrder
.getOperationOrderList()
.stream()
.filter(op -> op.getOperationName().equals("GRANULATION"))
.findFirst();
if (operationOrder1 != null) {
if (operationOrder1.isPresent()) {
Beans.get(ManufOrderWorkflowService.class)
.createAndAssignTrackingNumber(operationOrder1.get());
}
} else if (operationOrder2 != null) {
if (operationOrder2.isPresent()) {
Beans.get(ManufOrderWorkflowService.class)
.createAndAssignTrackingNumber(operationOrder2.get());
}
}
response.setReload(true);
}
public void createToReturnStockMove(ActionRequest request, ActionResponse response)
throws AxelorException {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
Beans.get(ManufOrderStockMoveService.class).createToReturnStockMove(manufOrder);
response.setReload(true);
}
public void planOF(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
if (manufOrder.getBillOfMaterial() != null) {
for (BillOfMaterial bom : manufOrder.getBillOfMaterial().getBillOfMaterialSet()) {
BillOfMaterialConsumption newBom =
Beans.get(BillOfMaterialServiceImpl.class)
.createBomConsumptionFromRawMaterial(bom, manufOrder);
manufOrder.addBillOfMaterialConsumptionListItem(newBom);
}
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void prefillConsumption(ActionRequest request, ActionResponse response) {
try {
Long manufOrderId = (Long) request.getContext().get("id");
ManufOrder manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrderId);
if (manufOrder.getBillOfMaterial() != null) {
Beans.get(BillOfMaterialServiceImpl.class).createBomAndAttachToManufOrder(manufOrder);
response.setReload(true);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void splitBillConsumption(ActionRequest request, ActionResponse response) {
try {
List<HashMap> selectedBillConsumptionMapList =
(List<HashMap>) request.getContext().get("billOfMaterialConsumptionList");
Map manufOrderMap = (Map<String, Object>) request.getContext().get("manufOrder");
if (selectedBillConsumptionMapList == null) {
response.setFlash(I18n.get("Please select at least one line."));
return;
}
List<BillOfMaterialConsumption> billConsumptionList = new ArrayList<>();
BillOfMaterialConsumptionRepository billConsumptionRepo =
Beans.get(BillOfMaterialConsumptionRepository.class);
for (HashMap map : selectedBillConsumptionMapList) {
BillOfMaterialConsumption billConsumption =
(BillOfMaterialConsumption) Mapper.toBean(BillOfMaterialConsumption.class, map);
billConsumptionList.add(billConsumptionRepo.find(billConsumption.getId()));
}
if (billConsumptionList.isEmpty()) {
response.setFlash(I18n.get("Please select at least one line."));
return;
}
BigDecimal splitQty = new BigDecimal(request.getContext().get("splitQty").toString());
if (splitQty == null || splitQty.compareTo(BigDecimal.ZERO) < 1) {
response.setFlash(I18n.get("Please enter a valid quantity."));
return;
}
ManufOrder manufOrder = Mapper.toBean(ManufOrder.class, manufOrderMap);
manufOrder = Beans.get(ManufOrderRepository.class).find(manufOrder.getId());
Beans.get(BillOfMaterialServiceImpl.class)
.splitBillOfMaterialConsumption(manufOrder, billConsumptionList, splitQty);
response.setCanClose(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void consumeStockMoveTemp(ActionRequest request, ActionResponse response) {
try {
Context context = request.getContext();
List<ManufOrder> manufOrders = new ArrayList<>();
if (context.get("id") != null) {
Long manufOrderId = (Long) request.getContext().get("id");
manufOrders.add(Beans.get(ManufOrderRepository.class).find(manufOrderId));
} else if (context.get("_ids") != null) {
manufOrders =
Beans.get(ManufOrderRepository.class)
.all()
.filter(
"self.id in ?1",
context.get("_ids"))
.fetch();
}
for (ManufOrder manufOrder : manufOrders) {
Beans.get(ManufOrderStockMoveService.class).createTempStockMove(manufOrder);
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,348 @@
/*
* 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.production.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.OperationOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.manuforder.ManufOrderWorkflowService;
import com.axelor.apps.production.service.operationorder.OperationOrderService;
import com.axelor.apps.production.service.operationorder.OperationOrderStockMoveService;
import com.axelor.apps.production.service.operationorder.OperationOrderWorkflowService;
import com.axelor.apps.report.engine.ReportSettings;
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.google.inject.Singleton;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import org.eclipse.birt.core.exception.BirtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class OperationOrderController {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void computeDuration(ActionRequest request, ActionResponse response) {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderWorkflowService.class).computeDuration(operationOrder);
response.setReload(true);
}
public void setPlannedDates(ActionRequest request, ActionResponse response) {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
LocalDateTime plannedStartDateT = operationOrder.getPlannedStartDateT();
LocalDateTime plannedEndDateT = operationOrder.getPlannedEndDateT();
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderWorkflowService.class)
.setPlannedDates(operationOrder, plannedStartDateT, plannedEndDateT);
}
public void setRealDates(ActionRequest request, ActionResponse response) {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
LocalDateTime realStartDateT = operationOrder.getRealStartDateT();
LocalDateTime realEndDateT = operationOrder.getRealEndDateT();
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderWorkflowService.class)
.setRealDates(operationOrder, realStartDateT, realEndDateT);
}
public void machineChange(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
OperationOrderRepository operationOrderRepo = Beans.get(OperationOrderRepository.class);
OperationOrderWorkflowService operationOrderWorkflowService =
Beans.get(OperationOrderWorkflowService.class);
operationOrder = operationOrderRepo.find(operationOrder.getId());
if (operationOrder != null
&& operationOrder.getStatusSelect() == OperationOrderRepository.STATUS_PLANNED) {
operationOrder = operationOrderWorkflowService.replan(operationOrder);
List<OperationOrder> operationOrderList =
operationOrderRepo
.all()
.filter(
"self.manufOrder = ?1 AND self.priority >= ?2 AND self.statusSelect = 3 AND self.id != ?3",
operationOrder.getManufOrder(),
operationOrder.getPriority(),
operationOrder.getId())
.order("priority")
.order("plannedEndDateT")
.fetch();
for (OperationOrder operationOrderIt : operationOrderList) {
operationOrderWorkflowService.replan(operationOrderIt);
}
response.setReload(true);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void plan(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
if (operationOrder.getManufOrder() != null
&& operationOrder.getManufOrder().getStatusSelect()
< ManufOrderRepository.STATUS_PLANNED) {
return;
}
Beans.get(OperationOrderWorkflowService.class)
.plan(Beans.get(OperationOrderRepository.class).find(operationOrder.getId()));
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void start(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
System.out.println("*******************************************");
System.out.println(operationOrder.getManufOrder().getTrackingNumber());
System.out.println(operationOrder.getOperationName());
System.out.println(operationOrder.getManufOrder().getTrackingNumber() == null);
System.out.println(operationOrder.getOperationName() == "MELANGE");
if (operationOrder.getManufOrder().getTrackingNumber() == null) {
System.out.println("***is null****99999");
if (operationOrder.getOperationName().equals("MELANGE")) {
System.out.println("*******************************************99999");
Beans.get(ManufOrderWorkflowService.class).createAndAssignTrackingNumber(operationOrder);
} else if (operationOrder.getOperationName().equals("GRANULATION")) {
Beans.get(ManufOrderWorkflowService.class).createAndAssignTrackingNumber(operationOrder);
}
}
Beans.get(OperationOrderWorkflowService.class).start(operationOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void pause(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderWorkflowService.class).pause(operationOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void resume(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(ManufOrderWorkflowService.class).resume(operationOrder.getManufOrder());
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void finish(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
// this attribute is not in the database, only in the view
LocalDateTime realStartDateT = operationOrder.getRealStartDateT();
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
operationOrder.setRealStartDateT(realStartDateT);
Beans.get(OperationOrderWorkflowService.class).finishAndAllOpFinished(operationOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void partialFinish(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderStockMoveService.class).partialFinish(operationOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void cancel(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
Beans.get(OperationOrderWorkflowService.class)
.cancel(Beans.get(OperationOrderRepository.class).find(operationOrder.getId()));
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Method that generate a Pdf file for an operation order
*
* @param request
* @param response
* @return
* @throws BirtException
* @throws IOException
*/
public void print(ActionRequest request, ActionResponse response) {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
String operationOrderIds = "";
try {
@SuppressWarnings("unchecked")
List<Integer> lstSelectedOperationOrder = (List<Integer>) request.getContext().get("_ids");
if (lstSelectedOperationOrder != null) {
for (Integer it : lstSelectedOperationOrder) {
operationOrderIds += it.toString() + ",";
}
}
if (!operationOrderIds.equals("")) {
operationOrderIds = operationOrderIds.substring(0, operationOrderIds.length() - 1);
operationOrder =
Beans.get(OperationOrderRepository.class)
.find(new Long(lstSelectedOperationOrder.get(0)));
} else if (operationOrder.getId() != null) {
operationOrderIds = operationOrder.getId().toString();
}
if (!operationOrderIds.equals("")) {
String name = " ";
if (operationOrder.getName() != null) {
name += lstSelectedOperationOrder == null ? "Op " + operationOrder.getName() : "Ops";
}
String fileLink =
ReportFactory.createReport(IReport.OPERATION_ORDER, name + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("OperationOrderId", operationOrderIds)
.generate()
.getFileLink();
LOG.debug("Printing " + name);
response.setView(ActionView.define(name).add("html", fileLink).map());
} else {
response.setFlash(I18n.get(IExceptionMessage.OPERATION_ORDER_1));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void chargeByMachineHours(ActionRequest request, ActionResponse response) {
try {
LocalDateTime fromDateTime =
LocalDateTime.parse(
request.getContext().get("fromDateTime").toString(), DateTimeFormatter.ISO_DATE_TIME);
LocalDateTime toDateTime =
LocalDateTime.parse(
request.getContext().get("toDateTime").toString(), DateTimeFormatter.ISO_DATE_TIME);
List<Map<String, Object>> dataList =
Beans.get(OperationOrderService.class).chargeByMachineHours(fromDateTime, toDateTime);
response.setData(dataList);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void chargeByMachineDays(ActionRequest request, ActionResponse response) {
try {
LocalDateTime fromDateTime =
LocalDateTime.parse(
request.getContext().get("fromDateTime").toString(), DateTimeFormatter.ISO_DATE_TIME);
LocalDateTime toDateTime =
LocalDateTime.parse(
request.getContext().get("toDateTime").toString(), DateTimeFormatter.ISO_DATE_TIME);
List<Map<String, Object>> dataList =
Beans.get(OperationOrderService.class).chargeByMachineDays(fromDateTime, toDateTime);
response.setData(dataList);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from operation order form, on consumed stock move line change. Call {@link
* OperationOrderService#checkConsumedStockMoveLineList(OperationOrder, OperationOrder)}
*
* @param request
* @param response
*/
public void checkConsumedStockMoveLineList(ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
OperationOrder oldOperationOrder =
Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderService.class)
.checkConsumedStockMoveLineList(operationOrder, oldOperationOrder);
} catch (Exception e) {
TraceBackService.trace(response, e);
response.setReload(true);
}
}
/**
* Called from operation order form, on consumed stock move line change.
*
* @param request
* @param response
*/
public void updateConsumedStockMoveFromOperationOrder(
ActionRequest request, ActionResponse response) {
try {
OperationOrder operationOrder = request.getContext().asType(OperationOrder.class);
operationOrder = Beans.get(OperationOrderRepository.class).find(operationOrder.getId());
Beans.get(OperationOrderService.class)
.updateConsumedStockMoveFromOperationOrder(operationOrder);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,146 @@
/*
* 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.production.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ProdProcess;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.ProdProcessRepository;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.ProdProcessService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.ResponseMessageType;
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.google.common.collect.Lists;
import com.google.inject.Singleton;
import java.util.List;
@Singleton
public class ProdProcessController {
public void validateProdProcess(ActionRequest request, ActionResponse response) {
ProdProcess prodProcess = request.getContext().asType(ProdProcess.class);
if (prodProcess.getIsConsProOnOperation()) {
BillOfMaterial bom = null;
if (request.getContext().getParent() != null
&& request
.getContext()
.getParent()
.getContextClass()
.getName()
.equals(BillOfMaterial.class.getName())) {
bom = request.getContext().getParent().asType(BillOfMaterial.class);
} else {
bom =
Beans.get(BillOfMaterialRepository.class)
.all()
.filter("self.prodProcess.id = ?1", prodProcess.getId())
.fetchOne();
}
if (bom != null) {
try {
Beans.get(ProdProcessService.class).validateProdProcess(prodProcess, bom);
} catch (AxelorException e) {
TraceBackService.trace(response, e, ResponseMessageType.ERROR);
}
}
}
}
public void changeProdProcessListOutsourcing(ActionRequest request, ActionResponse response)
throws AxelorException {
ProdProcess prodProcess = request.getContext().asType(ProdProcess.class);
if (prodProcess.getProdProcessLineList() != null) {
Beans.get(ProdProcessService.class).changeProdProcessListOutsourcing(prodProcess);
}
response.setValue("prodProcessLineList", prodProcess.getProdProcessLineList());
}
public void print(ActionRequest request, ActionResponse response) throws AxelorException {
ProdProcess prodProcess = request.getContext().asType(ProdProcess.class);
String prodProcessId = prodProcess.getId().toString();
String prodProcessLabel = prodProcess.getName().toString();
String fileLink =
ReportFactory.createReport(IReport.PROD_PROCESS, prodProcessLabel + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("ProdProcessId", prodProcessId)
.generate()
.getFileLink();
response.setView(ActionView.define(prodProcessLabel).add("html", fileLink).map());
}
public void checkOriginalProductionProcess(ActionRequest request, ActionResponse response) {
ProdProcessRepository prodProcessRepository = Beans.get(ProdProcessRepository.class);
ProdProcess prodProcess =
prodProcessRepository.find(request.getContext().asType(ProdProcess.class).getId());
List<ProdProcess> prodProcessSet = Lists.newArrayList();
prodProcessSet =
prodProcessRepository
.all()
.filter("self.originalProdProcess = :origin")
.bind("origin", prodProcess)
.fetch();
String message;
if (!prodProcessSet.isEmpty()) {
String existingVersions = "";
for (ProdProcess prodProcessVersion : prodProcessSet) {
existingVersions += "<li>" + prodProcessVersion.getFullName() + "</li>";
}
message =
String.format(
I18n.get(
"This production process already has the following versions : <br/><ul> %s </ul>And these versions may also have ones. Do you still wish to create a new one ?"),
existingVersions);
} else {
message = I18n.get("Do you really wish to create a new version of this production process ?");
}
response.setAlert(message);
}
public void generateNewVersion(ActionRequest request, ActionResponse response) {
ProdProcess prodProcess =
Beans.get(ProdProcessRepository.class)
.find(request.getContext().asType(ProdProcess.class).getId());
ProdProcess copy = Beans.get(ProdProcessService.class).generateNewVersion(prodProcess);
response.setView(
ActionView.define("Production process")
.model(ProdProcess.class.getName())
.add("form", "prod-process-form")
.add("grid", "prod-process-grid")
.context("_showRecord", String.valueOf(copy.getId()))
.map());
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.ProdProcessLine;
import com.axelor.apps.production.db.WorkCenter;
import com.axelor.apps.production.service.ProdProcessLineServiceImpl;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class ProdProcessLineController {
public void updateDuration(ActionRequest request, ActionResponse response) {
ProdProcessLine prodProcess = request.getContext().asType(ProdProcessLine.class);
WorkCenter workCenter = prodProcess.getWorkCenter();
if (workCenter != null) {
response.setValue(
"durationPerCycle",
Beans.get(ProdProcessLineServiceImpl.class)
.getProdProcessLineDurationFromWorkCenter(workCenter));
}
}
public void updateCapacitySettings(ActionRequest request, ActionResponse response) {
ProdProcessLine prodProcess = request.getContext().asType(ProdProcessLine.class);
WorkCenter workCenter = prodProcess.getWorkCenter();
if (workCenter != null) {
response.setValue(
"minCapacityPerCycle",
Beans.get(ProdProcessLineServiceImpl.class)
.getProdProcessLineMinCapacityPerCycleFromWorkCenter(workCenter));
response.setValue(
"maxCapacityPerCycle",
Beans.get(ProdProcessLineServiceImpl.class)
.getProdProcessLineMaxCapacityPerCycleFromWorkCenter(workCenter));
}
}
}

View File

@ -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.production.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.production.db.ProductionBatch;
import com.axelor.apps.production.db.repo.ProductionBatchRepository;
import com.axelor.apps.production.report.IReport;
import com.axelor.apps.production.service.batch.ProductionBatchService;
import com.axelor.apps.production.translation.ITranslation;
import com.axelor.apps.report.engine.ReportSettings;
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.lang.invoke.MethodHandles;
import java.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ProductionBatchController {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void computeValuation(ActionRequest request, ActionResponse response) {
ProductionBatch productionBatch = request.getContext().asType(ProductionBatch.class);
productionBatch = Beans.get(ProductionBatchRepository.class).find(productionBatch.getId());
Batch batch = Beans.get(ProductionBatchService.class).computeValuation(productionBatch);
if (batch != null) {
response.setFlash(batch.getComments());
}
response.setReload(true);
}
public void showValuation(ActionRequest request, ActionResponse response) throws AxelorException {
ProductionBatch productionBatch = request.getContext().asType(ProductionBatch.class);
productionBatch = Beans.get(ProductionBatchRepository.class).find(productionBatch.getId());
String name = I18n.get(ITranslation.WORK_IN_PROGRESS_VALUATION);
String fileLink =
ReportFactory.createReport(IReport.WORK_IN_PROGRESS_VALUATION, name + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam(
"companyId",
productionBatch.getCompany() != null ? productionBatch.getCompany().getId() : 0)
.addParam(
"locationId",
productionBatch.getWorkshopStockLocation() != null
? productionBatch.getWorkshopStockLocation().getId()
: 0)
.addParam(
"editionDate",
DateTimeFormatter.ofPattern("MMM d, yyyy, hh:mm a")
.format(productionBatch.getUpdatedOn()))
.generate()
.getFileLink();
LOG.debug("Printing {}", name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
}

View File

@ -0,0 +1,152 @@
/*
* 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.production.web;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.db.repo.ManufOrderRepository;
import com.axelor.apps.production.db.repo.ProductionOrderRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.production.service.productionorder.ProductionOrderService;
import com.axelor.apps.production.service.productionorder.ProductionOrderServiceImpl;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.db.mapper.Mapper;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
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.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Singleton
public class ProductionOrderController {
@SuppressWarnings("unchecked")
public void addManufOrder(ActionRequest request, ActionResponse response) throws AxelorException {
Context context = request.getContext();
if (context.get("qty") == null
|| new BigDecimal(context.get("qty").toString()).compareTo(BigDecimal.ZERO) <= 0) {
response.setFlash(I18n.get(IExceptionMessage.PRODUCTION_ORDER_3) + "!");
} else if (context.get("billOfMaterial") == null) {
response.setFlash(I18n.get(IExceptionMessage.PRODUCTION_ORDER_4) + "!");
} else {
Map<String, Object> bomContext = (Map<String, Object>) context.get("billOfMaterial");
BillOfMaterial billOfMaterial =
Beans.get(BillOfMaterialRepository.class)
.find(((Integer) bomContext.get("id")).longValue());
BigDecimal qty = new BigDecimal(context.get("qty").toString());
Product product = null;
if (context.get("product") != null) {
Map<String, Object> productContext = (Map<String, Object>) context.get("product");
product =
Beans.get(ProductRepository.class)
.find(((Integer) productContext.get("id")).longValue());
} else {
product = billOfMaterial.getProduct();
}
ZonedDateTime startDateT;
if (context.containsKey("_startDate") && context.get("_startDate") != null) {
startDateT =
ZonedDateTime.parse(
(CharSequence) context.get("_startDate"),
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault()));
} else {
startDateT = Beans.get(AppBaseService.class).getTodayDateTime();
}
ProductionOrder productionOrder =
Beans.get(ProductionOrderRepository.class)
.find(Long.parseLong(request.getContext().get("_id").toString()));
if (billOfMaterial.getProdProcess() != null) {
Beans.get(ProductionOrderService.class)
.addManufOrder(
productionOrder,
product,
billOfMaterial,
qty,
startDateT.toLocalDateTime(),
null,
productionOrder.getSaleOrder(),
ManufOrderService.ORIGIN_TYPE_OTHER);
} else {
response.setError(I18n.get(IExceptionMessage.MANUF_ORDER_NO_GENERATION));
}
response.setCanClose(true);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void generateConsumeStockMoveFromSelectedManufOrder(
ActionRequest request, ActionResponse response) {
try {
List<HashMap> selectedManufOrderMapList =
(List<HashMap>) request.getContext().get("manufOrderList");
Map productionOrderMap = (Map<String, Object>) request.getContext().get("productionOrder");
if (selectedManufOrderMapList == null) {
response.setFlash(I18n.get(IExceptionMessage.UNIT_COST_CALCULATION_IMPORT_FAIL_ERROR));
return;
}
List<ManufOrder> manufOrderList = new ArrayList<>();
ManufOrderRepository manufOrderRepository = Beans.get(ManufOrderRepository.class);
for (HashMap map : selectedManufOrderMapList) {
ManufOrder manufOrder = (ManufOrder) Mapper.toBean(ManufOrder.class, map);
manufOrderList.add(manufOrderRepository.find(manufOrder.getId()));
}
if (manufOrderList.isEmpty()) {
response.setFlash(I18n.get(IExceptionMessage.UNIT_COST_CALCULATION_IMPORT_FAIL_ERROR));
return;
}
ProductionOrder productionOrder = Mapper.toBean(ProductionOrder.class, productionOrderMap);
productionOrder = Beans.get(ProductionOrderRepository.class).find(productionOrder.getId());
StockMove stockMove =
Beans.get(ProductionOrderServiceImpl.class)
.generateConsumeStockMoveFromSelectedManufOrder(productionOrder, manufOrderList);
response.setCanClose(true);
response.setFlash("Generated successfully : " + stockMove.getStockMoveSeq());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.productionorder.ProductionOrderSaleOrderService;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
import com.axelor.exception.AxelorException;
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.google.common.base.Joiner;
import com.google.inject.Singleton;
import java.util.List;
@Singleton
public class ProductionOrderSaleOrderController {
public void createProductionOrders(ActionRequest request, ActionResponse response)
throws AxelorException {
try {
SaleOrder saleOrder = request.getContext().asType(SaleOrder.class);
saleOrder = Beans.get(SaleOrderRepository.class).find(saleOrder.getId());
List<Long> productionOrderIdList =
Beans.get(ProductionOrderSaleOrderService.class).generateProductionOrder(saleOrder);
if (productionOrderIdList != null && productionOrderIdList.size() == 1) {
response.setView(
ActionView.define(I18n.get("Production order"))
.model(ProductionOrder.class.getName())
.add("form", "production-order-form")
.add("grid", "production-order-grid")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(productionOrderIdList.get(0)))
.map());
} else if (productionOrderIdList != null && productionOrderIdList.size() > 1) {
response.setView(
ActionView.define(I18n.get("Production order"))
.model(ProductionOrder.class.getName())
.add("grid", "production-order-grid")
.add("form", "production-order-form")
.domain("self.id in (" + Joiner.on(",").join(productionOrderIdList) + ")")
.map());
} else {
response.setFlash(I18n.get(IExceptionMessage.PRODUCTION_ORDER_NO_GENERATION));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.ProductionOrder;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.productionorder.ProductionOrderWizardService;
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.axelor.rpc.Context;
import com.google.inject.Singleton;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
@Singleton
public class ProductionOrderWizardController {
public void validate(ActionRequest request, ActionResponse response) throws AxelorException {
Context context = request.getContext();
ZonedDateTime startDateT = null, endDateT = null;
if (context.get("_startDate") != null) {
startDateT =
ZonedDateTime.parse(
context.get("_startDate").toString(),
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault()));
if (ChronoUnit.MINUTES.between(
Beans.get(AppProductionService.class).getTodayDateTime(), startDateT)
< 0) {
response.setError(I18n.get(IExceptionMessage.PRODUCTION_ORDER_5));
}
}
if (context.get("_endDate") != null) {
endDateT =
ZonedDateTime.parse(
context.get("_endDate").toString(),
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault()));
if ((startDateT != null && ChronoUnit.MINUTES.between(startDateT, endDateT) < 0)
|| ChronoUnit.MINUTES.between(
Beans.get(AppProductionService.class).getTodayDateTime(), endDateT)
< 0) {
response.setError(I18n.get(IExceptionMessage.PRODUCTION_ORDER_5));
}
}
if (context.get("qty") == null
|| new BigDecimal((String) context.get("qty")).compareTo(BigDecimal.ZERO) <= 0) {
response.setFlash(I18n.get(IExceptionMessage.PRODUCTION_ORDER_3) + " !");
} else if (context.get("billOfMaterial") == null) {
response.setFlash(I18n.get(IExceptionMessage.PRODUCTION_ORDER_4) + " !");
} else {
response.setView(
ActionView.define(I18n.get("Production order generated"))
.model(ProductionOrder.class.getName())
.add("form", "production-order-form")
.add("grid", "production-order-grid")
.param("forceEdit", "true")
.context(
"_showRecord",
Beans.get(ProductionOrderWizardService.class).validate(context).toString())
.map());
response.setCanClose(true);
}
}
}

View File

@ -0,0 +1,119 @@
/*
* 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.production.web;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.production.service.manuforder.ManufOrderService;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.supplychain.service.ProjectedStockService;
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 java.util.Map;
public class ProductionProjectedStockController {
public static final String VIEW_BUILDING_QTY_TITLE = /*$$(*/ "%s building" /*)*/;
public static final String VIEW_MISSING_QTY_TITLE = /*$$(*/ "%s missing" /*)*/;
public static final String VIEW_CONSUME_QTY_TITLE = /*$$(*/ "%s consume" /*)*/;
public void showBuildingQuantityOfProduct(ActionRequest request, ActionResponse response) {
Map<String, Long> mapId =
Beans.get(ProjectedStockService.class)
.getProductIdCompanyIdStockLocationIdFromContext(request.getContext());
if (mapId == null || mapId.get("productId") == 0L) {
return;
}
Long productId = mapId.get("productId");
Long companyId = mapId.get("companyId");
Long stockLocationId = mapId.get("stockLocationId");
String domain =
Beans.get(ManufOrderService.class)
.getBuildingQtyForAProduct(productId, companyId, stockLocationId);
Product product = Beans.get(ProductRepository.class).find(mapId.get("productId"));
String title = I18n.get(VIEW_BUILDING_QTY_TITLE);
title = String.format(title, product.getName());
response.setView(
ActionView.define(title)
.model(StockMoveLine.class.getName())
.add("grid", "stock-move-line-produced-manuf-order-grid")
.add("form", "stock-move-line-form")
.domain(domain)
.param("popup", "true")
.param("popup-save", "false")
.map());
}
public void showConsumeQuantityOfProduct(ActionRequest request, ActionResponse response) {
Map<String, Long> mapId =
Beans.get(ProjectedStockService.class)
.getProductIdCompanyIdStockLocationIdFromContext(request.getContext());
if (mapId == null || mapId.get("productId") == 0L) {
return;
}
Long productId = mapId.get("productId");
Long companyId = mapId.get("companyId");
Long stockLocationId = mapId.get("stockLocationId");
String domain =
Beans.get(ManufOrderService.class)
.getConsumeAndMissingQtyForAProduct(productId, companyId, stockLocationId);
Product product = Beans.get(ProductRepository.class).find(mapId.get("productId"));
String title = I18n.get(VIEW_CONSUME_QTY_TITLE);
title = String.format(title, product.getName());
response.setView(
ActionView.define(title)
.model(StockMoveLine.class.getName())
.add("grid", "stock-move-line-consumed-manuf-order-grid")
.add("form", "stock-move-line-form")
.domain(domain)
.param("popup", "true")
.param("popup-save", "false")
.map());
}
public void showMissingQuantityOfProduct(ActionRequest request, ActionResponse response) {
Map<String, Long> mapId =
Beans.get(ProjectedStockService.class)
.getProductIdCompanyIdStockLocationIdFromContext(request.getContext());
if (mapId == null || mapId.get("productId") == 0L) {
return;
}
Long productId = mapId.get("productId");
Long companyId = mapId.get("companyId");
Long stockLocationId = mapId.get("stockLocationId");
String domain =
Beans.get(ManufOrderService.class)
.getConsumeAndMissingQtyForAProduct(productId, companyId, stockLocationId);
Product product = Beans.get(ProductRepository.class).find(mapId.get("productId"));
String title = I18n.get(VIEW_MISSING_QTY_TITLE);
title = String.format(title, product.getName());
response.setView(
ActionView.define(title)
.model(StockMoveLine.class.getName())
.add("grid", "stock-move-line-consumed-manuf-order-grid")
.add("form", "stock-move-line-form")
.domain(domain)
.param("popup", "true")
.param("popup-save", "false")
.map());
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.RawMaterialRequirement;
import com.axelor.apps.production.db.repo.RawMaterialRequirementRepository;
import com.axelor.apps.production.service.RawMaterialRequirementService;
import com.axelor.apps.production.service.RawMaterialRequirementServiceImpl;
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;
public class RawMaterialRequirementController {
/**
* Called from the raw material requirement view, on clicking "print" button. Call {@link
* RawMaterialRequirementService#print(RawMaterialRequirement)} and then show the printed report.
*
* @param request
* @param response
*/
public void print(ActionRequest request, ActionResponse response) {
try {
RawMaterialRequirement rawMaterialRequirement =
request.getContext().asType(RawMaterialRequirement.class);
rawMaterialRequirement =
Beans.get(RawMaterialRequirementRepository.class).find(rawMaterialRequirement.getId());
String fileLink =
Beans.get(RawMaterialRequirementService.class).print(rawMaterialRequirement);
response.setView(
ActionView.define(I18n.get(RawMaterialRequirementServiceImpl.RAW_MATERIAL_REPORT_TITLE))
.add("html", fileLink)
.map());
} catch (Exception e) {
TraceBackService.trace(e);
}
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.BillOfMaterial;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.BillOfMaterialService;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.exception.service.TraceBackService;
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 SaleOrderLineController {
public void customizeBillOfMaterial(ActionRequest request, ActionResponse response) {
try {
SaleOrderLine saleOrderLine = request.getContext().asType(SaleOrderLine.class);
BillOfMaterial copyBillOfMaterial =
Beans.get(BillOfMaterialService.class).customizeBillOfMaterial(saleOrderLine);
if (copyBillOfMaterial != null) {
response.setValue("billOfMaterial", copyBillOfMaterial);
response.setFlash(I18n.get(IExceptionMessage.SALE_ORDER_LINE_1));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.production.web;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.production.db.ManufOrder;
import com.axelor.apps.production.db.OperationOrder;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.manuforder.ManufOrderStockMoveService;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.service.StockMoveLineService;
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.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import java.util.Optional;
public class StockMoveLineController {
public void compute(ActionRequest request, ActionResponse response) throws AxelorException {
StockMoveLine stockMoveLine = request.getContext().asType(StockMoveLine.class);
Optional<StockMove> stockMove = Optional.ofNullable(stockMoveLine.getStockMove());
if (!stockMove.isPresent()) {
Context parentContext = request.getContext().getParent();
if (parentContext.getContextClass().equals(StockMove.class)) {
stockMove = Optional.ofNullable(parentContext.asType(StockMove.class));
} else if (parentContext.getContextClass().equals(ManufOrder.class)) {
ManufOrder manufOrder = parentContext.asType(ManufOrder.class);
ManufOrderStockMoveService manufOrderStockMoveService =
Beans.get(ManufOrderStockMoveService.class);
stockMove = manufOrderStockMoveService.getPlannedStockMove(manufOrder.getInStockMoveList());
}
if (!stockMove.isPresent()) {
return;
}
}
stockMoveLine = Beans.get(StockMoveLineService.class).compute(stockMoveLine, stockMove.get());
response.setValue("unitPriceUntaxed", stockMoveLine.getUnitPriceUntaxed());
response.setValue("unitPriceTaxed", stockMoveLine.getUnitPriceTaxed());
}
/**
* Called from stock move line form. Fill product info using the company either from the stock
* move line, from the parent stock move or the parent manuf order.
*
* @param request
* @param response
*/
public void setProductInfo(ActionRequest request, ActionResponse response) {
StockMoveLine stockMoveLine;
try {
stockMoveLine = request.getContext().asType(StockMoveLine.class);
Company company;
StockMove stockMove = stockMoveLine.getStockMove();
if (stockMove == null) {
Context parentContext = request.getContext().getParent();
if (parentContext.getContextClass().equals(StockMove.class)) {
stockMove = parentContext.asType(StockMove.class);
company = stockMove.getCompany();
} else if (parentContext.getContextClass().equals(ManufOrder.class)) {
ManufOrder manufOrder = parentContext.asType(ManufOrder.class);
company = manufOrder.getCompany();
} else if (parentContext.getContextClass().equals(OperationOrder.class)) {
OperationOrder operationOrder = parentContext.asType(OperationOrder.class);
if (operationOrder.getManufOrder() == null) {
return;
}
company = operationOrder.getManufOrder().getCompany();
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
IExceptionMessage.STOCK_MOVE_LINE_UNKNOWN_PARENT_CONTEXT);
}
} else {
company = stockMove.getCompany();
}
if (stockMoveLine.getProduct() == null) {
stockMoveLine = new StockMoveLine();
response.setValues(Mapper.toMap(stockMoveLine));
return;
}
Beans.get(StockMoveLineService.class).setProductInfo(stockMove, stockMoveLine, company);
response.setValues(stockMoveLine);
} catch (Exception e) {
stockMoveLine = new StockMoveLine();
response.setValues(Mapper.toMap(stockMoveLine));
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,135 @@
/*
* 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.production.web;
import com.axelor.apps.production.db.UnitCostCalculation;
import com.axelor.apps.production.db.repo.UnitCostCalculationRepository;
import com.axelor.apps.production.exceptions.IExceptionMessage;
import com.axelor.apps.production.service.app.AppProductionService;
import com.axelor.apps.production.service.costsheet.UnitCostCalculationService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.repo.MetaFileRepository;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.common.io.Files;
import com.google.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
@Singleton
public class UnitCostCalculationController {
public void runUnitCostCalc(ActionRequest request, ActionResponse response)
throws AxelorException {
try {
UnitCostCalculation unitCostCalculation =
request.getContext().asType(UnitCostCalculation.class);
unitCostCalculation =
Beans.get(UnitCostCalculationRepository.class).find(unitCostCalculation.getId());
Beans.get(UnitCostCalculationService.class).runUnitCostCalc(unitCostCalculation);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void updateUnitCosts(ActionRequest request, ActionResponse response)
throws AxelorException {
try {
UnitCostCalculation unitCostCalculation =
request.getContext().asType(UnitCostCalculation.class);
unitCostCalculation =
Beans.get(UnitCostCalculationRepository.class).find(unitCostCalculation.getId());
Beans.get(UnitCostCalculationService.class).updateUnitCosts(unitCostCalculation);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void exportUnitCostCalc(ActionRequest request, ActionResponse response)
throws IOException {
try {
UnitCostCalculation unitCostCalculation =
request.getContext().asType(UnitCostCalculation.class);
unitCostCalculation =
Beans.get(UnitCostCalculationRepository.class).find(unitCostCalculation.getId());
String fileName =
unitCostCalculation.getUnitCostCalcSeq()
+ "-"
+ Beans.get(AppProductionService.class)
.getTodayDateTime()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"))
+ ".csv";
MetaFile metaFile =
Beans.get(UnitCostCalculationService.class)
.exportUnitCostCalc(unitCostCalculation, fileName);
response.setView(
ActionView.define(fileName)
.add(
"html",
"ws/rest/com.axelor.meta.db.MetaFile/"
+ metaFile.getId()
+ "/content/download?v="
+ metaFile.getVersion())
.map());
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
@SuppressWarnings("unchecked")
public void importUnitCostCalc(ActionRequest request, ActionResponse response)
throws IOException {
try {
LinkedHashMap<String, Object> map =
(LinkedHashMap<String, Object>) request.getContext().get("metaFile");
MetaFile dataFile =
Beans.get(MetaFileRepository.class).find(((Integer) map.get("id")).longValue());
File csvFile = MetaFiles.getPath(dataFile).toFile();
Long unitCostCalculationId = Long.valueOf(request.getContext().get("_id").toString());
UnitCostCalculation unitCostCalculation =
Beans.get(UnitCostCalculationRepository.class).find(unitCostCalculationId);
if (Files.getFileExtension(csvFile.getName()).equals("csv")) {
Beans.get(UnitCostCalculationService.class)
.importUnitCostCalc(dataFile, unitCostCalculation);
response.setCanClose(true);
} else {
response.setError(IExceptionMessage.UNIT_COST_CALCULATION_IMPORT_CSV_ERROR);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.production.db.BillOfMaterial;
import com.axelor.apps.production.db.repo.BillOfMaterialRepository;
import com.axelor.apps.production.service.BillOfMaterialService;
import com.axelor.apps.production.service.costsheet.CostSheetService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.util.Map;
public class ImportBillOfMaterial {
@Inject BillOfMaterialService billOfMaterialService;
@Inject CostSheetService costSheetService;
@Inject BillOfMaterialRepository bomRepo;
@Transactional(rollbackOn = {Exception.class})
public Object computeCostPrice(Object bean, Map values) throws AxelorException {
if (bean == null) {
return bean;
}
assert bean instanceof BillOfMaterial;
BillOfMaterial bom = (BillOfMaterial) bean;
bom = bomRepo.save(bom);
costSheetService.computeCostPrice(bom, CostSheetService.ORIGIN_BILL_OF_MATERIAL, null);
billOfMaterialService.updateProductCostPrice(bom);
return bom;
}
}

View File

@ -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.csv.script;
import com.axelor.apps.sale.service.configurator.ConfiguratorCreatorImportService;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.util.Map;
public class ImportConfiguratorCreator {
@Inject ConfiguratorCreatorImportService configuratorCreatorImportService;
@Transactional
public Object importConfiguratorCreator(Object bean, Map values)
throws AxelorException, IOException {
configuratorCreatorImportService.importConfiguratorCreators(
"./production_configuratorCreator.xml");
return null;
}
}

View File

@ -0,0 +1,429 @@
<?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
}
def retrieveConfiguratorProdProcessLine = { cppl ->
configProdProcessLine = ""
configProdProcessLine += """
<configuratorProdProcessLine>
<name>"""
if (cppl.name != null) {
configProdProcessLine += "${cppl.name}"
}
configProdProcessLine += """</name>
<priority>${cppl.priority}</priority>
<workCenter>${cppl.workCenter?.id}</workCenter>
<outsourcing>${cppl.outsourcing}</outsourcing>
<stockLocation>"""
if (cppl.stockLocation?.id != null) {
configProdProcessLine += "${cppl.stockLocation?.id}"
}
configProdProcessLine += """</stockLocation>
<description>"""
if (cppl.description != null) {
configProdProcessLine += "${cppl.description}"
}
configProdProcessLine += """</description>
<minCapacityPerCycle>${cppl.minCapacityPerCycle}</minCapacityPerCycle>
<minCapacityPerCycleFormula>"""
if (cppl.minCapacityPerCycleFormula != null) {
configProdProcessLine += "${cppl.minCapacityPerCycleFormula}"
}
configProdProcessLine += """</minCapacityPerCycleFormula>
<defMinCapacityFormula>${cppl.defMinCapacityFormula}</defMinCapacityFormula>
<maxCapacityPerCycle>${cppl.maxCapacityPerCycle}</maxCapacityPerCycle>
<maxCapacityPerCycleFormula>"""
if (cppl.maxCapacityPerCycleFormula != null) {
configProdProcessLine += "${cppl.maxCapacityPerCycleFormula}"
}
configProdProcessLine += """</maxCapacityPerCycleFormula>
<defMaxCapacityFormula>${cppl.defMaxCapacityFormula}</defMaxCapacityFormula>
<durationPerCycle>${cppl.durationPerCycle}</durationPerCycle>
<durationPerCycleFormula>"""
if (cppl.durationPerCycleFormula != null) {
configProdProcessLine += "${cppl.durationPerCycleFormula}"
}
configProdProcessLine += """</durationPerCycleFormula>
<defDurationFormula>${cppl.defDurationFormula}</defDurationFormula>
</configuratorProdProcessLine>
"""
return configProdProcessLine
}
def configuratorProdProcessLineMethod = {
configuratorProdProcessLine = ""
cc(it).configuratorBom?.configuratorProdProcess?.configuratorProdProcessLineList?.each( { cppl ->
configuratorProdProcessLine = retrieveConfiguratorProdProcessLine(cppl)
})
return configuratorProdProcessLine
}
def retrieveConfiguratorBomWithChildren
def retrieveConfiguratorBomWithChildren2 = { index, childCb ->
return retrieveConfiguratorBomWithChildren( index, childCb )
}
configBom = ""
retrieveConfiguratorBomWithChildren = { index, cb ->
if (index < 20) {
configBom += """
<configurator-bom>
"""
if (cb.company?.id != null) {
configBom += "<companyCode>${cb.company?.code}</companyCode>"
}
configBom += """
<statusSelect>"""
if (cb.statusSelect != null) {
configBom += "${cb.statusSelect}"
}
configBom += "</statusSelect>"
configBom += """
<name>"""
if (cb.name != null) {
configBom += "${cb.name}"
}
configBom += """</name>
<importId>${cb.id}</importId>
<nameFormula>"""
if (cb.nameFormula != null) {
configBom += "${cb.nameFormula}"
}
configBom += """</nameFormula>
<defNameAsFormula>${cb.defNameAsFormula}</defNameAsFormula>"""
if (cb.product?.id != null) {
configBom += "<productCode>${cb.product?.code}</productCode>"
}
configBom += """
<productFormula>"""
if (cb.productFormula != null){
configBom += "${cb.productFormula}"
}
configBom += """</productFormula>
<defProductAsFormula>${cb.defProductAsFormula}</defProductAsFormula>
<defProductFromConfigurator>${cb.defProductFromConfigurator}</defProductFromConfigurator>
<qty>${cb.qty}</qty>
<qtyFormula>"""
if (cb.qtyFormula != null) {
configBom += "${cb.qtyFormula}"
}
configBom += """</qtyFormula>
<defQtyAsFormula>${cb.defQtyAsFormula}</defQtyAsFormula>"""
if (cb.unit?.id != null) {
configBom += """
<unitId>${cb.unit?.id}</unitId>"""
}
configBom += """
<unitFormula>"""
if (cb.unitFormula != null) {
configBom += "${cb.unitFormula}"
}
configBom += """</unitFormula>
<defUnitAsFormula>${cb.defUnitAsFormula}</defUnitAsFormula>"""
if (cb.prodProcess?.id != null) {
configBom += "<prodProcessCode>${cb.prodProcess?.code}</prodProcessCode>"
}
if (cb.configuratorProdProcess?.id != null) {
configBom += """
<configuratorProdProcess>
<importId>${cb.configuratorProdProcess?.id}</importId>
<statusSelect>${cb.configuratorProdProcess?.statusSelect}</statusSelect>
<name>${cb.configuratorProdProcess?.name}</name>
<code>${cb.configuratorProdProcess?.code}</code>
<codeFormula>${cb.configuratorProdProcess?.codeFormula}</codeFormula>
<defCodeAsFormula>${cb.configuratorProdProcess?.defCodeAsFormula}</defCodeAsFormula>
<companyCode>${cb.configuratorProdProcess?.company?.code}</companyCode>
<stockLocation>${cb.configuratorProdProcess?.stockLocation?.id}</stockLocation>
<stockLocationFormula>${cb.configuratorProdProcess?.stockLocationFormula}</stockLocationFormula>
<defStockLocationAsFormula>${cb.configuratorProdProcess?.defStockLocationAsFormula}</defStockLocationAsFormula>
<producedProductStockLocation>${cb.configuratorProdProcess?.producedProductStockLocation?.id}</producedProductStockLocation>
<producedProductStockLocationFormula>${cb.configuratorProdProcess?.producedProductStockLocationFormula}</producedProductStockLocationFormula>
<defProducedProductStockLocationAsFormula>${cb.configuratorProdProcess?.defProducedProductStockLocationAsFormula}</defProducedProductStockLocationAsFormula>
<workshopStockLocation>${cb.configuratorProdProcess?.workshopStockLocation?.id}</workshopStockLocation>
<workshopStockLocationFormula>${cb.configuratorProdProcess?.workshopStockLocationFormula}</workshopStockLocationFormula>
<defWorkshopStockLocationAsFormula>${cb.configuratorProdProcess?.defWorkshopStockLocationAsFormula}</defWorkshopStockLocationAsFormula>
<configuratorProdProcessLineList>"""
cb.configuratorProdProcess?.configuratorProdProcessLineList?.each( { cppl ->
configBom += retrieveConfiguratorProdProcessLine(cppl)
})
configBom += """ </configuratorProdProcessLineList>
</configuratorProdProcess>"""
}
configBom += """
<prodProcessFormula>"""
if (cb.prodProcessFormula != null) {
configBom += "${cb.prodProcessFormula}"
}
configBom += """</prodProcessFormula>
<defProdProcessAsFormula>${cb.defProdProcessAsFormula}</defProdProcessAsFormula>
<defProdProcessAsConfigurator>${cb.defProdProcessAsConfigurator}</defProdProcessAsConfigurator>
"""
if (cb.parentConfiguratorBOM?.id != null) {
configBom += "<parentConfiguratorBOMId>${cb.parentConfiguratorBOM?.id}</parentConfiguratorBOMId>"
}
configBom += """<useCondition>"""
if (cb.useCondition != null) {
configBom += "${cb.useCondition}"
}
configBom += """</useCondition>
<billOfMaterialId>${cb.billOfMaterialId}</billOfMaterialId>
</configurator-bom>"""
cb.configuratorBomList?.each( { childCb ->
configBom = retrieveConfiguratorBomWithChildren2( index+1, childCb )
})
} else {
configBom += "Some others ConfiguratorBOM"
}
return configBom
}
def configBomMethod = {
configBom = ""
if (cc(it).configuratorBom != null) {
configBom = retrieveConfiguratorBomWithChildren(0, cc(it).configuratorBom)
}
return configBom
}
%>
<configurator-export>
<configurator-boms><% __ids__.each( { it ->
out << configBomMethod(it) })%>
</configurator-boms>
<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>
<% if (cc(it).configuratorBom != null) { %>
<configuratorBomImportId><% out << cc(it).configuratorBom?.id %></configuratorBomImportId>
<% } %>
</configurator-creator><% })%>
</configurator-creators>
</configurator-export>

View File

@ -0,0 +1,4 @@
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
10;"productionOrder";"Production Order N°";1;4;"PDO";;1;0
11;"manufOrder";"Manufacturing Order N°";1;4;"MO";;1;0
12;"rawMaterialRequirement";"Raw material requirement";1;4;"RMR";;1;0
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 10 productionOrder Production Order N° 1 4 PDO 1 0
3 11 manufOrder Manufacturing Order N° 1 4 MO 1 0
4 12 rawMaterialRequirement Raw material requirement 1 4 RMR 1 0

View File

@ -0,0 +1,5 @@
"importId";"name";"typeSelect";"label";"code";"elementSelect";"archived";"sequence";"statusSelect"
3;"MPR - Manufacturing proposal";1;"Manufacturing proposal";"MPR";8;;"2";
5;"MOR - Manufacturing order";1;"Manufacturing order";"MOR";6;;"4";6
6;"NMP - Need / Manufacturing proposal";2;"Need / Manufacturing proposal";"NMP";9;;"5";
9;"NMO - Need / Manufacturing order";2;"Need / Manufacturing order";"NMO";7;;"8";
1 importId name typeSelect label code elementSelect archived sequence statusSelect
2 3 MPR - Manufacturing proposal 1 Manufacturing proposal MPR 8 2
3 5 MOR - Manufacturing order 1 Manufacturing order MOR 6 4 6
4 6 NMP - Need / Manufacturing proposal 2 Need / Manufacturing proposal NMP 9 5
5 9 NMO - Need / Manufacturing order 2 Need / Manufacturing order NMO 7 8

View File

@ -0,0 +1,4 @@
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
10;"productionOrder";"Ordre de production";1;4;"PDO";;1;0
11;"manufOrder";"Ordre de facturation";1;4;"MO";;1;0
12;"rawMaterialRequirement";"Besoin matière";1;4;"RMR";;1;0
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 10 productionOrder Ordre de production 1 4 PDO 1 0
3 11 manufOrder Ordre de facturation 1 4 MO 1 0
4 12 rawMaterialRequirement Besoin matière 1 4 RMR 1 0

View File

@ -0,0 +1,5 @@
"importId";"name";"typeSelect";"label";"code";"elementSelect";"archived";"sequence";"statusSelect"
3;"PRF - Proposition d'ordre de fabrication";1;"Proposition d'ordre de fabrication";"PRF";8;;"2";
5;"ORF - Ordre de fabrication";1;"Ordre de fabrication";"ORF";6;;"4";6
6;"BPF - Besoin / proposition de fabrication";2;"Besoin / proposition de fabrication";"BPF";9;;"5";
9;"BOF - Besoins / ordre de fabrication";2;"Besoins / ordre de fabrication";"BOF";7;;"8";
1 importId name typeSelect label code elementSelect archived sequence statusSelect
2 3 PRF - Proposition d'ordre de fabrication 1 Proposition d'ordre de fabrication PRF 8 2
3 5 ORF - Ordre de fabrication 1 Ordre de fabrication ORF 6 4 6
4 6 BPF - Besoin / proposition de fabrication 2 Besoin / proposition de fabrication BPF 9 5
5 9 BOF - Besoins / ordre de fabrication 2 Besoins / ordre de fabrication BOF 7 8

View File

@ -0,0 +1,16 @@
<?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>
<input file="supplychain_mrpLineType.csv" separator=";" type="com.axelor.apps.supplychain.db.MrpLineType" search="self.importId = :importId"/>
</csv-inputs>

View File

@ -0,0 +1,35 @@
<?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_appProduction.csv" separator=";" type="com.axelor.apps.base.db.AppProduction"
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="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>

View File

@ -0,0 +1,2 @@
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
"perm.production.all";"com.axelor.apps.production.db.*";"x";"x";"x";"x";"x";;;"Admin"
1 name object can_read can_write can_create can_remove can_export condition conditionParams roleName
2 perm.production.all com.axelor.apps.production.db.* x x x x x Admin

View File

@ -0,0 +1,2 @@
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
"Manufacturing";"production";18;"Manufacturing configuration";"app-production.png";"axelor-production";"supplychain";15
1 name code installOrder description imagePath modules dependsOn sequence
2 Manufacturing production 18 Manufacturing configuration app-production.png axelor-production supplychain 15

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,34 @@
"module";"object";"view";"field";"help"
"axelor-production";"ProdProcess";"prod-process-form";"isConsProOnOperation";"Box to be checked in order to activate a consumption by phase on the manufacturing orders attached to this production process. This option is useful to set a list of products to be consumed at each phase of the production process. "
"axelor-production";"ProdProcess";"prod-process-form";"outsourcing";"This box enables to indicate if the production process will be outsourced."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"outsourcing";"This box enables to indicate if the phase will be outsourced."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"minCapacityPerCycle";"Minimum capacity for completion of the phase at each cycle. "
"axelor-production";"ProdProcessLine";"prod-process-line-form";"maxCapacityPerCycle";"Maximum capacity for completion of the phase at each cycle."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"durationPerCycle";"Duration of a production cycle for phase. "
"axelor-production";"ProdProcessLine";"prod-process-line-form";"toConsumeProdProductList";"If the ""Manage the consumption by phase"" box is checked for the production process, this table enables to specify the list of products needed to complete the phase. "
"axelor-production";"BillOfMaterial";"bill-of-material-form";"computeCostPrice";"This button calculated the production costs of the product attached to the bill of material, based on sub-products and raw materials which have been used, and use of workstations for the corresponding production range. "
"axelor-production";"BillOfMaterial";"bill-of-material-form";"updateProductCostPrice";"This button allows you to update the cost price compared to the last calculation. Indeed, the prices of raw materials and labor can evolve and it becomes necessary to update the cost more or less regularly."
"axelor-production";"BillOfMaterial";"bill-of-material-line-form";"hasNoManageStock";"Tick this box to indicate that stocks will not be managed on this line. During production, no stock movement will be generated on this BOM line."
"axelor-production";"BillOfMaterial";"bill-of-material-line-form";"priority";"Specifies a priority for the BOM line."
"axelor-production";"WorkCenter";"work-center-form";"machine";"For work stations including a machine, this field indicates the default machine. "
"axelor-production";"WorkCenter";"work-center-form";"minCapacityPerCycle";"Minimum production capacity per machine cycle."
"axelor-production";"WorkCenter";"work-center-form";"maxCapacityPerCycle";"Maximum production capacity per machine cycle."
"axelor-production";"WorkCenter";"work-center-form";"durationPerCycle";"Duration of the production cycle of the machine."
"axelor-production";"WorkCenter";"work-center-form";"prodHumanResourceList";"This table enables to set the list of employees or of employee profiles needed on the work station, and duration of intervention for each one. "
"axelor-production";"Machine";"machine-form";"startingDuration";"The time the machine starts before it can be used."
"axelor-production";"Machine";"machine-form";"setupDuration";"Time between two cycles. This is the time to wait until the end of a cycle to start a new cycle."
"axelor-production";"Machine";"machine-form";"endingDuration";"Ending duration of the machine."
"axelor-production";"Machine";"machine-form";"weeklyPlanning";"Enables to select a general planning of machine availability (full-time or part-time). "
"axelor-production";"AppProduction";"app-production-config-form";"prodOrderMgtOnSO";"Allows you to activate production order management from sales orders. "
"axelor-production";"AppProduction";"app-production-config-form";"productionOrderGenerationAuto";"You can also choose to automate thanks to this option the generation of production orders from sale orders.
As soon as an order for the production of a product is confirmed, a production order will be generated automatically."
"axelor-production";"AppProduction";"app-production-config-form";"enableConfigurator";"This option enable to activate the BOM, production process and phase configurators.
"
"axelor-production";"AppProduction";"app-production-config-form";"manageBillOfMaterialVersion";"By enabling this option you will be able to create several BOM versions and thus keep a history of each version."
"axelor-production";"AppProduction";"app-production-config-form";"manageResidualProductOnBom";"This option makes it possible to indicate on each BOM the residual products generated by the production."
"axelor-production";"AppProduction";"app-production-config-form";"workCenterProduct";"This option makes it possible to define a product by default in the costing settings of the work centers."
"axelor-production";"AppProduction";"app-production-config-form";"manageProdProcessVersion";"By enabling this option you will be able to create several production process versions and thus keep a history of each version."
"axelor-production";"AppProduction";"app-production-config-form";"manageBusinessProduction";"This option allows to manage the production within the framework of a business project. You can associate production and manufacturing orders with business projects and invoice them."
"axelor-production";"AppProduction";"app-production-config-form";"enableTimesheetOnManufOrder";"This option enables the ability to impute user timesheets on manufacturing orders. On the form used, in the tab ""Timesheets"", there is the field ""Timesheets imputed on"", and you can select ""Timesheets imputed on manufacturing order"". This user will thus be able to select manufacturing orders, manufacturing operations and indicate the time spent on each order and manufacturing operation on his timesheets."
"axelor-production";"AppProduction";"app-production-config-form";"manufOrderFilterStatusSelect";"You can choose the manuf order status using as a fitler in the view ""Stock details by product"". "
"axelor-production";"ProductionConfig";"production-config-form";"stockMoveRealizeOrderSelect";"Allows you to choose whether the stock moves of the products needed for production are made at the start of production or at the end."
1 module object view field help
2 axelor-production ProdProcess prod-process-form isConsProOnOperation Box to be checked in order to activate a consumption by phase on the manufacturing orders attached to this production process. This option is useful to set a list of products to be consumed at each phase of the production process.
3 axelor-production ProdProcess prod-process-form outsourcing This box enables to indicate if the production process will be outsourced.
4 axelor-production ProdProcessLine prod-process-line-form outsourcing This box enables to indicate if the phase will be outsourced.
5 axelor-production ProdProcessLine prod-process-line-form minCapacityPerCycle Minimum capacity for completion of the phase at each cycle.
6 axelor-production ProdProcessLine prod-process-line-form maxCapacityPerCycle Maximum capacity for completion of the phase at each cycle.
7 axelor-production ProdProcessLine prod-process-line-form durationPerCycle Duration of a production cycle for phase.
8 axelor-production ProdProcessLine prod-process-line-form toConsumeProdProductList If the "Manage the consumption by phase" box is checked for the production process, this table enables to specify the list of products needed to complete the phase.
9 axelor-production BillOfMaterial bill-of-material-form computeCostPrice This button calculated the production costs of the product attached to the bill of material, based on sub-products and raw materials which have been used, and use of workstations for the corresponding production range.
10 axelor-production BillOfMaterial bill-of-material-form updateProductCostPrice This button allows you to update the cost price compared to the last calculation. Indeed, the prices of raw materials and labor can evolve and it becomes necessary to update the cost more or less regularly.
11 axelor-production BillOfMaterial bill-of-material-line-form hasNoManageStock Tick this box to indicate that stocks will not be managed on this line. During production, no stock movement will be generated on this BOM line.
12 axelor-production BillOfMaterial bill-of-material-line-form priority Specifies a priority for the BOM line.
13 axelor-production WorkCenter work-center-form machine For work stations including a machine, this field indicates the default machine.
14 axelor-production WorkCenter work-center-form minCapacityPerCycle Minimum production capacity per machine cycle.
15 axelor-production WorkCenter work-center-form maxCapacityPerCycle Maximum production capacity per machine cycle.
16 axelor-production WorkCenter work-center-form durationPerCycle Duration of the production cycle of the machine.
17 axelor-production WorkCenter work-center-form prodHumanResourceList This table enables to set the list of employees or of employee profiles needed on the work station, and duration of intervention for each one.
18 axelor-production Machine machine-form startingDuration The time the machine starts before it can be used.
19 axelor-production Machine machine-form setupDuration Time between two cycles. This is the time to wait until the end of a cycle to start a new cycle.
20 axelor-production Machine machine-form endingDuration Ending duration of the machine.
21 axelor-production Machine machine-form weeklyPlanning Enables to select a general planning of machine availability (full-time or part-time).
22 axelor-production AppProduction app-production-config-form prodOrderMgtOnSO Allows you to activate production order management from sales orders.
23 axelor-production AppProduction app-production-config-form productionOrderGenerationAuto You can also choose to automate thanks to this option the generation of production orders from sale orders. As soon as an order for the production of a product is confirmed, a production order will be generated automatically.
24 axelor-production AppProduction app-production-config-form enableConfigurator This option enable to activate the BOM, production process and phase configurators.
25 axelor-production AppProduction app-production-config-form manageBillOfMaterialVersion By enabling this option you will be able to create several BOM versions and thus keep a history of each version.
26 axelor-production AppProduction app-production-config-form manageResidualProductOnBom This option makes it possible to indicate on each BOM the residual products generated by the production.
27 axelor-production AppProduction app-production-config-form workCenterProduct This option makes it possible to define a product by default in the costing settings of the work centers.
28 axelor-production AppProduction app-production-config-form manageProdProcessVersion By enabling this option you will be able to create several production process versions and thus keep a history of each version.
29 axelor-production AppProduction app-production-config-form manageBusinessProduction This option allows to manage the production within the framework of a business project. You can associate production and manufacturing orders with business projects and invoice them.
30 axelor-production AppProduction app-production-config-form enableTimesheetOnManufOrder This option enables the ability to impute user timesheets on manufacturing orders. On the form used, in the tab "Timesheets", there is the field "Timesheets imputed on", and you can select "Timesheets imputed on manufacturing order". This user will thus be able to select manufacturing orders, manufacturing operations and indicate the time spent on each order and manufacturing operation on his timesheets.
31 axelor-production AppProduction app-production-config-form manufOrderFilterStatusSelect You can choose the manuf order status using as a fitler in the view "Stock details by product".
32 axelor-production ProductionConfig production-config-form stockMoveRealizeOrderSelect Allows you to choose whether the stock moves of the products needed for production are made at the start of production or at the end.

View File

@ -0,0 +1,34 @@
"module";"object";"view";"field";"help"
"axelor-production";"ProdProcess";"prod-process-form";"isConsProOnOperation";"Case à cocher permettant de déclencher une consommation par phase sur les ordres de fabrication rattachés à cette gamme. Cette option permettra de paramétrer une liste de produit à consommer sur chaque phase de la gamme."
"axelor-production";"ProdProcess";"prod-process-form";"outsourcing";"Cette case permet d'indiquer si cette gamme sera sous-traitée."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"outsourcing";"Cette case permet d'indiquer si cette phase sera sous-traitée."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"minCapacityPerCycle";"Capacité minimum par cycle de réalisation de la phase."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"maxCapacityPerCycle";"Capacité maximum par cycle de réalisation de la phase."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"durationPerCycle";"Durée du cycle de production pour la phase."
"axelor-production";"ProdProcessLine";"prod-process-line-form";"toConsumeProdProductList";"Si la case ""Gerer la consommation par phases"" est cochée sur la gamme, ce tableau permet de renseigner la liste des produits nécessaires à la réalisation de la phase."
"axelor-production";"BillOfMaterial";"bill-of-material-form";"computeCostPrice";"Ce bouton permet de calculer le coût de revient du produit associé à la nomenclature, sur la base des sous-produits et matière premières utilisées ainsi que sur la base des postes de charges et de la gamme de production utilisée."
"axelor-production";"BillOfMaterial";"bill-of-material-form";"updateProductCostPrice";"Ce bouton permet de mettre à jour le coût de revient par rapport au dernier calcul. En effet les prix des matières premières et de la main d'oeuvre peuvent évoluer et il devient nécessaire de mettre à jour le coût de revient plus ou moins régulièrement."
"axelor-production";"BillOfMaterial";"bill-of-material-line-form";"hasNoManageStock";"Cocher cette case permet d'indiquer que les stocks ne seront pas gérés sur cette ligne. Lors de la production, aucun mouvement de stock ne sera généré sur cette ligne de nomenclature."
"axelor-production";"BillOfMaterial";"bill-of-material-line-form";"priority";"Permet d'indiquer une priorité pour la ligne de nomenclature."
"axelor-production";"WorkCenter";"work-center-form";"machine";"Pour les postes de charges comprenant une machine, il s'agit de la machine utilisée par défaut."
"axelor-production";"WorkCenter";"work-center-form";"minCapacityPerCycle";"Capacité minimum de production par cycle de la machine."
"axelor-production";"WorkCenter";"work-center-form";"maxCapacityPerCycle";"Capacité maximum de production par cycle de la machine."
"axelor-production";"WorkCenter";"work-center-form";"durationPerCycle";"Durée du cycle de production de la machine."
"axelor-production";"WorkCenter";"work-center-form";"prodHumanResourceList";"Tableau permettant de configurer la liste d'employés ou de profils employés nécessaires sur le poste de charge ainsi que la durée d'intervention de chacun."
"axelor-production";"Machine";"machine-form";"startingDuration";"Temps que met la machine à démarrer avant de pouvoir être utilisée."
"axelor-production";"Machine";"machine-form";"setupDuration";"Temps entre deux cycles. Il s'agit du temps qu'il faut attendre après la fin d'un cycle pour pouvoir en commencer un nouveau."
"axelor-production";"Machine";"machine-form";"endingDuration";"Temps de clôture de la machine."
"axelor-production";"Machine";"machine-form";"weeklyPlanning";"Permet de sélectionner un planning général de disponibilité de la machine (mi-temps ou pleins-temps) ."
"axelor-production";"AppProduction";"app-production-config-form";"prodOrderMgtOnSO";"Permet d'activer la gestion des ordres de production depuis les commandes clients."
"axelor-production";"AppProduction";"app-production-config-form";"productionOrderGenerationAuto";"Vous pouvez aussi choisir d'automatiser grâce à cette option la génération des ordres de production depuis les commandes clients. Dès qu'une commande nécéssitant la production d'un produit sera confirmée, un ordre de production sera généré automatiquement.
"
"axelor-production";"AppProduction";"app-production-config-form";"enableConfigurator";"Cette option permet d'activer les configurateurs de nomenclature, de gamme et de phase.
"
"axelor-production";"AppProduction";"app-production-config-form";"manageBillOfMaterialVersion";"En activant cette option vous pourrez créer plusieurs versions d'une nomenclature et ainsi garder un historique de chaque version."
"axelor-production";"AppProduction";"app-production-config-form";"manageResidualProductOnBom";"Cette option va permettre d'indiquer sur chaque nomenclature les produits résiduels générés par la production."
"axelor-production";"AppProduction";"app-production-config-form";"workCenterProduct";"Cette option permet de définir un produit par défaut dans le paramètrage des coûts des postes de charge."
"axelor-production";"AppProduction";"app-production-config-form";"manageProdProcessVersion";"En activant cette option vous pourrez créer plusieurs versions d'une gamme et ainsi garder un historique de chaque version."
"axelor-production";"AppProduction";"app-production-config-form";"manageBusinessProduction";"Cette option permet de gérer la production dans le cadre d'une affaire. Vous pourrez associer des ordres de production et de fabrication à des affaires et ainsi les facturer."
"axelor-production";"AppProduction";"app-production-config-form";"enableTimesheetOnManufOrder";"Cette option permet d'activer la possibilité d'imputer les feuilles de temps d'un utilisateur sur des ordres de fabrication. Sur la fiche employé, dans l'onglet ""Feuilles de temps"", il y a le champ ""Feuilles de temps imputées sur"", et vous pouvez sélectionner ""Feuilles de temps imputées sur O.F"". Cet utilisateur pourra ainsi sur ses feuilles de temps sélectionner des ordres de fabrication, des opérations de fabrication et indiquer les temps passés sur chaque ordre et opération de fabrication."
"axelor-production";"AppProduction";"app-production-config-form";"manufOrderFilterStatusSelect";"Vous pouvez sélectionner le statut des ordres de fabrication utilisés comme filtres dans la vue ""Détails des stocks par produit"". "
"axelor-production";"ProductionConfig";"production-config-form";"stockMoveRealizeOrderSelect";"Vous permet de choisir si les mouvements de stocks des produits nécessaires à la production sont réalisés au démarrage de la production ou à la fin."
1 module object view field help
2 axelor-production ProdProcess prod-process-form isConsProOnOperation Case à cocher permettant de déclencher une consommation par phase sur les ordres de fabrication rattachés à cette gamme. Cette option permettra de paramétrer une liste de produit à consommer sur chaque phase de la gamme.
3 axelor-production ProdProcess prod-process-form outsourcing Cette case permet d'indiquer si cette gamme sera sous-traitée.
4 axelor-production ProdProcessLine prod-process-line-form outsourcing Cette case permet d'indiquer si cette phase sera sous-traitée.
5 axelor-production ProdProcessLine prod-process-line-form minCapacityPerCycle Capacité minimum par cycle de réalisation de la phase.
6 axelor-production ProdProcessLine prod-process-line-form maxCapacityPerCycle Capacité maximum par cycle de réalisation de la phase.
7 axelor-production ProdProcessLine prod-process-line-form durationPerCycle Durée du cycle de production pour la phase.
8 axelor-production ProdProcessLine prod-process-line-form toConsumeProdProductList Si la case "Gerer la consommation par phases" est cochée sur la gamme, ce tableau permet de renseigner la liste des produits nécessaires à la réalisation de la phase.
9 axelor-production BillOfMaterial bill-of-material-form computeCostPrice Ce bouton permet de calculer le coût de revient du produit associé à la nomenclature, sur la base des sous-produits et matière premières utilisées ainsi que sur la base des postes de charges et de la gamme de production utilisée.
10 axelor-production BillOfMaterial bill-of-material-form updateProductCostPrice Ce bouton permet de mettre à jour le coût de revient par rapport au dernier calcul. En effet les prix des matières premières et de la main d'oeuvre peuvent évoluer et il devient nécessaire de mettre à jour le coût de revient plus ou moins régulièrement.
11 axelor-production BillOfMaterial bill-of-material-line-form hasNoManageStock Cocher cette case permet d'indiquer que les stocks ne seront pas gérés sur cette ligne. Lors de la production, aucun mouvement de stock ne sera généré sur cette ligne de nomenclature.
12 axelor-production BillOfMaterial bill-of-material-line-form priority Permet d'indiquer une priorité pour la ligne de nomenclature.
13 axelor-production WorkCenter work-center-form machine Pour les postes de charges comprenant une machine, il s'agit de la machine utilisée par défaut.
14 axelor-production WorkCenter work-center-form minCapacityPerCycle Capacité minimum de production par cycle de la machine.
15 axelor-production WorkCenter work-center-form maxCapacityPerCycle Capacité maximum de production par cycle de la machine.
16 axelor-production WorkCenter work-center-form durationPerCycle Durée du cycle de production de la machine.
17 axelor-production WorkCenter work-center-form prodHumanResourceList Tableau permettant de configurer la liste d'employés ou de profils employés nécessaires sur le poste de charge ainsi que la durée d'intervention de chacun.
18 axelor-production Machine machine-form startingDuration Temps que met la machine à démarrer avant de pouvoir être utilisée.
19 axelor-production Machine machine-form setupDuration Temps entre deux cycles. Il s'agit du temps qu'il faut attendre après la fin d'un cycle pour pouvoir en commencer un nouveau.
20 axelor-production Machine machine-form endingDuration Temps de clôture de la machine.
21 axelor-production Machine machine-form weeklyPlanning Permet de sélectionner un planning général de disponibilité de la machine (mi-temps ou pleins-temps) .
22 axelor-production AppProduction app-production-config-form prodOrderMgtOnSO Permet d'activer la gestion des ordres de production depuis les commandes clients.
23 axelor-production AppProduction app-production-config-form productionOrderGenerationAuto Vous pouvez aussi choisir d'automatiser grâce à cette option la génération des ordres de production depuis les commandes clients. Dès qu'une commande nécéssitant la production d'un produit sera confirmée, un ordre de production sera généré automatiquement.
24 axelor-production AppProduction app-production-config-form enableConfigurator Cette option permet d'activer les configurateurs de nomenclature, de gamme et de phase.
25 axelor-production AppProduction app-production-config-form manageBillOfMaterialVersion En activant cette option vous pourrez créer plusieurs versions d'une nomenclature et ainsi garder un historique de chaque version.
26 axelor-production AppProduction app-production-config-form manageResidualProductOnBom Cette option va permettre d'indiquer sur chaque nomenclature les produits résiduels générés par la production.
27 axelor-production AppProduction app-production-config-form workCenterProduct Cette option permet de définir un produit par défaut dans le paramètrage des coûts des postes de charge.
28 axelor-production AppProduction app-production-config-form manageProdProcessVersion En activant cette option vous pourrez créer plusieurs versions d'une gamme et ainsi garder un historique de chaque version.
29 axelor-production AppProduction app-production-config-form manageBusinessProduction Cette option permet de gérer la production dans le cadre d'une affaire. Vous pourrez associer des ordres de production et de fabrication à des affaires et ainsi les facturer.
30 axelor-production AppProduction app-production-config-form enableTimesheetOnManufOrder Cette option permet d'activer la possibilité d'imputer les feuilles de temps d'un utilisateur sur des ordres de fabrication. Sur la fiche employé, dans l'onglet "Feuilles de temps", il y a le champ "Feuilles de temps imputées sur", et vous pouvez sélectionner "Feuilles de temps imputées sur O.F". Cet utilisateur pourra ainsi sur ses feuilles de temps sélectionner des ordres de fabrication, des opérations de fabrication et indiquer les temps passés sur chaque ordre et opération de fabrication.
31 axelor-production AppProduction app-production-config-form manufOrderFilterStatusSelect Vous pouvez sélectionner le statut des ordres de fabrication utilisés comme filtres dans la vue "Détails des stocks par produit".
32 axelor-production ProductionConfig production-config-form stockMoveRealizeOrderSelect Vous permet de choisir si les mouvements de stocks des produits nécessaires à la production sont réalisés au démarrage de la production ou à la fin.

View File

@ -0,0 +1,28 @@
"name";"roles.name"
"manufacturing-root";"Admin"
"manufacturing-root-products";"Admin"
"manufacturing-root-bill-of-material";"Admin"
"manufacturing-root-prod-process";"Admin"
"manufacturing-root-production-order";"Admin"
"manufacturing-root-manuf-order";"Admin"
"manufacturing-root-operation-order";"Admin"
"manufacturing-root-operation-order-planned-calendar";"Admin"
"manufacturing-root-operation-order-real-calendar";"Admin"
"manufacturing-root-charge-by-machine-dashboard";"Admin"
"manufacturing-root-report";"Admin"
"menu-manufacturing-dashboard-1";"Admin"
"menu-manufacturing-user-dashboard";"Admin"
"manufacturing-conf";"Admin"
"manufacturing-conf-work-center";"Admin"
"manufacturing-conf-machine";"Admin"
"manufacturing-conf-cost-sheet-group";"Admin"
"manufacturing-conf-configurators";"Admin"
"manufacturing-conf-configurators-creator";"Admin"
"manufacturing-conf-configurators-configurators";"Admin"
"manufacturing-conf-configurators-bom";"Admin"
"manufacturing-conf-configurators-prodprocess";"Admin"
"manufacturing-conf-configurators-prod-process-line";"Admin"
"top-manufacturing";"Admin"
"top-manufacturing-manuf-order";"Admin"
"top-manufacturing-operation-order";"Admin"
"top-manufacturing-bill-of-material";"Admin"
1 name roles.name
2 manufacturing-root Admin
3 manufacturing-root-products Admin
4 manufacturing-root-bill-of-material Admin
5 manufacturing-root-prod-process Admin
6 manufacturing-root-production-order Admin
7 manufacturing-root-manuf-order Admin
8 manufacturing-root-operation-order Admin
9 manufacturing-root-operation-order-planned-calendar Admin
10 manufacturing-root-operation-order-real-calendar Admin
11 manufacturing-root-charge-by-machine-dashboard Admin
12 manufacturing-root-report Admin
13 menu-manufacturing-dashboard-1 Admin
14 menu-manufacturing-user-dashboard Admin
15 manufacturing-conf Admin
16 manufacturing-conf-work-center Admin
17 manufacturing-conf-machine Admin
18 manufacturing-conf-cost-sheet-group Admin
19 manufacturing-conf-configurators Admin
20 manufacturing-conf-configurators-creator Admin
21 manufacturing-conf-configurators-configurators Admin
22 manufacturing-conf-configurators-bom Admin
23 manufacturing-conf-configurators-prodprocess Admin
24 manufacturing-conf-configurators-prod-process-line Admin
25 top-manufacturing Admin
26 top-manufacturing-manuf-order Admin
27 top-manufacturing-operation-order Admin
28 top-manufacturing-bill-of-material Admin

View File

@ -0,0 +1,2 @@
"code";"manageBillOfMaterialVersion";"manageResidualProductOnBom";"workCenterProduct.importId";"productCostSheetGroup.importId";"workCenterCostSheetGroup.importId";"cycleUnit.importId";"subtractProdResidualOnCostSheet"
"production";"true";"true";402;2;3;103;"true"
1 code manageBillOfMaterialVersion manageResidualProductOnBom workCenterProduct.importId productCostSheetGroup.importId workCenterCostSheetGroup.importId cycleUnit.importId subtractProdResidualOnCostSheet
2 production true true 402 2 3 103 true

View File

@ -0,0 +1,45 @@
"importId";"name";"code";"description";"productFamily.importId";"productCategory.importId";"expense";"procurementMethodSelect";"unit.importId";"productTypeSelect";"salePrice";"saleCurrency.code";"purchasePrice";"purchaseCurrency.code";"defaultSupplierPartner.importId";"startDate";"endDate";"saleSupplySelect";"costPrice";"hasWarranty";"warrantyNbrOfMonths";"isPerishable";"perishableNbrOfMonths";"defaultBillOfMaterial.importId";"managPriceCoef";"picture_fileName";"isActivity";"productVariantConfig.importId";"costSheetGroup.importId";"manageVariantPrice"
1;"Server hosting";"SUB-0001";"Dedicated Server i5-2400 4x3,1GHz 16GoRAM 2x 2To SATA2";1;1;"false";;105;"service";70;"EUR";"58.1";"EUR";;"TODAY[-5M1d]";;1;63;"false";;"false";;;"1.40";"img/product/SUB-0001_en.jpg";;;;
2;"Annual Maintenance";"SUB-0002";"Software updates, Daily backups, Restore, Ticket support portal";1;2;"false";;106;"service";2000;"EUR";1460;"EUR";;"TODAY[-5M1d]";;1;1800;"false";;"false";;;"1.40";"img/product/SUB-0002_en.jpg";;;;
100;"Pack Classic";"EQPT-0002";;2;18;"false";"produce";1;"storable";1845;"EUR";;"EUR";;"TODAY[1d]";;3;1661;"false";;"false";;60;"1.40";"img/product/EQPT-0001_en.jpg";;;2;
101;"Pack Performance";"EQPT-0001";"Classic Server + Laser Printer + 3 Paper Ream + ";2;18;"false";"produce";1;"storable";2890;"EUR";;"EUR";;"TODAY[1d]";;3;2601;"false";;"false";;80;"1.40";"img/product/EQPT-0002_en.jpg";;;2;
110;"Classic server";"EQPT-0004";"Rack case / Processor 4 core - 8 threads - 3,4 GHz - 8 Mo Cache / Hard disk SSD SATA 2*2To / RAM : 32 Go / Bandwidth : 200 Mbps / RAID : SOFT - 0/1";2;3;"false";"produce";1;"storable";1500;"EUR";1185;"EUR";;"TODAY[-5M1d]";;1;1350;"true";24;"false";;1;"1.40";"img/product/EQPT-0003_en.jpg";;;2;
111;"High Performance Server";"EQPT-0003";"Rack case / Processor 6 core - 12 threads - 3,2 GHz - 8 Mo Cache / Hard disk SSD 2*200 Go / RAM : 64 Go / Bandwidth : 10 Gbps / RAID : SOFT - 0/1";2;3;"false";"produce";1;"storable";2500;"EUR";2100;"EUR";;"TODAY[-5M1d]";;1;2250;"true";24;"false";;40;"1.40";"img/product/EQPT-0004_en.jpg";;;2;
120;"InkJet Printer";"EQPT-0005";"Printer resolution : 1200x1200 ppp / Printing speed : 20 ppm / Recto/verso : Automatique / Wifi : oui / Sacanner Resolution : 4800 ppp / Screen : Tactile CGD 10,92 cm";2;4;"false";"buy";1;"storable";329;"EUR";"250.04";"EUR";148;"TODAY[-5M1d]";;1;296;"true";12;"false";;;"1.40";"img/product/EQPT-0005_en.png";;;2;
121;"Laser Printer";"EQPT-0006";"Printer resolution : 1200x1200 dpi / Printing speed : 33 ppm / Recto/verso : Automatique / Wifi : oui / Sacanner Resolution : - / Screen : tactile couleur 8,89 cm";2;4;"false";"buy";1;"storable";429;"EUR";"360.36";"EUR";148;"TODAY[-5M1d]";;1;386;"true";12;"false";;;"1.40";"img/product/EQPT-0006_en.jpg";;;2;
130;"LED Screen HD 22 inch";"EQPT-0007";"Max. Resolution : 1920x1080p / Video Input : 2 HDMI, 1 VGA, 1 composite";2;17;"false";"buy";1;"storable";200;"EUR";170;"EUR";148;"TODAY[-4M1d]";;1;180;"true";12;"false";;;"1.40";"img/product/EQPT-0007_en.jpg";;;2;
131;"LED Screen HD 24 inch";"EQPT-0008";"Max. Resolution : 1920x1080p / Video Input : 2 HDMI, 1 VGA, 1 composite";2;17;"false";"buy";1;"storable";250;"EUR";"207.5";"EUR";148;"TODAY[-4M1d]";;1;225;"true";12;"false";;;"1.40";"img/product/EQPT-0008_en.jpg";;;2;
200;"Hard Disk SATA 1To";"COMP-0001";"Internal HDD 3,5'' - Capacity : 1 To / Memory : 64 Mo / Speed : 7200 trs/min";3;5;"false";"buy";1;"storable";60;"EUR";"46.2";"EUR";148;"TODAY[-4M1d]";;1;54;"true";12;"false";;;"1.40";"img/product/COMP-0001_en.png";;;1;
201;"Hard Disk SSD 100 Go";"COMP-0002";"DD SSD 100 Go - SATA 6Go/s - 7 mm";3;5;"false";"buy";1;"storable";80;"EUR";"64.8";"EUR";148;"TODAY[-4M1d]";;1;72;"true";12;"false";;;"1.40";"img/product/COMP-0002_en.png";;;1;
210;"Processor 4x3,4 GHz";"COMP-0003";"Processor 4 core - 8 threads - 3,4 GHz - 8 Mo Cache";3;6;"false";"buy";1;"storable";180;"EUR";"138.6";"EUR";152;"TODAY[-4M1d]";;1;162;"true";12;"false";;;"1.40";"img/product/COMP-0003_en.jpg";;;1;
211;"Processor 6x3,2 GHz";"COMP-0004";"Processor 6 core - 12 threads - 3,2 GHz - 8 Mo Cache";3;6;"false";"buy";1;"storable";540;"EUR";"448.2";"EUR";152;"TODAY[-4M1d]";;1;486;"true";12;"false";;;"1.40";"img/product/COMP-0004_en.jpg";;;1;
220;"Rack Casing";"COMP-0005";;3;7;"false";"buy";1;"storable";70;"EUR";"54.6";"EUR";148;"TODAY[-3M1d]";;1;63;"false";;"false";;;"1.40";"img/product/COMP-0005_en.jpg";;;1;
230;"Memory 8 Go DDR3";"COMP-0006";;3;8;"false";"buy";1;"storable";80;"EUR";"60.8";"EUR";170;"TODAY[-3M1d]";;1;72;"true";12;"false";;;"1.40";"img/product/COMP-0006_en.jpg";;;1;
231;"Memory 16 Go DDR3";"COMP-0007";;3;8;"false";"buy";1;"storable";160;"EUR";120;"EUR";170;"TODAY[-3M1d]";;1;144;"true";12;"false";;;"1.40";"img/product/COMP-0007_en.jpg";;;1;
240;"Classic MotherBoard";"COMP-0008";;3;9;"false";"buy";1;"storable";150;"EUR";105;"EUR";152;"TODAY[-3M1d]";;1;135;"true";12;"false";;;"1.40";"img/product/COMP-0008_en.png";;;1;
241;"High Performance MotherBoard";"COMP-0009";;3;9;"false";"buy";1;"storable";250;"EUR";"187.5";"EUR";152;"TODAY[-3M1d]";;1;225;"true";12;"false";;;"1.40";"img/product/COMP-0009_en.jpg";;;1;
250;"Power Supply";"COMP-0010";;3;9;"false";"buy";1;"storable";40;"EUR";"30.4";"EUR";152;"TODAY[-3M1d]";;1;36;"true";12;"false";;;"1.40";"img/product/COMP-0010_en.png";;;1;
300;"Cables set";"CONS-0001";;4;10;"false";"buy";1;"storable";20;"EUR";"15.6";"EUR";70;;;1;18;"false";;"false";;;"1.40";"img/product/CONS-0001_en.jpg";;;1;
301;"Screws set";"CONS-0002";;4;10;"false";"buy";1;"storable";4;"EUR";"3.16";"EUR";70;;;1;4;"false";;"false";;;"1.40";"img/product/CONS-0002_en.jpg";;;1;
302;"Ink Cartridge";"CONS-0003";"Color : Black, Magenta, Yellow, Blue/ Capacity : 1500 copies";4;10;"false";"buy";1;"storable";"32.9";"EUR";21;"EUR";70;"TODAY[-1M1d]";;1;"24.35";"false";;"false";;;"1.40";"img/product/CONS-0003_en.jpg";;;2;
303;"Laser Cartridge";"CONS-0004";"Color : Black, Magenta, Yellow, Blue/ Capacity : 2600 copies";4;10;"false";"buy";1;"storable";"84.5";"EUR";54;"EUR";70;"TODAY[-1M1d]";;1;"58.75";"false";;"false";;;"1.40";"img/product/CONS-0004_en.jpg";;;2;
304;"Paper ream 500 Sheets A4 Standard 80 g";"CONS-0005";"Format : A4 / Quantity : 500 Sheets / Mass : 80 g";4;10;"false";"buy";1;"storable";"6.99";"EUR";"5.4522";"EUR";70;"TODAY[-1M1d]";;1;6;"false";;"false";;;"1.40";"img/product/CONS-0005_en.jpg";;;2;
305;"Inkball Pen medium (Box 50)";"CONS-0006";"Available colors : Black, Blue, Red, Green";4;10;"false";"buy";1;"storable";15;"EUR";"10.8";"EUR";70;;;1;14;"false";;"false";;;"1.40";"img/product/CONS-0006_en.png";;1;2;"true"
306;"Ground coffee 250g";"CONS-0007";"Origin Columbia, 100% pur arabica";4;11;"false";"buy";1;"storable";3;"EUR";"2.49";"EUR";70;;;1;3;"true";;"true";12;;"1.40";"img/product/CONS-0007_en.png";;;2;
307;"Bottle of Water (1,5L)";"CONS-0008";;4;11;"false";"buy";1;"storable";"0.9";"EUR";"0.684";"EUR";70;;;1;1;"true";;"true";12;;"1.40";"img/product/CONS-0008_en.jpg";;;2;
400;"Project";"SERV-0001";;5;13;"false";;1;"service";8000;"EUR";6480;"EUR";;"TODAY[-2M1d]";;1;7200;"false";;"false";;;"1.40";"img/product/SERV-0001_en.jpg";"true";;;
401;"Task";"SERV-0002";;5;12;"false";;1;"service";1;"EUR";1;"EUR";;"TODAY[-2M1d]";;1;1;"false";;"false";;;"1.40";"img/product/SERV-0002_en.jpg";"true";;;
402;"WorkLoad";"SERV-0003";;5;12;"false";;1;"service";100;"EUR";100;"EUR";;"TODAY[-2M1d]";;1;90;"false";;"false";;;"1.40";"img/product/SERV-0003_en.jpg";"true";;;
410;"Project Manager";"SERV-0004";;5;12;"false";;103;"service";1000;"EUR";790;"EUR";;"TODAY[-2M1d]";;2;800;"false";;"false";;;"1.40";"img/product/SERV-0004_en.jpg";"true";;;
411;"Consultant";"SERV-0005";;5;12;"false";;103;"service";800;"EUR";568;"EUR";;"TODAY[-2M1d]";;2;640;"false";;"false";;;"1.40";"img/product/SERV-0005_en.jpg";"true";;;
412;"Technician/Operator Rk 1";"SERV-0006";;5;12;"false";;103;"service";400;"EUR";320;"EUR";;;;2;320;"false";;"false";;;"1.40";"img/product/SERV-0006_en.jpg";"true";;;
413;"Technician/Operator Rk 2";"SERV-0007";;5;12;"false";;103;"service";500;"EUR";380;"EUR";;;;2;400;"false";;"false";;;"1.40";"img/product/SERV-0007_en.jpg";"true";;;
414;"Sales";"SERV-0008";;5;12;"false";;103;"service";700;"EUR";525;"EUR";;;;2;560;"false";;"false";;;"1.40";"img/product/SERV-0008_en.png";"true";;;
415;"Manager";"SERV-0009";;5;12;"false";;103;"service";1100;"EUR";781;"EUR";;;;2;880;"false";;"false";;;"1.40";"img/product/SERV-0009_en.jpg";"true";;;
416;"General Manager";"SERV-0010";;5;12;"false";;103;"service";1500;"EUR";1110;"EUR";;;;2;1200;"false";;"false";;;"1.40";"img/product/SERV-0010_en.png";"true";;;
500;"Train/metro/tramway tickets";"NDF-0001";"Reimbursement of travel expenses for employees traveling outside the company by either Train, Metro or Subway";6;14;"true";;1;"service";80;"EUR";"63.2";"EUR";;;;3;72;"false";;"false";;;"1.40";"img/product/EXP-0001_en.jpg";;;;
501;"Plane ticket";"EXP-0002";"Reimbursement of travel expenses for employees traveling outside the company by Plane";6;14;"true";;1;"service";150;"EUR";"106.5";"EUR";;;;3;135;"false";;"false";;;"1.40";"img/product/EXP-0002_en.jpg";;;;
502;"Taxi/Car Rental";"EXP-0003";"Reimbursement of travel expenses for employees traveling outside the company by Taxi or Car Rental";6;14;"true";;1;"service";1;"EUR";"0.78";"EUR";;;;3;1;"false";;"false";;;"1.40";;;;;
503;"Mileage expenses";"EXP-0004";"Reimbursement of travel expenses for employees traveling outside the company while using their own vehicle.";6;14;"true";;1;"service";1;"EUR";"0.76";"EUR";;;;3;1;"false";;"false";;;"1.40";;;;;
504;"Meal";"EXP-0005";"Reimbursement of meal expenses for employees traveling outside the company.";6;14;"true";;1;"service";20;"EUR";15;"EUR";;;;3;18;"false";;"false";;;"1.40";"img/product/EXP-0005_en.jpg";;;;
505;"Hotel (or other accomodations)";"EXP-0006";"Reimbursement of accomodation expenses for employees traveling outside the company.";6;14;"true";;1;"service";90;"EUR";"63.9";"EUR";;;;3;81;"false";;"false";;;"1.40";"img/product/EXP-0006_en.jpg";;;;
1 importId name code description productFamily.importId productCategory.importId expense procurementMethodSelect unit.importId productTypeSelect salePrice saleCurrency.code purchasePrice purchaseCurrency.code defaultSupplierPartner.importId startDate endDate saleSupplySelect costPrice hasWarranty warrantyNbrOfMonths isPerishable perishableNbrOfMonths defaultBillOfMaterial.importId managPriceCoef picture_fileName isActivity productVariantConfig.importId costSheetGroup.importId manageVariantPrice
2 1 Server hosting SUB-0001 Dedicated Server i5-2400 4x3,1GHz 16GoRAM 2x 2To SATA2 1 1 false 105 service 70 EUR 58.1 EUR TODAY[-5M1d] 1 63 false false 1.40 img/product/SUB-0001_en.jpg
3 2 Annual Maintenance SUB-0002 Software updates, Daily backups, Restore, Ticket support portal 1 2 false 106 service 2000 EUR 1460 EUR TODAY[-5M1d] 1 1800 false false 1.40 img/product/SUB-0002_en.jpg
4 100 Pack Classic EQPT-0002 2 18 false produce 1 storable 1845 EUR EUR TODAY[1d] 3 1661 false false 60 1.40 img/product/EQPT-0001_en.jpg 2
5 101 Pack Performance EQPT-0001 Classic Server + Laser Printer + 3 Paper Ream + 2 18 false produce 1 storable 2890 EUR EUR TODAY[1d] 3 2601 false false 80 1.40 img/product/EQPT-0002_en.jpg 2
6 110 Classic server EQPT-0004 Rack case / Processor 4 core - 8 threads - 3,4 GHz - 8 Mo Cache / Hard disk SSD SATA 2*2To / RAM : 32 Go / Bandwidth : 200 Mbps / RAID : SOFT - 0/1 2 3 false produce 1 storable 1500 EUR 1185 EUR TODAY[-5M1d] 1 1350 true 24 false 1 1.40 img/product/EQPT-0003_en.jpg 2
7 111 High Performance Server EQPT-0003 Rack case / Processor 6 core - 12 threads - 3,2 GHz - 8 Mo Cache / Hard disk SSD 2*200 Go / RAM : 64 Go / Bandwidth : 10 Gbps / RAID : SOFT - 0/1 2 3 false produce 1 storable 2500 EUR 2100 EUR TODAY[-5M1d] 1 2250 true 24 false 40 1.40 img/product/EQPT-0004_en.jpg 2
8 120 InkJet Printer EQPT-0005 Printer resolution : 1200x1200 ppp / Printing speed : 20 ppm / Recto/verso : Automatique / Wifi : oui / Sacanner Resolution : 4800 ppp / Screen : Tactile CGD 10,92 cm 2 4 false buy 1 storable 329 EUR 250.04 EUR 148 TODAY[-5M1d] 1 296 true 12 false 1.40 img/product/EQPT-0005_en.png 2
9 121 Laser Printer EQPT-0006 Printer resolution : 1200x1200 dpi / Printing speed : 33 ppm / Recto/verso : Automatique / Wifi : oui / Sacanner Resolution : - / Screen : tactile couleur 8,89 cm 2 4 false buy 1 storable 429 EUR 360.36 EUR 148 TODAY[-5M1d] 1 386 true 12 false 1.40 img/product/EQPT-0006_en.jpg 2
10 130 LED Screen HD 22 inch EQPT-0007 Max. Resolution : 1920x1080p / Video Input : 2 HDMI, 1 VGA, 1 composite 2 17 false buy 1 storable 200 EUR 170 EUR 148 TODAY[-4M1d] 1 180 true 12 false 1.40 img/product/EQPT-0007_en.jpg 2
11 131 LED Screen HD 24 inch EQPT-0008 Max. Resolution : 1920x1080p / Video Input : 2 HDMI, 1 VGA, 1 composite 2 17 false buy 1 storable 250 EUR 207.5 EUR 148 TODAY[-4M1d] 1 225 true 12 false 1.40 img/product/EQPT-0008_en.jpg 2
12 200 Hard Disk SATA 1To COMP-0001 Internal HDD 3,5'' - Capacity : 1 To / Memory : 64 Mo / Speed : 7200 trs/min 3 5 false buy 1 storable 60 EUR 46.2 EUR 148 TODAY[-4M1d] 1 54 true 12 false 1.40 img/product/COMP-0001_en.png 1
13 201 Hard Disk SSD 100 Go COMP-0002 DD SSD 100 Go - SATA 6Go/s - 7 mm 3 5 false buy 1 storable 80 EUR 64.8 EUR 148 TODAY[-4M1d] 1 72 true 12 false 1.40 img/product/COMP-0002_en.png 1
14 210 Processor 4x3,4 GHz COMP-0003 Processor 4 core - 8 threads - 3,4 GHz - 8 Mo Cache 3 6 false buy 1 storable 180 EUR 138.6 EUR 152 TODAY[-4M1d] 1 162 true 12 false 1.40 img/product/COMP-0003_en.jpg 1
15 211 Processor 6x3,2 GHz COMP-0004 Processor 6 core - 12 threads - 3,2 GHz - 8 Mo Cache 3 6 false buy 1 storable 540 EUR 448.2 EUR 152 TODAY[-4M1d] 1 486 true 12 false 1.40 img/product/COMP-0004_en.jpg 1
16 220 Rack Casing COMP-0005 3 7 false buy 1 storable 70 EUR 54.6 EUR 148 TODAY[-3M1d] 1 63 false false 1.40 img/product/COMP-0005_en.jpg 1
17 230 Memory 8 Go DDR3 COMP-0006 3 8 false buy 1 storable 80 EUR 60.8 EUR 170 TODAY[-3M1d] 1 72 true 12 false 1.40 img/product/COMP-0006_en.jpg 1
18 231 Memory 16 Go DDR3 COMP-0007 3 8 false buy 1 storable 160 EUR 120 EUR 170 TODAY[-3M1d] 1 144 true 12 false 1.40 img/product/COMP-0007_en.jpg 1
19 240 Classic MotherBoard COMP-0008 3 9 false buy 1 storable 150 EUR 105 EUR 152 TODAY[-3M1d] 1 135 true 12 false 1.40 img/product/COMP-0008_en.png 1
20 241 High Performance MotherBoard COMP-0009 3 9 false buy 1 storable 250 EUR 187.5 EUR 152 TODAY[-3M1d] 1 225 true 12 false 1.40 img/product/COMP-0009_en.jpg 1
21 250 Power Supply COMP-0010 3 9 false buy 1 storable 40 EUR 30.4 EUR 152 TODAY[-3M1d] 1 36 true 12 false 1.40 img/product/COMP-0010_en.png 1
22 300 Cables set CONS-0001 4 10 false buy 1 storable 20 EUR 15.6 EUR 70 1 18 false false 1.40 img/product/CONS-0001_en.jpg 1
23 301 Screws set CONS-0002 4 10 false buy 1 storable 4 EUR 3.16 EUR 70 1 4 false false 1.40 img/product/CONS-0002_en.jpg 1
24 302 Ink Cartridge CONS-0003 Color : Black, Magenta, Yellow, Blue/ Capacity : 1500 copies 4 10 false buy 1 storable 32.9 EUR 21 EUR 70 TODAY[-1M1d] 1 24.35 false false 1.40 img/product/CONS-0003_en.jpg 2
25 303 Laser Cartridge CONS-0004 Color : Black, Magenta, Yellow, Blue/ Capacity : 2600 copies 4 10 false buy 1 storable 84.5 EUR 54 EUR 70 TODAY[-1M1d] 1 58.75 false false 1.40 img/product/CONS-0004_en.jpg 2
26 304 Paper ream 500 Sheets A4 Standard 80 g CONS-0005 Format : A4 / Quantity : 500 Sheets / Mass : 80 g 4 10 false buy 1 storable 6.99 EUR 5.4522 EUR 70 TODAY[-1M1d] 1 6 false false 1.40 img/product/CONS-0005_en.jpg 2
27 305 Inkball Pen medium (Box 50) CONS-0006 Available colors : Black, Blue, Red, Green 4 10 false buy 1 storable 15 EUR 10.8 EUR 70 1 14 false false 1.40 img/product/CONS-0006_en.png 1 2 true
28 306 Ground coffee 250g CONS-0007 Origin Columbia, 100% pur arabica 4 11 false buy 1 storable 3 EUR 2.49 EUR 70 1 3 true true 12 1.40 img/product/CONS-0007_en.png 2
29 307 Bottle of Water (1,5L) CONS-0008 4 11 false buy 1 storable 0.9 EUR 0.684 EUR 70 1 1 true true 12 1.40 img/product/CONS-0008_en.jpg 2
30 400 Project SERV-0001 5 13 false 1 service 8000 EUR 6480 EUR TODAY[-2M1d] 1 7200 false false 1.40 img/product/SERV-0001_en.jpg true
31 401 Task SERV-0002 5 12 false 1 service 1 EUR 1 EUR TODAY[-2M1d] 1 1 false false 1.40 img/product/SERV-0002_en.jpg true
32 402 WorkLoad SERV-0003 5 12 false 1 service 100 EUR 100 EUR TODAY[-2M1d] 1 90 false false 1.40 img/product/SERV-0003_en.jpg true
33 410 Project Manager SERV-0004 5 12 false 103 service 1000 EUR 790 EUR TODAY[-2M1d] 2 800 false false 1.40 img/product/SERV-0004_en.jpg true
34 411 Consultant SERV-0005 5 12 false 103 service 800 EUR 568 EUR TODAY[-2M1d] 2 640 false false 1.40 img/product/SERV-0005_en.jpg true
35 412 Technician/Operator Rk 1 SERV-0006 5 12 false 103 service 400 EUR 320 EUR 2 320 false false 1.40 img/product/SERV-0006_en.jpg true
36 413 Technician/Operator Rk 2 SERV-0007 5 12 false 103 service 500 EUR 380 EUR 2 400 false false 1.40 img/product/SERV-0007_en.jpg true
37 414 Sales SERV-0008 5 12 false 103 service 700 EUR 525 EUR 2 560 false false 1.40 img/product/SERV-0008_en.png true
38 415 Manager SERV-0009 5 12 false 103 service 1100 EUR 781 EUR 2 880 false false 1.40 img/product/SERV-0009_en.jpg true
39 416 General Manager SERV-0010 5 12 false 103 service 1500 EUR 1110 EUR 2 1200 false false 1.40 img/product/SERV-0010_en.png true
40 500 Train/metro/tramway tickets NDF-0001 Reimbursement of travel expenses for employees traveling outside the company by either Train, Metro or Subway 6 14 true 1 service 80 EUR 63.2 EUR 3 72 false false 1.40 img/product/EXP-0001_en.jpg
41 501 Plane ticket EXP-0002 Reimbursement of travel expenses for employees traveling outside the company by Plane 6 14 true 1 service 150 EUR 106.5 EUR 3 135 false false 1.40 img/product/EXP-0002_en.jpg
42 502 Taxi/Car Rental EXP-0003 Reimbursement of travel expenses for employees traveling outside the company by Taxi or Car Rental 6 14 true 1 service 1 EUR 0.78 EUR 3 1 false false 1.40
43 503 Mileage expenses EXP-0004 Reimbursement of travel expenses for employees traveling outside the company while using their own vehicle. 6 14 true 1 service 1 EUR 0.76 EUR 3 1 false false 1.40
44 504 Meal EXP-0005 Reimbursement of meal expenses for employees traveling outside the company. 6 14 true 1 service 20 EUR 15 EUR 3 18 false false 1.40 img/product/EXP-0005_en.jpg
45 505 Hotel (or other accomodations) EXP-0006 Reimbursement of accomodation expenses for employees traveling outside the company. 6 14 true 1 service 90 EUR 63.9 EUR 3 81 false false 1.40 img/product/EXP-0006_en.jpg

View File

@ -0,0 +1,4 @@
"importId";"company.importId"
10;1
11;1
12;1
1 importId company.importId
2 10 1
3 11 1
4 12 1

Some files were not shown because too many files have changed in this diff Show More