Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit983b314

Browse files
OBPIH-7542 Fix reorder report not to remove products affected by expi… (#5603)
Co-authored-by: Alan Nadolny <83239466+alannadolny@users.noreply.github.com>
1 parent41f2c0a commit983b314

File tree

10 files changed

+121
-85
lines changed

10 files changed

+121
-85
lines changed

‎grails-app/i18n/messages.properties‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2306,6 +2306,8 @@ putawayOrder.putAwayBin.label=Putaway Bin
23062306
# Reorder report messages
23072307
reorderReportFilterCommand.additionalLocations.invalid.locations=Every additional location has to support Manage Inventory activity
23082308
reorderReport.noMaxQtySet.label=No Max qty set - review based on monthly demand
2309+
reorderReport.expiredQuantityAvailableToPromise.label=Quantity expired
2310+
reorderReport.finalQuantityAvailableToPromise.label=Final quantity available
23092311

23102312
# Expiration history report messages
23112313
expirationHistoryReport.quantityLostToExpiry.label=Quantity Lost to Expiry
@@ -4857,9 +4859,9 @@ react.report.reorder.header.label=Reorder Report
48574859
react.report.reorder.additionalInventoryLocations.label=Additional Inventory Locations
48584860
react.report.reorder.expiredStock.label=Expired Stock
48594861
react.report.reorder.filterProducts.label=Filter products
4860-
react.report.reorder.removeExpiredStock.label=Remove expired stock
4861-
react.report.reorder.includeExpiredStock.label=Include expired stock
4862-
react.report.reorder.removeExpiringWithin.label=Remove expiring within ${days} days
4862+
react.report.reorder.subtractExpiredStock.label=Subtract expired stock
4863+
react.report.reorder.doNotSubtractExpiredStock.label=Do not subtract expired stock
4864+
react.report.reorder.subtractExpiringWithin.label=Subtract expiring within ${days} days
48634865
react.report.reorder.showAllProducts.label=Show all products
48644866
react.report.reorder.showProductsBelowReorder.label=Show products below reorder
48654867
react.report.reorder.showProductsBelowMaximum.label=Show products below maximum

‎grails-app/services/org/pih/warehouse/core/DashboardService.groovy‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,11 @@ class DashboardService {
619619

620620
// Depending on the provided inventory level status filter value, we have a different condition for filtering out the items
621621
Map<InventoryLevelStatus,Closure<Boolean>> inventoryLevelStatusFilterCondition= [
622-
(InventoryLevelStatus.IN_STOCK): { item.quantityAvailableToPromise>0 },
623-
(InventoryLevelStatus.BELOW_MAXIMUM): { inventoryLevel?.maxQuantity&& item.quantityAvailableToPromise<= inventoryLevel?.maxQuantity },
624-
(InventoryLevelStatus.BELOW_REORDER): { inventoryLevel?.reorderQuantity&& item.quantityAvailableToPromise<= inventoryLevel?.reorderQuantity },
625-
(InventoryLevelStatus.BELOW_MINIMUM): { inventoryLevel?.minQuantity&& item.quantityAvailableToPromise<= inventoryLevel?.minQuantity }
622+
// For ALL_PRODUCTS we want to include all products, regardless of their quantity available to promise or inventory level settings
623+
(InventoryLevelStatus.ALL_PRODUCTS): {true },
624+
(InventoryLevelStatus.BELOW_MAXIMUM): { inventoryLevel?.maxQuantity&& item.finalQuantityAvailableToPromise<= inventoryLevel?.maxQuantity },
625+
(InventoryLevelStatus.BELOW_REORDER): { inventoryLevel?.reorderQuantity&& item.finalQuantityAvailableToPromise<= inventoryLevel?.reorderQuantity },
626+
(InventoryLevelStatus.BELOW_MINIMUM): { inventoryLevel?.minQuantity&& item.finalQuantityAvailableToPromise<= inventoryLevel?.minQuantity }
626627
]
627628

628629
// If an item satisfies the predicate, call the buildReorderReportItem, otherwise return null so that .findResults filters out the record in the end
@@ -634,7 +635,7 @@ class DashboardService {
634635

635636
privateReorderReportItemDtobuildReorderReportItem(InventoryByProductitem,InventoryLevelinventoryLevel) {
636637
Map<String,Number> monthlyDemand= forecastingService.getDemand(AuthService.currentLocation,null, item.product)
637-
Integer quantityToOrder= inventoryLevel?.maxQuantity!=null? inventoryLevel.maxQuantity- item.quantityAvailableToPromise:null
638+
Integer quantityToOrder= inventoryLevel?.maxQuantity!=null? inventoryLevel.maxQuantity- item.finalQuantityAvailableToPromise:null
638639
Boolean hasRoleFinance= userService.hasRoleFinance(AuthService.currentUser)
639640
BigDecimal unitCost= hasRoleFinance? item.product.pricePerUnit:null
640641
BigDecimal expectedReorderCost= hasRoleFinance&& quantityToOrder&& item.product.pricePerUnit!=null
@@ -652,7 +653,8 @@ class DashboardService {
652653
tags: item.product.tags,
653654
inventoryLevel: inventoryLevel,
654655
monthlyDemand: monthlyDemand?.monthlyDemand?:0,
655-
quantityAvailableToPromise: item.quantityAvailableToPromise,
656+
finalQuantityAvailableToPromise: item.finalQuantityAvailableToPromise,
657+
expiredQuantityAvailableToPromise: item.expiredQuantityAvailableToPromise,
656658
quantityToOrder: quantityToOrder,
657659
unitCost: unitCost,
658660
expectedReorderCost: expectedReorderCost
@@ -676,6 +678,8 @@ class DashboardService {
676678
"${messageLocalizer.localize("inventoryLevel.maxQuantity.label", "Max quantity")}" { it?.maxQuantity }
677679
"${messageLocalizer.localize("report.averageMonthlyDemand.label", "Average Monthly Demand")}" { it?.averageMonthlyDemand }
678680
"${messageLocalizer.localize("product.quantityAvailableToPromise.label", "Quantity Available")}" { it?.quantityAvailableToPromise }
681+
"${messageLocalizer.localize("reorderReport.expiredQuantityAvailableToPromise.label", "Quantity expired")}" { it?.expiredQuantityAvailableToPromise }
682+
"${messageLocalizer.localize("reorderReport.finalQuantityAvailableToPromise.label", "Final quantity available")}" { it?.finalQuantityAvailableToPromise }
679683
"${messageLocalizer.localize("inventory.quantityToOrder.message", "Quantity to Order")}" { it?.quantityToOrder }
680684

681685
if (hasRoleFinance) {
@@ -700,7 +704,9 @@ class DashboardService {
700704
reorderQuantity: item.inventoryLevel?.reorderQuantity?:"",
701705
maxQuantity: item.inventoryLevel?.maxQuantity?:"",
702706
averageMonthlyDemand: item.monthlyDemand?:0,
703-
quantityAvailableToPromise: item.quantityAvailableToPromise,
707+
finalQuantityAvailableToPromise: item.finalQuantityAvailableToPromise,
708+
expiredQuantityAvailableToPromise: item.expiredQuantityAvailableToPromise,
709+
quantityAvailableToPromise: item.finalQuantityAvailableToPromise+ item.expiredQuantityAvailableToPromise,
704710
quantityToOrder: getQuantityToOrderDisplayValue(item.inventoryLevel?.maxQuantity, item.quantityToOrder)?:"",
705711
unitCost: item.unitCost?:"",
706712
expectedReorderCost: item.expectedReorderCost?:"",

‎grails-app/services/org/pih/warehouse/inventory/ProductAvailabilityService.groovy‎

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,47 @@ class ProductAvailabilityService {
526526
return quantityMap
527527
}
528528

529+
/**
530+
* Calculate quantities based on expiration filter.
531+
*@param productAvailabilityRecords
532+
*@param expirationFilter
533+
*@return
534+
* Return map with:
535+
* expiredQuantityAvailableToPromise - quantity available to promise that is expired based on expiration filter
536+
* finalQuantityAvailableToPromise - quantity available to promise after applying expiration filter
537+
* quantityOnHand - total quantity on hand (not affected by expiration filter)
538+
* quantityAvailableToPromise - total quantity available to promise (not affected by expiration filter)
539+
*/
540+
privatestaticMap<String,Integer>calculateQuantitiesByExpirationFilter(List<ProductAvailability>productAvailabilityRecords,ExpirationFilterexpirationFilter) {
541+
boolean removeExpiredStock= expirationFilter==ExpirationFilter.SUBTRACT_EXPIRED_STOCK
542+
Date today=newDate()
543+
Date maxDate= removeExpiredStock? today: today+ expirationFilter.days
544+
// Quantity available to promise before applying expiration filter
545+
Integer quantityAvailableToPromise= productAvailabilityRecords.sum { it.quantityAvailableToPromise?:0 }
546+
// Final quantity available to promise - quantity available to promise subtracted by expired stock based on expiration filter
547+
Integer finalQuantityAvailableToPromise= productAvailabilityRecords.sum {ProductAvailabilitypaRecord->
548+
// We can return immedietaly if we do not want to subtract expired stock
549+
if (expirationFilter==ExpirationFilter.DO_NOT_SUBTRACT_EXPIRED_STOCK) {
550+
return paRecord.quantityAvailableToPromise?:0
551+
}
552+
// We want to include inventory items that either don't have expiration date or have expiration date after maxDate (or >= maxDate if removing expired stock)
553+
InventoryItem inventoryItem= paRecord.inventoryItem
554+
if (!inventoryItem.expirationDate|| (removeExpiredStock? inventoryItem.expirationDate>= maxDate: inventoryItem.expirationDate> maxDate)) {
555+
return paRecord.quantityAvailableToPromise?:0
556+
}
557+
return0
558+
}
559+
// Quantity on hand is used only to calculate the inventory level status
560+
Integer quantityOnHand= productAvailabilityRecords.sum { it.quantityOnHand?:0 }
561+
562+
return [
563+
// Expired quantity is just quantity available to promise subtracted by final quantity available to promise (removed stock based on expiration filter)
564+
expiredQuantityAvailableToPromise: quantityAvailableToPromise- finalQuantityAvailableToPromise,
565+
finalQuantityAvailableToPromise: finalQuantityAvailableToPromise,
566+
quantityOnHand: quantityOnHand,
567+
]
568+
}
569+
529570
/**
530571
* Find product availability record grouped by product that satisfy the filters which contain primarily:
531572
* expiration (we can include/exclude inventory items that expire in 30/90/180/365 days),
@@ -539,52 +580,34 @@ class ProductAvailabilityService {
539580
if (additionalLocations) {
540581
locations.addAll(additionalLocations)
541582
}
542-
returnProductAvailability.createCriteria().list {
543-
projections {
544-
sum("quantityOnHand")
545-
sum("quantityAvailableToPromise")
546-
groupProperty("product")
547-
}
583+
List<ProductAvailability> productsAvailabilityRecords=ProductAvailability.createCriteria().list {
584+
setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
585+
createAlias("inventoryItem","ii",JoinType.INNER_JOIN)
586+
createAlias("product","p",JoinType.INNER_JOIN)
587+
createAlias("p.synonyms","syn",JoinType.LEFT_OUTER_JOIN)
588+
createAlias("p.category","category",JoinType.LEFT_OUTER_JOIN)
589+
createAlias("p.tags","tags",JoinType.LEFT_OUTER_JOIN)
548590
inList("location", locations)
549591

550-
// The additional if check is needed to avoid joining product twice in two separate ifs for categories/tags, as it could throw an error
551-
// if both categories and tags were provided. Another possible solution would be to store "usedAliases" and check if categories already joined the product.
552-
if (categories|| productTags) {
553-
product {
554-
if (categories) {
555-
inList("category", categories)
556-
}
557-
if (productTags) {
558-
tags {
559-
'in'("id", productTags.id)
560-
}
561-
}
562-
}
592+
if (categories) {
593+
inList("p.category", categories)
563594
}
564-
565-
if (expiration!=ExpirationFilter.INCLUDE_EXPIRED_STOCK) {
566-
add(getExpirationCriteria(expiration, delegate))
595+
if (productTags) {
596+
'in'("tags.id", productTags.id)
567597
}
568-
}.collect {newInventoryByProduct(
569-
quantityOnHand: it[0],
570-
quantityAvailableToPromise: it[1],
571-
product: it[2]
572-
)}
573-
}
574-
575-
privatestaticCriteriongetExpirationCriteria(ExpirationFilterexpirationFilter,org.grails.datastore.mapping.query.api.Criteriacriteria) {
576-
criteria.createAlias("inventoryItem","ii",JoinType.INNER_JOIN)
577-
boolean removeExpiredStock= expirationFilter==ExpirationFilter.REMOVE_EXPIRED_STOCK
578-
Date today=newDate()
579-
Date maxDate= removeExpiredStock? today: today+ expirationFilter.days
598+
}
599+
Map<Product,List<ProductAvailability>> productAvailabilityRecordsByProduct=
600+
productsAvailabilityRecords.groupBy { it.product }
580601

581-
// We want to include inventory items that either don't have expiration date or have expiration date after maxDate
582-
Junction expirationDisjunction=Restrictions.disjunction()
583-
.add(Restrictions.isNull("ii.expirationDate"))
584-
.add(removeExpiredStock?Restrictions.ge("ii.expirationDate", maxDate):Restrictions.gt("ii.expirationDate", maxDate))
585-
// We have to include the condition for qoh > 0, as we could potentially include items that are out of stock, but have "hidden" associated
586-
// inventory item with product availability record that is not visible in the stock history at that time
587-
returnRestrictions.and(expirationDisjunction,Restrictions.gt("quantityOnHand",0))
602+
return productAvailabilityRecordsByProduct.collect {Productproduct,List<ProductAvailability>paRecords->
603+
Map<String,Integer> quantities= calculateQuantitiesByExpirationFilter(paRecords, expiration)
604+
returnnewInventoryByProduct(
605+
product: product,
606+
quantityOnHand: quantities.quantityOnHand,
607+
finalQuantityAvailableToPromise: quantities.finalQuantityAvailableToPromise,
608+
expiredQuantityAvailableToPromise: quantities.expiredQuantityAvailableToPromise,
609+
)
610+
}
588611
}
589612

590613
Map<Product,Integer>getQuantityOnHandByProduct(Locationlocation) {

‎src/js/consts/filterOptions.js‎

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,70 @@
11
importtranslatefrom'utils/Translate';
22

33
exportconstEXPIRATION_FILTER={
4-
REMOVE_EXPIRED_STOCK:'REMOVE_EXPIRED_STOCK',
5-
INCLUDE_EXPIRED_STOCK:'INCLUDE_EXPIRED_STOCK',
6-
EXPIRING_WITHIN_MONTH:'EXPIRING_WITHIN_MONTH',
7-
EXPIRING_WITHIN_QUARTER:'EXPIRING_WITHIN_QUARTER',
8-
EXPIRING_WITHIN_HALF_YEAR:'EXPIRING_WITHIN_HALF_YEAR',
9-
EXPIRING_WITHIN_YEAR:'EXPIRING_WITHIN_YEAR',
4+
SUBTRACT_EXPIRED_STOCK:'SUBTRACT_EXPIRED_STOCK',
5+
DO_NOT_SUBTRACT_EXPIRED_STOCK:'DO_NOT_SUBTRACT_EXPIRED_STOCK',
6+
SUBTRACT_EXPIRING_WITHIN_MONTH:'SUBTRACT_EXPIRING_WITHIN_MONTH',
7+
SUBTRACT_EXPIRING_WITHIN_QUARTER:'SUBTRACT_EXPIRING_WITHIN_QUARTER',
8+
SUBTRACT_EXPIRING_WITHIN_HALF_YEAR:'SUBTRACT_EXPIRING_WITHIN_HALF_YEAR',
9+
SUBTRACT_EXPIRING_WITHIN_YEAR:'SUBTRACT_EXPIRING_WITHIN_YEAR',
1010
};
1111

1212
exportconstINVENTORY_LEVEL_STATUS={
13-
IN_STOCK:'IN_STOCK',
13+
ALL_PRODUCTS:'ALL_PRODUCTS',
1414
BELOW_REORDER:'BELOW_REORDER',
1515
BELOW_MAXIMUM:'BELOW_MAXIMUM',
1616
BELOW_MINIMUM:'BELOW_MINIMUM',
1717
};
1818

1919
exportconstgetExpiredStockOptions=()=>([
2020
{
21-
id:EXPIRATION_FILTER.REMOVE_EXPIRED_STOCK,
21+
id:EXPIRATION_FILTER.SUBTRACT_EXPIRED_STOCK,
2222
label:translate({
23-
id:'react.report.reorder.removeExpiredStock.label',
24-
defaultMessage:'Remove expired stock',
23+
id:'react.report.reorder.subtractExpiredStock.label',
24+
defaultMessage:'Subtract expired stock',
2525
}),
2626
},
2727
{
28-
id:EXPIRATION_FILTER.INCLUDE_EXPIRED_STOCK,
28+
id:EXPIRATION_FILTER.DO_NOT_SUBTRACT_EXPIRED_STOCK,
2929
label:translate({
30-
id:'react.report.reorder.includeExpiredStock.label',
31-
defaultMessage:'Include expired stock',
30+
id:'react.report.reorder.doNotSubtractExpiredStock.label',
31+
defaultMessage:'Do not subtract expired stock',
3232
}),
3333
},
3434
{
35-
id:EXPIRATION_FILTER.EXPIRING_WITHIN_MONTH,
35+
id:EXPIRATION_FILTER.SUBTRACT_EXPIRING_WITHIN_MONTH,
3636
label:translate({
37-
id:'react.report.reorder.removeExpiringWithin.label',
37+
id:'react.report.reorder.subtractExpiringWithin.label',
3838
defaultMessage:'Remove expiring within 30 days',
3939
data:{
4040
days:'30',
4141
},
4242
}),
4343
},
4444
{
45-
id:EXPIRATION_FILTER.EXPIRING_WITHIN_QUARTER,
45+
id:EXPIRATION_FILTER.SUBTRACT_EXPIRING_WITHIN_QUARTER,
4646
label:translate({
47-
id:'react.report.reorder.removeExpiringWithin.label',
47+
id:'react.report.reorder.subtractExpiringWithin.label',
4848
defaultMessage:'Remove expiring within 90 days',
4949
data:{
5050
days:'90',
5151
},
5252
}),
5353
},
5454
{
55-
id:EXPIRATION_FILTER.EXPIRING_WITHIN_HALF_YEAR,
55+
id:EXPIRATION_FILTER.SUBTRACT_EXPIRING_WITHIN_HALF_YEAR,
5656
label:translate({
57-
id:'react.report.reorder.removeExpiringWithin.label',
57+
id:'react.report.reorder.subtractExpiringWithin.label',
5858
defaultMessage:'Remove expiring within 180 days',
5959
data:{
6060
days:'180',
6161
},
6262
}),
6363
},
6464
{
65-
id:EXPIRATION_FILTER.EXPIRING_WITHIN_YEAR,
65+
id:EXPIRATION_FILTER.SUBTRACT_EXPIRING_WITHIN_YEAR,
6666
label:translate({
67-
id:'react.report.reorder.removeExpiringWithin.label',
67+
id:'react.report.reorder.subtractExpiringWithin.label',
6868
defaultMessage:'Remove expiring within 365 days',
6969
data:{
7070
days:'365',
@@ -75,7 +75,7 @@ export const getExpiredStockOptions = () => ([
7575

7676
exportconstgetFilterProductOptions=()=>([
7777
{
78-
id:INVENTORY_LEVEL_STATUS.IN_STOCK,
78+
id:INVENTORY_LEVEL_STATUS.ALL_PRODUCTS,
7979
label:translate({
8080
id:'react.report.reorder.showAllProducts.label',
8181
defaultMessage:'Show all products',

‎src/js/hooks/reporting/useReorderReportFilters.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
EXPIRATION_FILTER,
1515
getExpiredStockOptions,
1616
getFilterProductOptions,
17+
INVENTORY_LEVEL_STATUS,
1718
}from'consts/filterOptions';
1819
importuseCommonFiltersCleanerfrom'hooks/list-pages/useCommonFiltersCleaner';
1920
importuseSpinnerfrom'hooks/useSpinner';
@@ -115,11 +116,12 @@ const useReorderReportFilters = () => {
115116
defaultValues.filterProducts=getSelectedOption(
116117
selectedFilterProducts,
117118
getFilterProductOptions(),
119+
INVENTORY_LEVEL_STATUS.ALL_PRODUCTS,
118120
);
119121
defaultValues.expiredStock=getSelectedOption(
120122
expiredStock,
121123
getExpiredStockOptions(),
122-
EXPIRATION_FILTER.REMOVE_EXPIRED_STOCK,
124+
EXPIRATION_FILTER.SUBTRACT_EXPIRED_STOCK,
123125
);
124126
defaultValues.additionalInventoryLocations=setDefaultValue(
125127
additionalInventoryLocations,

‎src/main/groovy/org/pih/warehouse/inventory/ExpirationFilter.groovy‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
packageorg.pih.warehouse.inventory
22

33
enumExpirationFilter {
4-
INCLUDE_EXPIRED_STOCK(null),
5-
REMOVE_EXPIRED_STOCK(null),
6-
EXPIRING_WITHIN_MONTH(30),
7-
EXPIRING_WITHIN_QUARTER(90),
8-
EXPIRING_WITHIN_HALF_YEAR(180),
9-
EXPIRING_WITHIN_YEAR(365)
4+
SUBTRACT_EXPIRED_STOCK(null),
5+
DO_NOT_SUBTRACT_EXPIRED_STOCK(null),
6+
SUBTRACT_EXPIRING_WITHIN_MONTH(30),
7+
SUBTRACT_EXPIRING_WITHIN_QUARTER(90),
8+
SUBTRACT_EXPIRING_WITHIN_HALF_YEAR(180),
9+
SUBTRACT_EXPIRING_WITHIN_YEAR(365)
1010

1111
Integer days
1212

‎src/main/groovy/org/pih/warehouse/inventory/InventoryLevelStatus.groovy‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ enum InventoryLevelStatus {
1717
BELOW_MINIMUM(3),
1818
BELOW_REORDER(4),
1919
BELOW_MAXIMUM(5),
20-
ABOVE_MAXIMUM(6)
20+
ABOVE_MAXIMUM(6),
21+
ALL_PRODUCTS(7)
2122

2223
int sortOrder
2324

‎src/main/groovy/org/pih/warehouse/inventory/ReorderReportFilterCommand.groovy‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import org.pih.warehouse.product.Category
88

99
classReorderReportFilterCommandimplementsValidateable {
1010
List<Location> additionalLocations
11-
InventoryLevelStatus inventoryLevelStatus=InventoryLevelStatus.IN_STOCK
12-
ExpirationFilter expiration=ExpirationFilter.REMOVE_EXPIRED_STOCK
11+
InventoryLevelStatus inventoryLevelStatus=InventoryLevelStatus.ALL_PRODUCTS
12+
ExpirationFilter expiration=ExpirationFilter.SUBTRACT_EXPIRED_STOCK
1313
List<Category> categories
1414
List<Tag> tags
1515

‎src/main/groovy/org/pih/warehouse/inventory/ReorderReportItemDto.groovy‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class ReorderReportItemDto {
99
Set<Tag> tags
1010
InventoryLevel inventoryLevel
1111
Integer monthlyDemand
12-
Integer quantityAvailableToPromise
12+
Integer finalQuantityAvailableToPromise
13+
Integer expiredQuantityAvailableToPromise
1314
Integer quantityToOrder
1415
BigDecimal unitCost
1516
BigDecimal expectedReorderCost

‎src/main/groovy/org/pih/warehouse/inventory/product/availability/InventoryByProduct.groovy‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.pih.warehouse.product.Product
44

55
classInventoryByProduct {
66
Integer quantityOnHand
7-
Integer quantityAvailableToPromise
7+
Integer finalQuantityAvailableToPromise
8+
Integer expiredQuantityAvailableToPromise
89
Product product
910
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp