Use list and string functions to manage a list of values
- Last Updated: December 20, 2023
- 4 minute read
- OpenEdge
- Version 13.0
- Documentation
The h-BinCheck.p procedure needs to make a list of how many
Items are supplied by each Warehouse. There are various ways to
code this, but to illustrate some more of the string manipulation functions you were
introduced to in Use Basic ABL Constructs you build this as a character
string.
To update h-BinCheck.p to make a list of the number of Items supplied by each Warehouse:
- Add placeholders for the count of
Itemsin eachWarehouse. The following code forms a list with as many zeroes as there areWarehouses. The zero values are later incremented to countItemssupplied by eachWarehouse:FOR EACH Warehouse: cBestList = cBestList + "0,". END. cBestList = RIGHT-TRIM(cBestList, ",").Note: TheRIGHT-TRIMfunction removes the final comma from the list, rather than theIF-THEN-ELSEstatement in the assignment that created the item list in OrderProcs.p. These are just different ways of doing the same thing. TheRIGHT-TRIMfunction is a bit more efficient. - To loop through the list of Items, add a
DOblock with theNUM-ENTRIESfunction:DO iEntry = 1 TO NUM-ENTRIES(pcItemList):NUM-ENTRIEScounts the entries in a list using a comma as the delimiter between entries by default. If you need to use a delimiter other than a comma, the delimiter can be an optional second argument to the function. - Add a statement that embeds two built-in functions into one statement:
iItemNum = INTEGER(ENTRY(iEntry, pcItemList)).The
ENTRYfunction extracts entry numberiEntryfrompcItemList. It returns this to theINTEGERfunction, which converts the value back to an integer. So now you restored theItemnumber to its original form. - Add a block of code that operates on this
Itemnumber. TheBintable represents bins or containers in eachWarehousethat are used to store the variousItems. It has both anItemNumfield to point to theItemrecord, and aWarehouseNumfield to point to theWarehousewhere theBinis located. If theQty (quantity)field for aBinrecord is 0, then theWarehousethatBinis in cannot supply that part. The code builds up this list ofWarehousenames. TheLOOKUPfunction looks for a string in a list. If it finds it, it returns the position of the entry in the list. Otherwise, it returns 0 if the entry is not in the list. Here theLOOKUPfunction is used to make sure that aWarehousename is added to the list once only if it is not already there:FOR EACH Bin WHERE Bin.ItemNum = iItemNum: IF Bin.Qty = 0 THEN DO: FIND Warehouse WHERE Warehouse.WarehouseNum = Bin.WarehouseNum. IF LOOKUP(WarehouseName, pcWarehouseList) = 0 THEN pcWarehouseList = pcWarehouseList + (IF pcWarehouseList = "" THEN "" ELSE ",") + WarehouseName. END. END. - Still within the
DOblock that iterates on each item, add code that initializes two variables to zero using a singleASSIGNstatement:ASSIGN iWHQty = 0 iWHNum = 0.These variables hold the quantity of each item at a
Warehouseand theWarehousenumber.
To use the REPEAT PRESELECT block to pre-fetch records:
- Add a
REPEATblock that preselects eachBinthat holds the currentItem, along with theWarehousewhere theBinis located, filtering these to include onlyWarehousesin the USA. The records are sorted in descending order of their quantity. This identifies whichWarehousehas the largest quantity of theItemin inventory. Remember that thePRESELECTphrase forces the AVM to retrieve all the matching records before beginning to execute the statements in the block:REPEAT PRESELECT EACH Bin WHERE Bin.ItemNum = iItemNum, FIRST Warehouse WHERE Warehouse.WarehouseNum = Bin.WarehouseNum AND Warehouse.Country = "USA" BY Bin.Qty DESCENDING: - Add the code that finds the next
Warehouserecord in this preselected list. The first time through theREPEATblock, theFIND NEXTstatement finds the first record:FIND NEXT Warehouse.Why does the statement name the
Warehousebuffer and not theBin? The rule is that whenever you are doing aFINDon aPRESELECTresult set that involves a join, you must name the last table in the join. This makes sense, because if it is a one-to-many join, the record in the last (rightmost) table in the join is the only one to change on every iteration. The first table in the join might be the same for a number of records in the second table.Remember also that the
REPEATblock does not automatically iterate for you, even if you preselect the records. You have to use aFINDstatement to move from record to record. - Add the following statements to determine whether the
Warehousewith the highest inventory for theItemhas a quantity at least 100 greater than the next bestWarehouse. If so, it retrieves the entry in the list of bestWarehousesthat the code initialized with zeroes at the start of the procedure, increments it, and puts it back in the list, doing the necessary conversions to and from the INTEGER data type:IF iWHQty NE 0 AND iWHQty - Bin.Qty > 100 THEN DO: ASSIGN iBestWH = INTEGER(ENTRY(iWHNum, cBestList)) iBestWH = iBestWH + 1 ENTRY(iWHNum, cBestList) = STRING(iBestWH). END. ELSE IF iWHQty NE 0 THEN LEAVE. ASSIGN iWHQty = Bin.Qty iWHNum = Warehouse.WarehouseNum. - Terminate the
REPEATblock and theDOblock for each item:END. /* END REPEAT PRESELECT EACH Bin... */ END. /* END DO iEntry... */