Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions applications/datamodel/entitydef/workeffort-entitymodel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -890,4 +890,48 @@ under the License.
<key-map field-name="surveyId" rel-field-name="productStoreSurveyId"/>
</relation>
</entity>

<entity entity-name="WorkEffortInvRes"
package-name="org.apache.ofbiz.workeffort.workeffort"
title="Inventory Reservation for WorkEffort">
<field name="workEffortId" type="id"/>
<field name="inventoryItemId" type="id"/>
<field name="wegsProductId" type="id"/>
<field name="reserveOrderEnumId" type="id"/>
<field name="quantity" type="fixed-point"/>
<field name="quantityNotAvailable" type="fixed-point"/>
<field name="reservedDatetime" type="date-time"/>
<field name="createdDatetime" type="date-time"/>
<field name="promisedDatetime" type="date-time"/>
<field name="currentPromisedDate" type="date-time"/>
<field name="sequenceId" type="id"/>
<prim-key field="workEffortId"/>
<prim-key field="inventoryItemId"/>
<prim-key field="wegsProductId"/>
<relation type="one" fk-name="WEIR_WE" rel-entity-name="WorkEffort">
<key-map field-name="workEffortId"/>
</relation>
<relation type="one" fk-name="WEIR_INV" rel-entity-name="InventoryItem">
<key-map field-name="inventoryItemId"/>
</relation>
<relation type="one" fk-name="WEIR_WEGS_PROD" rel-entity-name="Product">
<key-map field-name="wegsProductId" rel-field-name="productId"/>
</relation>
</entity>
<view-entity entity-name="WorkEffortInvResAndItem"
package-name="org.apache.ofbiz.workeffort.workeffort"
title="Inventory Item and Inventory Reservation For Work Effort">
<description>Inventory Item and Inventory Reservation For Work Effort</description>
<member-entity entity-alias="WEIR" entity-name="WorkEffortInvRes"/>
<member-entity entity-alias="II" entity-name="InventoryItem"/>
<alias-all entity-alias="II"/>
<alias entity-alias="WEIR" name="workEffortId"/>
<alias entity-alias="WEIR" name="inventoryItemId"/>
<alias entity-alias="WEIR" name="quantity"/>
<alias entity-alias="WEIR" name="quantityNotAvailable"/>
<alias entity-alias="WEIR" name="wegsProductId"/>
<view-link entity-alias="WEIR" rel-entity-alias="II">
<key-map field-name="inventoryItemId"/>
</view-link>
</view-entity>
</entitymodel>
48 changes: 48 additions & 0 deletions applications/manufacturing/servicedef/services_production_run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,52 @@ under the License.
<description>Compute the actual costs for the production run task.</description>
<attribute mode="IN" name="productionRunTaskId" optional="false" type="String"/>
</service>
<service name="autoReserveWorkEffortInventory" engine="groovy"
location="component://manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunResServices.groovy"
invoke="autoReserveWorkEffortInventory" auth="true">
<description>Reserve Inventory for Production Run</description>
<attribute name="productionRunId" type="String" mode="IN" optional="false"/>
</service>
<service name="reserveWorkEffortInventoryItem" engine="groovy"
location="component://manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunResServices.groovy"
invoke="reserveWorkEffortInventoryItem" auth="true">
<description>Reserve Inventory for Production Run Task</description>
<attribute name="workEffortId" type="String" mode="IN" optional="false"/>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="facilityId" type="String" mode="IN" optional="false"/>
<attribute name="inventoryItemId" type="String" mode="IN" optional="true"/>
<attribute name="quantity" type="BigDecimal" mode="IN" optional="true"/>
</service>
<service name="releaseProductionRunTaskComponent" engine="groovy"
location="component://manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunResServices.groovy"
invoke="releaseProductionRunTaskComponent" auth="true">
<description>Reserve Inventory for Production Run Task</description>
<attribute name="workEffortId" type="String" mode="IN" optional="false"/>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="facilityId" type="String" mode="IN" optional="false"/>
<attribute name="wegsProductId" type="String" mode="IN" optional="true"/>
<attribute name="inventoryItemId" type="String" mode="IN" optional="true"/>
<attribute name="quantity" type="BigDecimal" mode="IN" optional="true"/>
</service>

<service name="reserveWorkEffortInventory" engine="groovy"
location="component://manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunResServices.groovy"
invoke="reserveWorkEffortInventory" auth="true">
<description>Create WorkEffortInvRes or increment existing reserved quantity.</description>
<auto-attributes mode="IN" entity-name="WorkEffortInvRes" include="pk" optional="false"/>
<auto-attributes mode="IN" entity-name="WorkEffortInvRes" include="nonpk" optional="true">
<exclude field-name="createdDate"/>
</auto-attributes>
<override name="quantity" optional="false"/>
</service>
<service name="getTaskQuantities" engine="groovy"
location="component://manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunResServices.groovy"
invoke="getTaskQuantities" auth="true">
<description>Get the total reserved and issued quantity for the component.</description>
<attribute name="workEffortId" mode="IN" type="String"/>
<attribute name="productId" mode="IN" type="String"/>
<attribute name="wegsProductId" mode="IN" type="String"/>
<attribute name="totalReserved" mode="OUT" type="BigDecimal"/>
<attribute name="totalIssued" mode="OUT" type="BigDecimal"/>
</service>
</services>
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import org.apache.ofbiz.base.util.UtilDateTime
import org.apache.ofbiz.entity.GenericValue
import org.apache.ofbiz.entity.condition.EntityCondition
import org.apache.ofbiz.entity.condition.EntityConditionBuilder
import org.apache.ofbiz.entity.util.EntityUtil

import java.sql.Timestamp

Map autoReserveWorkEffortInventory() {
String productionRunId = parameters.productionRunId

GenericValue productionRun = from('WorkEffort')
.where(workEffortId: productionRunId)
.queryOne()

if (!productionRun) {
return error("No production run found with ID: ${productionRunId}")
}

List<GenericValue> taskList = from('WorkEffort')
.where(workEffortParentId: productionRunId)
.queryList()

List<String> workEffortIds = EntityUtil.getFieldListFromEntityList(taskList, 'workEffortId', true)

EntityConditionBuilder exprBldr = new EntityConditionBuilder()
EntityCondition condition = exprBldr.AND {
IN(workEffortId: workEffortIds)
EQUALS(workEffortGoodStdTypeId: 'PRUNT_PROD_NEEDED')
}

List<GenericValue> components = from('WorkEffortGoodStandard')
.where(condition)
.orderBy('productId')
.filterByDate()
.queryList()

String facilityId = productionRun?.facilityId

// Reserve inventory for task components
components.each { component ->
Map<String, Object> serviceCtx = [:]
serviceCtx.workEffortId = component?.workEffortId
serviceCtx.productId = component?.productId
serviceCtx.facilityId = facilityId
run service: 'reserveWorkEffortInventoryItem', with: serviceCtx
}

return success("Inventory reservation completed for production run ${productionRunId}")
}

Map reserveWorkEffortInventoryItem() {
String inventoryItemId = parameters.inventoryItemId
String productId = parameters.productId
String facilityId = parameters.facilityId
String workEffortId = parameters.workEffortId
BigDecimal quantity = BigDecimal.ZERO

if (parameters.quantity) {
quantity = (BigDecimal) parameters.quantity
}

GenericValue component = from('WorkEffortGoodStandard')
.where(workEffortId: workEffortId,
workEffortGoodStdTypeId: 'PRUNT_PROD_NEEDED',
productId: productId)
.filterByDate()
.queryFirst()

if (!component) {
return error("Component not found for productId: ${productId} and workEffortId: ${workEffortId}")
}

Map<String, Object> taskQtyResult = run service: 'getTaskQuantities', with: [productId: productId,
workEffortId: workEffortId,
wegsProductId: productId]
BigDecimal totalReserved = taskQtyResult.totalReserved ?: BigDecimal.ZERO
BigDecimal totalIssued = taskQtyResult.totalIssued ?: BigDecimal.ZERO

BigDecimal qtyToBeReserved = component.estimatedQuantity - totalReserved - totalIssued

if (qtyToBeReserved > 0) {
Map<String, Object> avail = run service: 'getProductInventoryAvailable', with: [productId: productId,
facilityId: facilityId]
BigDecimal atp = avail.availableToPromiseTotal ?: BigDecimal.ZERO

if (qtyToBeReserved > 0) {
if (quantity > 0) {
if (quantity > qtyToBeReserved) {
quantity = qtyToBeReserved
}
quantity = (atp > 0 && atp <= quantity) ? atp : quantity
} else {
quantity = (atp > 0 && atp <= qtyToBeReserved) ? atp : qtyToBeReserved
}

Map<String, Object> reserveInventoryCtx = [productId: component.productId,
workEffortId: workEffortId,
requireInventory: 'N',
facilityId: facilityId,
wegsProductId: productId,
inventoryItemId: inventoryItemId,
quantity: quantity]

if (parameters.inventoryItemId) {
reserveInventoryCtx.inventoryItemId = parameters.inventoryItemId
}

run service: 'reserveProductInventoryByFacility', with: reserveInventoryCtx

qtyToBeReserved -= quantity
}
}

return success()
}

Map getTaskQuantities() {
String productId = parameters.productId
String workEffortId = parameters.workEffortId
String wegsProductId = parameters.wegsProductId

List<String> productIds = [productId]
// TODO: Add substitute product IDs to productIds

EntityConditionBuilder exprBldr = new EntityConditionBuilder()
EntityCondition condition = exprBldr.AND {
IN(productId: productIds)
EQUALS(workEffortId: workEffortId)
}
List<GenericValue> issuances = from('WorkEffortAndInventoryAssign')
.where(condition)
.queryList()

BigDecimal totalIssued = BigDecimal.ZERO
issuances.each { issuance ->
totalIssued += issuance.quantity ?: BigDecimal.ZERO
}

List<GenericValue> reservations = from('WorkEffortInvRes')
.where('workEffortId', workEffortId,
'wegsProductId', wegsProductId)
.queryList()

BigDecimal totalReserved = BigDecimal.ZERO
reservations.each { res ->
totalReserved += res.quantity ?: BigDecimal.ZERO
}

return [totalReserved: totalReserved, totalIssued: totalIssued]
}

Map reserveWorkEffortInventory() {
String workEffortId = parameters.workEffortId
String wegsProductId = parameters.wegsProductId
String inventoryItemId = parameters.inventoryItemId
BigDecimal quantity = parameters.quantity ?: BigDecimal.ZERO
BigDecimal quantityNotAvailable = parameters.quantityNotAvailable ?: BigDecimal.ZERO

if (!workEffortId || !inventoryItemId || quantity <= 0) {
return error('Missing required parameters or invalid quantity.')
}

GenericValue existingRes = from('WorkEffortInvRes')
.where(workEffortId: workEffortId, inventoryItemId: inventoryItemId, wegsProductId: wegsProductId)
.queryOne()

if (existingRes) {
// Update quantity and quantityNotAvailable
BigDecimal oldQty = existingRes.quantity ?: BigDecimal.ZERO
BigDecimal oldNotAvail = existingRes.quantityNotAvailable ?: BigDecimal.ZERO

existingRes.set('quantity', oldQty + quantity)
existingRes.set('quantityNotAvailable', oldNotAvail + quantityNotAvailable)
existingRes.store()
} else {
// Create new WorkEffortInvRes record
Set<String> validFields = delegator.getModelEntity('WorkEffortInvRes').getAllFieldNames() as Set
Map<String, Object> filteredParams = parameters.findAll { k, v -> validFields.contains(k) }

GenericValue newWorkEffortInvRes = delegator.makeValue('WorkEffortInvRes', filteredParams)

Timestamp nowTs = UtilDateTime.nowTimestamp()
newWorkEffortInvRes.set('createdDatetime', nowTs)

if (!newWorkEffortInvRes.reservedDatetime) {
newWorkEffortInvRes.set('reservedDatetime', nowTs)
}

newWorkEffortInvRes.create()
}

return success()
}

Map releaseProductionRunTaskComponent() {
String workEffortId = parameters.workEffortId
String wegsProductId = parameters.wegsProductId
String inventoryItemId = parameters.inventoryItemId

if (!workEffortId || !wegsProductId) {
return error('Missing required parameters: workEffortId and/or wegsProductId')
}

EntityConditionBuilder exprBldr = new EntityConditionBuilder()
EntityCondition condition = exprBldr.AND {
EQUALS(workEffortId: workEffortId)
EQUALS(wegsProductId: wegsProductId)
if (inventoryItemId) {
EQUALS(inventoryItemId: inventoryItemId)
}
}

List<GenericValue> reservations = from('WorkEffortInvRes')
.where(condition)
.queryList()

reservations.each { reservation ->
GenericValue inventoryItem = from('InventoryItem')
.where('inventoryItemId', reservation.inventoryItemId)
.queryOne()

if (inventoryItem?.inventoryItemTypeId == 'SERIALIZED_INV_ITEM') {
Map updateInventoryItemCtx = [inventoryItemId: reservation?.inventoryItemId,
statusId: 'INV_AVAILABLE']
run service: 'updateInventoryItem', with: updateInventoryItemCtx
} else {
Map createInventoryItemDetailCtx = [inventoryItemId: reservation?.inventoryItemId,
workEffortId: reservation.workEffortId,
availableToPromiseDiff: reservation.quantity]
run service: 'createInventoryItemDetail', with: createInventoryItemDetailCtx
}
reservation.remove()
}

return success()
}
Loading
Loading