Skip to content

Commit e0823de

Browse files
authored
OFBIZ-13248 Calculate labor cost when production run is completed (#1026)
OFBIZ-13248 Calculate labor cost when production run is completed Implemented the automatic calculation and recording of labor costs when a production run is completed. This is achieved by creating TimeEntry records associated with the production run task, using a specific labor rate type. **Changes made:** **Data (Seed/Demo):** - Added FLC (Factory Labor Cost) RateType in ManufacturingSeedData.xml. - Added sample PartyRate data in ManufacturingDemoData.xml to support testing of labor cost calculations. **UI Labels:** - Introduced new UI labels in ManufacturingUiLabels.xml for labor cost visibility. **Service Logic (ProductionRunServices.java):** - Enhanced the production run completion flow to calculate labor hours from the reported task time. - Automatically invokes the createTimeEntry service with rateTypeId="FLC" to record the labor cost for the work effort. Thanks Pierre Smits and Jacopo for design details on ticket.
1 parent 87adcf4 commit e0823de

4 files changed

Lines changed: 84 additions & 0 deletions

File tree

applications/datamodel/data/demo/ManufacturingDemoData.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,7 @@ under the License.
6161

6262
<FixedAssetDepMethod depreciationCustomMethodId="STR_LINE_DEP_FORMULA" fixedAssetId="DEMO_PROD_EQUIPMT_1"/>
6363
<FixedAssetDepMethod depreciationCustomMethodId="DBL_DECL_DEP_FORMULA" fixedAssetId="DEMO_PROD_EQUIPMT_2"/>
64+
65+
<!-- Labor Cost Data -->
66+
<RateAmount rateTypeId="FLC" rateCurrencyUomId="USD" periodTypeId="RATE_HOUR" workEffortId="_NA_" partyId="_NA_" emplPositionTypeId="_NA_" fromDate="2000-01-01 00:00:00.000" rateAmount="100.00"/>
6467
</entity-engine-xml>

applications/datamodel/data/seed/ManufacturingSeedData.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ under the License.
5252
<Enumeration description="Predecessor" enumCode="PREDECESSOR" enumId="WF_PREDECESSOR" enumTypeId="WORKFLOW" sequenceId="1"/>
5353
<Enumeration description="Successor" enumCode="SUCCESSOR" enumId="WF_SUCCESSOR" enumTypeId="WORKFLOW" sequenceId="2"/>
5454

55+
<!-- Rate Type for Labor Cost Data -->
56+
<RateType rateTypeId="FLC" description="Fully Loaded Cost (price for WorkEffort)"/>
57+
58+
5559
</entity-engine-xml>

applications/manufacturing/config/ManufacturingUiLabels.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4265,6 +4265,14 @@
42654265
<value xml:lang="zh">尝试从产品关联(productAssoc)获得物料清单时出错</value>
42664266
<value xml:lang="zh-TW">嘗試從產品結合(productAssoc)獲得物料清單時出錯</value>
42674267
</property>
4268+
<property key="ManufacturingProductionRunUnableToCreateLaborCosts">
4269+
<value xml:lang="en">Unable to create labor costs for the production run task ${productionRunTaskId}: ${errorString}</value>
4270+
<value xml:lang="it">Impossibile creare i costi di manodopera per l'attività di esecuzione dell'ordine di produzione ${productionRunTaskId}: ${errorString}</value>
4271+
<value xml:lang="ja">生産実行タスク ${productionRunTaskId} に対する作業人件費を作成できませんでした:${errorString}</value>
4272+
<value xml:lang="vi">Không thể tạo chi phí nhân công cho tác vụ thực hiện lệnh sản xuất ${productionRunTaskId}: ${errorString}</value>
4273+
<value xml:lang="zh">无法为生产执行任务 ${productionRunTaskId} 创建人工成本:${errorString}</value>
4274+
<value xml:lang="zh-TW">無法為生產執行任務 ${productionRunTaskId} 建立人工成本:${errorString}</value>
4275+
</property>
42684276
<property key="ManufacturingProductionRunUnableToCreateMaterialsCosts">
42694277
<value xml:lang="en">Unable to create materials costs for the production run task ${productionRunTaskId}: ${errorString}</value>
42704278
<value xml:lang="it">Non è possibile create i costi dei materiali per il compito dell'ordine di produzione ${productionRunTaskId}: ${errorString}</value>

applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunServices.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,57 @@ public static Map<String, Object> createProductionRunTaskCosts(DispatchContext c
13241324
return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingProductionRunUnableToCreateMaterialsCosts",
13251325
UtilMisc.toMap("productionRunTaskId", productionRunTaskId, "errorString", ge.getMessage()), locale));
13261326
}
1327+
1328+
// labor costs: these are the costs derived from the actual time logged on the production run task decleration
1329+
try {
1330+
List<GenericValue> timeEntries = EntityQuery.use(delegator).from("TimeEntry")
1331+
.where("workEffortId", productionRunTaskId)
1332+
.queryList();
1333+
for (GenericValue timeEntry : timeEntries) {
1334+
BigDecimal hours = timeEntry.getBigDecimal("hours");
1335+
if (UtilValidate.isNotEmpty(hours)) {
1336+
// Try to find specific rate for the party with FLC type
1337+
GenericValue rateAmount = EntityQuery.use(delegator).from("RateAmount")
1338+
.where("partyId", timeEntry.getString("partyId"), "rateTypeId", "FLC")
1339+
.filterByDate(timeEntry.getTimestamp("fromDate"))
1340+
.queryFirst();
1341+
1342+
// Fallback to generic rate if specific rate not found
1343+
if (UtilValidate.isEmpty(rateAmount)) {
1344+
rateAmount = EntityQuery.use(delegator).from("RateAmount")
1345+
.where("partyId", "_NA_", "rateTypeId", "FLC")
1346+
.filterByDate(timeEntry.getTimestamp("fromDate"))
1347+
.queryFirst();
1348+
}
1349+
1350+
if (UtilValidate.isNotEmpty(rateAmount)) {
1351+
BigDecimal rate = rateAmount.getBigDecimal("rateAmount");
1352+
if (UtilValidate.isNotEmpty(rate)) {
1353+
BigDecimal laborCost = rate.multiply(hours).setScale(DECIMALS, ROUNDING);
1354+
Map<String, Object> inMap = UtilMisc.<String, Object>toMap("userLogin", userLogin,
1355+
"workEffortId", productionRunTaskId);
1356+
inMap.put("costComponentTypeId", "ACTUAL_LABOR_COST");
1357+
inMap.put("costUomId", rateAmount.getString("rateCurrencyUomId"));
1358+
inMap.put("cost", laborCost);
1359+
serviceResult = dispatcher.runSync("createCostComponent", inMap);
1360+
if (ServiceUtil.isError(serviceResult)) {
1361+
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
1362+
}
1363+
}
1364+
}
1365+
}
1366+
}
1367+
} catch (GenericEntityException ge) {
1368+
return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE,
1369+
"ManufacturingProductionRunUnableToCreateLaborCosts",
1370+
UtilMisc.toMap("productionRunTaskId", productionRunTaskId, "errorString", ge.getMessage()),
1371+
locale));
1372+
} catch (Exception e) {
1373+
return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE,
1374+
"ManufacturingProductionRunUnableToCreateLaborCosts",
1375+
UtilMisc.toMap("productionRunTaskId", productionRunTaskId, "errorString", e.getMessage()),
1376+
locale));
1377+
}
13271378
return ServiceUtil.returnSuccess();
13281379
}
13291380

@@ -2433,6 +2484,24 @@ public static Map<String, Object> updateProductionRunTask(DispatchContext ctx, M
24332484
}
24342485
}
24352486

2487+
// Make TimeEntry records for the labor cost
2488+
try {
2489+
if (UtilValidate.isNotEmpty(addTaskTime) && addTaskTime.compareTo(BigDecimal.ZERO) > 0) {
2490+
Map<String, Object> serviceContext = UtilMisc.toMap("workEffortId", workEffortId,
2491+
"rateTypeId", "FLC",
2492+
"partyId", partyId,
2493+
"userLogin", userLogin);
2494+
serviceContext.put("hours", addTaskTime.divide(new BigDecimal(3600000), 6, ROUNDING).doubleValue());
2495+
serviceContext.put("comments", "Task time added via Declaration");
2496+
Map<String, Object> serviceResult = dispatcher.runSync("createTimeEntry", serviceContext);
2497+
if (ServiceUtil.isError(serviceResult)) {
2498+
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
2499+
}
2500+
}
2501+
} catch (GenericServiceException exc) {
2502+
return ServiceUtil.returnError(exc.getMessage());
2503+
}
2504+
24362505
// Create a new TimeEntry
24372506
try {
24382507
Map<String, Object> serviceContext = new HashMap<>();

0 commit comments

Comments
 (0)