/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo MES
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.mes.productionCountingWithCosts;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.lowagie.text.DocumentException;
import com.qcadoo.mes.costCalculation.CostCalculationService;
import com.qcadoo.mes.costCalculation.constants.CostCalculationFields;
import com.qcadoo.mes.costCalculation.constants.SourceOfMaterialCosts;
import com.qcadoo.mes.costNormsForMaterials.ProductsCostCalculationService;
import com.qcadoo.mes.costNormsForMaterials.constants.OrderFieldsCNFM;
import com.qcadoo.mes.costNormsForMaterials.orderRawMaterialCosts.OrderMaterialsCostDataGenerator;
import com.qcadoo.mes.costNormsForOperation.constants.CalculationOperationComponentFields;
import com.qcadoo.mes.costNormsForOperation.constants.CostNormsForOperationConstants;
import com.qcadoo.mes.orders.constants.OrderFields;
import com.qcadoo.mes.productionCounting.ProductionBalanceService;
import com.qcadoo.mes.productionCounting.ProductionCountingService;
import com.qcadoo.mes.productionCounting.constants.BalanceOperationProductInComponentFields;
import com.qcadoo.mes.productionCounting.constants.OrderFieldsPC;
import com.qcadoo.mes.productionCounting.constants.ProductionBalanceFields;
import com.qcadoo.mes.productionCounting.constants.ProductionCountingConstants;
import com.qcadoo.mes.productionCounting.constants.ProductionTrackingFields;
import com.qcadoo.mes.productionCountingWithCosts.constants.CalculationOperationComponentFieldsPCWC;
import com.qcadoo.mes.productionCountingWithCosts.constants.OperationCostComponentFields;
import com.qcadoo.mes.productionCountingWithCosts.constants.OperationPieceworkCostComponentFields;
import com.qcadoo.mes.productionCountingWithCosts.constants.ProductionBalanceFieldsPCWC;
import com.qcadoo.mes.productionCountingWithCosts.constants.ProductionCountingWithCostsConstants;
import com.qcadoo.mes.productionCountingWithCosts.constants.TechnologyOperationProductInCompFields;
import com.qcadoo.mes.productionCountingWithCosts.materials.RegisteredMaterialCostHelper;
import com.qcadoo.mes.productionCountingWithCosts.operations.RegisteredProductionCostHelper;
import com.qcadoo.mes.productionCountingWithCosts.pdf.ProductionBalanceWithCostsPdfService;
import com.qcadoo.model.api.BigDecimalUtils;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.NumberService;
import com.qcadoo.model.api.file.FileService;
import com.qcadoo.model.api.search.SearchRestrictions;
import com.qcadoo.plugin.api.PluginUtils;
@Service
public class GenerateProductionBalanceWithCosts implements Observer {
private static final String L_LABOR_COSTS = "laborCosts";
private static final String L_MACHINE_COSTS = "machineCosts";
private static final String L_PLANNED_MACHINE_TIME = "plannedMachineTime";
private static final String L_PLANNED_LABOR_TIME = "plannedLaborTime";
@Autowired
private DataDefinitionService dataDefinitionService;
@Autowired
private NumberService numberService;
@Autowired
private FileService fileService;
@Autowired
private ProductionCountingService productionCountingService;
@Autowired
private ProductionBalanceService productionBalanceService;
@Autowired
private ProductionBalanceWithCostsPdfService productionBalanceWithCostsPdfService;
@Autowired
private RegisteredMaterialCostHelper registeredMaterialCostHelper;
@Autowired
private RegisteredProductionCostHelper registeredProductionCostHelper;
@Autowired
private CostCalculationService costCalculationService;
@Autowired
private ProductsCostCalculationService productsCostCalculationService;
@Autowired
private OrderMaterialsCostDataGenerator orderMaterialsCostDataGenerator;
@Override
public void update(final Observable observable, final Object object) {
if (PluginUtils.isEnabled(ProductionCountingWithCostsConstants.PLUGIN_IDENTIFIER)) {
Entity productionBalance = (Entity) object;
Entity order = productionBalance.getBelongsToField(ProductionBalanceFields.ORDER);
productionBalance.setField(ProductionBalanceFields.ORDER, updateCostsInOrder(order));
doTheCostsPart(productionBalance);
fillFieldsAndGrids(productionBalance);
generateBalanceWithCostsReport(productionBalance);
}
}
private Entity updateCostsInOrder(Entity order) {
List<Entity> orderMaterialsCosts = orderMaterialsCostDataGenerator.generateUpdatedMaterialsListFor(order);
order.setField(OrderFieldsCNFM.TECHNOLOGY_INST_OPER_PRODUCT_IN_COMPS, orderMaterialsCosts);
return order.getDataDefinition().save(order);
}
void generateBalanceWithCostsReport(final Entity productionBalance) {
Locale locale = LocaleContextHolder.getLocale();
String localePrefix = "productionCounting.productionBalanceWithCosts.report.fileName";
Entity productionBalanceWithFileName = fileService.updateReportFileName(productionBalance, ProductionBalanceFields.DATE,
localePrefix);
String localePrefixToMatch = localePrefix;
try {
productionBalanceWithCostsPdfService.generateDocument(productionBalanceWithFileName, locale, localePrefixToMatch);
productionBalanceWithFileName.setField(ProductionBalanceFieldsPCWC.GENERATED_WITH_COSTS, Boolean.TRUE);
productionBalanceWithFileName.getDataDefinition().save(productionBalanceWithFileName);
} catch (IOException e) {
throw new IllegalStateException("Problem with saving productionBalanceWithCosts report", e);
} catch (DocumentException e) {
throw new IllegalStateException("Problem with generating productionBalanceWithCosts report", e);
}
}
public void doTheCostsPart(final Entity productionBalance) {
Entity order = productionBalance.getBelongsToField(ProductionBalanceFields.ORDER);
Entity technology = order.getBelongsToField(OrderFields.TECHNOLOGY);
Entity productionLine = order.getBelongsToField(OrderFields.PRODUCTION_LINE);
productionBalance.setField(ProductionBalanceFields.ORDER, order);
BigDecimal quantity = order.getDecimalField(OrderFields.PLANNED_QUANTITY);
productionBalance.setField(ProductionBalanceFieldsPCWC.QUANTITY, quantity);
productionBalance.setField(ProductionBalanceFieldsPCWC.TECHNOLOGY, technology);
productionBalance.setField(ProductionBalanceFieldsPCWC.PRODUCTION_LINE, productionLine);
// FIXME MAKU beware of side effects - order of below computations matter!
costCalculationService.calculateOperationsAndProductsCosts(productionBalance);
final BigDecimal productionCosts = costCalculationService.calculateProductionCost(productionBalance);
final BigDecimal doneQuantity = order.getDecimalField(OrderFields.DONE_QUANTITY);
costCalculationService.calculateTotalCosts(productionBalance, productionCosts, doneQuantity);
BigDecimal perUnit = BigDecimal.ZERO;
if (!BigDecimalUtils.valueEquals(BigDecimal.ZERO, doneQuantity)) {
BigDecimal totalTechnicalProductionCosts = productionBalance
.getDecimalField(ProductionBalanceFieldsPCWC.TOTAL_TECHNICAL_PRODUCTION_COSTS);
perUnit = totalTechnicalProductionCosts.divide(doneQuantity, numberService.getMathContext());
}
productionBalance.setField(ProductionBalanceFieldsPCWC.TOTAL_TECHNICAL_PRODUCTION_COST_PER_UNIT,
numberService.setScale(perUnit));
}
public void fillFieldsAndGrids(final Entity productionBalance) {
Entity order = productionBalance.getBelongsToField(ProductionBalanceFields.ORDER);
if ((order == null)
|| productionCountingService.isTypeOfProductionRecordingBasic(order
.getStringField(OrderFieldsPC.TYPE_OF_PRODUCTION_RECORDING))) {
return;
}
List<Entity> productionTrackings = productionCountingService.getProductionTrackingsForOrder(order);
Map<Long, Entity> productionTrackingsWithRegisteredTimes = productionBalanceService
.groupProductionTrackingsRegisteredTimes(productionBalance, productionTrackings);
Map<Entity, BigDecimal> productWithCosts = getPlannedProductsWithCosts(productionBalance, order);
fillMaterialValues(productionBalance, productWithCosts);
fillTechnologyOperationProductInComponents(productionBalance, productWithCosts);
if (productionCountingService.isCalculateOperationCostModeHourly(productionBalance
.getStringField(ProductionBalanceFields.CALCULATE_OPERATION_COST_MODE))
&& order.getBooleanField(OrderFieldsPC.REGISTER_PRODUCTION_TIME)) {
Map<Long, Map<String, Integer>> productionTrackingsWithPlannedTimes = productionBalanceService
.fillProductionTrackingsWithPlannedTimes(productionBalance, productionTrackings);
String typeOfProductionRecording = order.getStringField(OrderFieldsPC.TYPE_OF_PRODUCTION_RECORDING);
if (productionCountingService.isTypeOfProductionRecordingForEach(typeOfProductionRecording)) {
fillCostValues(productionBalance, productionTrackingsWithRegisteredTimes, productionTrackingsWithPlannedTimes);
fillOperationCostComponents(productionBalance, productionTrackingsWithRegisteredTimes,
productionTrackingsWithPlannedTimes);
} else if (productionCountingService.isTypeOfProductionRecordingCumulated(typeOfProductionRecording)) {
fillCostValues(productionBalance, productionTrackingsWithRegisteredTimes, productionTrackingsWithPlannedTimes);
}
} else if (productionCountingService.isCalculateOperationCostModePiecework(productionBalance
.getStringField(ProductionBalanceFields.CALCULATE_OPERATION_COST_MODE))
&& order.getBooleanField(OrderFieldsPC.REGISTER_PIECEWORK)) {
fillPieceworkCostValues(productionBalance, productionTrackingsWithRegisteredTimes);
fillOperationPieceworkCostComponents(productionBalance, productionTrackingsWithRegisteredTimes);
}
sumarizeCostValues(productionBalance, order);
}
private void fillMaterialValues(final Entity productionBalance, final Map<Entity, BigDecimal> productWithCosts) {
if (productionBalance == null) {
return;
}
BigDecimal componentsCosts = BigDecimal.ZERO;
for (Entry<Entity, BigDecimal> productWithCost : productWithCosts.entrySet()) {
Entity product = productWithCost.getKey();
Entity balanceOperationProductInComponent = getBalanceOperationProductInComponentFromDB(productionBalance, product);
if (balanceOperationProductInComponent != null) {
BigDecimal registeredQuantity = balanceOperationProductInComponent
.getDecimalField(BalanceOperationProductInComponentFields.USED_QUANTITY);
BigDecimal productRegisteredCost = BigDecimal.ZERO;
if (registeredQuantity != null) {
productRegisteredCost = getRegisteredProductWithCost(
productionBalance,
productsCostCalculationService.getAppropriateCostNormForProduct(product,
productionBalance.getBelongsToField(ProductionBalanceFields.ORDER),
productionBalance.getStringField(ProductionBalanceFieldsPCWC.SOURCE_OF_MATERIAL_COSTS)),
registeredQuantity);
}
componentsCosts = componentsCosts.add(productRegisteredCost, numberService.getMathContext());
}
}
final BigDecimal plannedComponentsCosts = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(CostCalculationFields.TOTAL_MATERIAL_COSTS));
BigDecimal componentsCostsBalance = componentsCosts.subtract(plannedComponentsCosts, numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.PLANNED_COMPONENTS_COSTS,
numberService.setScale(plannedComponentsCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.COMPONENTS_COSTS, numberService.setScale(componentsCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.COMPONENTS_COSTS_BALANCE,
numberService.setScale(componentsCostsBalance));
}
private void fillTechnologyOperationProductInComponents(final Entity productionBalance,
final Map<Entity, BigDecimal> productWithCosts) {
if (productionBalance == null) {
return;
}
List<Entity> technologyOperationProductInComponents = Lists.newArrayList();
for (Entry<Entity, BigDecimal> productWithCost : productWithCosts.entrySet()) {
Entity product = productWithCost.getKey();
BigDecimal productCost = productWithCost.getValue();
Entity balanceOperationProductInComponent = getBalanceOperationProductInComponentFromDB(productionBalance, product);
if (balanceOperationProductInComponent != null) {
BigDecimal registeredQuantity = balanceOperationProductInComponent
.getDecimalField(BalanceOperationProductInComponentFields.USED_QUANTITY);
BigDecimal productRegisteredCost = BigDecimal.ZERO;
if (registeredQuantity != null) {
productRegisteredCost = getRegisteredProductWithCost(
productionBalance,
productsCostCalculationService.getAppropriateCostNormForProduct(product,
productionBalance.getBelongsToField(ProductionBalanceFields.ORDER),
productionBalance.getStringField(ProductionBalanceFieldsPCWC.SOURCE_OF_MATERIAL_COSTS)),
registeredQuantity);
}
BigDecimal balance = productRegisteredCost.subtract(productCost, numberService.getMathContext());
Entity technologyOperationProductInComponent = dataDefinitionService.get(
ProductionCountingWithCostsConstants.PLUGIN_IDENTIFIER,
ProductionCountingWithCostsConstants.MODEL_TECHNOLOGY_OPERATION_PRODUCT_IN_COMPONENT).create();
technologyOperationProductInComponent.setField(TechnologyOperationProductInCompFields.PRODUCT, product);
technologyOperationProductInComponent.setField(TechnologyOperationProductInCompFields.PLANNED_COST,
numberService.setScale(productCost));
technologyOperationProductInComponent.setField(TechnologyOperationProductInCompFields.REGISTERED_COST,
numberService.setScale(productRegisteredCost));
technologyOperationProductInComponent.setField(TechnologyOperationProductInCompFields.BALANCE,
numberService.setScale(balance));
technologyOperationProductInComponents.add(technologyOperationProductInComponent);
}
}
productionBalance.setField(ProductionBalanceFieldsPCWC.TECHNOLOGY_OPERATION_PRODUCT_IN_COMPONENTS,
technologyOperationProductInComponents);
}
private void fillCostValues(final Entity productionBalance, final Map<Long, Entity> productionTrackingsWithRegisteredTimes,
final Map<Long, Map<String, Integer>> productionTrackingsWithPlannedTimes) {
if (productionBalance == null) {
return;
}
Map<String, BigDecimal> costs = Maps.newHashMap();
Entity order = productionBalance.getBelongsToField(ProductionBalanceFields.ORDER);
String typeOfProductionRecording = order.getStringField(OrderFieldsPC.TYPE_OF_PRODUCTION_RECORDING);
if (!productionTrackingsWithPlannedTimes.isEmpty()) {
if (productionCountingService.isTypeOfProductionRecordingForEach(typeOfProductionRecording)) {
costs = costValueForTypeOfProductionRecordingForEach(productionBalance, productionTrackingsWithRegisteredTimes);
} else if (productionCountingService.isTypeOfProductionRecordingCumulated(typeOfProductionRecording)) {
costs = costValueForTypeOfProductionRecordingCumulated(productionBalance, productionTrackingsWithRegisteredTimes);
}
}
final BigDecimal plannedMachineCosts = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(CostCalculationFields.TOTAL_MACHINE_HOURLY_COSTS));
final BigDecimal plannedLaborCosts = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(CostCalculationFields.TOTAL_LABOR_HOURLY_COSTS));
final BigDecimal machineCosts = costs.get(L_MACHINE_COSTS);
final BigDecimal laborCosts = costs.get(L_LABOR_COSTS);
final BigDecimal machineCostsBalance = machineCosts.subtract(plannedMachineCosts, numberService.getMathContext());
final BigDecimal laborCostsBalance = laborCosts.subtract(plannedLaborCosts, numberService.getMathContext());
productionBalance
.setField(ProductionBalanceFieldsPCWC.PLANNED_MACHINE_COSTS, numberService.setScale(plannedMachineCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.MACHINE_COSTS, numberService.setScale(machineCosts));
productionBalance
.setField(ProductionBalanceFieldsPCWC.MACHINE_COSTS_BALANCE, numberService.setScale(machineCostsBalance));
productionBalance.setField(ProductionBalanceFieldsPCWC.PLANNED_LABOR_COSTS, numberService.setScale(plannedLaborCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.LABOR_COSTS, numberService.setScale(laborCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.LABOR_COSTS_BALANCE, numberService.setScale(laborCostsBalance));
}
private Map<String, BigDecimal> costValueForTypeOfProductionRecordingForEach(final Entity productionBalance,
final Map<Long, Entity> productionTrackingsWithRegisteredTimes) {
Map<String, BigDecimal> costsValues = Maps.newHashMap();
BigDecimal machineCosts = BigDecimal.ZERO;
BigDecimal laborCosts = BigDecimal.ZERO;
for (Map.Entry<Long, Entity> productionTrackingsWithRegisteredTimesEntry : productionTrackingsWithRegisteredTimes
.entrySet()) {
Entity productionTracking = productionTrackingsWithRegisteredTimesEntry.getValue();
Entity calculationOperationComponent = getCalculationOperationComponent(productionBalance, productionTracking);
if (calculationOperationComponent != null) {
BigDecimal milisecondsInHour = BigDecimal.valueOf(3600);
BigDecimal machineHourlyCost = BigDecimalUtils.convertNullToZero(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.MACHINE_HOURLY_COST));
Integer machineTime = productionTracking.getIntegerField(ProductionTrackingFields.MACHINE_TIME);
BigDecimal machineTimeHours = BigDecimal.valueOf(machineTime).divide(milisecondsInHour,
numberService.getMathContext());
machineCosts = machineCosts.add(machineHourlyCost.multiply(machineTimeHours, numberService.getMathContext()),
numberService.getMathContext());
BigDecimal laborHourlyCost = BigDecimalUtils.convertNullToZero(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.LABOR_HOURLY_COST));
Integer laborTime = productionTracking.getIntegerField(ProductionTrackingFields.LABOR_TIME);
BigDecimal laborTimeHours = BigDecimal.valueOf(laborTime).divide(milisecondsInHour,
numberService.getMathContext());
laborCosts = laborCosts.add(laborHourlyCost.multiply(laborTimeHours, numberService.getMathContext()),
numberService.getMathContext());
}
}
costsValues.put(L_MACHINE_COSTS, machineCosts);
costsValues.put(L_LABOR_COSTS, laborCosts);
return costsValues;
}
private Map<String, BigDecimal> costValueForTypeOfProductionRecordingCumulated(final Entity productionBalance,
final Map<Long, Entity> productionTrackingsWithRegisteredTimes) {
Map<String, BigDecimal> costsValues = Maps.newHashMap();
BigDecimal machineCosts = BigDecimal.ZERO;
BigDecimal laborCosts = BigDecimal.ZERO;
for (Map.Entry<Long, Entity> productionTrackingWithRegisteredTimes : productionTrackingsWithRegisteredTimes.entrySet()) {
Entity productionTracking = productionTrackingWithRegisteredTimes.getValue();
BigDecimal milisecondsInHour = BigDecimal.valueOf(3600);
BigDecimal averageMachineHourlyCost = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(ProductionBalanceFieldsPCWC.AVERAGE_MACHINE_HOURLY_COST));
Integer machineTime = productionTracking.getIntegerField(ProductionTrackingFields.MACHINE_TIME);
BigDecimal machineTimeHours = BigDecimal.valueOf(machineTime).divide(milisecondsInHour,
numberService.getMathContext());
machineCosts = machineCosts.add(averageMachineHourlyCost.multiply(machineTimeHours, numberService.getMathContext()),
numberService.getMathContext());
BigDecimal averageLaborHourlyCost = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(ProductionBalanceFieldsPCWC.AVERAGE_LABOR_HOURLY_COST));
Integer laborTime = productionTracking.getIntegerField(ProductionTrackingFields.LABOR_TIME);
BigDecimal laborTimeHours = BigDecimal.valueOf(laborTime).divide(milisecondsInHour, numberService.getMathContext());
laborCosts = laborCosts.add(averageLaborHourlyCost.multiply(laborTimeHours, numberService.getMathContext()),
numberService.getMathContext());
}
costsValues.put(L_MACHINE_COSTS, machineCosts);
costsValues.put(L_LABOR_COSTS, laborCosts);
return costsValues;
}
private void fillOperationCostComponents(final Entity productionBalance,
final Map<Long, Entity> productionTrackingsWithRegisteredTimes,
final Map<Long, Map<String, Integer>> productionTrackingsWithPlannedTimes) {
if (productionBalance == null) {
return;
}
List<Entity> operationCostComponents = Lists.newArrayList();
if (!productionTrackingsWithPlannedTimes.isEmpty()) {
for (Map.Entry<Long, Entity> productionTrackingWithRegisteredTimes : productionTrackingsWithRegisteredTimes
.entrySet()) {
Long technologyOperationComponentId = productionTrackingWithRegisteredTimes.getKey();
Entity productionTracking = productionTrackingWithRegisteredTimes.getValue();
Entity calculationOperationComponent = getCalculationOperationComponent(productionBalance, productionTracking);
if (calculationOperationComponent != null) {
BigDecimal milisecondsInHour = BigDecimal.valueOf(3600);
BigDecimal machineHourlyCost = BigDecimalUtils.convertNullToZero(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.MACHINE_HOURLY_COST));
Integer plannedMachineTime = productionTrackingsWithPlannedTimes.get(technologyOperationComponentId).get(
L_PLANNED_MACHINE_TIME);
BigDecimal plannedMachineTimeHours = BigDecimal.valueOf(plannedMachineTime).divide(milisecondsInHour,
numberService.getMathContext());
BigDecimal plannedMachineCosts = machineHourlyCost.multiply(plannedMachineTimeHours,
numberService.getMathContext());
Integer machineTime = productionTracking.getIntegerField(ProductionTrackingFields.MACHINE_TIME);
BigDecimal machineTimeHours = BigDecimal.valueOf(machineTime).divide(milisecondsInHour,
numberService.getMathContext());
BigDecimal machineCosts = machineHourlyCost.multiply(machineTimeHours, numberService.getMathContext());
BigDecimal machineCostsBalance = machineCosts.subtract(plannedMachineCosts, numberService.getMathContext());
BigDecimal laborHourlyCost = BigDecimalUtils.convertNullToZero(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.LABOR_HOURLY_COST));
Integer plannedLaborTime = productionTrackingsWithPlannedTimes.get(technologyOperationComponentId).get(
L_PLANNED_LABOR_TIME);
BigDecimal plannedLaborTimeHours = BigDecimal.valueOf(plannedLaborTime).divide(milisecondsInHour,
numberService.getMathContext());
BigDecimal plannedLaborCosts = laborHourlyCost
.multiply(plannedLaborTimeHours, numberService.getMathContext());
Integer laborTime = productionTracking.getIntegerField(ProductionTrackingFields.LABOR_TIME);
BigDecimal laborTimeHours = BigDecimal.valueOf(laborTime).divide(milisecondsInHour,
numberService.getMathContext());
BigDecimal laborCosts = laborHourlyCost.multiply(laborTimeHours, numberService.getMathContext());
BigDecimal laborCostsBalance = laborCosts.subtract(plannedLaborCosts, numberService.getMathContext());
Entity operationCostComponent = dataDefinitionService.get(
ProductionCountingWithCostsConstants.PLUGIN_IDENTIFIER,
ProductionCountingWithCostsConstants.MODEL_OPERATION_COST_COMPONENT).create();
operationCostComponent.setField(OperationCostComponentFields.TECHNOLOGY_OPERATION_COMPONENT,
productionTracking.getBelongsToField(ProductionTrackingFields.TECHNOLOGY_OPERATION_COMPONENT));
operationCostComponent.setField(OperationCostComponentFields.PLANNED_MACHINE_COSTS,
numberService.setScale(plannedMachineCosts));
operationCostComponent.setField(OperationCostComponentFields.MACHINE_COSTS,
numberService.setScale(machineCosts));
operationCostComponent.setField(OperationCostComponentFields.MACHINE_COSTS_BALANCE,
numberService.setScale(machineCostsBalance));
operationCostComponent.setField(OperationCostComponentFields.PLANNED_LABOR_COSTS,
numberService.setScale(plannedLaborCosts));
operationCostComponent.setField(OperationCostComponentFields.LABOR_COSTS, numberService.setScale(laborCosts));
operationCostComponent.setField(OperationCostComponentFields.LABOR_COSTS_BALANCE,
numberService.setScale(laborCostsBalance));
operationCostComponents.add(operationCostComponent);
}
}
}
productionBalance.setField(ProductionBalanceFieldsPCWC.OPERATION_COST_COMPONENTS, operationCostComponents);
}
private void fillPieceworkCostValues(final Entity productionBalance,
final Map<Long, Entity> productionTrackingsWithRegisteredTimes) {
if (productionBalance == null) {
return;
}
BigDecimal cyclesCosts = BigDecimal.ZERO;
for (Map.Entry<Long, Entity> productionTrackingWithRegisteredTimes : productionTrackingsWithRegisteredTimes.entrySet()) {
Entity productionTracking = productionTrackingWithRegisteredTimes.getValue();
Entity calculationOperationComponent = getCalculationOperationComponent(productionBalance, productionTracking);
if (calculationOperationComponent != null) {
final BigDecimal pieces = BigDecimalUtils.convertNullToOne(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.PIECES));
final BigDecimal cost = BigDecimalUtils.convertNullToZero(
calculationOperationComponent.getDecimalField(CalculationOperationComponentFields.OPERATION_COST))
.divide(pieces, numberService.getMathContext());
if (productionTracking.getField(ProductionTrackingFields.EXECUTED_OPERATION_CYCLES) != null) {
cyclesCosts = cyclesCosts.add(cost.multiply(
productionTracking.getDecimalField(ProductionTrackingFields.EXECUTED_OPERATION_CYCLES),
numberService.getMathContext()), numberService.getMathContext());
}
}
}
final BigDecimal plannedCyclesCosts = BigDecimalUtils.convertNullToZero(productionBalance
.getDecimalField(CostCalculationFields.TOTAL_PIECEWORK_COSTS));
final BigDecimal cyclesCostsBalance = cyclesCosts.subtract(plannedCyclesCosts, numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.PLANNED_CYCLES_COSTS, numberService.setScale(plannedCyclesCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.CYCLES_COSTS, numberService.setScale(cyclesCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.CYCLES_COSTS_BALANCE, numberService.setScale(cyclesCostsBalance));
}
private void fillOperationPieceworkCostComponents(final Entity productionBalance,
final Map<Long, Entity> productionTrackingsWithRegisteredTimes) {
if (productionBalance == null) {
return;
}
List<Entity> operationPieceworkCostComponents = Lists.newArrayList();
for (Map.Entry<Long, Entity> productionTrackingWithRegisteredTimes : productionTrackingsWithRegisteredTimes.entrySet()) {
Entity productionTracking = productionTrackingWithRegisteredTimes.getValue();
Entity calculationOperationComponent = getCalculationOperationComponent(productionBalance, productionTracking);
if (calculationOperationComponent != null) {
final BigDecimal plannedCyclesCosts = BigDecimalUtils.convertNullToZero(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.OPERATION_COST));
final BigDecimal pieces = BigDecimalUtils.convertNullToOne(calculationOperationComponent
.getDecimalField(CalculationOperationComponentFields.PIECES));
final BigDecimal cost = BigDecimalUtils.convertNullToZero(
calculationOperationComponent.getDecimalField(CalculationOperationComponentFields.OPERATION_COST))
.divide(pieces, numberService.getMathContext());
BigDecimal cyclesCosts = BigDecimal.ZERO;
if (productionTracking.getField(ProductionTrackingFields.EXECUTED_OPERATION_CYCLES) != null) {
cyclesCosts = cost.multiply(
productionTracking.getDecimalField(ProductionTrackingFields.EXECUTED_OPERATION_CYCLES),
numberService.getMathContext());
}
BigDecimal cyclesCostsBalance = cyclesCosts.subtract(plannedCyclesCosts, numberService.getMathContext());
Entity operationPieceworkCostComponent = dataDefinitionService.get(
ProductionCountingWithCostsConstants.PLUGIN_IDENTIFIER,
ProductionCountingWithCostsConstants.MODEL_OPERATION_PIECEWORK_COST_COMPONENT).create();
operationPieceworkCostComponent.setField(OperationPieceworkCostComponentFields.TECHNOLOGY_OPERATION_COMPONENT,
productionTracking.getBelongsToField(ProductionTrackingFields.TECHNOLOGY_OPERATION_COMPONENT));
operationPieceworkCostComponent.setField(OperationPieceworkCostComponentFields.PLANNED_CYCLES_COSTS,
numberService.setScale(plannedCyclesCosts));
operationPieceworkCostComponent.setField(OperationPieceworkCostComponentFields.CYCLES_COSTS,
numberService.setScale(cyclesCosts));
operationPieceworkCostComponent.setField(OperationPieceworkCostComponentFields.CYCLES_COSTS_BALANCE,
numberService.setScale(cyclesCostsBalance));
operationPieceworkCostComponents.add(operationPieceworkCostComponent);
}
}
productionBalance.setField(ProductionBalanceFieldsPCWC.OPERATION_PIECEWORK_COST_COMPONENTS,
operationPieceworkCostComponents);
}
private void sumarizeCostValues(final Entity productionBalance, final Entity order) {
if ((productionBalance == null) || (order == null)) {
return;
}
BigDecimal registeredTotalTechnicalProductionCosts = BigDecimal.ZERO;
registeredTotalTechnicalProductionCosts = registeredTotalTechnicalProductionCosts.add(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.COMPONENTS_COSTS), numberService.getMathContext());
if (productionCountingService.isCalculateOperationCostModeHourly(productionBalance
.getStringField(ProductionBalanceFields.CALCULATE_OPERATION_COST_MODE))
&& order.getBooleanField(OrderFieldsPC.REGISTER_PRODUCTION_TIME)) {
registeredTotalTechnicalProductionCosts = registeredTotalTechnicalProductionCosts.add(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.MACHINE_COSTS), numberService.getMathContext());
registeredTotalTechnicalProductionCosts = registeredTotalTechnicalProductionCosts.add(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.LABOR_COSTS), numberService.getMathContext());
} else if (productionCountingService.isCalculateOperationCostModePiecework(productionBalance
.getStringField(ProductionBalanceFields.CALCULATE_OPERATION_COST_MODE))
&& order.getBooleanField(OrderFieldsPC.REGISTER_PIECEWORK)) {
registeredTotalTechnicalProductionCosts = registeredTotalTechnicalProductionCosts.add(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.CYCLES_COSTS), numberService.getMathContext());
}
BigDecimal balanceTechnicalProductionCosts = registeredTotalTechnicalProductionCosts.subtract(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.TOTAL_TECHNICAL_PRODUCTION_COSTS),
numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.REGISTERED_TOTAL_TECHNICAL_PRODUCTION_COSTS,
numberService.setScale(registeredTotalTechnicalProductionCosts));
productionBalance.setField(ProductionBalanceFieldsPCWC.BALANCE_TECHNICAL_PRODUCTION_COSTS,
numberService.setScale(balanceTechnicalProductionCosts));
registeredMaterialCostHelper.countRegisteredMaterialMarginValue(productionBalance);
registeredProductionCostHelper.countRegisteredProductionMarginValue(productionBalance);
costCalculationService.calculateTotalOverhead(productionBalance);
BigDecimal totalCosts = registeredTotalTechnicalProductionCosts.add(
BigDecimalUtils.convertNullToZero(productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.TOTAL_OVERHEAD)),
numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.TOTAL_COSTS, numberService.setScale(totalCosts));
final BigDecimal doneQuantity = order.getDecimalField(OrderFields.DONE_QUANTITY);
if (doneQuantity != null && BigDecimal.ZERO.compareTo(doneQuantity) != 0) {
final BigDecimal totalCostPerUnit = totalCosts.divide(doneQuantity, numberService.getMathContext());
final BigDecimal registeredTotalTechnicalProductionCostPerUnit = registeredTotalTechnicalProductionCosts.divide(
doneQuantity, numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.TOTAL_COST_PER_UNIT, numberService.setScale(totalCostPerUnit));
productionBalance.setField(ProductionBalanceFieldsPCWC.REGISTERED_TOTAL_TECHNICAL_PRODUCTION_COST_PER_UNIT,
numberService.setScale(registeredTotalTechnicalProductionCostPerUnit));
final BigDecimal balanceTechnicalProductionCostPerUnit = registeredTotalTechnicalProductionCostPerUnit.subtract(
productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.TOTAL_TECHNICAL_PRODUCTION_COST_PER_UNIT),
numberService.getMathContext());
productionBalance.setField(ProductionBalanceFieldsPCWC.BALANCE_TECHNICAL_PRODUCTION_COST_PER_UNIT,
numberService.setScale(balanceTechnicalProductionCostPerUnit));
} else {
productionBalance.setField(ProductionBalanceFieldsPCWC.TOTAL_COST_PER_UNIT, null);
productionBalance.setField(ProductionBalanceFieldsPCWC.REGISTERED_TOTAL_TECHNICAL_PRODUCTION_COST_PER_UNIT, null);
productionBalance.setField(ProductionBalanceFieldsPCWC.BALANCE_TECHNICAL_PRODUCTION_COST_PER_UNIT, null);
}
}
private Map<Entity, BigDecimal> getPlannedProductsWithCosts(final Entity productionBalance, final Entity order) {
BigDecimal givenQty = productionBalance.getDecimalField(ProductionBalanceFieldsPCWC.QUANTITY);
String sourceOfMaterialCosts = productionBalance.getStringField(ProductionBalanceFieldsPCWC.SOURCE_OF_MATERIAL_COSTS);
String calculateMaterialCostsMode = productionBalance
.getStringField(ProductionBalanceFieldsPCWC.CALCULATE_MATERIAL_COSTS_MODE);
if (SourceOfMaterialCosts.FROM_ORDERS_MATERIAL_COSTS.getStringValue().equals(sourceOfMaterialCosts)) {
return productsCostCalculationService.getProductWithCostForPlannedQuantities(
productionBalance.getBelongsToField(ProductionBalanceFieldsPCWC.TECHNOLOGY), givenQty,
calculateMaterialCostsMode, order);
} else if (SourceOfMaterialCosts.CURRENT_GLOBAL_DEFINITIONS_IN_PRODUCT.getStringValue().equals(sourceOfMaterialCosts)) {
return productsCostCalculationService.getProductWithCostForPlannedQuantities(
productionBalance.getBelongsToField(ProductionBalanceFieldsPCWC.TECHNOLOGY), givenQty,
calculateMaterialCostsMode);
}
return Maps.newHashMap();
}
private BigDecimal getRegisteredProductWithCost(final Entity productionBalance, final Entity product,
final BigDecimal registeredQuantity) {
String calculateMaterialCostsMode = productionBalance
.getStringField(ProductionBalanceFieldsPCWC.CALCULATE_MATERIAL_COSTS_MODE);
return productsCostCalculationService.calculateProductCostForGivenQuantity(product, registeredQuantity,
calculateMaterialCostsMode);
}
private Entity getBalanceOperationProductInComponentFromDB(final Entity productionBalance, final Entity product) {
return dataDefinitionService
.get(ProductionCountingConstants.PLUGIN_IDENTIFIER,
ProductionCountingConstants.MODEL_BALANCE_OPERATION_PRODUCT_IN_COMPONENT)
.find()
.add(SearchRestrictions.belongsTo(BalanceOperationProductInComponentFields.PRODUCTION_BALANCE, productionBalance))
.add(SearchRestrictions.belongsTo(BalanceOperationProductInComponentFields.PRODUCT, product)).setMaxResults(1)
.uniqueResult();
}
private Entity getCalculationOperationComponent(final Entity productionBalance, final Entity productionTracking) {
Entity technologyOperationComponent = productionTracking
.getBelongsToField(ProductionTrackingFields.TECHNOLOGY_OPERATION_COMPONENT);
if (technologyOperationComponent == null) {
return null;
} else {
return dataDefinitionService
.get(CostNormsForOperationConstants.PLUGIN_IDENTIFIER,
CostNormsForOperationConstants.MODEL_CALCULATION_OPERATION_COMPONENT)
.find()
.add(SearchRestrictions.belongsTo(CalculationOperationComponentFieldsPCWC.PRODUCTION_BALANCE,
productionBalance))
.add(SearchRestrictions.belongsTo(CalculationOperationComponentFields.TECHNOLOGY_OPERATION_COMPONENT,
technologyOperationComponent)).setMaxResults(1).uniqueResult();
}
}
}