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:

  1. Add placeholders for the count of Items in each Warehouse. The following code forms a list with as many zeroes as there are Warehouses. The zero values are later incremented to count Items supplied by each Warehouse:
    FOR EACH Warehouse:
      cBestList = cBestList + "0,".
    END.
    cBestList = RIGHT-TRIM(cBestList, ",").
    Note: The RIGHT-TRIM function removes the final comma from the list, rather than the IF-THEN-ELSE statement in the assignment that created the item list in OrderProcs.p. These are just different ways of doing the same thing. The RIGHT-TRIM function is a bit more efficient.
  2. To loop through the list of Items, add a DO block with the NUM-ENTRIES function:
    DO iEntry = 1 TO NUM-ENTRIES(pcItemList):

    NUM-ENTRIES counts 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.

  3. Add a statement that embeds two built-in functions into one statement:
      iItemNum = INTEGER(ENTRY(iEntry, pcItemList)).

    The ENTRY function extracts entry number iEntry from pcItemList. It returns this to the INTEGER function, which converts the value back to an integer. So now you restored the Item number to its original form.

  4. Add a block of code that operates on this Item number. The Bin table represents bins or containers in each Warehouse that are used to store the various Items. It has both an ItemNum field to point to the Item record, and a WarehouseNum field to point to the Warehouse where the Bin is located. If the Qty (quantity) field for a Bin record is 0, then the Warehouse that Bin is in cannot supply that part. The code builds up this list of Warehouse names. The LOOKUP function 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 the LOOKUP function is used to make sure that a Warehouse name 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.
  5. Still within the DO block that iterates on each item, add code that initializes two variables to zero using a single ASSIGN statement:
      ASSIGN iWHQty = 0 iWHNum = 0.

    These variables hold the quantity of each item at a Warehouse and the Warehouse number.

To use the REPEAT PRESELECT block to pre-fetch records:

  1. Add a REPEAT block that preselects each Bin that holds the current Item, along with the Warehouse where the Bin is located, filtering these to include only Warehouses in the USA. The records are sorted in descending order of their quantity. This identifies which Warehouse has the largest quantity of the Item in inventory. Remember that the PRESELECT phrase 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: 
  2. Add the code that finds the next Warehouse record in this preselected list. The first time through the REPEAT block, the FIND NEXT statement finds the first record:
        FIND NEXT Warehouse.

    Why does the statement name the Warehouse buffer and not the Bin? The rule is that whenever you are doing a FIND on a PRESELECT result 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 REPEAT block does not automatically iterate for you, even if you preselect the records. You have to use a FIND statement to move from record to record.

  3. Add the following statements to determine whether the Warehouse with the highest inventory for the Item has a quantity at least 100 greater than the next best Warehouse. If so, it retrieves the entry in the list of best Warehouses that 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.
  4. Terminate the REPEAT block and the DO block for each item:
      END. /* END REPEAT PRESELECT EACH Bin... */
    END. /* END DO iEntry... */