          PH.ARGS DRPT,BRCHS,BR,AOD,SEL.DATA,BASIS.LIST,DET.OPTS,SORTBY,EXT.AMT.VAL,INC.EXC.DATA,IQTYPES,CAOD,ADDL.DATA,RANKS,MISC.DATA,GROUP.RANK.DATA
** Version# 203.0002[27] - 08/24/2012 - 04:31pm - TSMITH - eclipse
*** V203.0002 Change - Custom Coding . - 08/24/2012 - TSMITH - eclipse
** Copied from BP INV.PHR.INV.VALUE Version# 203 - 06/13/2012 - 06:47pm - TRAVISC - main
*** Subroutine - INV.PHR.INV.VALUE
*-------------------------------------------------------------------------*
*** This is the phantom routine for running the Inventory Valuation
*** report, which is used to show the value of products within a Price
*** or Buy Line, a group of Price or Buy Lines, or all Price or Buy Lines
*** within one or more Branches.
***
*** HAS.ODBC logic has been added to this report to write the report data
*** and summary information to ODBC layout files that will allow the added
*** functionality of the solar reporter with this report. The information
*** will only be written to the ODBC file structure if the site has the
*** add-on key for the solar reporter.
*-------------------------------------------------------------------------*
*** NOTE: As of v169 of this phantom, we NO LONGER use the phantom
*** FIFO.PHR.INV.VALUE.
*-------------------------------------------------------------------------*
*** DRPT                 - Report printing defaults                [IN]
*** BRCHS                - Branches to generate report data for    [IN]
*** AOD                  - As of date                              [IN]
*** SEL.DATA             - Price/Buy Line data, SVM delimited:     (IN)
*** SEL.DATA<1,1>        - Price/Buy Lines.
*** SEL.DATA<1,2>        - Line Type.
*** BASIS.LIST           - A VM-List of basis columns that will    [IN]
***                      - need to be calculated to be included
***                      - on the report.
***                      -   @SVM1 -> Basis Number
***                      -   @SVM2 -> Unit Column #
***                      -   @SVM3 -> Extended Column #
*** DET.OPTS             - Report format options.  VM delimited    [IN]
*** DET.OPTS<1,1>        - DET - Report format option:
***                      -  'Detail' = shows Product Detail by Branch
***                      -  'Summary'= shows Subtotals.
***                      -  'Summary by Product' = shows Product
***                      -       Detail totaled for all Branches.
*** SORTBY               - 'Sort by' option:                       [IN]
***                      - Price/Buy Line or GL code
*** EXT.AMT.VAL          - Only show Detail for Extended Values    (IN)
***                      - greater than this value
*** INC.EXC.DATA         - VM delimited Include/Exclude/Only opts  (IN)
*** INC.EXC.DATA<1,1>    - Nonstocks
*** INC.EXC.DATA<1,2>    - Negative OnHand Qtys
*** INC.EXC.DATA<1,3>    - Zero OnHand Qtys
*** IQTYPES              - Quanity types                           (IN)
*** ADDL.DATA           - Consignment Data, SVM delimited:        (IN)
*** ADDL.DATA<1,1,1>    - Include/Exclude/Only show Consignment
***                      - Vendors
*** ADDL.DATA<1,1,2>    - Include/Exclude/Only show Consignment
***                      - Customers
*** ADDL.DATA<1,1,3>    - Consignment Vendor/Customer to report
***                      - data for
*** ADDL.DATA<1,1,4>    - Page Break on Consignment Vendor
*** ADDL.DATA<1,1,5>    - Page Break on Consignment Customer
*** ADDL.DATA<1,1,6>    - Show consignment customers on seperate line
*** ADDL.DATA<1,1,7>    - Show vendor consignment pricing on the ext val
*** ADDL.DATA<1,1,8>     - Number of years of history to consider
***                      - for FIFO.
*** ADDL.DATA<1,1,9>     - Boolean that, when TRUE, indicates that
***                      - Inventory Adjustments should be included
***                      - in FIFO calculations.
*** RANKS                - Product Ranks, VM delimited by Rank     (IN)
***                      - Number (possible 1-5) and SVM
***                      - delimited by Rank (possible A-X & G).
***                      - Only products having the selected Ranks
***                      - will be included on the report.
*** MISC.DATA            - Miscellaneous report processing data:   (IN)
*** MISC.DATA<1,1>       - ANY.ALL:  Any/All
***                      - 'Must Match Any/All Rank Selections' option:
***                      - If set to 'Any', products whose ranks
***                      - match any of the selected ranks will
***                      - be included on the report.
***                      - If set to 'All' only those products
***                      - whose ranks match ALL of the selected
***                      - ranks will be included on the report.
*** MISC.DATA<1,2>       - NO LONGER USED (Tagged Items selected
***                      - via column logic).
*** MISC.DATA<1,4>       - AVG.CST
***                      - Flag - If set to yes and AVG-COST is $0
***                      -        do not use REP-COST. Show $0 instead.
*** MISC.DATA<1,5>       - If set to XML this will generate an
***                        XML extract file for Inventory Modeling
*** MISC.DATA<1,6>       - Flag indicates if they have Outbound Email.
***                      - Only applies for Inventory Modeling
*** GROUP.RANK.DATA      - Group Rank Data, VM delimited.          (IN)
*** GROUP.RANK.DATA<1,1> - 'Group Ranks' option. If set to 'Yes',
***                      - then the report data will be grouped
***                      - and subtotaled by product rank.  For
***                      - example, products ranked as an 'A' item
***                      - in each of the Rank Numbers selected will
***                      - be grouped together. A product ranked as
***                      - an 'A' item for all Rank Numbers except
***                      - one would be in a different group.
*** GROUP.RANK.DATA<1,2> - 'Subtotal on Sort by' option.  This value
***                      - can only be set to 'No' when the 'Group
***                      - by Product Ranks' option is set to 'Yes'
***                      - Otherwise, it is always 'Yes'
*-------------------------------------------------------------------------*
*** COMMON Variables Used: DRPT, PRD, PRDD
*-------------------------------------------------------------------------*

          *** Go Initialize the data we'll need for this Phantom to work...
          GOSUB INIT

          IF ERR.MSG THEN
             SEND.MESSAGE "Phantom",USER.ID,ERR.MSG
             RETURN
          END
*-------------------------------------------------------------------------*
          *** Update our phantom status...
          WRITE 'Selecting...' ON PHSTFILE,PID$

          *** Go select data for our report...
          GOSUB SEL.IDS

          *** Update our phantom status...
          WRITE 'Spooling...' ON PHSTFILE,PID$
*-------------------------------------------------------------------------*
*** Build our report heading...

          IF SLIST THEN
             TEMP.LIST = SLIST
             CONVERT VM TO ',' IN TEMP.LIST
          END

          HDG  = REPORT.FORMAT:' Inventory Value as of ':AS.OF.DT
          HDG := ' * End of Day * - NonStocks: ':EXCL.NS

          IF EXT.AMT.VAL # '' THEN HDG := ' - Extended Value > ':EXT.AMT.VAL

          IF IQTYPES = '' AND QTYPES # 'V' AND QTYPES # 'C' THEN
             QTYP = 'ALL'
          END ELSE
             XX = DCOUNT(QTYPES,VM)
             IF XX > 0 THEN
                FOR QTP = 1 TO XX
                   BEGIN CASE
                   CASE QTYPES<1,QTP> = 'S'; TEMP.QTYP = 'Stock'
                   CASE QTYPES<1,QTP> = 'F'; TEMP.QTYP = 'Defective'
                   CASE QTYPES<1,QTP> = 'O'; TEMP.QTYP = 'Over Shipment'
                   CASE QTYPES<1,QTP> = 'R'; TEMP.QTYP = 'Review'
                   CASE QTYPES<1,QTP> = 'L'; TEMP.QTYP = 'Display'
                   CASE QTYPES<1,QTP> = 'T'; TEMP.QTYP = 'Tagged'
                   CASE QTYPES<1,QTP> = 'V'; TEMP.QTYP = 'Vend Consigned'
                   CASE QTYPES<1,QTP> = 'C'; TEMP.QTYP = 'Cust Consigned'
                   CASE OTHERWISE;           TEMP.QTYP = ''
                   END CASE

                   IF QTP   = 1 THEN
                      QTYP  = TEMP.QTYP
                   END ELSE
                      QTYP := ',':TEMP.QTYP
                   END
                NEXT QTP
                IF CCN THEN
                   READV NAME FROM CUSFILE,CCN,1 ELSE NAME = CCN
                   QTYP := '  for ':NAME
                END
             END
          END

          HDG<1,2>  = 'Quantity Types: ':QTYP
          HDG<1,2> := ' - Valued at: ':GBDESC:' - As of: ':AS.OF.DT
          HDG<1,2> := ' Price Date: ':OCONV(PRC.DATE,'D4/')
          IF AVG.CST THEN
             HDG<1,2> := ' -Show $0 Avg Cost: Yes'
          END ELSE
             HDG<1,2> := ' -Show $0 Avg Cost: No'
          END

          BRANCH.ALLOW = 129
          IF LEN(BRS) > BRANCH.ALLOW THEN BRS = BRS[1,BRANCH.ALLOW]:'...'

          IF RANKS THEN
             HDG<1,3> = 'Product Ranks: '
             FOR RNUMBER = 1 TO 5
                IF RANKS<1,RNUMBER> THEN
                   SLCTED.RANKS = RAISE(RANKS<1,RNUMBER>)
                   CONVERT VM TO ',' IN SLCTED.RANKS
                   HDG<1,3> := 'Rank#':RNUMBER:': ':SLCTED.RANKS:' '
                END
             NEXT RNUMBER

             IF ANY.ALL = 'All' THEN
                ANY.ALL.MSG = '- Must Match on All Ranks'
             END ELSE
                ANY.ALL.MSG = '- Matching Any Ranks'
             END

             HDG<1,3> := ANY.ALL.MSG

             IF DETAIL[1,6] = 'Detail' AND GROUP.RANKS THEN
                HDG<1,3> := ' - Grouped by Product Ranks'
             END
             LN = 4
          END ELSE
             IF DETAIL[1,6] = 'Detail' AND GROUP.RANKS THEN
                HDG<1,3> = 'Grouped by Product Ranks'
                LN = 4
             END ELSE
                LN = 3
             END
          END

          HDG<1,LN>  = 'Branch(es): ':BRS

          LN += 1

          HDG1.LENGTH = LEN(HDG<1,1>)
          HDG2.LENGTH = LEN(HDG<1,2>)
          LAST.HDG.LINE.LENGTH = LEN(HDG<1,LN>)

          IF LAST.HDG.LINE.LENGTH > HDG2.LENGTH THEN
             WDTH = LAST.HDG.LINE.LENGTH
          END ELSE
             WDTH = HDG2.LENGTH
          END

          *** Tack the page number onto the first line of our heading...
          HDG<1,1> := SPACE(WDTH - HDG1.LENGTH - 12):'Page: ^#####'

          *** Tack the any Price/Buy Lines selected onto the
          *** same line that our Branches are on...
          IF SLIST THEN
             LN -= 1
             HDG<1,LN> := '   ':SLCT:' Line(s): '
             HDG3.LENGTH = LEN(HDG<1,LN>)
             TEMP.LIST.LENGTH = LEN(TEMP.LIST)

             *** If we don't have room to print all the Lines...
             IF (HDG3.LENGTH + TEMP.LIST.LENGTH) > WDTH THEN
                HDG<1,LN> := '*Multi*'
             END ELSE
                HDG<1,LN> := TEMP.LIST
             END
          END

          LN += 2
          COL.HDG = ""
          REPORT.LAYOUT.V.PRINT.STR "","H",COL.RECORD,"",COL.HDG
          HDG<1,LN> = LOWER(COL.HDG)
          WDTH = MAXIMUM(LENS(RAISE(HDG<1>)))

          IF DRPT<33> = '' THEN
             TITLE = REPORT.FORMAT:' Inventory Valuation as of ':AS.OF.DT
          END ELSE
             TITLE = DRPT<33>
          END

          *** Determine if the site has the solar reporter to update an
          *** ODBC File and bucket information to report on...
          IF MODE # 'XML' THEN
             UT.SEC3 30,HAS.ODBC
          END
          HAS.ODBC = NO

          *** ODBC Is not supported at this time.  If it ever becomes a
          *** product, this will need to be corrected.
          IF HAS.ODBC THEN
             GOSUB SETUP.ODBC
          END
          IF MODE # 'XML' THEN
             PRINTER.ON WDTH,TITLE,DOC.ID,HDG,RPT.DFLT=DRPT
             FILE.ID = "ODBC.":DOC.ID
          END
          IF HAS.ODBC THEN
             BUCKETS = ""

             *** Create the for the ODBC file information.
             TRY.CT = 0
RETRY.CREATE: *
             TRY.CT += 1
             CMD     = "CREATE-FILE ":FILE.ID:" 1 500,4,30"
             EXECUTE CMD CAPTURING MSG
             REC.NUM = 0
             UT.OPEN.FILE FILE.ID,ODBCFILE,ERR.MSG,YES
             IF ERR.MSG THEN
                ERR.MSG = ""
                IF TRY.CT < 3 THEN
                   GOTO RETRY.CREATE
                END ELSE
                   ODBC.MSG = 'Unable to open an ODBC File to run : ':TITLE
                   SEND.MESSAGE "Phantom",USER.ID,ODBC.MSG
                   GOTO OPEN.ERR
                END
             END

             *** Create the summary bucket files for product...
             CMD     = "CREATE-FILE ":FILE.ID:".PN 1 500,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".PN",PN.ODBCFILE,ERR.MSG,YES

             *** Create the summary bucket files for the Price/Buy Line...
             IF SLCT[1,3] = 'Buy' THEN
                FILE.LINE = 'BUY_LINE'
             END ELSE
                FILE.LINE = 'PRICE_LINE'
             END

             CMD     = "CREATE-FILE ":FILE.ID:".":FILE.LINE:" 1 101,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".":FILE.LINE,PL.ODBCFILE,ERR.MSG,YES

             *** Create the summary bucket files for the G/L Code...
             CMD     = "CREATE-FILE ":FILE.ID:".G/L_CODE 1 101,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".G/L_CODE",GL.ODBCFILE,ERR.MSG,YES

             IF DETAIL[1,6] = 'Detail' THEN
                *** Create the summary bucket files for the product ranks..
                CMD     = "CREATE-FILE ":FILE.ID:".PROD.RANK 1 101,4,30"
                EXECUTE CMD CAPTURING MSG
                UT.OPEN.FILE FILE.ID:".PROD.RANK",RANK.ODBCFILE,ERR.MSG,YES
             END
          END

*** Print our report data...
          ITM.CT     = 0
          ENT.CT     = 0
          LINE.CT    = 0
          RANK.CT    = 0
          SEQUENCE   = ''
          BRK.ON     = ''
          SORT.DESC  = ''
          LAST.BRK   = '@@'
          LAST.DESC  = '@@'
          LAST.RANKS = '@@'
          LAST.EBK   = '@@'
          LTOLS      = '' ; *Sortby Totals
          PTOLS      = '' ; *Product Totals
          RTOLS      = '' ; *Product Ranks Totals
          ETOLS      = '' ; *Entity Totals
          BTOLS      = '' ; *Branch Totals
          GTOLS      = '' ; *Grand Totals
          LTOLS.UM   = '' ; *Sortby Totals Units of Measure
          RTOLS.UM   = '' ; *Product Rank Totals UoM
          ETOLS.UM   = '' ; *Entity Totals UoM
          BTOLS.UM   = '' ; *Branch Totals UoM
          GTOLS.UM   = '' ; *Grand Totals UoM
          PRT.SBY    = NO
          PRT.RANKS  = NO
          SEL.CCN    = ''

          *** If the user has selected a Consignment Entity...
          IF CCN THEN SEL.CCN = CCN
          BLANK.LINE.PRINTED  = NO
          PRINTING.GTOTALS    = NO

          SSELECT TEMPFILE
          LOOP
             READNEXT TID ELSE EXIT
             READ TMP FROM TEMPFILE,TID ELSE CONTINUE

             * If we are printing Summary by Branch, remove the branch
             * at the beginning of the record so the rest of the logic
             * will flow through.
             IF INDEX(DETAIL,'by Branch',1) THEN
                TID = FIELD(TID,'~',2,99)
             END

*** Parse out the data form our Tempfile Id...
             *** If we're Page Breaking on a Consignment Entity...
             IF ENTBK THEN
                CN = FIELD(TID,'~',2)
                BEGIN CASE
                *** If the User wanted the data grouped by Product Ranks
                *** and sorted by Product GL Code...
                CASE GROUP.RANKS AND SORTBY[1,7] = 'Prod GL'
                   PRODUCT.RANKS = FIELD(TID,'~',3)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',4))
                   PN = FIELD(TID,'~',7)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',8)
                   END
                CASE GROUP.RANKS
                   PRODUCT.RANKS = FIELD(TID,'~',3)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',4))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                 IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',5)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',6 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',7 + VPOS)
                   END
                CASE SORTBY[1,7] = 'Prod GL'
                   SORT.VALUE = TRIM(FIELD(TID,'~',3))
                   PN = FIELD(TID,'~',6)
                   BR = FIELD(TID,'~',7)
                CASE OTHERWISE
                   SORT.VALUE = TRIM(FIELD(TID,'~',3))
                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                  IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',4)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',5 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',6 + VPOS)
                   END
                END CASE
             END ELSE
                BEGIN CASE
                CASE GROUP.RANKS AND SORTBY[1,7] = 'Prod GL'
                   PRODUCT.RANKS = FIELD(TID,'~',1)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',2))
                   PN = FIELD(TID,'~',5)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',6)
                   END
                CASE GROUP.RANKS
                   PRODUCT.RANKS = FIELD(TID,'~',1)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',2))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                 IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',3)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',4 + VPOS)


                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',5 + VPOS)
                   END
                CASE SORTBY[1,7] = 'Prod GL'
                   SORT.VALUE = TRIM(FIELD(TID,'~',1))
                   PN = FIELD(TID,'~',4)
                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',5)
                   END
                CASE OTHERWISE
                   SORT.VALUE = TRIM(FIELD(TID,'~',1))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                   IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',3 + VPOS)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',3 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',4 + VPOS)
                   END
                END CASE
             END

             TAG.QTY   = TMP<1>
             STK.QTY   = TMP<2>
             DISP.QTY  = TMP<3>
             REV.QTY   = TMP<4>
             VCN.QTY   = TMP<5>
             EXT.BASE  = RAISE(TMP<7>)
             TAG.EXT   = TMP<16>

             TAG.IDS   = ''
             TAG.QTYS  = ''
             TAG.COSTS = ''
             TAG.BASE  = ''

             IF TAG.QTY+0 # 0 THEN
                TAG.BASE = OCONV(TAG.EXT,'MR3') / TAG.QTY
             END
             TAG.EXT = ICONV(OCONV(TAG.EXT,'MR3'),'MR2')

             ITEM.TOLS      = STK.QTY
             ITEM.TOLS<1,2> = TAG.QTY
             ITEM.TOLS<1,3> = TAG.EXT
             ITEM.TOLS<1,5> = DISP.QTY
             ITEM.TOLS<1,6> = REV.QTY
             ITEM.TOLS<1,7> = VCN.QTY

             EXT.TOLS = ""
             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                EXT.AMT = EXT.BASE<ECT,1>
                COL     = EXT.BASE<ECT,2>
                * add to column if there is an ext column
                IF COL # 0 THEN EXT.TOLS<COL> = EXT.AMT
             NEXT ECT

             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PER        = TMP<8>
                PER.UM     = TMP<9>
                DFLT.PER   = TMP<10>
                DFLT.ALPHA = TMP<11>
                UNIT.BASE  = RAISE(TMP<12>)

                IF DETAIL[1,6] = 'Detail' THEN
                   TAG.IDS   = TMP<13>
                   TAG.QTYS  = TMP<14>
                   TAG.COSTS = TMP<15>
                   * if splitting out tag costs, remove from extended
                   IF TAG.COSTS # '' THEN
                      FOR ECT = 1 TO EXT.CT
                         EXT.BASE<ECT,1> -= TAG.EXT
                      NEXT ECT
                   END
                END
                CCN.LIST   = TMP<17>
                CCN.QTYS   = TMP<18>
                VCN.LIST   = TMP<19>
                VCN.QTYS   = TMP<20>
                FIFO.IDS   = TMP<21>
                FIFO.QTYS  = TMP<22>
                FIFO.COSTS = TMP<23>
                FIFO.DTS   = TMP<24>

                *** Add consignment amounts to tols for product
                CCN.CT = DCOUNT(CCN.LIST<1>,VM)
                VCN.CT = DCOUNT(VCN.LIST<1>,VM)
                FOR XX = 1 TO CCN.CT
                   ITEM.TOLS<1,1> += CCN.QTYS<1,XX>/DFLT.PER
                   * add to each extended amt
                   EXT.CT = DCOUNT(EXT.BASE,AM)
                   FOR ECT = 1 TO EXT.CT
                      COL = EXT.BASE<ECT,2>
                      * add to column if there is an ext column
                      IF COL # 0 THEN
                         CCN.EXT = (CCN.QTYS<1,XX>*100)*(OCONV(UNIT.BASE<ECT,1>,'MR9'))
                         EXT.TOLS<COL> += CCN.EXT
                      END
                   NEXT ECT
                NEXT XX

                FOR XX = 1 TO VCN.CT
                   ITEM.TOLS<1,7> += VCN.QTYS<1,XX>/DFLT.PER
                   * Exclude vendor consignment in extended price calc,
                   * since we do not own title to the inventory.
                   * Unless requested to include it.
                   IF SHOW.VPRC THEN
                      EXT.CT = DCOUNT(EXT.BASE,AM)
                      FOR ECT = 1 TO EXT.CT
                         COL = EXT.BASE<ECT,2>
                         * add to column if there is an ext column
                         IF COL # 0 THEN
                            VCN.EXT = (VCN.QTYS<1,XX>*100)*(OCONV(UNIT.BASE<ECT,1>,'MR9'))
                            EXT.TOLS<COL> += VCN.EXT
                         END
                      NEXT ECT
                   END
                NEXT XX
             END ELSE
                *** If we only picked up one Unit of Measure (type)...
                IF TMP<11,2> = '' THEN
                   LOCATE TMP<11> IN LTOLS.UM<1> SETTING LPOS ELSE
                      LTOLS.UM<1,LPOS> = TMP<11>
                   END
                   LOCATE TMP<11> IN RTOLS.UM<1> SETTING RPOS ELSE
                      RTOLS.UM<1,RPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN ETOLS.UM<1> SETTING EPOS ELSE
                      ETOLS.UM<1,EPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN BTOLS.UM<1> SETTING BPOS ELSE
                      BTOLS.UM<1,BPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN GTOLS.UM<1> SETTING GPOS ELSE
                      GTOLS.UM<1,GPOS> = TMP<11>
                   END
                END ELSE
                   *** Otherwise, we know we won't be able to display
                   *** a Unit of Measure for the Product Rank, Entity,
                   *** or Grand Totals...
                   IF RTOLS.UM<1,2> = '' THEN RTOLS.UM<1,2> = '**'
                   IF ETOLS.UM<1,2> = '' THEN ETOLS.UM<1,2> = '**'
                   IF BTOLS.UM<1,2> = '' THEN BTOLS.UM<1,2> = '**'
                   IF GTOLS.UM<1,2> = '' THEN GTOLS.UM<1,2> = '**'
                   IF LTOLS.UM<1,2> = '' THEN LTOLS.UM<1,2> = '**'
                END
             END
             *** Go print one line of the report...
             GOSUB PRT.ONE
          REPEAT

*** Print out any leftover totals (either for sorby or entity)...
          *** Print the last of our Sort by Totals...
          IF NOT(ENTBK) THEN
             IF GROUP.RANKS THEN
                IF ITM.CT THEN GOSUB PRT.RTOLS
             END ELSE
                IF ITM.CT THEN
                   IF INDEX(DETAIL,'by Branch',1) THEN
                      GOSUB PRT.BTOLS
                   END ELSE
                      GOSUB PRT.SUB
                   END
                END
             END
          END ELSE
             *** Print the last of our Consignment Entity Totals...
             IF ENT.CT THEN TMP.CN = CN; GOSUB PRT.ETOLS
          END

          *** Go print our Grand Totals...
          IF NOT(INDEX(DETAIL,'No Totals',1)) THEN
             TOL.DESC         = 'Grand Totals ------'
             ODET             = DET
             ODET             = DET
             TOLS             = GTOLS
             TOLS.UM          = GTOLS.UM
             TOLS.EXT         = GTOLS.EXT
             DET              = YES
             PRINTING.GTOTALS = YES

             GOSUB PRT.TOLS
          END

          *** Reset the value for what level of detail so that the ODBC
          *** file is properly created.
          DET = ODET

          IF MODE # 'XML' THEN
             PRINTER.OFF DOC.ID
          END

          IF HAS.ODBC THEN
             *** Create the dictionaries for the created file format.
             GOSUB CREATE.DICTS
             GOSUB WRITE.BCKTS
          END
OPEN.ERR: **

          UT.TEMPFILE.DELETE FLNM.ID
          UT.PH.CLEANUP

          IF MODE = 'XML' THEN
             LN.CT = DCOUNT(FOOTER.RESP,AM)
             FOR LN = 1 TO LN.CT
                WRITESEQ FOOTER.RESP<LN> ON OUTFILE ELSE EXIT
                IF NOT(OUTBOUND.EMAIL) THEN
                   HOLD.LN.CT += 1
                END
             NEXT LN

             UT.OPEN.FILE 'VOC',VOCFILE,ERR.MSG,YES
             IF ERR.MSG THEN RETURN
             READV ATTACH.PATH FROM VOCFILE,'MSG-OUT',2 ELSE
                ATTACH.PATH = '/u2/msg-out'
             END
             IF OS.TYPE$ = 'NT' THEN       ;* Windows NT Version
                ATTACH.PATH := '\':OUT.ID
             END ELSE
                ATTACH.PATH := '/':OUT.ID
             END

             IF OUTBOUND.EMAIL THEN
                TO.ADDRESS  = DRPT<58,1>
                SUBJECT     = DRPT<58,5>
                CC.ADDRESS  = DRPT<58,7>
                BCC.ADDRESS = DRPT<58,8>
                O.BODY      = DRPT<57>
                FROM.NAME   = DRPT<58,12>
                NETMAIL.OUT TO.ADDRESS,SUBJECT,O.BODY,FROM.NAME,BCC.ADDRESS,CC.ADDRESS,ATTACH.PATH
             END ELSE
                RPT<10> = HOLD.LN.CT
                RPT<66> = 'Binary File'
                WRITE RPT ON RPTFILE,HOLD.ID
                CLOSESEQ OUTFILE
             END
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' XML Extract is Complete'

             * Clean up the extract file
             IF OS.TYPE$ = "NT" THEN
                COMMAND = "del "
                ASROOT  = NO
             END ELSE
                COMMAND = "rm "
                ASROOT  = YES
             END
             COMMAND := ATTACH.PATH
             EXECUTE.OS.COMMAND MSG,COMMAND,ASROOT
          END ELSE
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' is Complete'
          END
          STOP
*-------------------------------------------------------------------------*
PRT.ONE:
          *** Modified the code to return if a valid PN cannot be read.

          MATREAD PRD FROM PRDFILE,PN  ELSE RETURN

          BR.SHORT.DESC  = ''
          BR.COST.CENTER = ''
          IF BR # '' THEN
             READ TERR FROM TERRFILE,BR ELSE TERR = ''
             BR.SHORT.DESC  = TERR<1>           ;* Branch Short Description
             BR.COST.CENTER = TERR<34>          ;* Branch Cost Center
          END

          *** If we're displaying the Product's Ranks...
          *** We are checking the TEMP.PRODUCT.RANKS var to make sure
          *** we have RANKS in them as we don't want to print it if the
          *** user required ranking as part of this report.
          IF DISPLAY.RANKS THEN
             PRDC.BR.GET.VAL BR,PN,1,TEMP.PRODUCT.RANKS
             CONVERT VM TO ',' IN TEMP.PRODUCT.RANKS
          END
          GL.CODE  = PRD(2)
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             PRDD.BR.GET BR,PN
          END

          IF SLCT[1,3] = 'Buy' THEN
             TLINE = PRD(12)
          END ELSE
             TLINE = PRD(9)
          END

          BEGIN CASE
          CASE INDEX(DETAIL,'by Branch',1)
             BRK.ON = BR
          CASE SORTBY[1,7] = 'Prod GL'
             BRK.ON = GL.CODE
          CASE SORTBY = 'Price Line'
             BRK.ON = PRD(9)
          CASE SORTBY = 'Buy Line'
             BRK.ON = PRD(12)
          CASE SORTBY = 'Sell Group'
             PRD.BR.GET.VAL BRCHS<1,1>,PN,24,SEL.GRP
             SEL.GRP = SEL.GRP<1,1,1>
             BRK.ON = SEL.GRP
          CASE SORTBY = 'Buy Group'
             PRD.BR.GET.VAL BRCHS<1,1>,PN,23,BUY.GRP
             BUY.GRP = BUY.GRP<1,1,1>
             BRK.ON = BUY.GRP
          CASE SORTBY = 'Prod ID'
             BRK.ON = PRD(9)
          END CASE

*** Check whether we need to print any Totals...
          *** If we're not Page Breaking on Consignment Vendors
          *** or Customers...

          IF NOT(ENTBK) THEN
             *** If we're grouping the data by product ranks...
             IF GROUP.RANKS THEN
                IF (LAST.RANKS # PRODUCT.RANKS) AND LAST.RANKS # '@@' THEN
                   *** Need to print our Sort by Subtotals first...
                   IF ITM.CT THEN GOSUB PRT.RTOLS
                END ELSE
                   LAST.BRK  = BRK.ON
                   LAST.DESC = SORT.DESC
                   ITM.CT    = 0
                END
                LAST.RANKS   = PRODUCT.RANKS
             END ELSE
                IF LAST.BRK # BRK.ON THEN
                   *** If we have in fact printed data for a line/GL Code,
                   *** go print Subtotals for the last one...
                   IF LAST.BRK # '@@' AND ITM.CT AND SUBT.ON.SORTBY THEN
                      IF INDEX(DETAIL,'by Branch',1) THEN
                         GOSUB PRT.BTOLS
                      END ELSE
                         GOSUB PRT.SUB
                      END
                   END
                   LAST.BRK  = BRK.ON
                   LAST.DESC = SORT.DESC
                   ITM.CT    = 0
                END
             END
          END ELSE
             EBK.ON = CN
             IF LAST.EBK # EBK.ON THEN
                IF LAST.EBK # '@@' THEN
                   IF ENT.CT THEN TMP.CN = LAST.EBK; GOSUB PRT.ETOLS
                END
                LAST.EBK    = EBK.ON
                ENT.CT      = 0
                LAST.RANKS  = PRODUCT.RANKS
             END ELSE
                *** If we're grouping the data by product ranks...
                IF GROUP.RANKS THEN
                   IF (LAST.RANKS # PRODUCT.RANKS) AND LAST.RANKS#'@@' THEN
                      *** Need to print our Sort by Subtotals first...
                      IF ITM.CT THEN GOSUB PRT.RTOLS
                   END
                   LAST.RANKS = PRODUCT.RANKS
                END
             END
          END

          PTOLS       = ''
          PRINTED.DET = NO
          GOSUB PRINT.QTYS
          LAST.BR.DESC     = BR.SHORT.DESC
          LAST.COST.CENTER = BR.COST.CENTER

          RETURN
*-------------------------------------------------------------------------*
CLR.KIT:  *** Clear out all kit values for dynamic kit so it will act like
          *** a "normal" stock item
          PRD(52) = ''
          PRD(53) = ''
          PRD(85) = ''
          PRD(86) = ''
          PRD(87) = ''

          RETURN
*-------------------------------------------------------------------------*
GET.VAL:  ***
          UNIT.BASE = ""
          EXT.BASE  = ""
          BASN.CT = DCOUNT(BASIS.LIST,AM)
          FOR BCT = 1 TO BASN.CT
             GBASN    = BASIS.LIST<BCT,1>
             UNIT.COL = BASIS.LIST<BCT,2> + 0
             EXT.COL  = BASIS.LIST<BCT,3> + 0

             IF (GBASN > 30) THEN
                GOSUB GET.FIFO
             END ELSE
                GOSUB GET.ONE.VAL
             END

             UNIT.BASE<-1> = BASE:VM:UNIT.COL

             * Tags need to be included into the totals for each base
             IF OHQ # "" OR ADD.TAG THEN
                EXT = ICONV(OCONV(TAG.EXT.AMT,'MR3'),'MR2')
                IF OHQ # "" THEN
                   EXT += ICONV((OCONV(BASE,'MR9') * OHQ),'MR2')
                END
                EXT.BASE<-1> = EXT:VM:EXT.COL
             END
          NEXT BCT

          RETURN
*-------------------------------------------------------------------------*
GET.ONE.VAL: *** Get the dollar value of one unit of inventory for current
          *** product.
          *** PN and BR must be active.. BASE is the useful value returned.

          GET.ALL.PRD BR,PN
          GLOBAL.BASN.GET GBASN,BASN

          KOPTS = PRD(86)
          IF KOPTS<1,KPT>#'' THEN
             KOPTS<1,1> = KOPTS<1,KPT>
          END

          LOCTN.CTRL.TYPE = PRDD.BR(11)

          BASE = 0
          IF GBDESC = "Avg Lot Actual Cost" THEN
             GOSUB GET.AVG.LOT.CST
          END

          IF BASE = 0 THEN
             * If 'Show $0 Average Cost' is Yes (AVG.CST) and basis is
             * for an average cost then set ZERO.OK so that GET.BASE.AOD
             * will not use the current cost when there isn't any pricing
             * found in the product log.
             BEGIN CASE
             CASE NOT(AVG.CST); ZERO.OK = NO
             CASE BASN = 8    ; ZERO.OK = YES ;* Avg Actual Cost
             CASE BASN = 22   ; ZERO.OK = YES ;* Avg Landed Cost
             CASE BASN = 23   ; ZERO.OK = YES ;* Frozen Avg Cost
             CASE BASN = 24   ; ZERO.OK = YES ;* Frozen Last Cost
             CASE BASN = 26   ; ZERO.OK = YES ;* Frozen Avg Landed
             CASE BASN = 27   ; ZERO.OK = YES ;* Frozen Landed
             CASE OTHERWISE   ; ZERO.OK = NO
             END CASE

             GET.BASE.AOD PN,BR,BASN,CAOD,BASE,PER,KOPTS,PRD(52),PRD(53),PRD(87),,ZERO.OK

          END

          BASE = ICONV(ICONV(OCONV(BASE,'MR3'),'MR9')/PER,'MR0')

          RETURN
*-------------------------------------------------------------------------*
GET.FIFO: ***
          GET.ALL.PRD BR,PN

          CHK.QTY = STK.QTY + TAG.QTY

          FIFO.BR = BR

          GET.FIFO.COST FIFO.COST,PN,FIFO.BR,AOD,CHK.QTY,FIFO.QTYS,FIFO.IDS,FIFO.COSTS,FIFO.DTS,HIST.YEARS,INCL.ADJ,IS.CRRBS

          BASE = ICONV(FIFO.COST,'MR9')

          RETURN
*-------------------------------------------------------------------------*
GET.AVG.LOT.CST:
          LOTS = ''
          QTYS = ''
          L.CT = DCOUNT(PRDD.BR(8),VM)
          FOR L = 1 TO L.CT
            IF PRDD.BR(1)<1,L>+0 # 0 THEN
               LOT = FIELD(FIELD(FIELD(PRDD.BR(8)<1,L>,'~',2),'|',2),'-',1)
               LOT.QTY = PRDD.BR(1)<1,L>
               LOCATE LOT IN LOTS SETTING POS ELSE
                  LOTS<POS> = LOT
                  QTYS<POS> = 0
               END
               QTYS<POS> += LOT.QTY
            END
          NEXT L
          TOT.COST.ADDR = 0
          COST.ADDR     = 0
          TOT.LOT.COST  = 0
          TOT.LOT.QTY   = 0
          LOT.CT        = 0
          L.CT = DCOUNT(LOTS,AM)
          FOR L = 1 TO L.CT
             LOT = LOTS<L>
             QTY = QTYS<L>
             IF QTY > 0 THEN
                ID = PN:'~':LOT
                READ LREC FROM DLOTFILE,ID THEN
                   PRICE.PER.GET PPER,PER.UM,,,PRC.DATE
                   LOT.COST = LREC<28>/PPER
                   COST.ADDR = LREC<19>*QTY
                   IF LOT.COST # "" THEN ;* Note 0 is ok.
            * Note a 0 LOT.COST indicates that it's intended so it's used.
            * Null LOT.COSTS are not intended, so they are not used.
                      TOT.COST.ADDR += COST.ADDR
                      TOT.LOT.COST += LOT.COST*QTY
                      TOT.LOT.QTY  += QTY
                      LOT.CT       += 1
                   END
                END
             END
          NEXT L

          IF TOT.LOT.QTY - 0 # 0 THEN
             BASE = (TOT.LOT.COST+TOT.COST.ADDR)/TOT.LOT.QTY
             PRICE.PER.GET PER,PER.UM,,,PRC.DATE
          END

          RETURN
*-------------------------------------------------------------------------*
PRINT.QTYS:*

          *** If we're printing it in Detail format, we'll need to
          *** check/store the Qty Unit of Measure so that we can determine
          *** whether they are displayable in our totals...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             IF DETAIL[1,6] = 'Detail' THEN
                *** Product Rank Totals...
                LOCATE DFLT.ALPHA IN RTOLS.UM<1> SETTING RPOS ELSE
                   RTOLS.UM<1,RPOS> = DFLT.ALPHA
                END
             END
             *** Sortby Totals...
             LOCATE DFLT.ALPHA IN LTOLS.UM<1> SETTING LPOS ELSE
                LTOLS.UM<1,LPOS> = DFLT.ALPHA
             END
             *** Entity Totals...
             LOCATE DFLT.ALPHA IN ETOLS.UM<1> SETTING EPOS ELSE
                ETOLS.UM<1,EPOS> = DFLT.ALPHA
             END
             *** Branch Totals...
             LOCATE DFLT.ALPHA IN BTOLS.UM<1> SETTING BPOS ELSE
                BTOLS.UM<1,BPOS> = DFLT.ALPHA
             END
             *** Grand Totals...
             LOCATE DFLT.ALPHA IN GTOLS.UM<1> SETTING GPOS ELSE
                GTOLS.UM<1,GPOS> = DFLT.ALPHA
             END
          END

          *** Add to the Total arrays...
          PTOLS = ADDS(PTOLS,ITEM.TOLS) ;*Product Totals
          RTOLS = ADDS(RTOLS,ITEM.TOLS) ;*Product Rank Totals
          LTOLS = ADDS(LTOLS,ITEM.TOLS) ;*Sortby Totals
          ETOLS = ADDS(ETOLS,ITEM.TOLS) ;*Entity Totals
          BTOLS = ADDS(BTOLS,ITEM.TOLS) ;*Branch Totals
          GTOLS = ADDS(GTOLS,ITEM.TOLS) ;*Grand Totals

          EXT.CT = DCOUNT(EXT.TOLS,AM)
          FOR COL = 1 TO EXT.CT
             PTOLS.EXT<COL> += EXT.TOLS<COL>
             RTOLS.EXT<COL> += EXT.TOLS<COL>
             LTOLS.EXT<COL> += EXT.TOLS<COL>
             ETOLS.EXT<COL> += EXT.TOLS<COL>
             BTOLS.EXT<COL> += EXT.TOLS<COL>
             GTOLS.EXT<COL> += EXT.TOLS<COL>
          NEXT ECT

          ITM.CT  += 1
          ENT.CT  += 1
          LINE.CT += 1
          RANK.CT += 1

          PRINTED.DET = YES

          BEGIN CASE
          CASE SORTBY = 'Price Line'
             READV SORT.DESC FROM PLNEFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY = 'Buy Line'
             READV SORT.DESC FROM BLNEFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY = 'Sell Group' OR SORTBY = 'Buy Group'
             READV SORT.DESC FROM PGRPFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY[1,7] = 'Prod GL'
             SORT.DESC = PRD(2)
          CASE SORTBY = 'Prod ID'
             PN = FIELD(PRD(93)<1,3>,'.',1)
             SORT.DESC = PN

          END CASE

          *** If the report format is Detail OR Summary by Product...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             DESC = PRD(1)
             CONVERT VM TO ' ' IN DESC

             *** Only write to the ODBC file if they have the add-on key
             *** for solar reporter.
             IF HAS.ODBC THEN
                *** Don't need to determine the Sequence if we already
                *** know what it is...
                IF NOT(SEQUENCE) THEN
                   IF SLCT[1,3] = 'Buy' THEN
                      GET.BLINE.IDS ,L,TLINE
                   END ELSE
                      GET.LINE.IDS ,L,TLINE
                   END
                   LOCATE PN IN L<1> SETTING SEQUENCE ELSE SEQUENCE = ''
                END

                REC      = ''
                REC.NUM += 1
                REC<1>   = DESC
                REC<2>   = SORT.VALUE
                REC<3>   = SORT.DESC

                REC<4>   = ICONV(OCONV(STK.QTY,"MR0"),"MR2")
                REC<5>   = ICONV(OCONV(TAG.QTY,"MR0"),"MR2")
                REC<6>   = ICONV(OCONV(REV.QTY,"MR0"),"MR2")
                REC<7>   = ICONV(OCONV(DISP.QTY,"MR0"),"MR2")
                REC<8>   = ICONV(OCONV(VCN.QTY,"MR0"),"MR2")

                REC<9>   = ICONV((OCONV(BASE,"MR9")*PER),"MR3")
                REC<10>  = PER.UM:'/':PER
                REC<11>  = ICONV(OCONV(EXT,"MR2"),"MR2")
                REC<12>  = DFLT.ALPHA:'/':DFLT.PER
                REC<13>  = BR
                REC<14>  = GL.CODE
                REC<15>  = PN
                REC<16>  = SEQUENCE

                PRD.BR.GET.VAL BR,PN,25,SER.TRACK

                *** If they're putting the product ranks on the report
                *** Insert our info into our report arrays, pushing all our
                *** other report values up at the same time...
                IF DISPLAY.RANKS THEN
                   *** We can only store the Rank Numbers that were
                   *** selected on, so that our report will 'break'
                   *** (subtotal) properly...
                   ODBC.RANKS = FIELD(TEMP.PRODUCT.RANKS,',',1,RANK.NUM.CT)
                   REC        = INSERT(REC,4;ODBC.RANKS)
                END

                *** If we're Page Breaking on Consignment Vendors
                *** or Customers...
                IF ENTBK THEN
                   READV EBK.DESC FROM CUSFILE,EBK.ON,1 ELSE
                      EBK.DESC = EBK.ON
                   END
                   REC = INSERT(REC,1;EBK.DESC)
                END

                WRITE REC ON ODBCFILE,REC.NUM "R%7"

                BCKTS    = PN
                BCKTS<2> = SORT.VALUE
                BCKTS<3> = GL.CODE
                IF GROUP.RANKS AND DETAIL[1,6] = 'Detail' THEN
                   BCKTS<4> = TEMP.PRODUCT.RANKS
                END

                BEGIN CASE
                CASE ENTBK AND DISPLAY.RANKS
                   SUMRY.ATTB = 6
                CASE ENTBK OR DISPLAY.RANKS
                   SUMRY.ATTB = 5
                CASE OTHERWISE
                   SUMRY.ATTB = 4
                END CASE

                SUMRY    = ""
                SUMRY<1> = REC<SUMRY.ATTB>
                SUMRY<2> = REC<SUMRY.ATTB+1>
                SUMRY<3> = REC<SUMRY.ATTB+2>
                SUMRY<4> = REC<SUMRY.ATTB+3>
                SUMRY<5> = REC<SUMRY.ATTB+6>
                GOSUB UPD.SUMS
             END

             * If breaking out tagged information, do not print the tag
             * qty on the main line
             IF DETAIL[1,6] = 'Detail' THEN TAG.QTY = 0
             CCN.CT = DCOUNT(CCN.LIST<1>,VM)
             VCN.CT = DCOUNT(VCN.LIST<1>,VM)

             *** We need to always print the header line if we have tags
             *** to display as the tagged qty for main line may be zero.
             IF (STK.QTY+0#0 OR TAG.QTY+0#0 OR REV.QTY+0#0 OR DISP.QTY+0#0 OR VCN.QTY+0#0 OR (SUP.ZERO = 'Include' AND VCSGN # 'Only' AND CCSGN # 'Only') OR (DETAIL[1,6] = 'Detail' AND TAG.IDS)) THEN
                IF MODE = 'XML' THEN
                   GOSUB WRITE.PN
                END ELSE
                   GOSUB PRINT.PN
                END
             END
             IF MODE # 'XML' THEN
                GOSUB PRINT.CCN
                GOSUB PRINT.VCN
                IF TAG.IDS THEN GOSUB PRT.TAGS
                GOSUB PRINT.SNS
                BLANK.LINE.PRINTED = NO
             END

          END

          RETURN
*-------------------------------------------------------------------------*
WRITE.PN:
          GET.PIL PIL,BR,PN,DATE()
          IF PIL < 0 THEN PIL = 0

          GET.ALL.PRD BR,PN,-1
          GET.PCGID PCGID,PRD(18),PRD(12)
          PRD.LEAD.TIME.GET LEAD,PN,BR,PRD(12),PRDC.BR(5),PCGID
          GET.BUY.DATA BR,PN,DEMAND,EOQ,ADJ.SF,LEAD,MANS,BUYPKG,MQ,MIN,MAX,IS.DIV

          GLOBAL.BASN.GET GBASN,BASN

          START.DATE = AOD-365
          END.DATE = AOD+0

          PROD.INFO BR,PN,START.DATE,END.DATE,SLS,,COGS,,AVEQOH

          COGS = ICONV(COGS,'MR0')
          GOSUB GET.ANZR
          ANNUAL.DOLLAR = ICONV(COGS * ANNUALIZER,'MR0')
          ANNUAL.DOLLAR = OCONV(ANNUAL.DOLLAR,'MR2')

          IF SLS  = 0 OR BASN # 8 THEN
             KOPTS = PRD(86)
             IF BASN # 1 AND KOPTS<1,3>#'' THEN
                KOPTS<1,1> = KOPTS<1,3>
             END
             GET.BASE PN,BR,BASN,AOD,BASE,PPER,KOPTS,PRD(52),PRD(53),PRD(87)
             * Since our Cost is based on a certain Qty, and we want
             * to get this value down to the lowest possible Unit of
             * Measure, we'll divide the Value by the Price Per. This
             * will give us our Cost per 'each'...
             AVE.COST = OCONV(BASE,'MR3') / PPER
          END ELSE
             * Average Cost per 'each' of this product...
             AVE.COST = OCONV((COGS / SLS),'MR2')
          END

          * OnHand Qty Dollars...
          AVE.$OH = (AVEQOH * ICONV(AVE.COST,'MR2'))

          * This product's Total OnHand Qty Dollars for this branches...
          PROD.AVE.$OH = OCONV(AVE.$OH,'MR2')

          * Calculate Turns...
          * Turns equals Annual Dollar divided by On Hand $
          IF PROD.AVE.$OH = 0 THEN
             TURNS = -1
          END ELSE
             TURNS = ANNUAL.DOLLAR / PROD.AVE.$OH
          END
          IF TURNS < 0 THEN TURNS = 0

          DISP.RANK1 = PRDC.BR(1)<1,1>
          DISP.RANK2 = PRDC.BR(1)<1,2>
          DISP.RANK3 = PRDC.BR(1)<1,3>
          DISP.RANK4 = PRDC.BR(1)<1,4>
          DISP.RANK5 = PRDC.BR(1)<1,5>
          BEGIN CASE
          CASE SF.RANK = 1
             SF.RANK.VAL = DISP.RANK1
          CASE SF.RANK = 2
             SF.RANK.VAL = DISP.RANK2
          CASE SF.RANK = 3
             SF.RANK.VAL = DISP.RANK3
          CASE SF.RANK = 4
             SF.RANK.VAL = DISP.RANK4
          CASE OTHERWISE
             SF.RANK.VAL = DISP.RANK5
          END CASE

          HI.QTY      = PRDC.BR(21)

          DMD.DAY     = OCONV(PRDC.BR(4),'MR3')
          RAW.HITS    = PRDC.BR(9)<1,1>
          AVG.SALE    = PRDC.BR(6)<1,2>
          ANNUAL.HITS = PRDC.BR(3)
          HITS.MOS    = ANNUAL.HITS / 12


          IF PRD(3) = 1 OR PRD(3) = 4 OR PRD(3) = 7 THEN STK.ITEM = 'Yes' ELSE STK.ITEM = 'No'

          IF AVG.SALE <= 0 OR NOT(AVG.SALE) THEN AVG.SALE = 1
          IF IS.DIV THEN IDIV = 'Y' ELSE IDIV = 'N'
          IF NOT(BUYPKG) THEN BUYPKG = 1
          REP.COST = OCONV(UNIT.BASE<1,1>,'MR9')*PER

          IF DETAIL[1,6] = 'Detail' THEN
             TAG.QTY = SUM(TAG.QTYS)
          END

          GOSUB GET.OC
          DFLT.PER.GET 'P',PUR.PER
          MS      = ROUNDUP(MANS,PUR.PER)
          EOQ     = ROUNDUP(MQ,PUR.PER)
          EOQ.BUY = ROUND.UP(MQ,BUYPKG,MQ)
          EOQ.BUY = ROUNDUP(EOQ.BUY,PUR.PER)

          PROJ.SL = PRD.BR(60)
          IF NOT(PROJ.SL) THEN
             SF = PRD.BR(14)
             IF SF = '' THEN
                BUYLINE.BR.GET.VAL BR,PRD(12),12,SF
                IF NOT(NUM(SF)) THEN SF = ''
                IF SF = '' THEN SF = 1 ELSE SF = OCONV(SF,'MR2')
             END
             PROD.INVMA.SL.SF.CALC PROJ.SL,SF
          END

          THIS.ITEM.RESP = ITEM.RESP
          XML.DESC       = CHANGE(DESC,'&','&amp;')
          XML.DESC       = CHANGE(XML.DESC,'"','&quot;')

          UT.REP.STR THIS.ITEM.RESP,'&PN&',PN
          UT.REP.STR THIS.ITEM.RESP,'&BLINE&',PRD(12)
          UT.REP.STR THIS.ITEM.RESP,'&DESC&',XML.DESC
          UT.REP.STR THIS.ITEM.RESP,'&STK.ONHAND&',STK.QTY
          UT.REP.STR THIS.ITEM.RESP,'&TAG.ONHAND&',TAG.QTY
          UT.REP.STR THIS.ITEM.RESP,'&RFO.ONHAND&',REV.QTY
          UT.REP.STR THIS.ITEM.RESP,'&DISP.ONHAND&',DISP.QTY
          UT.REP.STR THIS.ITEM.RESP,'&SFRANK&',SF.RANK.VAL
          UT.REP.STR THIS.ITEM.RESP,'&RANK1&',DISP.RANK1
          UT.REP.STR THIS.ITEM.RESP,'&RANK2&',DISP.RANK2
          UT.REP.STR THIS.ITEM.RESP,'&RANK3&',DISP.RANK3
          UT.REP.STR THIS.ITEM.RESP,'&RANK4&',DISP.RANK4
          UT.REP.STR THIS.ITEM.RESP,'&RANK5&',DISP.RANK5
          UT.REP.STR THIS.ITEM.RESP,'&REP.COST&',REP.COST
          UT.REP.STR THIS.ITEM.RESP,'&OH.DOL&',OCONV(EXT.BASE<1,1>,'MR2')
          UT.REP.STR THIS.ITEM.RESP,'&FCST.HITS&',RAW.HITS
          UT.REP.STR THIS.ITEM.RESP,'&MOS.HITS&',HITS.MOS
          UT.REP.STR THIS.ITEM.RESP,'&DMD.DAY&',DMD.DAY
          UT.REP.STR THIS.ITEM.RESP,'&AVG.QTY&',AVG.SALE
          UT.REP.STR THIS.ITEM.RESP,'&HI.QTY&',HI.QTY
          UT.REP.STR THIS.ITEM.RESP,'&STK.ITEM&',STK.ITEM
          UT.REP.STR THIS.ITEM.RESP,'&PIL&',PIL
          UT.REP.STR THIS.ITEM.RESP,'&PER.UOM&',PER.UM
          UT.REP.STR THIS.ITEM.RESP,'&PER.QTY&',PER
          UT.REP.STR THIS.ITEM.RESP,'&PKG.DIV&',IDIV
          UT.REP.STR THIS.ITEM.RESP,'&PKG.QTY&',BUYPKG
          UT.REP.STR THIS.ITEM.RESP,'&LEAD.TM&',LEAD
          UT.REP.STR THIS.ITEM.RESP,'&ANNUAL.DOLLAR&',ANNUAL.DOLLAR
          UT.REP.STR THIS.ITEM.RESP,'&ANNUAL.HITS&',ANNUAL.HITS
          UT.REP.STR THIS.ITEM.RESP,'&ORDER.CYCLE&',OC
          UT.REP.STR THIS.ITEM.RESP,'&MANUAL.SAFETY&',MS
          UT.REP.STR THIS.ITEM.RESP,'&MQ&',MQ
          UT.REP.STR THIS.ITEM.RESP,'&PURCH.PER&',PUR.PER
          UT.REP.STR THIS.ITEM.RESP,'&TURNS&',TURNS
          UT.REP.STR THIS.ITEM.RESP,'&PROJ.SL&',PROJ.SL
          CONVERT AM TO '' IN THIS.ITEM.RESP
          WRITESEQ THIS.ITEM.RESP ON OUTFILE ELSE RETURN

          IF NOT(OUTBOUND.EMAIL) THEN
             HOLD.LN.CT += 1
          END

          RETURN
*-------------------------------------------------------------------------*
PRINT.PN:

          PRT.STR = ''
          COL.DATA = ''

          * Allow drill down capabilities for product.
          COL.DATA<1> = '^':PN

          COL.DATA<2> = DESC
          COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
          COL.DATA<5> = STK.QTY "R2,"
          IF DETAIL[1,6] # 'Detail' THEN
             COL.DATA<6>  = TAG.QTY  "R2,"
             COL.DATA<40> = TAG.BASE "R3,"
             COL.DATA<69> = OCONV(TAG.EXT,'MR2') "R2,"
          END
          COL.DATA<7> = REV.QTY "R2,"
          COL.DATA<8> = DISP.QTY "R2,"
          COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
          COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"
          BASE.CT = DCOUNT(UNIT.BASE,AM)
          FOR BCT = 1 TO BASE.CT
             BASE = UNIT.BASE<BCT,1>
             COL  = UNIT.BASE<BCT,2>
             IF (COL = 0) THEN CONTINUE
             COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
          NEXT BCT

          EXT.CT = DCOUNT(EXT.BASE,AM)
          FOR ECT = 1 TO EXT.CT
             EXT = EXT.BASE<ECT,1>
             COL = EXT.BASE<ECT,2>
             IF (COL = 0) THEN CONTINUE
             COL.DATA<COL> = OCONV(EXT,"MR2")          "R2,"
          NEXT BCT

          IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
             COL.DATA<9> = VCN.QTY "R2,"
          END
          IF DETAIL[1,6] = 'Detail' THEN
             COL.DATA<70> = BR
             COL.DATA<77> = BR.SHORT.DESC
             COL.DATA<78> = BR.COST.CENTER
          END
          IF TSORTBY # 'Prod GL Code' THEN
             COL.DATA<71> = GL.CODE
          END
          *** If we're displaying the Product's Ranks...
          IF DISPLAY.RANKS THEN
             COL.DATA<74> = TEMP.PRODUCT.RANKS
          END
          REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
          IF (TRIM(PRT.STR) # "") THEN
             PRINT PRT.STR
          END
          F.CT  = DCOUNT(FIFO.IDS,VM)
          IF (STK.QTY <= 0) THEN F.CT = 0
          FOR FF = 1 TO F.CT
             FIFO.QTY  = FIFO.QTYS<1,FF>
             FIFO.EXT  = FIFO.COSTS<1,FF>
             FIFO.BASE = ICONV((OCONV(FIFO.EXT,'MR2') / FIFO.QTY),'MR9')
             FIFO.ID   = FIFO.IDS<1,FF>
             FIFO.DT   = FIFO.DTS<1,FF>

             COL.DATA = ""
             IF FIFO.ID[1,3] = '***' THEN
                COL.DATA<75> = FIFO.ID           "L#17"
             END ELSE
                COL.DATA<75> = FIFO.ID           "L#17"
             END
             IF IS.CRRBS<1,FF> THEN
                COL.DATA<76> = FIFO.DT           "D4/":" *"
             END ELSE
                COL.DATA<76> = FIFO.DT           "D4/"
             END

             COL.DATA<5> = FIFO.QTY              "R,"
             COL.DATA<79>= OCONV(FIFO.BASE * PER,'MR9') "R3,"
             COL.DATA<80>= OCONV(FIFO.EXT,'MR2')"R2,"
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT FF

          RETURN
*-------------------------------------------------------------------------*
PRINT.CCN:
          FOR XX = 1 TO CCN.CT
             PRT.STR = ''
             COL.DATA = ''

             * Allow drill down capabilities for product.
             COL.DATA<1> = '^':PN

             COL.DATA<2> = DESC
             COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
             COL.DATA<5> = CCN.QTYS<1,XX>/DFLT.PER "R2,"
             COL.DATA<6> = '0' "R2,"
             COL.DATA<7> = '0' "R2,"
             COL.DATA<8> = '0' "R2,"
             COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             BASE.CT = DCOUNT(UNIT.BASE,AM)
             FOR BCT = 1 TO BASE.CT
                BASE = UNIT.BASE<BCT,1>
                COL  = UNIT.BASE<BCT,2>
                IF (COL = 0) THEN CONTINUE
                COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
             NEXT BCT

             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                BASE = UNIT.BASE<ECT,1>
                COL  = EXT.BASE<ECT,2>
                IF (COL = 0) THEN CONTINUE
                EXT = (CCN.QTYS<1,XX>)*(OCONV(BASE,"MR9"))
                COL.DATA<COL> = EXT                       "R2,"
             NEXT BCT

             IF DETAIL[1,6] = 'Detail' THEN
                COL.DATA<70> = BR
                COL.DATA<77> = BR.SHORT.DESC
                COL.DATA<78> = BR.COST.CENTER
             END
             IF TSORTBY # 'Prod GL Code' THEN
                COL.DATA<71> = GL.CODE
             END
             *** If we're displaying the Product's Ranks...
             IF DISPLAY.RANKS THEN
                COL.DATA<74> = TEMP.PRODUCT.RANKS
             END
             CCN = FIELD(CCN.LIST<1,XX>,'~',2)
             READV NAME FROM CUSFILE,CCN,1 ELSE NAME = CCN
             COL.DATA<72> = NAME

             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT XX
          IF CCN.CT > 0 THEN
            ITM.CT  += CCN.CT-1
            ENT.CT  += CCN.CT-1
            LINE.CT += CCN.CT-1
            RANK.CT += CCN.CT-1
          END
          RETURN
*-------------------------------------------------------------------------*
PRINT.VCN:
          FOR XX = 1 TO VCN.CT
             PRT.STR = ''
             COL.DATA = ''

             * Allow drill down capabilities for product.
             COL.DATA<1> = '^':PN

             COL.DATA<2> = DESC
             COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
             COL.DATA<5> = '0' "R2,"
             COL.DATA<6> = '0' "R2,"
             COL.DATA<7> = '0' "R2,"
             COL.DATA<8> = '0' "R2,"
             COL.DATA<9> = VCN.QTYS<1,XX>/DFLT.PER "R2,"
             COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             IF SHOW.VPRC THEN
                BASE.CT = DCOUNT(UNIT.BASE,AM)
                FOR BCT = 1 TO BASE.CT
                   BASE = UNIT.BASE<BCT,1>
                   COL  = UNIT.BASE<BCT,2>
                   IF (COL = 0) THEN CONTINUE
                   COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
                NEXT BCT

                EXT.CT = DCOUNT(EXT.BASE,AM)
                FOR ECT = 1 TO EXT.CT
                   EXT = EXT.BASE<ECT,1>
                   COL = EXT.BASE<ECT,2>
                   IF (COL = 0) THEN CONTINUE
                   EXT = (VCN.QTYS<1,XX>)*(OCONV(BASE,"MR9"))
                   COL.DATA<COL> = EXT "R2,"
                NEXT BCT
             END

             COL.DATA<71>= GL.CODE

             IF DETAIL[1,6] = 'Detail' THEN
                COL.DATA<70> = BR
                COL.DATA<77> = BR.SHORT.DESC
                COL.DATA<78> = BR.COST.CENTER
             END
             IF TSORTBY # 'Prod GL Code' THEN
                COL.DATA<71> = GL.CODE
             END
             *** If we're displaying the Product's Ranks...
             IF DISPLAY.RANKS THEN
                COL.DATA<74> = TEMP.PRODUCT.RANKS
             END
             VCN = FIELD(VCN.LIST<1,XX>,'~',2)
             READV NAME FROM CUSFILE,VCN,1 ELSE NAME = VCN
             COL.DATA<72> = NAME

             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT XX
          IF VCN.CT > 0 THEN
            ITM.CT  += VCN.CT-1
            ENT.CT  += VCN.CT-1
            LINE.CT += VCN.CT-1
            RANK.CT += VCN.CT-1
          END
          RETURN
*-------------------------------------------------------------------------*
PRINT.SNS:
          *** Display any serial numbers that have a Qty # 0...
          PRD.BR.GET.VAL BR,PN,25,SER.TRACK
          IF SER.TRACK AND SER.TRACK # 'N' THEN
             SNUMS = PRDD.BR(20)
             SQTYS = PRDD.BR(21)
             IF SLC.FLAG THEN
                CONVERT SVM TO VM IN SNUMS
                CONVERT SVM TO VM IN SQTYS
             END
             SN.CT    = DCOUNT(SNUMS<1>,VM)
             FOR SNUM = 1 TO SN.CT
                IF SNUMS<1,SNUM> = "" THEN GOTO SKP.SNUM
                PRT.STR = ""
                COL.DATA = ""
                COL.DATA<3> = SNUMS<1,SNUM>:" - ":SQTYS<1,SNUM>
                REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
                IF (TRIM(PRT.STR) # "") THEN
                   PRINT PRT.STR
                END
SKP.SNUM:    NEXT SNUM
          END
          RETURN
*-------------------------------------------------------------------------*
PRT.TAGS: *** Print the Tag information if this is being run in 'Detail'
          *** and the Use PO Costs on tagged items is set.
          TCT    = DCOUNT(TAG.IDS,VM)
          FOR TGN = 1 TO TCT
             TAG.ID   = TAG.IDS<1,TGN>
             TAG.QTY  = TAG.QTYS<1,TGN>
             TAG.BASE = ICONV(OCONV(TAG.COSTS<1,TGN>,'MR9'),'MR3')
             TAG.EXT  = TAG.QTY * TAG.BASE

             PRT.STR = ''
             COL.DATA = ''
             COL.DATA<6>  = TAG.QTY/DFLT.PER "R2,"
             * include tag ext into all bases for detail printing
             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                EXT = TAG.EXT
                COL = EXT.BASE<ECT,2>
                IF (COL = 0) THEN CONTINUE
                COL.DATA<COL> = OCONV(EXT,"MR3") "R2,"
             NEXT BCT

             COL.DATA<73> = TAG.ID
             COL.DATA<40> = OCONV(TAG.BASE,'MR3')*PER "R3,"
             COL.DATA<69> = OCONV(TAG.EXT,'MR3') "R2,"

             * Make sure that Tag ID, Tag Unit Amt or Extended Amt is
             * included on the report. Otherwise, we'll just be showing
             * blank lines!
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) = "") THEN CONTINUE

             COL.DATA<10> = PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11> = DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             * Now format in the other columns that might be displayed.
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END

             * Write our Tag lines out for the ODBC Report...
             IF HAS.ODBC THEN
                REC      = ''
                REC.NUM += 1

                * We need to store the Line, GL Code, and Product Ranks,
                * if applicable, so that we don't get subtotaling before
                * and after our TAG data.

                REC<2>  = SORT.VALUE
                REC<8>  = TAG.BASE*PER
                REC<9>  = PER.UM
                REC<12> = PRD(2)
                REC<15> = TAG.ID
                REC<17> = TAG.QTY
                REC<18> = ICONV(OCONV(TAG.EXT,"MR3"),"MR2")

                * If they're putting the product ranks on the report
                * Insert our info into our report arrays, pushing all
                * our other report values up at the same time...
                IF DISPLAY.RANKS THEN
                   * We can only store the Rank Numbers that were
                   * selected on, so that our report will 'break'
                   * (subtotal) properly...
                   REC = INSERT(REC,4;ODBC.RANKS)
                END

                WRITE REC ON ODBCFILE,REC.NUM "R%7"
             END
          NEXT FF

          RETURN
*-------------------------------------------------------------------------*
GET.QTYS: *** Get our OnHand Qtys...
          INCLUDE.PRODUCT = NO
          *** Get the OnHand Qtys for the current product at
          *** the current Branch...
          GET.PREV.ONHANDS BR,PN,INV.DT,OH.QTYS,OH.LOCS
          LOC.CT      = DCOUNT(OH.QTYS,VM)

          CCN.LIST    = ''
          CCN.QTYS    = ''
          VCN.LIST    = ''
          VCN.QTYS    = ''
          STK.QTY     = 0
          TAG.QTY     = 0
          REV.QTY     = 0
          DISP.QTY    = 0
          VCN.QTY     = 0
          TAG.IDS     = ''
          TAG.QTYS    = ''
          TAG.COSTS   = ''
          FIFO.IDS    = ''
          FIFO.QTYS   = ''
          FIFO.COSTS  = ''
          FIFO.DTS    = ''
          TAG.EXT.AMT = 0
          TAG.CT      = 0

          *** If we're not getting Consignment products, and the report
          *** will print in Detail format, but we don't have any OnHands
          *** we'll need to make sure to go through our loop at least once.
          IF NOT(ENTBK) THEN
             *** If there isn't any OnHand at any location, we still need
             *** to make sure we go through our loop at least once.
             IF NOT(LOC.CT) THEN LOC.CT = 1
             FOUND.CONSIGNMENT = NO
          END ELSE
             FOUND.CONSIGNMENT = NO
          END

          *** Loop thru each location for this branch...
          CONSIGN.CT      = 0
          NAME            = ''
          CONSIGNMENT.QTY = ''
          TMP.CN          = ''
          CONSIGN.EXT     = ''
          CONSIGN.DPER    = ''
          CONSIGN.DALPHA  = ''
          CONSIGN.PER     = ''
          CONSIGN.UM      = ''
          TAG.EXT.AMT     = 0

          FOR LL  = 1 TO LOC.CT

             *** Check whether the product is on consignment at this loc...
             IF ENTBK THEN
                LOC = FIELD(OH.LOCS<1,LL>,'~',1)
                QTYPE = FIELD(OH.LOCS<1,LL>,'~',1)

                IF NEG = 'O' AND OH.QTYS<1,LL> >= 0 THEN
                   GOTO SKIP.LOC
                * If they want to 'Exclude' Negative OnHand Qtys and our
                * Qtys are all negative then skip this one...
                END ELSE IF NEG = 'E' AND OH.QTYS<1,LL> <0 THEN
                   GOTO SKIP.LOC
                END

                OHQ     = OH.QTYS<1,LL>
                ADD.TAG = NO  ;* no tag totals
                GOSUB GET.VAL

                IF EXT.AMT.VAL # '' AND EXT.AMT.VAL * 100 >= EXT THEN GOTO SKIP.LOC

                *** Get our Price Per Qty and Unit of Measure...
                PRICE.PER.GET PER,PER.UM,,,PRC.DATE
                *** Get our Qty Per Qty and Unit of Measure for Invoices...
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA

                *** If the product at the current location isn't
                *** on consignment, skip the location qty...
                IF ENTBK # LOC[1,1] AND ENTBK # 'V' THEN CONTINUE
                IF ENTBK = 'V' AND LOC[1,1] # 'S' THEN CONTINUE

                *** If no customer then don't add to tempfile...
                IF LOC[2,99] = ''   THEN CONTINUE

                FOUND.CONSIGNMENT = YES
                CONSIGN.CT       +=1

                IF ENTBK # 'V' OR SHOW.VPRC THEN
                   CONSIGN.EXT<CONSIGN.CT> = LOWER(EXT.BASE)
                END ELSE
                   *** Exclude vendor consignment in ext. price calc. since
                   *** we do not own title to the inventory
                   CONSIGN.EXT<CONSIGN.CT> = 0
                END
                CONSIGN.DPER<CONSIGN.CT>   = DFLT.PER
                CONSIGN.DALPHA<CONSIGN.CT> = DFLT.ALPHA
                CONSIGN.PER<CONSIGN.CT>    = PER
                CONSIGN.UM<CONSIGN.CT>     = PER.UM
                *** Want to alphabetize the sorting of entity so make
                *** entity name part of the ID of tempfile and remove
                *** any '~' in name to use in the ID
                TMP.CN<CONSIGN.CT>         = LOC[2,99]

                READV CNAME FROM CUSFILE,TMP.CN<CONSIGN.CT>,1 ELSE CNAME = TMP.CN
                CNAME   = OCONV(CNAME,'MCU')
                CONVERT '~' TO '' IN CNAME
                NAME<CONSIGN.CT> = CNAME "L#35"

                CONSIGNMENT.QTY<CONSIGN.CT> = OH.QTYS<1,LL>

             END ELSE
                QTYPE = FIELD(OH.LOCS<1,LL>,'~',1)

                *** The next two conditions will determine whether to
                *** include the current product for the current Branch
                *** on the report...
                IF QTYPES THEN
                   WQTYPE = QTYPE
                   IF WQTYPE[1,1] = 'S' AND WQTYPE # 'S' THEN WQTYPE = 'V'
                   IF NOT(INDEX(QTYPES,WQTYPE[1,1],1)) THEN CONTINUE
                END

                IF SEL.CCN AND QTYPE[2,999] # SEL.CCN AND (QTYPE[1,1] = 'C' OR WQTYPE[1,1] = 'V') THEN CONTINUE
                IF QTYPES = 'C' OR FND.VQTYPE THEN
                   IF QTYPE[1,1] = 'C' OR WQTYPE = 'V' THEN
                      FOUND.CONSIGNMENT = YES
                   END
                END

                BEGIN CASE
                CASE QTYPE[1,1] = 'S'
                   IF QTYPE[2,999] # "" AND DETAIL[1,6] = 'Detail' THEN
                      VCN.LISTID = 'S~':QTYPE[2,999]
                      LOCATE VCN.LISTID IN VCN.LIST<1> SETTING CPOS ELSE
                         VCN.LIST<1,CPOS> = VCN.LISTID
                      END
                      VCN.QTYS<1,CPOS> += OH.QTYS<1,LL>
                   END ELSE
                      IF QTYPE[2,999] # "" THEN
                         VCN.QTY += OH.QTYS<1,LL>
                      END ELSE
                         STK.QTY += OH.QTYS<1,LL>
                      END
                   END
                CASE QTYPE[1,1] = 'C'
                   *** Create a list of the consignment customers and qtys
                   IF DETAIL[1,6] = 'Detail' THEN
                      CCN.LISTID = 'C~':QTYPE[2,999]
                      LOCATE CCN.LISTID IN CCN.LIST<1> SETTING CPOS ELSE
                         CCN.LIST<1,CPOS> = CCN.LISTID
                      END
                      CCN.QTYS<1,CPOS> += OH.QTYS<1,LL>
                   END ELSE
                      STK.QTY += OH.QTYS<1,LL>
                   END
                CASE QTYPE = 'T'
                   OK.TO.INCREMENT = YES
                   GOSUB GET.POCST
                   IF OK.TO.INCREMENT THEN TAG.QTY += OH.QTYS<1,LL>
                CASE QTYPE = 'L'
                   DISP.QTY += OH.QTYS<1,LL>
                CASE OTHERWISE
                   REV.QTY  += OH.QTYS<1,LL>
                END CASE
             END
SKIP.LOC: NEXT LL

          IF ENTBK THEN
             IF NOT(FOUND.CONSIGNMENT) THEN RETURN
          END
          IF QTYPES = 'C' OR QTYPES = 'V' THEN
             IF NOT(FOUND.CONSIGNMENT) THEN RETURN
          END

          IF NOT(ENTBK) THEN
             * If we're Page Breaking on Consignment Customers or Vendors
*            IF ENTBK   = 'V' THEN
*               REV.QTY = SUM(CONSIGNMENT.QTY)
*            END ELSE IF ENTBK = 'C' THEN
*               STK.QTY = SUM(CONSIGNMENT.QTY)
*            END

             ALL.POSITIVE.QTYS = NO
             ALL.NEGATIVE.QTYS = NO
             ZERO.QTYS         = NO
             CCN.SUM = SUM(CCN.QTYS)

             * Evaluate if qtys are all negative, positive or zero
             BEGIN CASE
             CASE STK.QTY=0 AND TAG.QTY=0 AND REV.QTY=0 AND DISP.QTY=0 AND CCN.SUM=0 AND NOT(VCN.QTYS)
                ZERO.QTYS = YES
             CASE STK.QTY>=0 AND TAG.QTY>=0 AND REV.QTY>=0 AND DISP.QTY>=0 AND CCN.SUM>=0
                ALL.POSITIVE.QTYS = YES
             CASE STK.QTY<=0 AND TAG.QTY<=0 AND REV.QTY<=0 AND DISP.QTY<=0 AND CCN.SUM<=0
                ALL.NEGATIVE.QTYS = YES
             END CASE

             BEGIN CASE
             * We won't worry about the Negative OnHand Qtys validation.
             CASE SUP.ZERO = 'Include' AND ZERO.QTYS;  GOTO GETVAL
             * If they want to Exclude Zero OnHand Qtys...
             CASE SUP.ZERO = 'Exclude' AND ZERO.QTYS;  RETURN
             END CASE

             BEGIN CASE
             * If they 'Only' want Negative OnHand Qtys, but our Qtys are
             * all positive then skip this one...
             CASE NEG = 'O' AND ALL.POSITIVE.QTYS;     RETURN
             * If they want to 'Exclude' Negative OnHand Qtys and our Qtys
             * are all negative then skip this one...
             CASE NEG = 'E' AND ALL.NEGATIVE.QTYS;     RETURN
             END CASE

GETVAL:      TOT.QTY = STK.QTY + DISP.QTY + REV.QTY
             IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
                IF DETAIL[1,6] = 'Detail' THEN
                   VCN.QTYS.CT = DCOUNT(VCN.QTYS,VM)
                   FOR QTY.CTR = 1 TO VCN.QTYS.CT
                      TOT.QTY += VCN.QTYS<1,QTY.CTR>
                   NEXT QTY.CTR
                END ELSE
                   TOT.QTY += VCN.QTY
                END
             END

             OHQ     = TOT.QTY
             ADD.TAG = YES  ;* include tag totals
             GOSUB GET.VAL

             IF EXT.AMT.VAL # '' THEN
                ATAG.EXT.AMT = ICONV(OCONV(TAG.EXT.AMT,'MR3'),'MR2')
                IF EXT.AMT.VAL*100 >= EXT+ATAG.EXT.AMT THEN RETURN
             END

             IF (BASN < 23) OR (BASN > 27) THEN
                *** Get our Price Per Qty and Unit of Measure...
                PRICE.PER.GET PER,PER.UM,,,PRC.DATE
                *** Get our Qty Per Qty and Unit of Measure for Invoices...
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA
             END ELSE
                *** Use Frozen UoM and Price if BASN is a 'Frozen' Type...
                READV FROZEN FROM PRDCFILE,PN:"*":BR,26 ELSE FROZEN = ''
                DFLT.ALPHA  = FROZEN<1,1>; PER.UM = FROZEN<1,1>
                PER         = FROZEN<1,2>
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA
                IF NOT(PER) THEN PER = DFLT.PER
                IF NOT(PER.UM) THEN PER.UM = DFLT.ALPHA
             END
             IF NOT(PER) THEN PER = 1
             IF NOT(PER.UM) THEN PER.UM = 'ea'
          END

          INCLUDE.PRODUCT = YES

          RETURN
*-------------------------------------------------------------------------*
GET.POCST: *** Determine the actual PO Cost for tagged items.
           *** PN and PRD must be active for the product we are looking at.
           *** NOTE!!! All PRD arrays will get trodden on.

          * Block increment total tag qty ct if we return early.
          OK.TO.INCREMENT = NO
          TAG.INFO = FIELD(OH.LOCS<1,LL>,'~',3)
          PO.INFO  = FIELD(TAG.INFO,'^',1)
          TAGGED.ORDER = FIELD(TAG.INFO,'^',2)

          * Pull PO information for both tagged sales orders & work orders.
          * On Work Orders make sure we only pick up PO type tags.
          IF INDEX('SW',PO.INFO[1,1],1) AND TAGGED.ORDER[1,1] # 'S' THEN PO.INFO = TAGGED.ORDER

          OID  = FIELD(PO.INFO,'.',1)
          LDID = FIELD(PO.INFO,'.',2)

          IF NOT(OID) THEN RETURN
          MATREAD LED FROM LEDFILE,OID THEN
             LD.GET LDID
             *** If we're looking at a transfer type order, we want to
             *** use the right date for the current branch we're looking at
             IF OID[1,1] = 'T' THEN
                G.CT = DCOUNT(LED(2),VM)
                FOUND = NO
                FOR GEN = 1 TO G.CT
                   IF LED(2)<1,GEN,2> = BR THEN
                      FOUND = YES
                      EXIT
                   END
                NEXT GEN
                IF NOT(FOUND) THEN
                   IF OH.QTYS<1,LL> >= 0 THEN FNDR = 'RX' ELSE FNDR = 'SX'
                   LOCATE FNDR IN LED(30)<1> SETTING GEN ELSE GEN = 1
                END
             END ELSE
                FINDSTR "T~" IN LD(7)<1> SETTING XX,GEN ELSE GEN = 1
             END
             PO.CST = LD(8)<1,GEN>
             *** Need to add a fail safe for tagged orders to verify
             *** that it does not fall outside our as of date since under
             *** certain case GET.PREV.ONHANDS will not correctly
             *** decrement the quantities from PRDD(1)
             IF NOT(PO.CST) THEN TINV.DT = LED(4)<1,GEN> ELSE
                TINV.DT = LED(23)<1,GEN>
             END
             IF TINV.DT AND TINV.DT > AOD THEN RETURN

             *** These four attributes will be set no matter if the prod
             *** is a kit or not.
             TQTY              = OH.QTYS<1,LL>
             TAG.CT           += 1
             TAG.IDS<TAG.CT>   = OID
             TAG.QTYS<TAG.CT>  = TQTY
             * OK to update total qty ct since we incremented tag count
             OK.TO.INCREMENT   = YES

             *** If the ld(1) product is a kit product, the active PRD will
             *** be for the component we are dealing with.
             IF PN # LD(1) THEN
                LOCATE PN IN LD(31)<1> SETTING XX THEN
                   GOSUB PROCESS.KIT
                END
                *** If the prd we are looking at isn't in the kit,
                *** something's wrong, so don't include the product.
             END

             *** Finally, set the tagged amount.
             TAG.COSTS<TAG.CT> = PO.CST
             TAG.EXT.AMT      += ICONV(OCONV(PO.CST,'MR9') * TQTY,'MR3')
          END

          RETURN
*-------------------------------------------------------------------------*
PROCESS.KIT: *** Work out the inventory amount to use for this
          *** kit component. We need LD, PN and PRD active. LD will be for
          *** the kit product, PN and PRD will be for the component.
          *** NOTE! All PRD globals will get trodden on.

          *** First work out what the total value of the kit should be
          *** according to the kit components and quantities.
          *** Also work out what the value of the PN in question is
          *** and the ratio between the two.
          COMP.CT = DCOUNT(LD(31),VM)
          TOT.COST = 0
          TOT.COMPS = 0; * THis is needed incase there are no costs
                         * associated to the components. We just divide
                         * the po amout by the total comps.
          COMP.COST = 0
          *** We need to save the old PN because we need the original
          *** value inside the loop, but we must set PN with each
          *** component for each call to get.val.
          OLD.PN = PN
          OLD.BASE = BASE
          FOR COMP.POS = 1 TO COMP.CT
             PN  = LD(31)<1,COMP.POS>
             OHQ = ""
             ADD.TAG = NO   ;* no tag totals
             GOSUB GET.VAL
             TOT.COST += BASE * LD(30)<1,COMP.POS>
             TOT.COMPS += LD(30)<1,COMP.POS>
             IF LD(31)<1,COMP.POS> = OLD.PN THEN COMP.COST = BASE
          NEXT COMP.POS
          PN = OLD.PN
          BASE = OLD.BASE

          *** Use the ratio to work out the actual cost of the individual
          *** component.
          IF TOT.COST = 0 THEN
             PO.CST = OCONV(PO.CST / TOT.COMPS,'MR0')
          END ELSE
             PO.CST = OCONV((COMP.COST / TOT.COST) * PO.CST,'MR0')
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.SUB:  *** Print Subtotals for our Sortby...
          IF MODE = 'XML' THEN RETURN

          IF LINE.CT > 1 OR DETAIL[1,6] # 'Detail' THEN

             IF SORTBY[1,7] # 'Prod GL' THEN
                TOL.DESC = LAST.BRK:' - ':SORT.DESC:' '
             END ELSE
                TOL.DESC = 'Totals for G/L Code: ':LAST.BRK:' '
             END

             TOL.DESC.LENGTH = LEN(TOL.DESC)
             TOL.DESC = TOL.DESC:STR('-',(35 - TOL.DESC.LENGTH))
             TOL.DESC = TOL.DESC "L#35"

             TOLS     = LTOLS
             TOLS.UM  = LTOLS.UM
             TOLS.EXT = LTOLS.EXT

             *** Set this flag so if there is an entity break requested,
             *** we only print the double line totals for Entity Totals...
             PRT.SBY = YES
             GOSUB PRT.TOLS
             PRT.SBY = NO
             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PRINT
             END
          END ELSE
             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PRINT
                BLANK.LINE.PRINTED = YES
             END
          END

          LTOLS     = ""
          LTOLS.UM  = ""
          LTOLS.EXT = ""
          LINE.CT   = 0

          RETURN
*-------------------------------------------------------------------------*
PRT.ETOLS:*** Print our Entity Totals...
          IF MODE = 'XML' THEN RETURN

          *** If we grouped our data by product ranks, we'll need to
          *** go print the rank subtotals first...
          IF GROUP.RANKS THEN GOSUB PRT.RTOLS

          PRINT
          READV NAME FROM CUSFILE,TMP.CN,1 ELSE NAME = TMP.CN
          TOL.DESC = 'Totals For Entity : ':NAME"L#25"

          TOLS     = ETOLS
          TOLS.UM  = ETOLS.UM
          TOLS.EXT = ETOLS.EXT
          GOSUB PRT.TOLS
          ETOLS     = ""
          ETOLS.UM  = ""
          ETOLS.EXT = ""

          *** Print our ascii character Page Break if printing detail
          IF DETAIL[1,6] = 'Detail' THEN
             PRINT CHAR(12)
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.BTOLS:*** Print our Branch Totals...
          IF MODE = 'XML' THEN RETURN

          IF DETAIL[1,6] = 'Detail' THEN
             PRINT
             TOL.DESC = 'Totals For Branch : ':LAST.BRK
          END ELSE
             TOL.DESC = ''
          END

          TOLS     = BTOLS
          TOLS.UM  = BTOLS.UM
          TOLS.EXT = BTOLS.EXT
          GOSUB PRT.TOLS
          BTOLS    = ""
          BTOLS.UM = ""
          BTOLS.EXT= ""

          *** Print our ascii character Page Break if printing detail.
          IF DETAIL[1,6] = 'Detail' THEN
             PRINT CHAR(12)
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.RTOLS:*** Print the Subtotals for the product ranks that we've been
          *** grouping by...
          IF MODE = 'XML' THEN RETURN

          *** We need to print our Sort by Totals first...
          IF NOT(ENTBK) AND SUBT.ON.SORTBY THEN
             IF LINE.CT > 1 OR DETAIL[1,6] # 'Detail' THEN
                GOSUB PRT.SUB
             END ELSE
                LTOLS    = ''
                LTOLS.UM = ''
                LINE.CT  = 0
             END

             LAST.BRK = BRK.ON
             ITM.CT   = 0
          END

          IF RANK.CT > 1 OR DETAIL[1,6] # 'Detail' THEN
             TOT.RANKS = LAST.RANKS
             CONVERT VM TO ',' IN TOT.RANKS

             TOL.DESC = 'Totals for Products with Ranks: ':TOT.RANKS:' '

             TOL.DESC.LENGTH  = LEN(TOL.DESC)
             TOT.RANKS.LENGTH = LEN(TOT.RANKS)

             *** If we're sorting by Line...
             TOL.DESC  = TOL.DESC:STR('-',(68 - TOL.DESC.LENGTH))
             TOL.DESC  = TOL.DESC "L#68"

             TOL.DESC := '------'

             TOLS      = RTOLS
             TOLS.UM   = RTOLS.UM
             TOLS.EXT  = RTOLS.EXT

             PRT.RANKS = YES
             GOSUB PRT.TOLS
             PRT.RANKS = NO
             PRINT
          END ELSE
             IF NOT(BLANK.LINE.PRINTED) THEN
                PRINT
             END
          END

          *** Clear out our rank totals...
          RTOLS     = ""
          RTOLS.UM  = ""
          RTOLS.EXT = ""
          RANK.CT   = 0

          RETURN
*-------------------------------------------------------------------------*
PRT.TOLS: * This is our generic function for printing Totals. The Totals
          * we are printing depends on what we set TOLS equal to...

          IF MODE = 'XML' THEN RETURN

          *** If all the products we've printed info for so far
          *** have the same Qty Unit of Measure, we can display it.
          *** Otherwise, we'll display asterics to show that the qty
          *** totals are based on products with different Qty UoMs...
          IF TOLS.UM<1,2> = '' THEN QUM = TOLS.UM<1> ELSE QUM = '**'

          *** Only write the "bucket" ODBC information if they have solar
          IF HAS.ODBC THEN
             *** If the report is printing in Summary or Summary by
             *** Product format, and we're not printing a Grand Total
             *** right now...
             IF DETAIL[1,6] # 'Detail' AND TOL.DESC[1,12] # "Grand Totals" THEN
                *** We wont print a separate total line for the
                *** Product Ranks...
                IF NOT(PRT.RANKS)THEN
                   REC.NUM += 1
                   REC      = ""
                   REC<1>   = LAST.BRK
                   REC<2>   = ICONV(TOLS<1,1>,"MR0")
                   REC<3>   = ICONV(TOLS<1,2>,"MR0")
                   REC<4>   = ICONV(TOLS<1,6>,"MR0")
                   REC<5>   = ICONV(TOLS<1,5>,"MR0")
                   REC<6>   = ICONV(TOLS<1,7>,"MR0")
                   REC<7>   = ICONV(OCONV(TOLS<1,3>,"MR2"),"MR2")
                   REC<11>  = QUM

                   IF GROUP.RANKS THEN
                      TOT.REC.RANKS = LAST.RANKS
                      CONVERT VM TO ',' IN TOT.REC.RANKS
                      REC = INSERT(REC,2;TOT.REC.RANKS)
                   END

                   IF SORTBY[1,7] # 'Prod GL' THEN
                      READV LAST.LDESC FROM PLNEFILE,LAST.BRK,1 ELSE
                         LAST.LDESC = ''
                      END
                      REC = INSERT(REC,2;LAST.LDESC)
                   END

                   WRITE REC ON ODBCFILE,REC.NUM "R%7"
                END
             END
          END

          *** If Tag Onhd is less than 0, print out a value of 0
          IF TOLS<1,2> < 0 THEN
             TOLS<1,2> = 0
          END

          PRINT.STRING = ""
          TOTAL.STRING = ""
          COL.DATA = ""
          SEP.CHAR = ""
          COL.DATA<5> = TOLS<1,1> "R2,"
          COL.DATA<6> = TOLS<1,2> "R,"
          COL.DATA<7> = TOLS<1,6> "R,"
          COL.DATA<8> = TOLS<1,5> "R,"
          IF SHOW.VPRC THEN
             COL.DATA<9> = TOLS<1,7> "R2,"
          END
          COL.DATA<11> = QUM

          EXT.CT = DCOUNT(EXT.BASE,AM)
          FOR ECT = 1 TO EXT.CT
             COL = EXT.BASE<ECT,2>
             * add to column if there is an ext column
             IF COL # 0 THEN
                COL.DATA<COL> = OCONV(TOLS.EXT<COL>,"MR2")  "R2,"
             END
          NEXT ECT

          * If doing 'Summary by Branch...', make sure to pass the branch.
          IF INDEX(DETAIL,'by Branch',1) AND TOL.DESC[1,12]#"Grand Totals" THEN
             COL.DATA<70> = LAST.BRK
             COL.DATA<77> = LAST.BR.DESC
             COL.DATA<78> = LAST.COST.CENTER
          END

          * Extended total amount for tags.
          COL.DATA<69> = OCONV(TOLS<1,3>,"MR2")          "R2,"

          *** If the report format is Detail OR Summary by Product...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             IF PRINTING.GTOTALS THEN
                SEP.CHAR = "="
             END ELSE
                SEP.CHAR = "-"
             END
          END

          REPORT.LAYOUT.V.PRINT.STR COL.DATA,SEP.CHAR,COL.RECORD,TOTAL.STRING,PRINT.STRING
          * This is necessary because there is no 'fixed' data that can be
          * replaced with the total title
          LEADIN.STRING = TOL.DESC
          GOSUB REPL.LSTR
          IF TOTAL.STRING # '' THEN
             PRINT TOTAL.STRING
          END
          PRINT PRINT.STRING

          RETURN
*-------------------------------------------------------------------------*
REPL.LSTR:* Add totals string to string that is returned from variable
          * column routine.
          REMAINING.LENGTH = LEN(PRINT.STRING)-LEN(LEADIN.STRING)
          IF REMAINING.LENGTH > 0 THEN
             PRINT.STRING = LEADIN.STRING:PRINT.STRING[REMAINING.LENGTH]
          END ELSE
             PRINT.STRING = LEADIN.STRING
          END
          RETURN
*-------------------------------------------------------------------------*
SEL.IDS:  * Select data for our report...

          *** If the User has specified Price or Buy Lines to report
          *** our data for...

          IF SLIST#'' AND SLCT = 'Product' THEN
             IDS = RAISE(SLIST)
             SELECT IDS
          END ELSE IF SLIST # '' AND (SLCT = 'Price Line' OR SLCT = 'Buy Line') THEN
             IDS = ''
             CT  = DCOUNT(SLIST,VM)
             FOR J = 1 TO CT
                NEXT.LINE = SLIST<1,J>
                *** Get all valid product for this Price/Buy Line...
                IF SLCT[1,1] = 'P' THEN
                   ** Note: We can't read the PNs from the PROD.SEQ record
                   ** because deleted products are excluded from these
                   ** records.  They are not excluded from the PROD.DYNAM
                   ** so if we use the PROD.SEQ records we will get
                   ** inconsistent results
                   EXE = 'SELECT PRODUCT WITH LINE = "':NEXT.LINE:'"'
                   EXECUTE EXE CAPTURING MSG
                   READLIST ID2 ELSE ID2 = ''

                END ELSE
                   EXE = 'SELECT PRODUCT WITH BUY.LINE = "':NEXT.LINE:'"'
                   EXECUTE EXE CAPTURING MSG
                   READLIST ID2 ELSE ID2 = ''
                END
                IF IDS = '' THEN
                   IDS = ID2
                END ELSE
                   IF ID2 THEN
                      ID2.CNT = DCOUNT(ID2,AM)
                      *** Strip out duplicate part numbers
                      FOR XX = 1 TO ID2.CNT
                         LOCATE ID2<XX> IN IDS SETTING POS ELSE
                            IDS<-1> = ID2<XX>
                         END
                      NEXT XX
                   END
                 IF DCOUNT(IDS,AM) > 100000 THEN EXIT
                END
             NEXT J
             *** If we have more than 100,000 products, we'll try and
             *** shorten the list...
             IF DCOUNT(IDS,AM) > 100000 THEN
                IDS = ''
                *** Get a list of the products that are active....
                PRDD.PN.SELECT
                LOOP
                   READNEXT PN ELSE EXIT
                   *** Set the Line Attribute - Price or Buy...
                   IF SLCT[1,1] = 'P' THEN ATTB = 9 ELSE ATTB = 12

                   READV LINE FROM PRDFILE,PN,ATTB THEN
                      LOCATE LINE IN SLIST<1> SETTING POS THEN
                         IDS<-1> = PN
                      END
                   END
                REPEAT
                SELECT IDS
             END ELSE
                SELECT IDS
             END
          END ELSE
             *** Get a list of the products that are active....
             PRDD.PN.SELECT
          END

          GOSUB CREATE.TEMPFILE

          RETURN
*-------------------------------------------------------------------------*
CREATE.TEMPFILE:* Create a Tempfile for our report data so that we can
                * get it sorted properly...

          UT.TEMPFILE.CREATE FLNM.ID,TEMPFILE
          *** Loop through the list of products we picked up before we
          *** got here...
          LOOP
             READNEXT PN ELSE EXIT
             MATREAD PRD FROM PRDFILE,PN ELSE MAT PRD = ''
             GLCODE = PRD(2)

             *** Go make sure the product should really be on the report...
             GOSUB FILTER.PRODUCT

             *** If it didn't pass our filters then skip it...
             IF NOT(PRODUCT.OK) THEN CONTINUE

             IF SORTBY = 'Price Line' OR SORTBY = 'Buy Line' THEN
                *** Find Seq for product
                IF SLCT[1,3] = 'Buy' THEN
                   GET.BLINE.IDS ,LINES,BUY.LN
                END ELSE
                   GET.LINE.IDS ,LINES,PRC.LN
                END
                SORT.CODE = TRIM(PRD(20))
                IF SORT.CODE AND NUM(SORT.CODE) THEN
                   SORT.CODE += 0
                END
                IF SORT.CODE THEN
                   SEQ = SORT.CODE
                END ELSE
                   LOCATE PN IN LINES<1> SETTING SEQ ELSE SEQ = 1
                END
             END
             *** Set sortby for each product
             BEGIN CASE
             CASE SORTBY = 'Price Line'
                SBY = PRC.LN "L#10":'~':SEQ "R%9"
             CASE SORTBY = 'Buy Line'
                SBY = BUY.LN "L#10":'~':SEQ "R%9"
             CASE SORTBY = 'Prod GL Code'
                SBY      = GLCODE "L#10":'~'

                IF SLCT  = 'Price Line' OR SLCT = 'Sell Group' THEN
                   SBY  := PRD(9) "L#10"
                END ELSE IF SLCT[1,3] = 'Buy' THEN
                   SBY  := PRD(12) "L#10"
                END
             CASE SORTBY = 'Sell Group'
                SBY = SEL.GRP "L#10"
             CASE SORTBY = 'Buy Group'
                SBY = BUY.GRP "L#10"
             CASE SORTBY = 'Prod ID'
                SBY = PN "L#10"
             END CASE

             *** PER was not being reset for each PN.  Reset here before
             *** the branch.
             PER = ''
             TMP.REC = ''
             FOR BRN = 1 TO BR.CT
                BR   = BRCHS<1,BRN>

                *** Check that the product is active in the current branch
                READV NADA FROM PRDDFILE,PN:'*':BR,0 ELSE CONTINUE

                *** If the User has selected on specific Product Ranks, but
                *** we are not grouping our data according to Product Ranks
                *** we'll need to go validate the ranks right now...
                IF NOT(GROUP.RANKS) AND RANKS THEN
                   *** Get all the ranks for this product at the current
                   *** branch...
                   PRDC.BR.GET.VAL BR,PN,1,PROD.RANKS
                   *** if there aren't any ranks returned we don't want to
                   *** use resources to process.
                   RANKED.OK  = YES
                   TEMP.RANKS = ''
                   GOSUB GET.PRODUCT.RANKS

                   *** If one of the product's ranks was not a match for
                   *** what the User selected, we'll skip this product...
                   IF NOT(RANKED.OK) THEN CONTINUE
                END

                *** Go get the OnHand Qtys for the current product at the
                *** the current Branch...
                GOSUB GET.QTYS

                *** If it didn't pass the location/qty filters we won't
                *** include it on the report...
                IF NOT(INCLUDE.PRODUCT) THEN CONTINUE

                *** If we're page breaking on Consignment
                *** Vendors/Customers...
                IF ENTBK THEN
                   FOR XX = 1 TO CONSIGN.CT
                      TAG.QTY  = 0
                      DISP.QTY = 0

                      *** If we're Page Breaking on Consignment Vendors...
                      IF ENTBK = 'V' THEN
                         VCN.QTY = CONSIGNMENT.QTY<XX>; STK.QTY = 0; REV.QTY = 0
                      END ELSE
                         *** Otherwise we're page Breaking on Consignement
                         *** Customers...
                         STK.QTY = CONSIGNMENT.QTY<XX>; REV.QTY = 0
                      END

                      *** Set up Consignment Entity part of our Tempfile
                      *** Record...
                      TEMP.ID    = NAME<XX>:'~':TMP.CN<XX>:'~'
                      EXT.BASE   = RAISE(CONSIGN.EXT<XX>)
                      DFLT.PER   = CONSIGN.DPER<XX>
                      DFLT.ALPHA = CONSIGN.DALPHA<XX>
                      PER        = CONSIGN.PER<XX>
                      PER.UM     = CONSIGN.UM<XX>

                      GOSUB ADD.REC
                   NEXT XX
                *** Otherwise we're in here cause we're grouping products
                *** by their ranks...
                END ELSE
                   IF NOT(INCLUDE.PRODUCT) THEN CONTINUE

                   TEMP.ID = ''

                   GOSUB ADD.REC
                END
             NEXT BRN
          REPEAT

          RETURN
*-------------------------------------------------------------------------*
ADD.REC:  *** Create a record in our Tempfile for the current product
          *** at the current Branch...
          *** If we need to group our data by product ranks...
          IF GROUP.RANKS THEN
             *** Get all the ranks for this product at the current
             *** branch...
             PRDC.BR.GET.VAL BR,PN,1,PROD.RANKS
             *** If the User has selected certain Product Ranks make
             *** sure this product has all the right ones...
             IF RANKS THEN
                RANKED.OK  = YES
                TEMP.RANKS = ''

                GOSUB GET.PRODUCT.RANKS

                *** If one of the product's ranks was not a match for
                *** what the User selected, we'll skip this product...
                IF NOT(RANKED.OK) THEN RETURN
             END ELSE
                TEMP.RANKS = RAISE(PROD.RANKS)
             END

             *** Do this so we can make it part of our Tempfile Id...
             CONVERT AM TO '!' IN TEMP.RANKS

             TEMP.ID := TEMP.RANKS:'~'
          END

          PDESC       = PRD(1)<1,1>
          CONVERT '~' TO ' ' IN PDESC

          TEMP.ID    := SBY

          *** If our report is in Detail OR 'Summary by...' Format...
          TEMP.ID := '~':PDESC:'~':PN
          BEGIN CASE
             CASE INDEX(DETAIL,'by Branch',1)
                TEMP.ID  = BR "R#4":'~':TEMP.ID:'~':BR
             CASE DETAIL = 'Detail' OR DETAIL = 'Summary'
                TEMP.ID := '~':BR
          END CASE

          READ TMP.REC FROM TEMPFILE,TEMP.ID ELSE TMP.REC = ''

          TMP.REC<1> += TAG.QTY / DFLT.PER
          TMP.REC<2> += STK.QTY / DFLT.PER
          TMP.REC<3> += DISP.QTY / DFLT.PER
          TMP.REC<4> += REV.QTY / DFLT.PER
          TMP.REC<5> += VCN.QTY / DFLT.PER

          BASN.CT = DCOUNT(EXT.BASE,AM)
          FOR BCT = 1 TO BASN.CT
             TMP.REC<7,BCT,1> += EXT.BASE<BCT,1>
             TMP.REC<7,BCT,2>  = EXT.BASE<BCT,2>
          NEXT BCT

          * The extended tag amount should always be written so it can be
          * summed into the total in summary reports
          TMP.REC<16> = TAG.EXT.AMT

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             *** We only need to store this stuff if we're
             *** printing a Detail or Summary by Product Report...
             TMP.REC<8>  = PER
             TMP.REC<9>  = PER.UM
             TMP.REC<10> = DFLT.PER
             TMP.REC<11> = DFLT.ALPHA
             TMP.REC<12> = LOWER(UNIT.BASE)

             IF DETAIL[1,6] = 'Detail' THEN
                TMP.REC<13> = LOWER(TAG.IDS)
                TMP.REC<14> = LOWER(TAG.QTYS)
                TMP.REC<15> = LOWER(TAG.COSTS)

                TMP.REC<21> = FIFO.IDS
                TMP.REC<22> = FIFO.QTYS
                TMP.REC<23> = FIFO.COSTS
                TMP.REC<24> = FIFO.DTS
             END

             TMP.REC<17> = CCN.LIST
             TMP.REC<18> = CCN.QTYS
             TMP.REC<19> = VCN.LIST
             TMP.REC<20> = VCN.QTYS
          END ELSE
             LOCATE DFLT.ALPHA IN TMP.REC<11> SETTING APOS ELSE
                TMP.REC<11,APOS> = DFLT.ALPHA
             END
          END

          WRITE TMP.REC ON TEMPFILE,TEMP.ID

          RETURN
*-------------------------------------------------------------------------*
GET.PRODUCT.RANKS: *** Get the Product Ranks for this product and validate
                   *** against any ranks the User has selected...
          IF TRIM(PROD.RANKS,VM) = "" THEN
             RANKED.OK = NO
             RETURN
          END

          FOUND.A.MATCH = NO
          FOR RNUM  = 1 TO RANK.NUM.CT
             SELECTED.RANKS = RAISE(RANKS<1,RNUM>)
             IF SELECTED.RANKS<1,1> = '' THEN CONTINUE

             PRD.RANK = PROD.RANKS<1,RNUM>
             LOCATE PRD.RANK IN SELECTED.RANKS<1> SETTING RNKPOS THEN
                *** If it's a match, we can add it to our temp list.
                TEMP.RANKS<RNUM> = PRD.RANK
                FOUND.A.MATCH    = YES
             END ELSE
                TEMP.RANKS<RNUM> = '*'
                *** If they only want products that match 'All'
                *** Product Rank Selections (vs. 'Any')...
                IF ANY.ALL = 'All' THEN
                   *** If we find one that isn't a match, we can't
                   *** put this product on the report...
                   RANKED.OK = NO
                   EXIT
                END
             END

          NEXT RNUM

          *** If there were no matches then we don't want this product
          *** on the report...
          IF NOT(FOUND.A.MATCH) THEN RANKED.OK = NO

          RETURN
*-------------------------------------------------------------------------*
FILTER.PRODUCT: *** Make sure the product should really be on the report...
          PRODUCT.OK = NO

          *** Check the Override account on the product
          IF INV.ACCTS # "" THEN
             READV PL.INV.ACCT FROM PLNEFILE,PRD(9),24 ELSE PL.INV.ACCT=''
             IF PRD(89) = '' AND PL.INV.ACCT = '' AND NOT(INC.PRI.INV) THEN RETURN
             IF NOT(PRD(89) = '' AND PL.INV.ACCT = '' AND INC.PRI.INV) THEN
                IF PRD(89) = "" THEN
                   LOCATE PL.INV.ACCT IN INV.ACCTS<1> SETTING POS ELSE RETURN
                END ELSE
                   LOCATE PRD(89) IN INV.ACCTS<1> SETTING POS ELSE RETURN
                END
             END
          END

          *** If our Dynamic Kit flag is set for this product...
          IF PRD(106) THEN GOSUB CLR.KIT

          *** If this product is a 'Misc. Charge', a 'Comment' or a
          *** 'Lot Item', skip it...
          IF PRD(3)  = 3 OR PRD(3) = 6 OR PRD(3) = 9 THEN RETURN
          *** If this product isn't a 'Stock' item, and the User wants
          *** to 'Exclude' Nonstocks, skip it...
          IF STK.ITEM = 'No' AND EXCL.NS[1,1] = 'E'      THEN RETURN
          *** If this product is a 'Stock' item, and the User 'Only'
          *** wants Nonstocks, skip it...
          IF PRD(3)  = 1 AND EXCL.NS[1,1] = 'O'      THEN RETURN
          *** If this product is a Kit, skip it...
          IF PRD(53) # ''                            THEN RETURN

          PRC.LN = PRD(9)
          BUY.LN = PRD(12)
          PRD.BR.GET.VAL BRCHS<1,1>,PN,24,SEL.GRP
          SEL.GRP = SEL.GRP<1,1,1>
          PRD.BR.GET.VAL BRCHS<1,1>,PN,23,BUY.GRP
          BUY.GRP = BUY.GRP<1,1,1>
          IF SLIST # '' THEN
             BEGIN CASE
             *** Filter against product list
             CASE SLCT = 'Product'
                LOCATE PN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = PN
             *** Filter against any Price Lines that have been selected...
             CASE SLCT = 'Price Line'
                LOCATE PRC.LN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = PRC.LN
             *** Filter against any Buy Lines selected
             CASE SLCT = 'Buy Line'
                LOCATE BUY.LN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = BUY.LN
             *** Filter against any Sell Groups that have been selected...
             CASE SLCT = 'Sell Group'
                LOCATE SEL.GRP IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = SEL.GRP
             *** Filter against any Buy Groups that have been selected...
             CASE SLCT = 'Buy Group'
                LOCATE BUY.GRP IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = BUY.GRP
             END CASE
          END

          PRODUCT.OK = YES

          RETURN
*-------------------------------------------------------------------------*
INIT:     *** Initialize the data we'll need for this Phantom to work...

          ERR.MSG        = ''
          MODE           = MISC.DATA<1,5>
          OUTBOUND.EMAIL = MISC.DATA<1,6>

          BASIS.LIST     = RAISE(BASIS.LIST)

          *** Get the number of columns, desc, and lengths
          REPORT.PREFIX = 'INV.VALUE'
          *** no summary version
          REPORT.V.COL.GET.DATA REPORT.PREFIX,DRPT<56>,COL.RECORD,'D'
          IF COL.RECORD = '' THEN
             ERR.MSG = 'Missing report data for Inventory Valuation. Contact your support representative'
             RETURN
          END

          IF MODE = 'XML' THEN
             GET.NEW.ID 'NEXT.INV.WKSHT.ID',OUT.ID,,,,YES
             OUT.ID = 'InventoryModel-':OUT.ID:'.xml'

             UT.OPEN.FILE 'XML.FORMS',XMLFILE,ERR.MSG
             IF ERR.MSG THEN RETURN

             READ ITEM.RESP FROM XMLFILE,'INV.MODEL.ITEM.TEMPLATE' ELSE
                ERR.MSG = 'Unable to Locate Item Template'
                RETURN
             END

             READ BODY.RESP FROM XMLFILE,'INV.MODEL.XLS' ELSE
                ERR.MSG = 'Unable to Locate Excel Template'
                RETURN
             END

             READ FOOTER.RESP FROM XMLFILE,'INV.MODEL.FOOTER' ELSE
                FOOTER.RESP = ''
             END

             READ INV.PARAMS FROM CTRLFILE,'INV.MODEL.PARAMS' ELSE
                INV.PARAMS = ''
             END
             DEMAND.ROUNDING = OCONV(INV.PARAMS<1>,'MR2')
             TARGET.TURNS = INV.PARAMS<2>
             SF.RANK = INV.PARAMS<3>
             READV DAYS.DEMAND FROM CTRLFILE,'MIN.ORD.CYCLE',1 ELSE
                DAYS.DEMAND = 7
             END
             READV HIT.CUTOFF FROM CTRBFILE,'MIN.BR.HITS~':BR,1  ELSE
                HIT.CUTOFF = ''
             END
          END

          UT.OPEN.COMMON.FILE 'PROD.LOT', HNDL
          IF HNDL > 0 THEN
             DLOTFILE = FILES(HNDL)
          END ELSE
             PRINT BELL:;
             SYS.LOG.CMT 'Cannot open PROD.LOT.','INV.PHR.INV.VALUE'
             CSTAT "'Cannot open PROD.LOT from 'INV.PHR.INV.VALUE'"
             RETURN
          END

          *** Break out our Price/Buy Line data...
          SLIST = RAISE(SEL.DATA<1,1>)
          SLCT  = SEL.DATA<1,2>

          * Break out our additional data...
          VCSGN      = ADDL.DATA<1,1,1>
          CCSGN      = ADDL.DATA<1,1,2>
          CCN        = ADDL.DATA<1,1,3>
          VPBK       = ADDL.DATA<1,1,4>
          CPBK       = ADDL.DATA<1,1,5>
          SHOW.VPRC  = ADDL.DATA<1,1,7>
          HIST.YEARS = ADDL.DATA<1,1,8>
          INCL.ADJ   = ADDL.DATA<1,1,9>

          * If page break requested on consignment customer or vendor
          * set variable ENTBK appropriately
          BEGIN CASE
          CASE VPBK AND NOT(CCN);       ENTBK = 'V'
          CASE CPBK AND NOT(CCN);       ENTBK = 'C'
          CASE OTHERWISE;               ENTBK = ''
          END CASE

          * Make sure that if the costing date was "" we use today.
          IF NOT(CAOD) THEN CAOD = DATE()

          AS.OF.DT         = OCONV(AOD,'D4/')
          CAS.OF.DT        = OCONV(CAOD,'D4/')
          PRC.DATE         = CAOD
          INV.DT           = AOD + 1
          DETAIL           = DET.OPTS<1,1>
          REPORT.FORMAT    = DETAIL
          SHOW.PN          = DET.OPTS<1,2>
          ANY.ALL          = MISC.DATA<1,1>
          INV.ACCTS        = MISC.DATA<1,3>
          INV.ACCTS        = RAISE(INV.ACCTS)
          AVG.CST          = MISC.DATA<1,4>
          BEG.DT           = ICONV('01/01/':AS.OF.DT[7,4],'D')
          SEL.CCN          = ''
          TEST.AVG         = ''
          IF CCN THEN SEL.CCN = CCN

          * Parse our data out from our Include/Exclude parameter...
          EXCL.NS  = INC.EXC.DATA<1,1>
          NEG      = INC.EXC.DATA<1,2>
          SUP.ZERO = INC.EXC.DATA<1,3>

          IF RANKS THEN
             RANK.NUM.CT   = DCOUNT(RANKS<1>,VM)
          END ELSE
             * There 5 possible Rank Numbers (methods)...
             RANK.NUM.CT   = 5
          END

          GROUP.RANKS      = GROUP.RANK.DATA<1,1>
          SUBT.ON.SORTBY   = GROUP.RANK.DATA<1,2>
          TSORTBY          = SORTBY

          * If the user has selected to show the Rank column, then show
          * ranks.
          RANK.COL = NO
          LOCATE '74' IN COL.RECORD<1> SETTING POS THEN
             RANK.COL = YES
          END

          IF DETAIL[1,6] = 'Detail' AND (RANKS OR GROUP.RANKS OR RANK.COL) THEN
             DISPLAY.RANKS = YES
          END ELSE
             DISPLAY.RANKS = NO
          END

          * If no qty types were specified, set to ALL by default
          IF IQTYPES = '' THEN
             QTYPES = 'S':VM:'F':VM:'O':VM:'R':VM:'L':VM:'T'
             IF VCSGN = 'Include' THEN QTYPES<1,-1> = 'V'
             IF CCSGN = 'Include' THEN QTYPES<1,-1> = 'C'
          END ELSE
             QTYPES = IQTYPES
          END

          IF VCSGN = 'Only' THEN QTYPES = 'V'
          IF CCSGN = 'Only' THEN QTYPES = 'C'

          LOCATE 'V' IN QTYPES<1> SETTING DUM THEN
             FND.VQTYPE = YES
          END ELSE
             FND.VQTYPE = NO
          END

          NEG   = NEG[1,1]

          BR.CT = DCOUNT(BRCHS,VM)
          BRS   = BR

          BCNT = DCOUNT(BASIS.LIST,AM)
          IF BCNT > 1 THEN
             GBDESC = "**Multiple**"
          END ELSE
             GLOBAL.BNAME.GET BASIS.LIST<1,1,1>,GBDESC
          END

          BEGIN CASE
          CASE GBDESC = "COGS-COST";            KPT = 3
          CASE GBDESC = "DFLT-COST";            KPT = 4
          CASE OTHERWISE;                       KPT = 1
          END CASE

          * Build some dashed lines for when we need to print totals...

          IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
             SINGLE.LINES  = '  ':STR('-',11):' ':STR('-',9):' ':STR('-',9)
             SINGLE.LINES := ' ':STR('-',9):' ':STR('-',9):SPACE(24):STR('-',13)
          END ELSE
             SINGLE.LINES  = '  ':STR('-',11):' ':STR('-',9):' ':STR('-',9)
             SINGLE.LINES := ' ':STR('-',9):SPACE(24):STR('-',13)
          END
          DOUBLE.LINES = SINGLE.LINES
          CONVERT '-' TO '=' IN DOUBLE.LINES

          * See if they are storing serial numbers by location.
          READV SLC.FLAG FROM CTRLFILE,'RF.LOC.BY.SERS',1 ELSE SLC.FLAG=''
          READV SCONV FROM CTRLFILE,'SERIAL.CONV.NEEDED',1 ELSE SCONV = ''
          * If a conversion still needs to run then serials are
          * being stored the opposite of what is in RF.LOC.BY.SERS
          IF SCONV THEN SLC.FLAG = NOT(SLC.FLAG)

          CCN.LIST  = ''
          CCN.QTYS  = ''
          VCN.LIST  = ''
          VCN.QTYS  = ''

          PTOLS.EXT = ""
          RTOLS.EXT = ""
          LTOLS.EXT = ""
          ETOLS.EXT = ""
          BTOLS.EXT = ""
          GTOLS.EXT = ""

          * This is a check to see if we are including primary inventory
          * in our selection.  Products that are primary will have a NULL
          * value in inventory account override field on the product and
          * price line.
          INC.PRI.INV = NO
          LOCATE 'INVTY' IN GL.AUTO<1> SETTING POS THEN
             INVTY.ACCT = GL.AUTO<2,POS>
             LOCATE INVTY.ACCT IN INV.ACCTS<1> SETTING POS2 THEN
                INC.PRI.INV = YES
             END
          END

          IF MODE = 'XML' THEN
             IF OUTBOUND.EMAIL THEN
                OPENSEQ 'MSG-OUT',OUT.ID TO OUTFILE ELSE NULL
             END ELSE
                GET.NEW.ID 'NEXT.DOC',HOLD.ID,,,RPTFILE

                OPENSEQ '&HOLD&',HOLD.ID TO OUTFILE ELSE NULL

                HOLD.LN.CT = 0
                RPT        = ''
                RPT<1>     = OUT.ID
                RPT<2>     = 100
                RPT<3>     = 60
                RPT<4>     = USER.ID
                RPT<5>     = DATE()
                RPT<6>     = INT(TIME())
                RPT<11>    = DATE()
                RPT<12>    = INT(TIME())
                RPT<36>    = USER.ID
             END

             UT.REP.STR BODY.RESP,'&INV.BR&',BRS
             UT.REP.STR BODY.RESP,'&INVTY.DT&',AS.OF.DT
             UT.REP.STR BODY.RESP,'&GBASIS&',GBDESC
             UT.REP.STR BODY.RESP,'&DEMAND.ROUNDING&',DEMAND.ROUNDING
             UT.REP.STR BODY.RESP,'&TARGET.TURNS&',TARGET.TURNS
             UT.REP.STR BODY.RESP,'&DAYS.DEMAND&',DAYS.DEMAND
             UT.REP.STR BODY.RESP,'&HIT.CUTOFF&',HIT.CUTOFF

             LN.CT = DCOUNT(BODY.RESP,AM)
             FOR LN = 1 TO LN.CT
                WRITESEQ BODY.RESP<LN> ON OUTFILE ELSE EXIT
                IF NOT(OUTBOUND.EMAIL) THEN
                   HOLD.LN.CT += 1
                END
             NEXT LN
          END
          RETURN
*-------------------------------------------------------------------------*
SETUP.ODBC:  * Setup the report information so that it's recognized as an
             * ODBC data source.
          DRPT<40> = "1"
          DRPT<41> = ''

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
*** Setup the column justifications and widths.
             DRPT<41,1>  = "L#35"
             DRPT<41,2>  = "L#10"; *Line Id
             DRPT<41,3>  = "L#25"; *Line Description
             DRPT<41,4>  = "R#13"
             DRPT<41,5>  = "R#10"
             DRPT<41,6>  = "R#10"
             DRPT<41,7>  = "R#10"
             DRPT<41,8>  = "R#11"
             DRPT<41,9>  = "R#10"
             DRPT<41,10> = "L#5"
             DRPT<41,11> = "R#13"
             DRPT<41,12> = "L#6"
             DRPT<41,13> = "R#5"
             DRPT<41,14> = "L#10"
             DRPT<41,15> = "L#6"
             DRPT<41,16> = "R#5"

*** Setup the column headings for the report.
             DRPT<45>    = ""
             DRPT<45,1>  = "Product Description"
             DRPT<45,2>  = SLCT
             DRPT<45,3>  = "Description"
             DRPT<45,4>  = "Stock Onhand"
             DRPT<45,5>  = "Tag Onhand"
             DRPT<45,6>  = "R/F/O Onhand"
             DRPT<45,7>  = "Display Onhand"
             DRPT<45,8>  = "Vendor Consign"
             DRPT<45,9>  = "Unit Value"
             DRPT<45,10> = "UM"
             DRPT<45,11> = "Extended Value"
             DRPT<45,12> = "Qty UM"
             DRPT<45,13> = "Br"
             DRPT<45,14> = "G/L Code"
             DRPT<45,15> = "PN"
             DRPT<45,16> = "Seq"

*** Setup which columns should show totals on a break line

             DRPT<42>    = ""
             *** Stock OnHand
             DRPT<42,4>  = YES
             *** Tag OnHand
             DRPT<42,5>  = YES
             *** R/F/O OnHand
             DRPT<42,6>  = YES
             *** Disp OnHand
             DRPT<42,7>  = YES
             *** Ext Value
             DRPT<42,11> = YES

             *** If they're putting the product ranks on the report
             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF DISPLAY.RANKS THEN
                DRPT<41> = INSERT(DRPT<41>,1,4;"L#11")
                DRPT<42> = INSERT(DRPT<42>,1,4;0)
                DRPT<45> = INSERT(DRPT<45>,1,4;"Product Ranks")
             END

             *** If they're running consignment and breaking on the entity
             *** add the entity column to the front of the report and
             *** push all our other report values up at the same time...
             IF ENTBK THEN
                DRPT<41> = INSERT(DRPT<41>,1,1;"L#20")
                DRPT<42> = INSERT(DRPT<42>,1,1;0)
                DRPT<45> = INSERT(DRPT<45>,1,1;"Entity")
             END

*** Set up the items we should be breaking on...
             DRPT<43>    = ""

             *** Set the break on to either the line or gl type column.
             IF SORTBY[1,1] = "L" THEN
                BRK.POS = 2
             END ELSE
                *** If Product Ranks are being displayed, the GL Code
                *** will be in column 14...
                IF DISPLAY.RANKS THEN BRK.POS = 14 ELSE BRK.POS = 13
             END

             *** If we're pg. breaking on consignment entity, all our
             *** data columns will be moved over one...
             IF ENTBK THEN
                BRK.POS += 1
             END

             DRPT<43,BRK.POS> = YES

             *** Set the break for the consignment entity...
             IF ENTBK THEN
                DRPT<43,1> = YES
             END

             *** A break for the Product Ranks if Group by Product Ranks
             *** option is set to yes...
             IF GROUP.RANKS THEN
                IF ENTBK THEN DRPT<43,5> = YES ELSE DRPT<43,4> = YES
             END

*** Setup the initial sortby of the report.
             SORT.COLS = ''
             BEGIN CASE
             CASE GROUP.RANKS AND ENTBK AND SORTBY[1,7] # 'Prod GL'
                SORT.COLS<1> = 1;  *Entity
                SORT.COLS<2> = 5;  *Product Ranks
                SORT.COLS<3> = 3;  *Line
                SORT.COLS<4> = 17; *Seq
             CASE GROUP.RANKS AND ENTBK AND SORTBY[1,7] = 'Prod GL'
                SORT.COLS<1> = 1;  *Entity
                SORT.COLS<2> = 5;  *Product Ranks
                SORT.COLS<3> = 14; *GL Code
                SORT.COLS<4> = 3;  *Line
                SORT.COLS<5> = 17; *Seq
             CASE (GROUP.RANKS OR ENTBK) AND SORTBY[1,7] # 'Prod GL'
                IF ENTBK THEN
                   SORT.COLS<1> = 1;  *Entity
                   SORT.COLS<2> = 3;  *Line
                END ELSE
                   SORT.COLS<1> = 3;  *Product Ranks
                   SORT.COLS<2> = 2;  *Line
                END
                SORT.COLS<3>    = 17; *Seq
             CASE (GROUP.RANKS OR ENTBK) AND SORTBY[1,7] = 'Prod GL'
                IF ENTBK THEN
                   SORT.COLS<1> = 1;  *Entity
                   SORT.COLS<3> = 3;  *Line
                END ELSE
                   SORT.COLS<1> = 3;  *Product Ranks
                   SORT.COLS<3> = 2;  *Line
                END
                SORT.COLS<2>    = 14; *GL Code
                SORT.COLS<4>    = 17; *Seq
             CASE OTHERWISE
                IF SORTBY[1,7]  # 'Prod GL' THEN
                   SORT.COLS<1> = 2;  *Line
                   SORT.COLS<2> = 17; *Seq
                END ELSE
                   SORT.COLS<1> = 14; *GL Code
                   SORT.COLS<3> = 2;  *Line
                   SORT.COLS<4> = 17; *Seq
                END
             END CASE

             DRPT<47>  = ''
             COLUMN.CT = DCOUNT(SORT.COLS,AM)
             FOR CC = 1 TO COLUMN.CT
                DRPT<47,CC> = SORT.COLS<CC>:SVM:"A"
             NEXT CC

*** Setup the columns that will be "bucketed" for selection
             DRPT<49>    = ""
             DRPT<49,1>  = YES
             DRPT<49,2>  = YES
             DRPT<49,14> = YES
             IF GROUP.RANKS THEN
                DRPT<49> = INSERT(DRPT<49>,1,4;YES)
             END
             IF ENTBK THEN
                DRPT<49> = INSERT(DRPT<49>,1,1;YES)
             END

          END ELSE

             *** We'll only be displaying the Stock OnHand, Tag OnHand,
             *** R.F.O OnHand, Disp OnHand and Extended Value columns for
             *** the Summary format...

*** Setup the column justifications and widths.
             DRPT<41,1> = "L#10"
             DRPT<41,2> = "R#13"
             DRPT<41,3> = "R#10"
             DRPT<41,4> = "R#10"
             DRPT<41,5> = "R#10"
             DRPT<41,6> = "R#13"

*** Setup which columns should show totals when we've done a break...
             DRPT<42>    = ""
             DRPT<42,3>  = YES
             DRPT<42,4>  = YES
             DRPT<42,5>  = YES
             DRPT<42,6>  = YES

*** Since this is summary there are not any break ons.
             DRPT<43> = ""

*** Setup the column headings for the report.
             DRPT<45> = ""
             IF SORTBY[1,7] # "Prod GL" THEN
                DRPT<45,1>  = SLCT
             END ELSE
                DRPT<45,1>  = "G/L Code"
             END
             DRPT<45,2> = "Stock Onhand"
             DRPT<45,3> = "Tag Onhand"
             DRPT<45,4> = "R/F/O Onhand"
             DRPT<45,5> = "Display Onhand"
             DRPT<45,6> = "Extended Value"

             *** Setup the sortby on the summary report.
             DRPT<47> = 1:SVM:"A"

             *** If they're putting the product ranks on the report
             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF GROUP.RANKS THEN
                DRPT<41> = INSERT(DRPT<41>,1,2;"L#11")
                DRPT<42> = INSERT(DRPT<42>,1,2;0)
                DRPT<43> = INSERT(DRPT<43>,1,2;YES)
                DRPT<45> = INSERT(DRPT<45>,1,2;"Product Ranks")
             END

             *** Insert our Line Description column...
             IF SORTBY[1,7] # 'Prod GL' THEN
                DRPT<41> = INSERT(DRPT<41>,1,2;"L#25")
                DRPT<42> = INSERT(DRPT<42>,1,2;0)
                DRPT<43> = INSERT(DRPT<43>,1,2;0)
                DRPT<45> = INSERT(DRPT<45>,1,2;"Description")
             END
          END

          *** Set up the number column formats on the report...
          DRPT<44>    = ""
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             DRPT<44,4>  = "MR2"
             DRPT<44,5>  = "MR2"
             DRPT<44,6>  = "MR2"
             DRPT<44,7>  = "MR2"
             DRPT<44,8>  = "MR2"
             DRPT<44,9>  = "MR3"
             DRPT<44,11> = "MR2"
             DRPT<44,15> = "MR0"
          END ELSE
             DRPT<44,2>  = "MR0"
             DRPT<44,3>  = "MR0"
             DRPT<44,4>  = "MR0"
             DRPT<44,5>  = "MR0"
             DRPT<44,6>  = "MR2"
             *** Move our data up to accomodate for the Line Description...
             IF SORTBY[1,7] # 'Prod GL' THEN
                DRPT<44>  = INSERT(DRPT<44>,1,2;" ")
             END
          END

          *** Insert our info into our report arrays, pushing all our
          *** other report values up at the same time...
          IF DISPLAY.RANKS THEN
             BEGIN CASE
             CASE DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B'
                DRPT<44> = INSERT(DRPT<44>,1,4;" ")
             *** Summary format and sorted by 'Line'...
             CASE SORTBY[LEN(SORTBY)-4,4] = 'Line'
                DRPT<44> = INSERT(DRPT<44>,1,3;" ")
             *** Summary format and sorted by 'GL Code'...
             CASE OTHERWISE
                DRPT<44> = INSERT(DRPT<44>,1,2;" ")
             END CASE
          END

          IF ENTBK THEN
             DRPT<44> = INSERT(DRPT<44>,1,1;" ")
          END

          *** Setup default column specific data based on the selections
          *** made by the user for branch, start and end date.
          DRPT<46>    = ""
          DRPT<46,1>  = BRCHS<1,1>
          DRPT<46,2>  = BRCHS<1,1>
          DRPT<46,3>  = AOD
          DRPT<46,4>  = AOD

          RETURN
*-------------------------------------------------------------------------*
UPD.SUMS: *** Update the summary buckets for the specified items
          IF NOT(HAS.ODBC) THEN RETURN

          *** Product Number...
          IF BCKTS<1> THEN
             *** Check whether we've already added values for this product.
             LOCATE BCKTS<1> IN BUCKETS<1> SETTING POS ELSE
                BUCKETS<1,POS> = BCKTS<1>
             END
             BUCKETS<2,POS> = ADDS(BUCKETS<2,POS>,LOWER(LOWER(SUMRY)))
          END

          *** Line...
          IF BCKTS<2> THEN
             *** Check whether we've already added values for this line...
             LOCATE BCKTS<2> IN BUCKETS<3> SETTING POS ELSE
                BUCKETS<3,POS> = BCKTS<2>
             END
             BUCKETS<4,POS> = ADDS(BUCKETS<4,POS>,LOWER(LOWER(SUMRY)))
          END

          *** G/L Code...
          IF BCKTS<3> THEN
             *** Check whether we've already added values for this G/L Code
             LOCATE BCKTS<3> IN BUCKETS<5> SETTING POS ELSE
                BUCKETS<5,POS> = BCKTS<3>
             END
             BUCKETS<6,POS> = ADDS(BUCKETS<6,POS>,LOWER(LOWER(SUMRY)))
          END

          *** Product Ranks...
          IF DETAIL[1,6] = 'Detail' THEN
             IF BCKTS<4> THEN
                *** Check whether we've already added values for these
                *** Product Ranks...
                LOCATE BCKTS<4> IN BUCKETS<7> SETTING POS ELSE
                   BUCKETS<7,POS> = BCKTS<4>
                END
                BUCKETS<8,POS> = ADDS(BUCKETS<8,POS>,LOWER(LOWER(SUMRY)))
             END
          END

          RETURN
*-------------------------------------------------------------------------*
CREATE.DICTS:   * Create the dictionaries for the ODBC report file.

          WRITE 'Creating Dictionaries...' ON PHSTFILE,PID$

          *** Setup a "dummy" report writer data set to auto-create the
          *** dictionaries available for the users from this report.
          WORK.ITEM = ""

          *** Setup that this report was run off of the product file.
          WORK.ITEM<2>    = "PRODUCT"

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             *** Set up the known dictionaries for the respective
             *** columns on the report.
             LINE.COL = 2
             PN.COL   = 14
             IF ENTBK THEN
                LINE.COL += 1
                PN.COL   += 1
             END
             IF DISPLAY.RANKS THEN
                LINE.COL += 1
             END

             WORK.ITEM<4,LINE.COL>  = "LINE"
             WORK.ITEM<4,PN.COL>    = "@ID"

             *** Setup the initial column names.
             WORK.ITEM<7,1>  = "Product Description"
             WORK.ITEM<7,2>  = SLCT
             WORK.ITEM<7,3>  = "Description"
             WORK.ITEM<7,4>  = "Stock Onhand"
             WORK.ITEM<7,5>  = "Tag Onhand"
             WORK.ITEM<7,6>  = "R/F/O Onhand"
             WORK.ITEM<7,7>  = "Display Onhand"
             WORK.ITEM<7,8>  = "Unit Value"
             WORK.ITEM<7,9>  = "UM"
             WORK.ITEM<7,10>  = "Extended Value"
             WORK.ITEM<7,11> = "Qty UM"
             WORK.ITEM<7,12> = "Br"
             WORK.ITEM<7,13> = "G/L Code"
             WORK.ITEM<7,14> = "PN"
             WORK.ITEM<7,15> = "Seq"

             *** Setup the widths on each column.
             WORK.ITEM<6,1>  = "35"
             WORK.ITEM<6,2>  = "10"
             WORK.ITEM<6,3>  = "15"
             WORK.ITEM<6,4>  = "13"
             WORK.ITEM<6,5>  = "10"
             WORK.ITEM<6,6>  = "10"
             WORK.ITEM<6,7>  = "10"
             WORK.ITEM<6,8>  = "11"
             WORK.ITEM<6,9>  = "3"
             WORK.ITEM<6,10> = "13"
             WORK.ITEM<6,11> = "5"
             WORK.ITEM<6,12> = "3"
             WORK.ITEM<6,13> = "10"
             WORK.ITEM<6,14> = "6"
             WORK.ITEM<6,15> = "6"

             *** Setup the number formats, only for numeric columns.
             WORK.ITEM<26,4>  = "MR2"
             WORK.ITEM<26,5>  = "MR2"
             WORK.ITEM<26,6>  = "MR2"
             WORK.ITEM<26,7>  = "MR2"
             WORK.ITEM<26,8>  = "MR3"
             WORK.ITEM<26,10> = "MR2"
             WORK.ITEM<26,15> = "MR0"

             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF DISPLAY.RANKS THEN
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,4;" ")
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,4;"11")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,4;"Product Ranks")
             END

             IF ENTBK THEN
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,1;" ")
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,1;"20")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,1;"Entity")
             END
          END ELSE
             *** Setup the known dictionaries for the respective columns on
             *** the report.
             IF SORTBY[1,7] # "Prod GL" THEN
                WORK.ITEM<4,1> = "LINE"
                WORK.ITEM<7,1> = SLCT
             END ELSE
                WORK.ITEM<7,1> = "G/L Code"
             END

             *** Setup the initial column names.
             WORK.ITEM<7,2> = "Stock Onhand"
             WORK.ITEM<7,3> = "Tag Onhand"
             WORK.ITEM<7,4> = "R/F/O Onhand"
             WORK.ITEM<7,5> = "Display Onhand"
             WORK.ITEM<7,6> = "Extended Value"

             *** Setup the widths on each column.
             WORK.ITEM<6,1>  = "10"
             WORK.ITEM<6,2>  = "13"
             WORK.ITEM<6,3>  = "10"
             WORK.ITEM<6,4>  = "10"
             WORK.ITEM<6,5>  = "10"
             WORK.ITEM<6,6>  = "13"

             *** Setup the number formats, only for numeric columns.
             WORK.ITEM<26,2> = "MR0"
             WORK.ITEM<26,3> = "MR0"
             WORK.ITEM<26,4> = "MR0"
             WORK.ITEM<26,5> = "MR0"
             WORK.ITEM<26,6> = "MR2"

             IF SORTBY[1,7] # 'Prod GL' THEN
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,2;"25")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,2;"Description")
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,2;" ")
             END
          END

          *** Flag the dictionary creation to create all related file
          *** dictionaries for this report.
          WORK.ITEM<50,3> = YES
          ODBC.SET.DICTS FILE.ID, WORK.ITEM

          RETURN
*-------------------------------------------------------------------------*
WRITE.BCKTS:   * Write the summary information to the files.

          WRITE 'Creating Summaries...    ' ON PHSTFILE,PID$

          BCKTFILE  = PN.ODBCFILE
          BFILE.ID  = FILE.ID:'.PN'
          BCKT.DATA = RAISE(BUCKETS<1>)
          BCKT.VALS = RAISE(BUCKETS<2>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          BCKTFILE  = PL.ODBCFILE
          BFILE.ID  = FILE.ID:'.':FILE.LINE
          BCKT.DATA = RAISE(BUCKETS<3>)
          BCKT.VALS = RAISE(BUCKETS<4>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          BCKTFILE  = GL.ODBCFILE
          BFILE.ID  = FILE.ID:'.G/L_CODE'
          BCKT.DATA = RAISE(BUCKETS<5>)
          BCKT.VALS = RAISE(BUCKETS<6>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          *** Since Product Ranks are Branch specific, we can only
          *** have subtotals if the report is in Detail format...
          IF DETAIL[1,6] = 'Detail' THEN
             *** Product Rank Totals...
             BCKTFILE  = RANK.ODBCFILE
             BFILE.ID  = FILE.ID:'.PROD.RANK'
             BCKT.DATA = RAISE(BUCKETS<7>)
             BCKT.VALS = RAISE(BUCKETS<8>)
             GOSUB WRT.DATA
             GOSUB BCKT.DICTS
          END

          RETURN
*-------------------------------------------------------------------------*
WRT.DATA: * Actually perform the bucket writes.
          BCKT.CT = DCOUNT(BCKT.DATA,AM)

          FOR BPOS = 1 TO BCKT.CT
             WRITE RAISE(BCKT.VALS<BPOS>) ON BCKTFILE,BCKT.DATA<BPOS>
          NEXT BPOS

          RETURN
*-------------------------------------------------------------------------*
BCKT.DICTS:   * Create the dictionaries for the ODBC summary files.
          WRITE 'Creating Dictionaries...' ON PHSTFILE,PID$

          *** Setup a "dummy" report writer data set to auto-create the
          *** dictionaries available for the users from this report.
          WORK.ITEM = ""

          *** Setup that this report was run off of the product file.
          WORK.ITEM<2>    = "PRODUCT"

          *** Setup the initial column names.
          WORK.ITEM<7,1>  = "Stock Onhand"
          WORK.ITEM<7,2>  = "Tag Onhand"
          WORK.ITEM<7,3>  = "R/F/O Onhand"
          WORK.ITEM<7,4>  = "Display Onhand"
          WORK.ITEM<7,5>  = "Unit Value"
          WORK.ITEM<7,6>  = "Extended Value"

          *** Setup the widths on each column.
          WORK.ITEM<6,1>  = "14"
          WORK.ITEM<6,2>  = "14"
          WORK.ITEM<6,3>  = "14"
          WORK.ITEM<6,4>  = "14"
          WORK.ITEM<6,5>  = "14"
          WORK.ITEM<6,6>  = "16"

          *** Setup the number formats, only for numeric columns.
          WORK.ITEM<26,1> = "MR2"
          WORK.ITEM<26,2> = "MR2"
          WORK.ITEM<26,3> = "MR2"
          WORK.ITEM<26,4> = "MR2"
          WORK.ITEM<26,5> = "MR3"
          WORK.ITEM<26,6> = "MR2"

          ODBC.SET.DICTS BFILE.ID, WORK.ITEM

          RETURN
*-------------------------------------------------------------------------*
GET.ANZR: *** Create an Annualizer (multiplier for calculating annualized
          *** data)...

          IF PRD(26)<1,1> > START.DATE THEN
             IF PRD(26)<1,1> > END.DATE THEN
                ANNUALIZER = 0
             END ELSE
                ANNUALIZER = 365 / (END.DATE - PRD(26)<1,1> + 1)
             END
          END ELSE
             ANNUALIZER = 365 / (END.DATE - START.DATE + 1)
          END

          RETURN
*-------------------------------------------------------------------------*
GET.OC:   *** Get the order cycle for the product
          IS.WHSE = NO

          WBRS = WHSE.LIST(BR,PCGID)
          WBRS = WBRS<1>
          LOCATE BR IN WBRS<1> BY 'AL' SETTING POS ELSE
             WBRS=INSERT(WBRS,1,POS;BR)
          END

          PBRS = BUY.FOR.BR(BR,PCGID)
          WHOF = WHSE.OF(BR,PCGID)
          BB   = PURCH.BRS(BR,PCGID)

          IF PBRS # BR AND PBRS # '' THEN
             IS.WHSE = YES
          END

          IF WBRS # BR AND WBRS # PBRS AND WBRS # '' THEN
             IF WHOF = BR THEN IS.WHSE = YES
          END

          IF WHOF = BR THEN IS.WHSE = YES

          IF IS.WHSE THEN
             OC    = GET.CYCLE(PRD(12),BB,PRD(18))
          END ELSE
             WBRS  = WHSE.BRS(BR,PCGID)
             WHSE  = WBRS<1,1>

             CID = 'AUTO.XFER.DFLT~':WHSE:'.':BR
             READV OC FROM CTRBFILE,CID,1 ELSE OC = ''
             OC = OC<1,1>
          END

          RETURN
*-------------------------------------------------------------------------*
!TSMITH~08/24/12~16:31
          PH.ARGS DRPT,BRCHS,BR,AOD,SEL.DATA,BASIS.LIST,DET.OPTS,SORTBY,EXT.AMT.VAL,INC.EXC.DATA,IQTYPES,CAOD,ADDL.DATA,RANKS,MISC.DATA,GROUP.RANK.DATA
** Version# 203.0002[27] - 08/24/2012 - 04:31pm - TSMITH - eclipse
*** V203.0002 Change - Custom Coding . - 08/24/2012 - TSMITH - eclipse
** Copied from BP INV.PHR.INV.VALUE Version# 203 - 06/13/2012 - 06:47pm - TRAVISC - main
*** Subroutine - INV.PHR.INV.VALUE
*-------------------------------------------------------------------------*
*** This is the phantom routine for running the Inventory Valuation
*** report, which is used to show the value of products within a Price
*** or Buy Line, a group of Price or Buy Lines, or all Price or Buy Lines
*** within one or more Branches.
***
*** HAS.ODBC logic has been added to this report to write the report data
*** and summary information to ODBC layout files that will allow the added
*** functionality of the solar reporter with this report. The information
*** will only be written to the ODBC file structure if the site has the
*** add-on key for the solar reporter.
*-------------------------------------------------------------------------*
*** NOTE: As of v169 of this phantom, we NO LONGER use the phantom
*** FIFO.PHR.INV.VALUE.
*-------------------------------------------------------------------------*
*** DRPT                 - Report printing defaults                [IN]
*** BRCHS                - Branches to generate report data for    [IN]
*** AOD                  - As of date                              [IN]
*** SEL.DATA             - Price/Buy Line data, SVM delimited:     (IN)
*** SEL.DATA<1,1>        - Price/Buy Lines.
*** SEL.DATA<1,2>        - Line Type.
*** BASIS.LIST           - A VM-List of basis columns that will    [IN]
***                      - need to be calculated to be included
***                      - on the report.
***                      -   @SVM1 -> Basis Number
***                      -   @SVM2 -> Unit Column #
***                      -   @SVM3 -> Extended Column #
*** DET.OPTS             - Report format options.  VM delimited    [IN]
*** DET.OPTS<1,1>        - DET - Report format option:
***                      -  'Detail' = shows Product Detail by Branch
***                      -  'Summary'= shows Subtotals.
***                      -  'Summary by Product' = shows Product
***                      -       Detail totaled for all Branches.
*** SORTBY               - 'Sort by' option:                       [IN]
***                      - Price/Buy Line or GL code
*** EXT.AMT.VAL          - Only show Detail for Extended Values    (IN)
***                      - greater than this value
*** INC.EXC.DATA         - VM delimited Include/Exclude/Only opts  (IN)
*** INC.EXC.DATA<1,1>    - Nonstocks
*** INC.EXC.DATA<1,2>    - Negative OnHand Qtys
*** INC.EXC.DATA<1,3>    - Zero OnHand Qtys
*** IQTYPES              - Quanity types                           (IN)
*** ADDL.DATA           - Consignment Data, SVM delimited:        (IN)
*** ADDL.DATA<1,1,1>    - Include/Exclude/Only show Consignment
***                      - Vendors
*** ADDL.DATA<1,1,2>    - Include/Exclude/Only show Consignment
***                      - Customers
*** ADDL.DATA<1,1,3>    - Consignment Vendor/Customer to report
***                      - data for
*** ADDL.DATA<1,1,4>    - Page Break on Consignment Vendor
*** ADDL.DATA<1,1,5>    - Page Break on Consignment Customer
*** ADDL.DATA<1,1,6>    - Show consignment customers on seperate line
*** ADDL.DATA<1,1,7>    - Show vendor consignment pricing on the ext val
*** ADDL.DATA<1,1,8>     - Number of years of history to consider
***                      - for FIFO.
*** ADDL.DATA<1,1,9>     - Boolean that, when TRUE, indicates that
***                      - Inventory Adjustments should be included
***                      - in FIFO calculations.
*** RANKS                - Product Ranks, VM delimited by Rank     (IN)
***                      - Number (possible 1-5) and SVM
***                      - delimited by Rank (possible A-X & G).
***                      - Only products having the selected Ranks
***                      - will be included on the report.
*** MISC.DATA            - Miscellaneous report processing data:   (IN)
*** MISC.DATA<1,1>       - ANY.ALL:  Any/All
***                      - 'Must Match Any/All Rank Selections' option:
***                      - If set to 'Any', products whose ranks
***                      - match any of the selected ranks will
***                      - be included on the report.
***                      - If set to 'All' only those products
***                      - whose ranks match ALL of the selected
***                      - ranks will be included on the report.
*** MISC.DATA<1,2>       - NO LONGER USED (Tagged Items selected
***                      - via column logic).
*** MISC.DATA<1,4>       - AVG.CST
***                      - Flag - If set to yes and AVG-COST is $0
***                      -        do not use REP-COST. Show $0 instead.
*** MISC.DATA<1,5>       - If set to XML this will generate an
***                        XML extract file for Inventory Modeling
*** MISC.DATA<1,6>       - Flag indicates if they have Outbound Email.
***                      - Only applies for Inventory Modeling
*** GROUP.RANK.DATA      - Group Rank Data, VM delimited.          (IN)
*** GROUP.RANK.DATA<1,1> - 'Group Ranks' option. If set to 'Yes',
***                      - then the report data will be grouped
***                      - and subtotaled by product rank.  For
***                      - example, products ranked as an 'A' item
***                      - in each of the Rank Numbers selected will
***                      - be grouped together. A product ranked as
***                      - an 'A' item for all Rank Numbers except
***                      - one would be in a different group.
*** GROUP.RANK.DATA<1,2> - 'Subtotal on Sort by' option.  This value
***                      - can only be set to 'No' when the 'Group
***                      - by Product Ranks' option is set to 'Yes'
***                      - Otherwise, it is always 'Yes'
*-------------------------------------------------------------------------*
*** COMMON Variables Used: DRPT, PRD, PRDD
*-------------------------------------------------------------------------*

          *** Go Initialize the data we'll need for this Phantom to work...
          GOSUB INIT

          IF ERR.MSG THEN
             SEND.MESSAGE "Phantom",USER.ID,ERR.MSG
             RETURN
          END
*-------------------------------------------------------------------------*
          *** Update our phantom status...
          WRITE 'Selecting...' ON PHSTFILE,PID$

          *** Go select data for our report...
          GOSUB SEL.IDS

          *** Update our phantom status...
          WRITE 'Spooling...' ON PHSTFILE,PID$
*-------------------------------------------------------------------------*
*** Build our report heading...

          IF SLIST THEN
             TEMP.LIST = SLIST
             CONVERT VM TO ',' IN TEMP.LIST
          END

          HDG  = REPORT.FORMAT:' Inventory Value as of ':AS.OF.DT
          HDG := ' * End of Day * - NonStocks: ':EXCL.NS

          IF EXT.AMT.VAL # '' THEN HDG := ' - Extended Value > ':EXT.AMT.VAL

          IF IQTYPES = '' AND QTYPES # 'V' AND QTYPES # 'C' THEN
             QTYP = 'ALL'
          END ELSE
             XX = DCOUNT(QTYPES,VM)
             IF XX > 0 THEN
                FOR QTP = 1 TO XX
                   BEGIN CASE
                   CASE QTYPES<1,QTP> = 'S'; TEMP.QTYP = 'Stock'
                   CASE QTYPES<1,QTP> = 'F'; TEMP.QTYP = 'Defective'
                   CASE QTYPES<1,QTP> = 'O'; TEMP.QTYP = 'Over Shipment'
                   CASE QTYPES<1,QTP> = 'R'; TEMP.QTYP = 'Review'
                   CASE QTYPES<1,QTP> = 'L'; TEMP.QTYP = 'Display'
                   CASE QTYPES<1,QTP> = 'T'; TEMP.QTYP = 'Tagged'
                   CASE QTYPES<1,QTP> = 'V'; TEMP.QTYP = 'Vend Consigned'
                   CASE QTYPES<1,QTP> = 'C'; TEMP.QTYP = 'Cust Consigned'
                   CASE OTHERWISE;           TEMP.QTYP = ''
                   END CASE

                   IF QTP   = 1 THEN
                      QTYP  = TEMP.QTYP
                   END ELSE
                      QTYP := ',':TEMP.QTYP
                   END
                NEXT QTP
                IF CCN THEN
                   READV NAME FROM CUSFILE,CCN,1 ELSE NAME = CCN
                   QTYP := '  for ':NAME
                END
             END
          END

          HDG<1,2>  = 'Quantity Types: ':QTYP
          HDG<1,2> := ' - Valued at: ':GBDESC:' - As of: ':AS.OF.DT
          HDG<1,2> := ' Price Date: ':OCONV(PRC.DATE,'D4/')
          IF AVG.CST THEN
             HDG<1,2> := ' -Show $0 Avg Cost: Yes'
          END ELSE
             HDG<1,2> := ' -Show $0 Avg Cost: No'
          END

          BRANCH.ALLOW = 129
          IF LEN(BRS) > BRANCH.ALLOW THEN BRS = BRS[1,BRANCH.ALLOW]:'...'

          IF RANKS THEN
             HDG<1,3> = 'Product Ranks: '
             FOR RNUMBER = 1 TO 5
                IF RANKS<1,RNUMBER> THEN
                   SLCTED.RANKS = RAISE(RANKS<1,RNUMBER>)
                   CONVERT VM TO ',' IN SLCTED.RANKS
                   HDG<1,3> := 'Rank#':RNUMBER:': ':SLCTED.RANKS:' '
                END
             NEXT RNUMBER

             IF ANY.ALL = 'All' THEN
                ANY.ALL.MSG = '- Must Match on All Ranks'
             END ELSE
                ANY.ALL.MSG = '- Matching Any Ranks'
             END

             HDG<1,3> := ANY.ALL.MSG

             IF DETAIL[1,6] = 'Detail' AND GROUP.RANKS THEN
                HDG<1,3> := ' - Grouped by Product Ranks'
             END
             LN = 4
          END ELSE
             IF DETAIL[1,6] = 'Detail' AND GROUP.RANKS THEN
                HDG<1,3> = 'Grouped by Product Ranks'
                LN = 4
             END ELSE
                LN = 3
             END
          END

          HDG<1,LN>  = 'Branch(es): ':BRS

          LN += 1

          HDG1.LENGTH = LEN(HDG<1,1>)
          HDG2.LENGTH = LEN(HDG<1,2>)
          LAST.HDG.LINE.LENGTH = LEN(HDG<1,LN>)

          IF LAST.HDG.LINE.LENGTH > HDG2.LENGTH THEN
             WDTH = LAST.HDG.LINE.LENGTH
          END ELSE
             WDTH = HDG2.LENGTH
          END

          *** Tack the page number onto the first line of our heading...
          HDG<1,1> := SPACE(WDTH - HDG1.LENGTH - 12):'Page: ^#####'

          *** Tack the any Price/Buy Lines selected onto the
          *** same line that our Branches are on...
          IF SLIST THEN
             LN -= 1
             HDG<1,LN> := '   ':SLCT:' Line(s): '
             HDG3.LENGTH = LEN(HDG<1,LN>)
             TEMP.LIST.LENGTH = LEN(TEMP.LIST)

             *** If we don't have room to print all the Lines...
             IF (HDG3.LENGTH + TEMP.LIST.LENGTH) > WDTH THEN
                HDG<1,LN> := '*Multi*'
             END ELSE
                HDG<1,LN> := TEMP.LIST
             END
          END

          LN += 2
          COL.HDG = ""
          REPORT.LAYOUT.V.PRINT.STR "","H",COL.RECORD,"",COL.HDG
          HDG<1,LN> = LOWER(COL.HDG)
          WDTH = MAXIMUM(LENS(RAISE(HDG<1>)))

          IF DRPT<33> = '' THEN
             TITLE = REPORT.FORMAT:' Inventory Valuation as of ':AS.OF.DT
          END ELSE
             TITLE = DRPT<33>
          END

          *** Determine if the site has the solar reporter to update an
          *** ODBC File and bucket information to report on...
          IF MODE # 'XML' THEN
             UT.SEC3 30,HAS.ODBC
          END
          HAS.ODBC = NO

          *** ODBC Is not supported at this time.  If it ever becomes a
          *** product, this will need to be corrected.
          IF HAS.ODBC THEN
             GOSUB SETUP.ODBC
          END
          IF MODE # 'XML' THEN
             PRINTER.ON WDTH,TITLE,DOC.ID,HDG,RPT.DFLT=DRPT
             FILE.ID = "ODBC.":DOC.ID
          END
          IF HAS.ODBC THEN
             BUCKETS = ""

             *** Create the for the ODBC file information.
             TRY.CT = 0
RETRY.CREATE: *
             TRY.CT += 1
             CMD     = "CREATE-FILE ":FILE.ID:" 1 500,4,30"
             EXECUTE CMD CAPTURING MSG
             REC.NUM = 0
             UT.OPEN.FILE FILE.ID,ODBCFILE,ERR.MSG,YES
             IF ERR.MSG THEN
                ERR.MSG = ""
                IF TRY.CT < 3 THEN
                   GOTO RETRY.CREATE
                END ELSE
                   ODBC.MSG = 'Unable to open an ODBC File to run : ':TITLE
                   SEND.MESSAGE "Phantom",USER.ID,ODBC.MSG
                   GOTO OPEN.ERR
                END
             END

             *** Create the summary bucket files for product...
             CMD     = "CREATE-FILE ":FILE.ID:".PN 1 500,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".PN",PN.ODBCFILE,ERR.MSG,YES

             *** Create the summary bucket files for the Price/Buy Line...
             IF SLCT[1,3] = 'Buy' THEN
                FILE.LINE = 'BUY_LINE'
             END ELSE
                FILE.LINE = 'PRICE_LINE'
             END

             CMD     = "CREATE-FILE ":FILE.ID:".":FILE.LINE:" 1 101,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".":FILE.LINE,PL.ODBCFILE,ERR.MSG,YES

             *** Create the summary bucket files for the G/L Code...
             CMD     = "CREATE-FILE ":FILE.ID:".G/L_CODE 1 101,4,30"
             EXECUTE CMD CAPTURING MSG
             UT.OPEN.FILE FILE.ID:".G/L_CODE",GL.ODBCFILE,ERR.MSG,YES

             IF DETAIL[1,6] = 'Detail' THEN
                *** Create the summary bucket files for the product ranks..
                CMD     = "CREATE-FILE ":FILE.ID:".PROD.RANK 1 101,4,30"
                EXECUTE CMD CAPTURING MSG
                UT.OPEN.FILE FILE.ID:".PROD.RANK",RANK.ODBCFILE,ERR.MSG,YES
             END
          END

*** Print our report data...
          ITM.CT     = 0
          ENT.CT     = 0
          LINE.CT    = 0
          RANK.CT    = 0
          SEQUENCE   = ''
          BRK.ON     = ''
          SORT.DESC  = ''
          LAST.BRK   = '@@'
          LAST.DESC  = '@@'
          LAST.RANKS = '@@'
          LAST.EBK   = '@@'
          LTOLS      = '' ; *Sortby Totals
          PTOLS      = '' ; *Product Totals
          RTOLS      = '' ; *Product Ranks Totals
          ETOLS      = '' ; *Entity Totals
          BTOLS      = '' ; *Branch Totals
          GTOLS      = '' ; *Grand Totals
          LTOLS.UM   = '' ; *Sortby Totals Units of Measure
          RTOLS.UM   = '' ; *Product Rank Totals UoM
          ETOLS.UM   = '' ; *Entity Totals UoM
          BTOLS.UM   = '' ; *Branch Totals UoM
          GTOLS.UM   = '' ; *Grand Totals UoM
          PRT.SBY    = NO
          PRT.RANKS  = NO
          SEL.CCN    = ''

          *** If the user has selected a Consignment Entity...
          IF CCN THEN SEL.CCN = CCN
          BLANK.LINE.PRINTED  = NO
          PRINTING.GTOTALS    = NO

          SSELECT TEMPFILE
          LOOP
             READNEXT TID ELSE EXIT
             READ TMP FROM TEMPFILE,TID ELSE CONTINUE

             * If we are printing Summary by Branch, remove the branch
             * at the beginning of the record so the rest of the logic
             * will flow through.
             IF INDEX(DETAIL,'by Branch',1) THEN
                TID = FIELD(TID,'~',2,99)
             END

*** Parse out the data form our Tempfile Id...
             *** If we're Page Breaking on a Consignment Entity...
             IF ENTBK THEN
                CN = FIELD(TID,'~',2)
                BEGIN CASE
                *** If the User wanted the data grouped by Product Ranks
                *** and sorted by Product GL Code...
                CASE GROUP.RANKS AND SORTBY[1,7] = 'Prod GL'
                   PRODUCT.RANKS = FIELD(TID,'~',3)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',4))
                   PN = FIELD(TID,'~',7)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',8)
                   END
                CASE GROUP.RANKS
                   PRODUCT.RANKS = FIELD(TID,'~',3)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',4))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                 IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',5)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',6 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',7 + VPOS)
                   END
                CASE SORTBY[1,7] = 'Prod GL'
                   SORT.VALUE = TRIM(FIELD(TID,'~',3))
                   PN = FIELD(TID,'~',6)
                   BR = FIELD(TID,'~',7)
                CASE OTHERWISE
                   SORT.VALUE = TRIM(FIELD(TID,'~',3))
                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                  IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',4)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',5 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',6 + VPOS)
                   END
                END CASE
             END ELSE
                BEGIN CASE
                CASE GROUP.RANKS AND SORTBY[1,7] = 'Prod GL'
                   PRODUCT.RANKS = FIELD(TID,'~',1)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',2))
                   PN = FIELD(TID,'~',5)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',6)
                   END
                CASE GROUP.RANKS
                   PRODUCT.RANKS = FIELD(TID,'~',1)
                   CONVERT '!' TO VM IN PRODUCT.RANKS

                   SORT.VALUE = TRIM(FIELD(TID,'~',2))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                 IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',3)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',4 + VPOS)


                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',5 + VPOS)
                   END
                CASE SORTBY[1,7] = 'Prod GL'
                   SORT.VALUE = TRIM(FIELD(TID,'~',1))
                   PN = FIELD(TID,'~',4)
                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',5)
                   END
                CASE OTHERWISE
                   SORT.VALUE = TRIM(FIELD(TID,'~',1))

                   *** If they sorted on Price Line or Buy Line then we
                   *** have to account for the fact that the Sequence (SEQ)
                   *** is part of the Tempfile Id...
                   IF SORTBY   = 'Price Line' OR SORTBY = 'Buy Line' THEN
                      VPOS     = 1
                      SEQUENCE = FIELD(TID,'~',3 + VPOS)
                      IF NUM(SEQUENCE) THEN SEQUENCE += 0
                   END ELSE
                      VPOS     = 0
                   END

                   PN = FIELD(TID,'~',3 + VPOS)

                   IF DETAIL # 'Summary' THEN
                      BR = FIELD(TID,'~',4 + VPOS)
                   END
                END CASE
             END

             TAG.QTY   = TMP<1>
             STK.QTY   = TMP<2>
             DISP.QTY  = TMP<3>
             REV.QTY   = TMP<4>
             VCN.QTY   = TMP<5>
             EXT.BASE  = RAISE(TMP<7>)
             TAG.EXT   = TMP<16>

             TAG.IDS   = ''
             TAG.QTYS  = ''
             TAG.COSTS = ''
             TAG.BASE  = ''

             IF TAG.QTY+0 # 0 THEN
                TAG.BASE = OCONV(TAG.EXT,'MR3') / TAG.QTY
             END
             TAG.EXT = ICONV(OCONV(TAG.EXT,'MR3'),'MR2')

             ITEM.TOLS      = STK.QTY
             ITEM.TOLS<1,2> = TAG.QTY
             ITEM.TOLS<1,3> = TAG.EXT
             ITEM.TOLS<1,5> = DISP.QTY
             ITEM.TOLS<1,6> = REV.QTY
             ITEM.TOLS<1,7> = VCN.QTY

             EXT.TOLS = ""
             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                EXT.AMT = EXT.BASE<ECT,1>
                COL     = EXT.BASE<ECT,2>
                * add to column if there is an ext column
                IF COL # 0 THEN EXT.TOLS<COL> = EXT.AMT
             NEXT ECT

             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PER        = TMP<8>
                PER.UM     = TMP<9>
                DFLT.PER   = TMP<10>
                DFLT.ALPHA = TMP<11>
                UNIT.BASE  = RAISE(TMP<12>)

                IF DETAIL[1,6] = 'Detail' THEN
                   TAG.IDS   = TMP<13>
                   TAG.QTYS  = TMP<14>
                   TAG.COSTS = TMP<15>
                   * if splitting out tag costs, remove from extended
                   IF TAG.COSTS # '' THEN
                      FOR ECT = 1 TO EXT.CT
                         EXT.BASE<ECT,1> -= TAG.EXT
                      NEXT ECT
                   END
                END
                CCN.LIST   = TMP<17>
                CCN.QTYS   = TMP<18>
                VCN.LIST   = TMP<19>
                VCN.QTYS   = TMP<20>
                FIFO.IDS   = TMP<21>
                FIFO.QTYS  = TMP<22>
                FIFO.COSTS = TMP<23>
                FIFO.DTS   = TMP<24>

                *** Add consignment amounts to tols for product
                CCN.CT = DCOUNT(CCN.LIST<1>,VM)
                VCN.CT = DCOUNT(VCN.LIST<1>,VM)
                FOR XX = 1 TO CCN.CT
                   ITEM.TOLS<1,1> += CCN.QTYS<1,XX>/DFLT.PER
                   * add to each extended amt
                   EXT.CT = DCOUNT(EXT.BASE,AM)
                   FOR ECT = 1 TO EXT.CT
                      COL = EXT.BASE<ECT,2>
                      * add to column if there is an ext column
                      IF COL # 0 THEN
                         CCN.EXT = (CCN.QTYS<1,XX>*100)*(OCONV(UNIT.BASE<ECT,1>,'MR9'))
                         EXT.TOLS<COL> += CCN.EXT
                      END
                   NEXT ECT
                NEXT XX

                FOR XX = 1 TO VCN.CT
                   ITEM.TOLS<1,7> += VCN.QTYS<1,XX>/DFLT.PER
                   * Exclude vendor consignment in extended price calc,
                   * since we do not own title to the inventory.
                   * Unless requested to include it.
                   IF SHOW.VPRC THEN
                      EXT.CT = DCOUNT(EXT.BASE,AM)
                      FOR ECT = 1 TO EXT.CT
                         COL = EXT.BASE<ECT,2>
                         * add to column if there is an ext column
                         IF COL # 0 THEN
                            VCN.EXT = (VCN.QTYS<1,XX>*100)*(OCONV(UNIT.BASE<ECT,1>,'MR9'))
                            EXT.TOLS<COL> += VCN.EXT
                         END
                      NEXT ECT
                   END
                NEXT XX
             END ELSE
                *** If we only picked up one Unit of Measure (type)...
                IF TMP<11,2> = '' THEN
                   LOCATE TMP<11> IN LTOLS.UM<1> SETTING LPOS ELSE
                      LTOLS.UM<1,LPOS> = TMP<11>
                   END
                   LOCATE TMP<11> IN RTOLS.UM<1> SETTING RPOS ELSE
                      RTOLS.UM<1,RPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN ETOLS.UM<1> SETTING EPOS ELSE
                      ETOLS.UM<1,EPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN BTOLS.UM<1> SETTING BPOS ELSE
                      BTOLS.UM<1,BPOS> = DFLT.ALPHA
                   END
                   LOCATE TMP<11> IN GTOLS.UM<1> SETTING GPOS ELSE
                      GTOLS.UM<1,GPOS> = TMP<11>
                   END
                END ELSE
                   *** Otherwise, we know we won't be able to display
                   *** a Unit of Measure for the Product Rank, Entity,
                   *** or Grand Totals...
                   IF RTOLS.UM<1,2> = '' THEN RTOLS.UM<1,2> = '**'
                   IF ETOLS.UM<1,2> = '' THEN ETOLS.UM<1,2> = '**'
                   IF BTOLS.UM<1,2> = '' THEN BTOLS.UM<1,2> = '**'
                   IF GTOLS.UM<1,2> = '' THEN GTOLS.UM<1,2> = '**'
                   IF LTOLS.UM<1,2> = '' THEN LTOLS.UM<1,2> = '**'
                END
             END
             *** Go print one line of the report...
             GOSUB PRT.ONE
          REPEAT

*** Print out any leftover totals (either for sorby or entity)...
          *** Print the last of our Sort by Totals...
          IF NOT(ENTBK) THEN
             IF GROUP.RANKS THEN
                IF ITM.CT THEN GOSUB PRT.RTOLS
             END ELSE
                IF ITM.CT THEN
                   IF INDEX(DETAIL,'by Branch',1) THEN
                      GOSUB PRT.BTOLS
                   END ELSE
                      GOSUB PRT.SUB
                   END
                END
             END
          END ELSE
             *** Print the last of our Consignment Entity Totals...
             IF ENT.CT THEN TMP.CN = CN; GOSUB PRT.ETOLS
          END

          *** Go print our Grand Totals...
          IF NOT(INDEX(DETAIL,'No Totals',1)) THEN
             TOL.DESC         = 'Grand Totals ------'
             ODET             = DET
             ODET             = DET
             TOLS             = GTOLS
             TOLS.UM          = GTOLS.UM
             TOLS.EXT         = GTOLS.EXT
             DET              = YES
             PRINTING.GTOTALS = YES

             GOSUB PRT.TOLS
          END

          *** Reset the value for what level of detail so that the ODBC
          *** file is properly created.
          DET = ODET

          IF MODE # 'XML' THEN
             PRINTER.OFF DOC.ID
          END

          IF HAS.ODBC THEN
             *** Create the dictionaries for the created file format.
             GOSUB CREATE.DICTS
             GOSUB WRITE.BCKTS
          END
OPEN.ERR: **

          UT.TEMPFILE.DELETE FLNM.ID
          UT.PH.CLEANUP

          IF MODE = 'XML' THEN
             LN.CT = DCOUNT(FOOTER.RESP,AM)
             FOR LN = 1 TO LN.CT
                WRITESEQ FOOTER.RESP<LN> ON OUTFILE ELSE EXIT
                IF NOT(OUTBOUND.EMAIL) THEN
                   HOLD.LN.CT += 1
                END
             NEXT LN

             UT.OPEN.FILE 'VOC',VOCFILE,ERR.MSG,YES
             IF ERR.MSG THEN RETURN
             READV ATTACH.PATH FROM VOCFILE,'MSG-OUT',2 ELSE
                ATTACH.PATH = '/u2/msg-out'
             END
             IF OS.TYPE$ = 'NT' THEN       ;* Windows NT Version
                ATTACH.PATH := '\':OUT.ID
             END ELSE
                ATTACH.PATH := '/':OUT.ID
             END

             IF OUTBOUND.EMAIL THEN
                TO.ADDRESS  = DRPT<58,1>
                SUBJECT     = DRPT<58,5>
                CC.ADDRESS  = DRPT<58,7>
                BCC.ADDRESS = DRPT<58,8>
                O.BODY      = DRPT<57>
                FROM.NAME   = DRPT<58,12>
                NETMAIL.OUT TO.ADDRESS,SUBJECT,O.BODY,FROM.NAME,BCC.ADDRESS,CC.ADDRESS,ATTACH.PATH
             END ELSE
                RPT<10> = HOLD.LN.CT
                RPT<66> = 'Binary File'
                WRITE RPT ON RPTFILE,HOLD.ID
                CLOSESEQ OUTFILE
             END
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' XML Extract is Complete'

             * Clean up the extract file
             IF OS.TYPE$ = "NT" THEN
                COMMAND = "del "
                ASROOT  = NO
             END ELSE
                COMMAND = "rm "
                ASROOT  = YES
             END
             COMMAND := ATTACH.PATH
             EXECUTE.OS.COMMAND MSG,COMMAND,ASROOT
          END ELSE
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' is Complete'
          END
          STOP
*-------------------------------------------------------------------------*
PRT.ONE:
          *** Modified the code to return if a valid PN cannot be read.

          MATREAD PRD FROM PRDFILE,PN  ELSE RETURN

          BR.SHORT.DESC  = ''
          BR.COST.CENTER = ''
          IF BR # '' THEN
             READ TERR FROM TERRFILE,BR ELSE TERR = ''
             BR.SHORT.DESC  = TERR<1>           ;* Branch Short Description
             BR.COST.CENTER = TERR<34>          ;* Branch Cost Center
          END

          *** If we're displaying the Product's Ranks...
          *** We are checking the TEMP.PRODUCT.RANKS var to make sure
          *** we have RANKS in them as we don't want to print it if the
          *** user required ranking as part of this report.
          IF DISPLAY.RANKS THEN
             PRDC.BR.GET.VAL BR,PN,1,TEMP.PRODUCT.RANKS
             CONVERT VM TO ',' IN TEMP.PRODUCT.RANKS
          END
          GL.CODE  = PRD(2)
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             PRDD.BR.GET BR,PN
          END

          IF SLCT[1,3] = 'Buy' THEN
             TLINE = PRD(12)
          END ELSE
             TLINE = PRD(9)
          END

          BEGIN CASE
          CASE INDEX(DETAIL,'by Branch',1)
             BRK.ON = BR
          CASE SORTBY[1,7] = 'Prod GL'
             BRK.ON = GL.CODE
          CASE SORTBY = 'Price Line'
             BRK.ON = PRD(9)
          CASE SORTBY = 'Buy Line'
             BRK.ON = PRD(12)
          CASE SORTBY = 'Sell Group'
             PRD.BR.GET.VAL BRCHS<1,1>,PN,24,SEL.GRP
             SEL.GRP = SEL.GRP<1,1,1>
             BRK.ON = SEL.GRP
          CASE SORTBY = 'Buy Group'
             PRD.BR.GET.VAL BRCHS<1,1>,PN,23,BUY.GRP
             BUY.GRP = BUY.GRP<1,1,1>
             BRK.ON = BUY.GRP
          CASE SORTBY = 'Prod ID'
             BRK.ON = PRD(9)
          END CASE

*** Check whether we need to print any Totals...
          *** If we're not Page Breaking on Consignment Vendors
          *** or Customers...

          IF NOT(ENTBK) THEN
             *** If we're grouping the data by product ranks...
             IF GROUP.RANKS THEN
                IF (LAST.RANKS # PRODUCT.RANKS) AND LAST.RANKS # '@@' THEN
                   *** Need to print our Sort by Subtotals first...
                   IF ITM.CT THEN GOSUB PRT.RTOLS
                END ELSE
                   LAST.BRK  = BRK.ON
                   LAST.DESC = SORT.DESC
                   ITM.CT    = 0
                END
                LAST.RANKS   = PRODUCT.RANKS
             END ELSE
                IF LAST.BRK # BRK.ON THEN
                   *** If we have in fact printed data for a line/GL Code,
                   *** go print Subtotals for the last one...
                   IF LAST.BRK # '@@' AND ITM.CT AND SUBT.ON.SORTBY THEN
                      IF INDEX(DETAIL,'by Branch',1) THEN
                         GOSUB PRT.BTOLS
                      END ELSE
                         GOSUB PRT.SUB
                      END
                   END
                   LAST.BRK  = BRK.ON
                   LAST.DESC = SORT.DESC
                   ITM.CT    = 0
                END
             END
          END ELSE
             EBK.ON = CN
             IF LAST.EBK # EBK.ON THEN
                IF LAST.EBK # '@@' THEN
                   IF ENT.CT THEN TMP.CN = LAST.EBK; GOSUB PRT.ETOLS
                END
                LAST.EBK    = EBK.ON
                ENT.CT      = 0
                LAST.RANKS  = PRODUCT.RANKS
             END ELSE
                *** If we're grouping the data by product ranks...
                IF GROUP.RANKS THEN
                   IF (LAST.RANKS # PRODUCT.RANKS) AND LAST.RANKS#'@@' THEN
                      *** Need to print our Sort by Subtotals first...
                      IF ITM.CT THEN GOSUB PRT.RTOLS
                   END
                   LAST.RANKS = PRODUCT.RANKS
                END
             END
          END

          PTOLS       = ''
          PRINTED.DET = NO
          GOSUB PRINT.QTYS
          LAST.BR.DESC     = BR.SHORT.DESC
          LAST.COST.CENTER = BR.COST.CENTER

          RETURN
*-------------------------------------------------------------------------*
CLR.KIT:  *** Clear out all kit values for dynamic kit so it will act like
          *** a "normal" stock item
          PRD(52) = ''
          PRD(53) = ''
          PRD(85) = ''
          PRD(86) = ''
          PRD(87) = ''

          RETURN
*-------------------------------------------------------------------------*
GET.VAL:  ***
          UNIT.BASE = ""
          EXT.BASE  = ""
          BASN.CT = DCOUNT(BASIS.LIST,AM)
          FOR BCT = 1 TO BASN.CT
             GBASN    = BASIS.LIST<BCT,1>
             UNIT.COL = BASIS.LIST<BCT,2> + 0
             EXT.COL  = BASIS.LIST<BCT,3> + 0

             IF (GBASN > 30) THEN
                GOSUB GET.FIFO
             END ELSE
                GOSUB GET.ONE.VAL
             END

             UNIT.BASE<-1> = BASE:VM:UNIT.COL

             * Tags need to be included into the totals for each base
             IF OHQ # "" OR ADD.TAG THEN
                EXT = ICONV(OCONV(TAG.EXT.AMT,'MR3'),'MR2')
                IF OHQ # "" THEN
                   EXT += ICONV((OCONV(BASE,'MR9') * OHQ),'MR2')
                END
                EXT.BASE<-1> = EXT:VM:EXT.COL
             END
          NEXT BCT

          RETURN
*-------------------------------------------------------------------------*
GET.ONE.VAL: *** Get the dollar value of one unit of inventory for current
          *** product.
          *** PN and BR must be active.. BASE is the useful value returned.

          GET.ALL.PRD BR,PN
          GLOBAL.BASN.GET GBASN,BASN

          KOPTS = PRD(86)
          IF KOPTS<1,KPT>#'' THEN
             KOPTS<1,1> = KOPTS<1,KPT>
          END

          LOCTN.CTRL.TYPE = PRDD.BR(11)

          BASE = 0
          IF GBDESC = "Avg Lot Actual Cost" THEN
             GOSUB GET.AVG.LOT.CST
          END

          IF BASE = 0 THEN
             * If 'Show $0 Average Cost' is Yes (AVG.CST) and basis is
             * for an average cost then set ZERO.OK so that GET.BASE.AOD
             * will not use the current cost when there isn't any pricing
             * found in the product log.
             BEGIN CASE
             CASE NOT(AVG.CST); ZERO.OK = NO
             CASE BASN = 8    ; ZERO.OK = YES ;* Avg Actual Cost
             CASE BASN = 22   ; ZERO.OK = YES ;* Avg Landed Cost
             CASE BASN = 23   ; ZERO.OK = YES ;* Frozen Avg Cost
             CASE BASN = 24   ; ZERO.OK = YES ;* Frozen Last Cost
             CASE BASN = 26   ; ZERO.OK = YES ;* Frozen Avg Landed
             CASE BASN = 27   ; ZERO.OK = YES ;* Frozen Landed
             CASE OTHERWISE   ; ZERO.OK = NO
             END CASE

             GET.BASE.AOD PN,BR,BASN,CAOD,BASE,PER,KOPTS,PRD(52),PRD(53),PRD(87),,ZERO.OK

          END

          BASE = ICONV(ICONV(OCONV(BASE,'MR3'),'MR9')/PER,'MR0')

          RETURN
*-------------------------------------------------------------------------*
GET.FIFO: ***
          GET.ALL.PRD BR,PN

          CHK.QTY = STK.QTY + TAG.QTY

          FIFO.BR = BR

          GET.FIFO.COST FIFO.COST,PN,FIFO.BR,AOD,CHK.QTY,FIFO.QTYS,FIFO.IDS,FIFO.COSTS,FIFO.DTS,HIST.YEARS,INCL.ADJ,IS.CRRBS

          BASE = ICONV(FIFO.COST,'MR9')

          RETURN
*-------------------------------------------------------------------------*
GET.AVG.LOT.CST:
          LOTS = ''
          QTYS = ''
          L.CT = DCOUNT(PRDD.BR(8),VM)
          FOR L = 1 TO L.CT
            IF PRDD.BR(1)<1,L>+0 # 0 THEN
               LOT = FIELD(FIELD(FIELD(PRDD.BR(8)<1,L>,'~',2),'|',2),'-',1)
               LOT.QTY = PRDD.BR(1)<1,L>
               LOCATE LOT IN LOTS SETTING POS ELSE
                  LOTS<POS> = LOT
                  QTYS<POS> = 0
               END
               QTYS<POS> += LOT.QTY
            END
          NEXT L
          TOT.COST.ADDR = 0
          COST.ADDR     = 0
          TOT.LOT.COST  = 0
          TOT.LOT.QTY   = 0
          LOT.CT        = 0
          L.CT = DCOUNT(LOTS,AM)
          FOR L = 1 TO L.CT
             LOT = LOTS<L>
             QTY = QTYS<L>
             IF QTY > 0 THEN
                ID = PN:'~':LOT
                READ LREC FROM DLOTFILE,ID THEN
                   PRICE.PER.GET PPER,PER.UM,,,PRC.DATE
                   LOT.COST = LREC<28>/PPER
                   COST.ADDR = LREC<19>*QTY
                   IF LOT.COST # "" THEN ;* Note 0 is ok.
            * Note a 0 LOT.COST indicates that it's intended so it's used.
            * Null LOT.COSTS are not intended, so they are not used.
                      TOT.COST.ADDR += COST.ADDR
                      TOT.LOT.COST += LOT.COST*QTY
                      TOT.LOT.QTY  += QTY
                      LOT.CT       += 1
                   END
                END
             END
          NEXT L

          IF TOT.LOT.QTY - 0 # 0 THEN
             BASE = (TOT.LOT.COST+TOT.COST.ADDR)/TOT.LOT.QTY
             PRICE.PER.GET PER,PER.UM,,,PRC.DATE
          END

          RETURN
*-------------------------------------------------------------------------*
PRINT.QTYS:*

          *** If we're printing it in Detail format, we'll need to
          *** check/store the Qty Unit of Measure so that we can determine
          *** whether they are displayable in our totals...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             IF DETAIL[1,6] = 'Detail' THEN
                *** Product Rank Totals...
                LOCATE DFLT.ALPHA IN RTOLS.UM<1> SETTING RPOS ELSE
                   RTOLS.UM<1,RPOS> = DFLT.ALPHA
                END
             END
             *** Sortby Totals...
             LOCATE DFLT.ALPHA IN LTOLS.UM<1> SETTING LPOS ELSE
                LTOLS.UM<1,LPOS> = DFLT.ALPHA
             END
             *** Entity Totals...
             LOCATE DFLT.ALPHA IN ETOLS.UM<1> SETTING EPOS ELSE
                ETOLS.UM<1,EPOS> = DFLT.ALPHA
             END
             *** Branch Totals...
             LOCATE DFLT.ALPHA IN BTOLS.UM<1> SETTING BPOS ELSE
                BTOLS.UM<1,BPOS> = DFLT.ALPHA
             END
             *** Grand Totals...
             LOCATE DFLT.ALPHA IN GTOLS.UM<1> SETTING GPOS ELSE
                GTOLS.UM<1,GPOS> = DFLT.ALPHA
             END
          END

          *** Add to the Total arrays...
          PTOLS = ADDS(PTOLS,ITEM.TOLS) ;*Product Totals
          RTOLS = ADDS(RTOLS,ITEM.TOLS) ;*Product Rank Totals
          LTOLS = ADDS(LTOLS,ITEM.TOLS) ;*Sortby Totals
          ETOLS = ADDS(ETOLS,ITEM.TOLS) ;*Entity Totals
          BTOLS = ADDS(BTOLS,ITEM.TOLS) ;*Branch Totals
          GTOLS = ADDS(GTOLS,ITEM.TOLS) ;*Grand Totals

          EXT.CT = DCOUNT(EXT.TOLS,AM)
          FOR COL = 1 TO EXT.CT
             PTOLS.EXT<COL> += EXT.TOLS<COL>
             RTOLS.EXT<COL> += EXT.TOLS<COL>
             LTOLS.EXT<COL> += EXT.TOLS<COL>
             ETOLS.EXT<COL> += EXT.TOLS<COL>
             BTOLS.EXT<COL> += EXT.TOLS<COL>
             GTOLS.EXT<COL> += EXT.TOLS<COL>
          NEXT ECT

          ITM.CT  += 1
          ENT.CT  += 1
          LINE.CT += 1
          RANK.CT += 1

          PRINTED.DET = YES

          BEGIN CASE
          CASE SORTBY = 'Price Line'
             READV SORT.DESC FROM PLNEFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY = 'Buy Line'
             READV SORT.DESC FROM BLNEFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY = 'Sell Group' OR SORTBY = 'Buy Group'
             READV SORT.DESC FROM PGRPFILE,SORT.VALUE,1 ELSE SORT.DESC = ''
          CASE SORTBY[1,7] = 'Prod GL'
             SORT.DESC = PRD(2)
          CASE SORTBY = 'Prod ID'
             PN = FIELD(PRD(93)<1,3>,'.',1)
             SORT.DESC = PN

          END CASE

          *** If the report format is Detail OR Summary by Product...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             DESC = PRD(1)
             CONVERT VM TO ' ' IN DESC

             *** Only write to the ODBC file if they have the add-on key
             *** for solar reporter.
             IF HAS.ODBC THEN
                *** Don't need to determine the Sequence if we already
                *** know what it is...
                IF NOT(SEQUENCE) THEN
                   IF SLCT[1,3] = 'Buy' THEN
                      GET.BLINE.IDS ,L,TLINE
                   END ELSE
                      GET.LINE.IDS ,L,TLINE
                   END
                   LOCATE PN IN L<1> SETTING SEQUENCE ELSE SEQUENCE = ''
                END

                REC      = ''
                REC.NUM += 1
                REC<1>   = DESC
                REC<2>   = SORT.VALUE
                REC<3>   = SORT.DESC

                REC<4>   = ICONV(OCONV(STK.QTY,"MR0"),"MR2")
                REC<5>   = ICONV(OCONV(TAG.QTY,"MR0"),"MR2")
                REC<6>   = ICONV(OCONV(REV.QTY,"MR0"),"MR2")
                REC<7>   = ICONV(OCONV(DISP.QTY,"MR0"),"MR2")
                REC<8>   = ICONV(OCONV(VCN.QTY,"MR0"),"MR2")

                REC<9>   = ICONV((OCONV(BASE,"MR9")*PER),"MR3")
                REC<10>  = PER.UM:'/':PER
                REC<11>  = ICONV(OCONV(EXT,"MR2"),"MR2")
                REC<12>  = DFLT.ALPHA:'/':DFLT.PER
                REC<13>  = BR
                REC<14>  = GL.CODE
                REC<15>  = PN
                REC<16>  = SEQUENCE

                PRD.BR.GET.VAL BR,PN,25,SER.TRACK

                *** If they're putting the product ranks on the report
                *** Insert our info into our report arrays, pushing all our
                *** other report values up at the same time...
                IF DISPLAY.RANKS THEN
                   *** We can only store the Rank Numbers that were
                   *** selected on, so that our report will 'break'
                   *** (subtotal) properly...
                   ODBC.RANKS = FIELD(TEMP.PRODUCT.RANKS,',',1,RANK.NUM.CT)
                   REC        = INSERT(REC,4;ODBC.RANKS)
                END

                *** If we're Page Breaking on Consignment Vendors
                *** or Customers...
                IF ENTBK THEN
                   READV EBK.DESC FROM CUSFILE,EBK.ON,1 ELSE
                      EBK.DESC = EBK.ON
                   END
                   REC = INSERT(REC,1;EBK.DESC)
                END

                WRITE REC ON ODBCFILE,REC.NUM "R%7"

                BCKTS    = PN
                BCKTS<2> = SORT.VALUE
                BCKTS<3> = GL.CODE
                IF GROUP.RANKS AND DETAIL[1,6] = 'Detail' THEN
                   BCKTS<4> = TEMP.PRODUCT.RANKS
                END

                BEGIN CASE
                CASE ENTBK AND DISPLAY.RANKS
                   SUMRY.ATTB = 6
                CASE ENTBK OR DISPLAY.RANKS
                   SUMRY.ATTB = 5
                CASE OTHERWISE
                   SUMRY.ATTB = 4
                END CASE

                SUMRY    = ""
                SUMRY<1> = REC<SUMRY.ATTB>
                SUMRY<2> = REC<SUMRY.ATTB+1>
                SUMRY<3> = REC<SUMRY.ATTB+2>
                SUMRY<4> = REC<SUMRY.ATTB+3>
                SUMRY<5> = REC<SUMRY.ATTB+6>
                GOSUB UPD.SUMS
             END

             * If breaking out tagged information, do not print the tag
             * qty on the main line
             IF DETAIL[1,6] = 'Detail' THEN TAG.QTY = 0
             CCN.CT = DCOUNT(CCN.LIST<1>,VM)
             VCN.CT = DCOUNT(VCN.LIST<1>,VM)

             *** We need to always print the header line if we have tags
             *** to display as the tagged qty for main line may be zero.
             IF (STK.QTY+0#0 OR TAG.QTY+0#0 OR REV.QTY+0#0 OR DISP.QTY+0#0 OR VCN.QTY+0#0 OR (SUP.ZERO = 'Include' AND VCSGN # 'Only' AND CCSGN # 'Only') OR (DETAIL[1,6] = 'Detail' AND TAG.IDS)) THEN
                IF MODE = 'XML' THEN
                   GOSUB WRITE.PN
                END ELSE
                   GOSUB PRINT.PN
                END
             END
             IF MODE # 'XML' THEN
                GOSUB PRINT.CCN
                GOSUB PRINT.VCN
                IF TAG.IDS THEN GOSUB PRT.TAGS
                GOSUB PRINT.SNS
                BLANK.LINE.PRINTED = NO
             END

          END

          RETURN
*-------------------------------------------------------------------------*
WRITE.PN:
          GET.PIL PIL,BR,PN,DATE()
          IF PIL < 0 THEN PIL = 0

          GET.ALL.PRD BR,PN,-1
          GET.PCGID PCGID,PRD(18),PRD(12)
          PRD.LEAD.TIME.GET LEAD,PN,BR,PRD(12),PRDC.BR(5),PCGID
          GET.BUY.DATA BR,PN,DEMAND,EOQ,ADJ.SF,LEAD,MANS,BUYPKG,MQ,MIN,MAX,IS.DIV

          GLOBAL.BASN.GET GBASN,BASN

          START.DATE = AOD-365
          END.DATE = AOD+0

          PROD.INFO BR,PN,START.DATE,END.DATE,SLS,,COGS,,AVEQOH

          COGS = ICONV(COGS,'MR0')
          GOSUB GET.ANZR
          ANNUAL.DOLLAR = ICONV(COGS * ANNUALIZER,'MR0')
          ANNUAL.DOLLAR = OCONV(ANNUAL.DOLLAR,'MR2')

          IF SLS  = 0 OR BASN # 8 THEN
             KOPTS = PRD(86)
             IF BASN # 1 AND KOPTS<1,3>#'' THEN
                KOPTS<1,1> = KOPTS<1,3>
             END
             GET.BASE PN,BR,BASN,AOD,BASE,PPER,KOPTS,PRD(52),PRD(53),PRD(87)
             * Since our Cost is based on a certain Qty, and we want
             * to get this value down to the lowest possible Unit of
             * Measure, we'll divide the Value by the Price Per. This
             * will give us our Cost per 'each'...
             AVE.COST = OCONV(BASE,'MR3') / PPER
          END ELSE
             * Average Cost per 'each' of this product...
             AVE.COST = OCONV((COGS / SLS),'MR2')
          END

          * OnHand Qty Dollars...
          AVE.$OH = (AVEQOH * ICONV(AVE.COST,'MR2'))

          * This product's Total OnHand Qty Dollars for this branches...
          PROD.AVE.$OH = OCONV(AVE.$OH,'MR2')

          * Calculate Turns...
          * Turns equals Annual Dollar divided by On Hand $
          IF PROD.AVE.$OH = 0 THEN
             TURNS = -1
          END ELSE
             TURNS = ANNUAL.DOLLAR / PROD.AVE.$OH
          END
          IF TURNS < 0 THEN TURNS = 0

          DISP.RANK1 = PRDC.BR(1)<1,1>
          DISP.RANK2 = PRDC.BR(1)<1,2>
          DISP.RANK3 = PRDC.BR(1)<1,3>
          DISP.RANK4 = PRDC.BR(1)<1,4>
          DISP.RANK5 = PRDC.BR(1)<1,5>
          BEGIN CASE
          CASE SF.RANK = 1
             SF.RANK.VAL = DISP.RANK1
          CASE SF.RANK = 2
             SF.RANK.VAL = DISP.RANK2
          CASE SF.RANK = 3
             SF.RANK.VAL = DISP.RANK3
          CASE SF.RANK = 4
             SF.RANK.VAL = DISP.RANK4
          CASE OTHERWISE
             SF.RANK.VAL = DISP.RANK5
          END CASE

          HI.QTY      = PRDC.BR(21)

          DMD.DAY     = OCONV(PRDC.BR(4),'MR3')
          RAW.HITS    = PRDC.BR(9)<1,1>
          AVG.SALE    = PRDC.BR(6)<1,2>
          ANNUAL.HITS = PRDC.BR(3)
          HITS.MOS    = ANNUAL.HITS / 12


          IF PRD(3) = 1 OR PRD(3) = 4 OR PRD(3) = 7 THEN STK.ITEM = 'Yes' ELSE STK.ITEM = 'No'

          IF AVG.SALE <= 0 OR NOT(AVG.SALE) THEN AVG.SALE = 1
          IF IS.DIV THEN IDIV = 'Y' ELSE IDIV = 'N'
          IF NOT(BUYPKG) THEN BUYPKG = 1
          REP.COST = OCONV(UNIT.BASE<1,1>,'MR9')*PER

          IF DETAIL[1,6] = 'Detail' THEN
             TAG.QTY = SUM(TAG.QTYS)
          END

          GOSUB GET.OC
          DFLT.PER.GET 'P',PUR.PER
          MS      = ROUNDUP(MANS,PUR.PER)
          EOQ     = ROUNDUP(MQ,PUR.PER)
          EOQ.BUY = ROUND.UP(MQ,BUYPKG,MQ)
          EOQ.BUY = ROUNDUP(EOQ.BUY,PUR.PER)

          PROJ.SL = PRD.BR(60)
          IF NOT(PROJ.SL) THEN
             SF = PRD.BR(14)
             IF SF = '' THEN
                BUYLINE.BR.GET.VAL BR,PRD(12),12,SF
                IF NOT(NUM(SF)) THEN SF = ''
                IF SF = '' THEN SF = 1 ELSE SF = OCONV(SF,'MR2')
             END
             PROD.INVMA.SL.SF.CALC PROJ.SL,SF
          END

          THIS.ITEM.RESP = ITEM.RESP
          XML.DESC       = CHANGE(DESC,'&','&amp;')
          XML.DESC       = CHANGE(XML.DESC,'"','&quot;')

          UT.REP.STR THIS.ITEM.RESP,'&PN&',PN
          UT.REP.STR THIS.ITEM.RESP,'&BLINE&',PRD(12)
          UT.REP.STR THIS.ITEM.RESP,'&DESC&',XML.DESC
          UT.REP.STR THIS.ITEM.RESP,'&STK.ONHAND&',STK.QTY
          UT.REP.STR THIS.ITEM.RESP,'&TAG.ONHAND&',TAG.QTY
          UT.REP.STR THIS.ITEM.RESP,'&RFO.ONHAND&',REV.QTY
          UT.REP.STR THIS.ITEM.RESP,'&DISP.ONHAND&',DISP.QTY
          UT.REP.STR THIS.ITEM.RESP,'&SFRANK&',SF.RANK.VAL
          UT.REP.STR THIS.ITEM.RESP,'&RANK1&',DISP.RANK1
          UT.REP.STR THIS.ITEM.RESP,'&RANK2&',DISP.RANK2
          UT.REP.STR THIS.ITEM.RESP,'&RANK3&',DISP.RANK3
          UT.REP.STR THIS.ITEM.RESP,'&RANK4&',DISP.RANK4
          UT.REP.STR THIS.ITEM.RESP,'&RANK5&',DISP.RANK5
          UT.REP.STR THIS.ITEM.RESP,'&REP.COST&',REP.COST
          UT.REP.STR THIS.ITEM.RESP,'&OH.DOL&',OCONV(EXT.BASE<1,1>,'MR2')
          UT.REP.STR THIS.ITEM.RESP,'&FCST.HITS&',RAW.HITS
          UT.REP.STR THIS.ITEM.RESP,'&MOS.HITS&',HITS.MOS
          UT.REP.STR THIS.ITEM.RESP,'&DMD.DAY&',DMD.DAY
          UT.REP.STR THIS.ITEM.RESP,'&AVG.QTY&',AVG.SALE
          UT.REP.STR THIS.ITEM.RESP,'&HI.QTY&',HI.QTY
          UT.REP.STR THIS.ITEM.RESP,'&STK.ITEM&',STK.ITEM
          UT.REP.STR THIS.ITEM.RESP,'&PIL&',PIL
          UT.REP.STR THIS.ITEM.RESP,'&PER.UOM&',PER.UM
          UT.REP.STR THIS.ITEM.RESP,'&PER.QTY&',PER
          UT.REP.STR THIS.ITEM.RESP,'&PKG.DIV&',IDIV
          UT.REP.STR THIS.ITEM.RESP,'&PKG.QTY&',BUYPKG
          UT.REP.STR THIS.ITEM.RESP,'&LEAD.TM&',LEAD
          UT.REP.STR THIS.ITEM.RESP,'&ANNUAL.DOLLAR&',ANNUAL.DOLLAR
          UT.REP.STR THIS.ITEM.RESP,'&ANNUAL.HITS&',ANNUAL.HITS
          UT.REP.STR THIS.ITEM.RESP,'&ORDER.CYCLE&',OC
          UT.REP.STR THIS.ITEM.RESP,'&MANUAL.SAFETY&',MS
          UT.REP.STR THIS.ITEM.RESP,'&MQ&',MQ
          UT.REP.STR THIS.ITEM.RESP,'&PURCH.PER&',PUR.PER
          UT.REP.STR THIS.ITEM.RESP,'&TURNS&',TURNS
          UT.REP.STR THIS.ITEM.RESP,'&PROJ.SL&',PROJ.SL
          CONVERT AM TO '' IN THIS.ITEM.RESP
          WRITESEQ THIS.ITEM.RESP ON OUTFILE ELSE RETURN

          IF NOT(OUTBOUND.EMAIL) THEN
             HOLD.LN.CT += 1
          END

          RETURN
*-------------------------------------------------------------------------*
PRINT.PN:

          PRT.STR = ''
          COL.DATA = ''

          * Allow drill down capabilities for product.
          COL.DATA<1> = '^':PN

          COL.DATA<2> = DESC
          COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
          COL.DATA<5> = STK.QTY "R2,"
          IF DETAIL[1,6] # 'Detail' THEN
             COL.DATA<6>  = TAG.QTY  "R2,"
             COL.DATA<40> = TAG.BASE "R3,"
             COL.DATA<69> = OCONV(TAG.EXT,'MR2') "R2,"
          END
          COL.DATA<7> = REV.QTY "R2,"
          COL.DATA<8> = DISP.QTY "R2,"
          COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
          COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"
          BASE.CT = DCOUNT(UNIT.BASE,AM)
          FOR BCT = 1 TO BASE.CT
             BASE = UNIT.BASE<BCT,1>
             COL  = UNIT.BASE<BCT,2>
             IF (COL = 0) THEN CONTINUE
             COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
          NEXT BCT

          EXT.CT = DCOUNT(EXT.BASE,AM)
          FOR ECT = 1 TO EXT.CT
             EXT = EXT.BASE<ECT,1>
             COL = EXT.BASE<ECT,2>
             IF (COL = 0) THEN CONTINUE
             COL.DATA<COL> = OCONV(EXT,"MR2")          "R2,"
          NEXT BCT

          IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
             COL.DATA<9> = VCN.QTY "R2,"
          END
          IF DETAIL[1,6] = 'Detail' THEN
             COL.DATA<70> = BR
             COL.DATA<77> = BR.SHORT.DESC
             COL.DATA<78> = BR.COST.CENTER
          END
          IF TSORTBY # 'Prod GL Code' THEN
             COL.DATA<71> = GL.CODE
          END
          *** If we're displaying the Product's Ranks...
          IF DISPLAY.RANKS THEN
             COL.DATA<74> = TEMP.PRODUCT.RANKS
          END
          REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
          IF (TRIM(PRT.STR) # "") THEN
             PRINT PRT.STR
          END
          F.CT  = DCOUNT(FIFO.IDS,VM)
          IF (STK.QTY <= 0) THEN F.CT = 0
          FOR FF = 1 TO F.CT
             FIFO.QTY  = FIFO.QTYS<1,FF>
             FIFO.EXT  = FIFO.COSTS<1,FF>
             FIFO.BASE = ICONV((OCONV(FIFO.EXT,'MR2') / FIFO.QTY),'MR9')
             FIFO.ID   = FIFO.IDS<1,FF>
             FIFO.DT   = FIFO.DTS<1,FF>

             COL.DATA = ""
             IF FIFO.ID[1,3] = '***' THEN
                COL.DATA<75> = FIFO.ID           "L#17"
             END ELSE
                COL.DATA<75> = FIFO.ID           "L#17"
             END
             IF IS.CRRBS<1,FF> THEN
                COL.DATA<76> = FIFO.DT           "D4/":" *"
             END ELSE
                COL.DATA<76> = FIFO.DT           "D4/"
             END

             COL.DATA<5> = FIFO.QTY              "R,"
             COL.DATA<79>= OCONV(FIFO.BASE * PER,'MR9') "R3,"
             COL.DATA<80>= OCONV(FIFO.EXT,'MR2')"R2,"
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT FF

          RETURN
*-------------------------------------------------------------------------*
PRINT.CCN:
          FOR XX = 1 TO CCN.CT
             PRT.STR = ''
             COL.DATA = ''

             * Allow drill down capabilities for product.
             COL.DATA<1> = '^':PN

             COL.DATA<2> = DESC
             COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
             COL.DATA<5> = CCN.QTYS<1,XX>/DFLT.PER "R2,"
             COL.DATA<6> = '0' "R2,"
             COL.DATA<7> = '0' "R2,"
             COL.DATA<8> = '0' "R2,"
             COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             BASE.CT = DCOUNT(UNIT.BASE,AM)
             FOR BCT = 1 TO BASE.CT
                BASE = UNIT.BASE<BCT,1>
                COL  = UNIT.BASE<BCT,2>
                IF (COL = 0) THEN CONTINUE
                COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
             NEXT BCT

             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                BASE = UNIT.BASE<ECT,1>
                COL  = EXT.BASE<ECT,2>
                IF (COL = 0) THEN CONTINUE
                EXT = (CCN.QTYS<1,XX>)*(OCONV(BASE,"MR9"))
                COL.DATA<COL> = EXT                       "R2,"
             NEXT BCT

             IF DETAIL[1,6] = 'Detail' THEN
                COL.DATA<70> = BR
                COL.DATA<77> = BR.SHORT.DESC
                COL.DATA<78> = BR.COST.CENTER
             END
             IF TSORTBY # 'Prod GL Code' THEN
                COL.DATA<71> = GL.CODE
             END
             *** If we're displaying the Product's Ranks...
             IF DISPLAY.RANKS THEN
                COL.DATA<74> = TEMP.PRODUCT.RANKS
             END
             CCN = FIELD(CCN.LIST<1,XX>,'~',2)
             READV NAME FROM CUSFILE,CCN,1 ELSE NAME = CCN
             COL.DATA<72> = NAME

             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT XX
          IF CCN.CT > 0 THEN
            ITM.CT  += CCN.CT-1
            ENT.CT  += CCN.CT-1
            LINE.CT += CCN.CT-1
            RANK.CT += CCN.CT-1
          END
          RETURN
*-------------------------------------------------------------------------*
PRINT.VCN:
          FOR XX = 1 TO VCN.CT
             PRT.STR = ''
             COL.DATA = ''

             * Allow drill down capabilities for product.
             COL.DATA<1> = '^':PN

             COL.DATA<2> = DESC
             COL.DATA<4> = SORT.VALUE "L#8":" ":SORT.DESC "L#25"
             COL.DATA<5> = '0' "R2,"
             COL.DATA<6> = '0' "R2,"
             COL.DATA<7> = '0' "R2,"
             COL.DATA<8> = '0' "R2,"
             COL.DATA<9> = VCN.QTYS<1,XX>/DFLT.PER "R2,"
             COL.DATA<10>= PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11>= DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             IF SHOW.VPRC THEN
                BASE.CT = DCOUNT(UNIT.BASE,AM)
                FOR BCT = 1 TO BASE.CT
                   BASE = UNIT.BASE<BCT,1>
                   COL  = UNIT.BASE<BCT,2>
                   IF (COL = 0) THEN CONTINUE
                   COL.DATA<COL> = OCONV(BASE,"MR9")*PER     "R3,"
                NEXT BCT

                EXT.CT = DCOUNT(EXT.BASE,AM)
                FOR ECT = 1 TO EXT.CT
                   EXT = EXT.BASE<ECT,1>
                   COL = EXT.BASE<ECT,2>
                   IF (COL = 0) THEN CONTINUE
                   EXT = (VCN.QTYS<1,XX>)*(OCONV(BASE,"MR9"))
                   COL.DATA<COL> = EXT "R2,"
                NEXT BCT
             END

             COL.DATA<71>= GL.CODE

             IF DETAIL[1,6] = 'Detail' THEN
                COL.DATA<70> = BR
                COL.DATA<77> = BR.SHORT.DESC
                COL.DATA<78> = BR.COST.CENTER
             END
             IF TSORTBY # 'Prod GL Code' THEN
                COL.DATA<71> = GL.CODE
             END
             *** If we're displaying the Product's Ranks...
             IF DISPLAY.RANKS THEN
                COL.DATA<74> = TEMP.PRODUCT.RANKS
             END
             VCN = FIELD(VCN.LIST<1,XX>,'~',2)
             READV NAME FROM CUSFILE,VCN,1 ELSE NAME = VCN
             COL.DATA<72> = NAME

             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END
          NEXT XX
          IF VCN.CT > 0 THEN
            ITM.CT  += VCN.CT-1
            ENT.CT  += VCN.CT-1
            LINE.CT += VCN.CT-1
            RANK.CT += VCN.CT-1
          END
          RETURN
*-------------------------------------------------------------------------*
PRINT.SNS:
          *** Display any serial numbers that have a Qty # 0...
          PRD.BR.GET.VAL BR,PN,25,SER.TRACK
          IF SER.TRACK AND SER.TRACK # 'N' THEN
             SNUMS = PRDD.BR(20)
             SQTYS = PRDD.BR(21)
             IF SLC.FLAG THEN
                CONVERT SVM TO VM IN SNUMS
                CONVERT SVM TO VM IN SQTYS
             END
             SN.CT    = DCOUNT(SNUMS<1>,VM)
             FOR SNUM = 1 TO SN.CT
                IF SNUMS<1,SNUM> = "" THEN GOTO SKP.SNUM
                PRT.STR = ""
                COL.DATA = ""
                COL.DATA<3> = SNUMS<1,SNUM>:" - ":SQTYS<1,SNUM>
                REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
                IF (TRIM(PRT.STR) # "") THEN
                   PRINT PRT.STR
                END
SKP.SNUM:    NEXT SNUM
          END
          RETURN
*-------------------------------------------------------------------------*
PRT.TAGS: *** Print the Tag information if this is being run in 'Detail'
          *** and the Use PO Costs on tagged items is set.
          TCT    = DCOUNT(TAG.IDS,VM)
          FOR TGN = 1 TO TCT
             TAG.ID   = TAG.IDS<1,TGN>
             TAG.QTY  = TAG.QTYS<1,TGN>
             TAG.BASE = ICONV(OCONV(TAG.COSTS<1,TGN>,'MR9'),'MR3')
             TAG.EXT  = TAG.QTY * TAG.BASE

             PRT.STR = ''
             COL.DATA = ''
             COL.DATA<6>  = TAG.QTY/DFLT.PER "R2,"
             * include tag ext into all bases for detail printing
             EXT.CT = DCOUNT(EXT.BASE,AM)
             FOR ECT = 1 TO EXT.CT
                EXT = TAG.EXT
                COL = EXT.BASE<ECT,2>
                IF (COL = 0) THEN CONTINUE
                COL.DATA<COL> = OCONV(EXT,"MR3") "R2,"
             NEXT BCT

             COL.DATA<73> = TAG.ID
             COL.DATA<40> = OCONV(TAG.BASE,'MR3')*PER "R3,"
             COL.DATA<69> = OCONV(TAG.EXT,'MR3') "R2,"

             * Make sure that Tag ID, Tag Unit Amt or Extended Amt is
             * included on the report. Otherwise, we'll just be showing
             * blank lines!
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) = "") THEN CONTINUE

             COL.DATA<10> = PER.UM "L#3":"/":PER "L#4"
             COL.DATA<11> = DFLT.ALPHA "L#3":"/":DFLT.PER "L#4"

             * Now format in the other columns that might be displayed.
             REPORT.LAYOUT.V.PRINT.STR COL.DATA,'',COL.RECORD,'',PRT.STR
             IF (TRIM(PRT.STR) # "") THEN
                PRINT PRT.STR
             END

             * Write our Tag lines out for the ODBC Report...
             IF HAS.ODBC THEN
                REC      = ''
                REC.NUM += 1

                * We need to store the Line, GL Code, and Product Ranks,
                * if applicable, so that we don't get subtotaling before
                * and after our TAG data.

                REC<2>  = SORT.VALUE
                REC<8>  = TAG.BASE*PER
                REC<9>  = PER.UM
                REC<12> = PRD(2)
                REC<15> = TAG.ID
                REC<17> = TAG.QTY
                REC<18> = ICONV(OCONV(TAG.EXT,"MR3"),"MR2")

                * If they're putting the product ranks on the report
                * Insert our info into our report arrays, pushing all
                * our other report values up at the same time...
                IF DISPLAY.RANKS THEN
                   * We can only store the Rank Numbers that were
                   * selected on, so that our report will 'break'
                   * (subtotal) properly...
                   REC = INSERT(REC,4;ODBC.RANKS)
                END

                WRITE REC ON ODBCFILE,REC.NUM "R%7"
             END
          NEXT FF

          RETURN
*-------------------------------------------------------------------------*
GET.QTYS: *** Get our OnHand Qtys...
          INCLUDE.PRODUCT = NO
          *** Get the OnHand Qtys for the current product at
          *** the current Branch...
          GET.PREV.ONHANDS BR,PN,INV.DT,OH.QTYS,OH.LOCS
          LOC.CT      = DCOUNT(OH.QTYS,VM)

          CCN.LIST    = ''
          CCN.QTYS    = ''
          VCN.LIST    = ''
          VCN.QTYS    = ''
          STK.QTY     = 0
          TAG.QTY     = 0
          REV.QTY     = 0
          DISP.QTY    = 0
          VCN.QTY     = 0
          TAG.IDS     = ''
          TAG.QTYS    = ''
          TAG.COSTS   = ''
          FIFO.IDS    = ''
          FIFO.QTYS   = ''
          FIFO.COSTS  = ''
          FIFO.DTS    = ''
          TAG.EXT.AMT = 0
          TAG.CT      = 0

          *** If we're not getting Consignment products, and the report
          *** will print in Detail format, but we don't have any OnHands
          *** we'll need to make sure to go through our loop at least once.
          IF NOT(ENTBK) THEN
             *** If there isn't any OnHand at any location, we still need
             *** to make sure we go through our loop at least once.
             IF NOT(LOC.CT) THEN LOC.CT = 1
             FOUND.CONSIGNMENT = NO
          END ELSE
             FOUND.CONSIGNMENT = NO
          END

          *** Loop thru each location for this branch...
          CONSIGN.CT      = 0
          NAME            = ''
          CONSIGNMENT.QTY = ''
          TMP.CN          = ''
          CONSIGN.EXT     = ''
          CONSIGN.DPER    = ''
          CONSIGN.DALPHA  = ''
          CONSIGN.PER     = ''
          CONSIGN.UM      = ''
          TAG.EXT.AMT     = 0

          FOR LL  = 1 TO LOC.CT

             *** Check whether the product is on consignment at this loc...
             IF ENTBK THEN
                LOC = FIELD(OH.LOCS<1,LL>,'~',1)
                QTYPE = FIELD(OH.LOCS<1,LL>,'~',1)

                IF NEG = 'O' AND OH.QTYS<1,LL> >= 0 THEN
                   GOTO SKIP.LOC
                * If they want to 'Exclude' Negative OnHand Qtys and our
                * Qtys are all negative then skip this one...
                END ELSE IF NEG = 'E' AND OH.QTYS<1,LL> <0 THEN
                   GOTO SKIP.LOC
                END

                OHQ     = OH.QTYS<1,LL>
                ADD.TAG = NO  ;* no tag totals
                GOSUB GET.VAL

                IF EXT.AMT.VAL # '' AND EXT.AMT.VAL * 100 >= EXT THEN GOTO SKIP.LOC

                *** Get our Price Per Qty and Unit of Measure...
                PRICE.PER.GET PER,PER.UM,,,PRC.DATE
                *** Get our Qty Per Qty and Unit of Measure for Invoices...
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA

                *** If the product at the current location isn't
                *** on consignment, skip the location qty...
                IF ENTBK # LOC[1,1] AND ENTBK # 'V' THEN CONTINUE
                IF ENTBK = 'V' AND LOC[1,1] # 'S' THEN CONTINUE

                *** If no customer then don't add to tempfile...
                IF LOC[2,99] = ''   THEN CONTINUE

                FOUND.CONSIGNMENT = YES
                CONSIGN.CT       +=1

                IF ENTBK # 'V' OR SHOW.VPRC THEN
                   CONSIGN.EXT<CONSIGN.CT> = LOWER(EXT.BASE)
                END ELSE
                   *** Exclude vendor consignment in ext. price calc. since
                   *** we do not own title to the inventory
                   CONSIGN.EXT<CONSIGN.CT> = 0
                END
                CONSIGN.DPER<CONSIGN.CT>   = DFLT.PER
                CONSIGN.DALPHA<CONSIGN.CT> = DFLT.ALPHA
                CONSIGN.PER<CONSIGN.CT>    = PER
                CONSIGN.UM<CONSIGN.CT>     = PER.UM
                *** Want to alphabetize the sorting of entity so make
                *** entity name part of the ID of tempfile and remove
                *** any '~' in name to use in the ID
                TMP.CN<CONSIGN.CT>         = LOC[2,99]

                READV CNAME FROM CUSFILE,TMP.CN<CONSIGN.CT>,1 ELSE CNAME = TMP.CN
                CNAME   = OCONV(CNAME,'MCU')
                CONVERT '~' TO '' IN CNAME
                NAME<CONSIGN.CT> = CNAME "L#35"

                CONSIGNMENT.QTY<CONSIGN.CT> = OH.QTYS<1,LL>

             END ELSE
                QTYPE = FIELD(OH.LOCS<1,LL>,'~',1)

                *** The next two conditions will determine whether to
                *** include the current product for the current Branch
                *** on the report...
                IF QTYPES THEN
                   WQTYPE = QTYPE
                   IF WQTYPE[1,1] = 'S' AND WQTYPE # 'S' THEN WQTYPE = 'V'
                   IF NOT(INDEX(QTYPES,WQTYPE[1,1],1)) THEN CONTINUE
                END

                IF SEL.CCN AND QTYPE[2,999] # SEL.CCN AND (QTYPE[1,1] = 'C' OR WQTYPE[1,1] = 'V') THEN CONTINUE
                IF QTYPES = 'C' OR FND.VQTYPE THEN
                   IF QTYPE[1,1] = 'C' OR WQTYPE = 'V' THEN
                      FOUND.CONSIGNMENT = YES
                   END
                END

                BEGIN CASE
                CASE QTYPE[1,1] = 'S'
                   IF QTYPE[2,999] # "" AND DETAIL[1,6] = 'Detail' THEN
                      VCN.LISTID = 'S~':QTYPE[2,999]
                      LOCATE VCN.LISTID IN VCN.LIST<1> SETTING CPOS ELSE
                         VCN.LIST<1,CPOS> = VCN.LISTID
                      END
                      VCN.QTYS<1,CPOS> += OH.QTYS<1,LL>
                   END ELSE
                      IF QTYPE[2,999] # "" THEN
                         VCN.QTY += OH.QTYS<1,LL>
                      END ELSE
                         STK.QTY += OH.QTYS<1,LL>
                      END
                   END
                CASE QTYPE[1,1] = 'C'
                   *** Create a list of the consignment customers and qtys
                   IF DETAIL[1,6] = 'Detail' THEN
                      CCN.LISTID = 'C~':QTYPE[2,999]
                      LOCATE CCN.LISTID IN CCN.LIST<1> SETTING CPOS ELSE
                         CCN.LIST<1,CPOS> = CCN.LISTID
                      END
                      CCN.QTYS<1,CPOS> += OH.QTYS<1,LL>
                   END ELSE
                      STK.QTY += OH.QTYS<1,LL>
                   END
                CASE QTYPE = 'T'
                   OK.TO.INCREMENT = YES
                   GOSUB GET.POCST
                   IF OK.TO.INCREMENT THEN TAG.QTY += OH.QTYS<1,LL>
                CASE QTYPE = 'L'
                   DISP.QTY += OH.QTYS<1,LL>
                CASE OTHERWISE
                   REV.QTY  += OH.QTYS<1,LL>
                END CASE
             END
SKIP.LOC: NEXT LL

          IF ENTBK THEN
             IF NOT(FOUND.CONSIGNMENT) THEN RETURN
          END
          IF QTYPES = 'C' OR QTYPES = 'V' THEN
             IF NOT(FOUND.CONSIGNMENT) THEN RETURN
          END

          IF NOT(ENTBK) THEN
             * If we're Page Breaking on Consignment Customers or Vendors
*            IF ENTBK   = 'V' THEN
*               REV.QTY = SUM(CONSIGNMENT.QTY)
*            END ELSE IF ENTBK = 'C' THEN
*               STK.QTY = SUM(CONSIGNMENT.QTY)
*            END

             ALL.POSITIVE.QTYS = NO
             ALL.NEGATIVE.QTYS = NO
             ZERO.QTYS         = NO
             CCN.SUM = SUM(CCN.QTYS)

             * Evaluate if qtys are all negative, positive or zero
             BEGIN CASE
             CASE STK.QTY=0 AND TAG.QTY=0 AND REV.QTY=0 AND DISP.QTY=0 AND CCN.SUM=0 AND NOT(VCN.QTYS)
                ZERO.QTYS = YES
             CASE STK.QTY>=0 AND TAG.QTY>=0 AND REV.QTY>=0 AND DISP.QTY>=0 AND CCN.SUM>=0
                ALL.POSITIVE.QTYS = YES
             CASE STK.QTY<=0 AND TAG.QTY<=0 AND REV.QTY<=0 AND DISP.QTY<=0 AND CCN.SUM<=0
                ALL.NEGATIVE.QTYS = YES
             END CASE

             BEGIN CASE
             * We won't worry about the Negative OnHand Qtys validation.
             CASE SUP.ZERO = 'Include' AND ZERO.QTYS;  GOTO GETVAL
             * If they want to Exclude Zero OnHand Qtys...
             CASE SUP.ZERO = 'Exclude' AND ZERO.QTYS;  RETURN
             END CASE

             BEGIN CASE
             * If they 'Only' want Negative OnHand Qtys, but our Qtys are
             * all positive then skip this one...
             CASE NEG = 'O' AND ALL.POSITIVE.QTYS;     RETURN
             * If they want to 'Exclude' Negative OnHand Qtys and our Qtys
             * are all negative then skip this one...
             CASE NEG = 'E' AND ALL.NEGATIVE.QTYS;     RETURN
             END CASE

GETVAL:      TOT.QTY = STK.QTY + DISP.QTY + REV.QTY
             IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
                IF DETAIL[1,6] = 'Detail' THEN
                   VCN.QTYS.CT = DCOUNT(VCN.QTYS,VM)
                   FOR QTY.CTR = 1 TO VCN.QTYS.CT
                      TOT.QTY += VCN.QTYS<1,QTY.CTR>
                   NEXT QTY.CTR
                END ELSE
                   TOT.QTY += VCN.QTY
                END
             END

             OHQ     = TOT.QTY
             ADD.TAG = YES  ;* include tag totals
             GOSUB GET.VAL

             IF EXT.AMT.VAL # '' THEN
                ATAG.EXT.AMT = ICONV(OCONV(TAG.EXT.AMT,'MR3'),'MR2')
                IF EXT.AMT.VAL*100 >= EXT+ATAG.EXT.AMT THEN RETURN
             END

             IF (BASN < 23) OR (BASN > 27) THEN
                *** Get our Price Per Qty and Unit of Measure...
                PRICE.PER.GET PER,PER.UM,,,PRC.DATE
                *** Get our Qty Per Qty and Unit of Measure for Invoices...
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA
             END ELSE
                *** Use Frozen UoM and Price if BASN is a 'Frozen' Type...
                READV FROZEN FROM PRDCFILE,PN:"*":BR,26 ELSE FROZEN = ''
                DFLT.ALPHA  = FROZEN<1,1>; PER.UM = FROZEN<1,1>
                PER         = FROZEN<1,2>
                DFLT.PER.GET 'I',DFLT.PER,DFLT.ALPHA
                IF NOT(PER) THEN PER = DFLT.PER
                IF NOT(PER.UM) THEN PER.UM = DFLT.ALPHA
             END
             IF NOT(PER) THEN PER = 1
             IF NOT(PER.UM) THEN PER.UM = 'ea'
          END

          INCLUDE.PRODUCT = YES

          RETURN
*-------------------------------------------------------------------------*
GET.POCST: *** Determine the actual PO Cost for tagged items.
           *** PN and PRD must be active for the product we are looking at.
           *** NOTE!!! All PRD arrays will get trodden on.

          * Block increment total tag qty ct if we return early.
          OK.TO.INCREMENT = NO
          TAG.INFO = FIELD(OH.LOCS<1,LL>,'~',3)
          PO.INFO  = FIELD(TAG.INFO,'^',1)
          TAGGED.ORDER = FIELD(TAG.INFO,'^',2)

          * Pull PO information for both tagged sales orders & work orders.
          * On Work Orders make sure we only pick up PO type tags.
          IF INDEX('SW',PO.INFO[1,1],1) AND TAGGED.ORDER[1,1] # 'S' THEN PO.INFO = TAGGED.ORDER

          OID  = FIELD(PO.INFO,'.',1)
          LDID = FIELD(PO.INFO,'.',2)

          IF NOT(OID) THEN RETURN
          MATREAD LED FROM LEDFILE,OID THEN
             LD.GET LDID
             *** If we're looking at a transfer type order, we want to
             *** use the right date for the current branch we're looking at
             IF OID[1,1] = 'T' THEN
                G.CT = DCOUNT(LED(2),VM)
                FOUND = NO
                FOR GEN = 1 TO G.CT
                   IF LED(2)<1,GEN,2> = BR THEN
                      FOUND = YES
                      EXIT
                   END
                NEXT GEN
                IF NOT(FOUND) THEN
                   IF OH.QTYS<1,LL> >= 0 THEN FNDR = 'RX' ELSE FNDR = 'SX'
                   LOCATE FNDR IN LED(30)<1> SETTING GEN ELSE GEN = 1
                END
             END ELSE
                FINDSTR "T~" IN LD(7)<1> SETTING XX,GEN ELSE GEN = 1
             END
             PO.CST = LD(8)<1,GEN>
             *** Need to add a fail safe for tagged orders to verify
             *** that it does not fall outside our as of date since under
             *** certain case GET.PREV.ONHANDS will not correctly
             *** decrement the quantities from PRDD(1)
             IF NOT(PO.CST) THEN TINV.DT = LED(4)<1,GEN> ELSE
                TINV.DT = LED(23)<1,GEN>
             END
             IF TINV.DT AND TINV.DT > AOD THEN RETURN

             *** These four attributes will be set no matter if the prod
             *** is a kit or not.
             TQTY              = OH.QTYS<1,LL>
             TAG.CT           += 1
             TAG.IDS<TAG.CT>   = OID
             TAG.QTYS<TAG.CT>  = TQTY
             * OK to update total qty ct since we incremented tag count
             OK.TO.INCREMENT   = YES

             *** If the ld(1) product is a kit product, the active PRD will
             *** be for the component we are dealing with.
             IF PN # LD(1) THEN
                LOCATE PN IN LD(31)<1> SETTING XX THEN
                   GOSUB PROCESS.KIT
                END
                *** If the prd we are looking at isn't in the kit,
                *** something's wrong, so don't include the product.
             END

             *** Finally, set the tagged amount.
             TAG.COSTS<TAG.CT> = PO.CST
             TAG.EXT.AMT      += ICONV(OCONV(PO.CST,'MR9') * TQTY,'MR3')
          END

          RETURN
*-------------------------------------------------------------------------*
PROCESS.KIT: *** Work out the inventory amount to use for this
          *** kit component. We need LD, PN and PRD active. LD will be for
          *** the kit product, PN and PRD will be for the component.
          *** NOTE! All PRD globals will get trodden on.

          *** First work out what the total value of the kit should be
          *** according to the kit components and quantities.
          *** Also work out what the value of the PN in question is
          *** and the ratio between the two.
          COMP.CT = DCOUNT(LD(31),VM)
          TOT.COST = 0
          TOT.COMPS = 0; * THis is needed incase there are no costs
                         * associated to the components. We just divide
                         * the po amout by the total comps.
          COMP.COST = 0
          *** We need to save the old PN because we need the original
          *** value inside the loop, but we must set PN with each
          *** component for each call to get.val.
          OLD.PN = PN
          OLD.BASE = BASE
          FOR COMP.POS = 1 TO COMP.CT
             PN  = LD(31)<1,COMP.POS>
             OHQ = ""
             ADD.TAG = NO   ;* no tag totals
             GOSUB GET.VAL
             TOT.COST += BASE * LD(30)<1,COMP.POS>
             TOT.COMPS += LD(30)<1,COMP.POS>
             IF LD(31)<1,COMP.POS> = OLD.PN THEN COMP.COST = BASE
          NEXT COMP.POS
          PN = OLD.PN
          BASE = OLD.BASE

          *** Use the ratio to work out the actual cost of the individual
          *** component.
          IF TOT.COST = 0 THEN
             PO.CST = OCONV(PO.CST / TOT.COMPS,'MR0')
          END ELSE
             PO.CST = OCONV((COMP.COST / TOT.COST) * PO.CST,'MR0')
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.SUB:  *** Print Subtotals for our Sortby...
          IF MODE = 'XML' THEN RETURN

          IF LINE.CT > 1 OR DETAIL[1,6] # 'Detail' THEN

             IF SORTBY[1,7] # 'Prod GL' THEN
                TOL.DESC = LAST.BRK:' - ':SORT.DESC:' '
             END ELSE
                TOL.DESC = 'Totals for G/L Code: ':LAST.BRK:' '
             END

             TOL.DESC.LENGTH = LEN(TOL.DESC)
             TOL.DESC = TOL.DESC:STR('-',(35 - TOL.DESC.LENGTH))
             TOL.DESC = TOL.DESC "L#35"

             TOLS     = LTOLS
             TOLS.UM  = LTOLS.UM
             TOLS.EXT = LTOLS.EXT

             *** Set this flag so if there is an entity break requested,
             *** we only print the double line totals for Entity Totals...
             PRT.SBY = YES
             GOSUB PRT.TOLS
             PRT.SBY = NO
             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PRINT
             END
          END ELSE
             IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
                PRINT
                BLANK.LINE.PRINTED = YES
             END
          END

          LTOLS     = ""
          LTOLS.UM  = ""
          LTOLS.EXT = ""
          LINE.CT   = 0

          RETURN
*-------------------------------------------------------------------------*
PRT.ETOLS:*** Print our Entity Totals...
          IF MODE = 'XML' THEN RETURN

          *** If we grouped our data by product ranks, we'll need to
          *** go print the rank subtotals first...
          IF GROUP.RANKS THEN GOSUB PRT.RTOLS

          PRINT
          READV NAME FROM CUSFILE,TMP.CN,1 ELSE NAME = TMP.CN
          TOL.DESC = 'Totals For Entity : ':NAME"L#25"

          TOLS     = ETOLS
          TOLS.UM  = ETOLS.UM
          TOLS.EXT = ETOLS.EXT
          GOSUB PRT.TOLS
          ETOLS     = ""
          ETOLS.UM  = ""
          ETOLS.EXT = ""

          *** Print our ascii character Page Break if printing detail
          IF DETAIL[1,6] = 'Detail' THEN
             PRINT CHAR(12)
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.BTOLS:*** Print our Branch Totals...
          IF MODE = 'XML' THEN RETURN

          IF DETAIL[1,6] = 'Detail' THEN
             PRINT
             TOL.DESC = 'Totals For Branch : ':LAST.BRK
          END ELSE
             TOL.DESC = ''
          END

          TOLS     = BTOLS
          TOLS.UM  = BTOLS.UM
          TOLS.EXT = BTOLS.EXT
          GOSUB PRT.TOLS
          BTOLS    = ""
          BTOLS.UM = ""
          BTOLS.EXT= ""

          *** Print our ascii character Page Break if printing detail.
          IF DETAIL[1,6] = 'Detail' THEN
             PRINT CHAR(12)
          END

          RETURN
*-------------------------------------------------------------------------*
PRT.RTOLS:*** Print the Subtotals for the product ranks that we've been
          *** grouping by...
          IF MODE = 'XML' THEN RETURN

          *** We need to print our Sort by Totals first...
          IF NOT(ENTBK) AND SUBT.ON.SORTBY THEN
             IF LINE.CT > 1 OR DETAIL[1,6] # 'Detail' THEN
                GOSUB PRT.SUB
             END ELSE
                LTOLS    = ''
                LTOLS.UM = ''
                LINE.CT  = 0
             END

             LAST.BRK = BRK.ON
             ITM.CT   = 0
          END

          IF RANK.CT > 1 OR DETAIL[1,6] # 'Detail' THEN
             TOT.RANKS = LAST.RANKS
             CONVERT VM TO ',' IN TOT.RANKS

             TOL.DESC = 'Totals for Products with Ranks: ':TOT.RANKS:' '

             TOL.DESC.LENGTH  = LEN(TOL.DESC)
             TOT.RANKS.LENGTH = LEN(TOT.RANKS)

             *** If we're sorting by Line...
             TOL.DESC  = TOL.DESC:STR('-',(68 - TOL.DESC.LENGTH))
             TOL.DESC  = TOL.DESC "L#68"

             TOL.DESC := '------'

             TOLS      = RTOLS
             TOLS.UM   = RTOLS.UM
             TOLS.EXT  = RTOLS.EXT

             PRT.RANKS = YES
             GOSUB PRT.TOLS
             PRT.RANKS = NO
             PRINT
          END ELSE
             IF NOT(BLANK.LINE.PRINTED) THEN
                PRINT
             END
          END

          *** Clear out our rank totals...
          RTOLS     = ""
          RTOLS.UM  = ""
          RTOLS.EXT = ""
          RANK.CT   = 0

          RETURN
*-------------------------------------------------------------------------*
PRT.TOLS: * This is our generic function for printing Totals. The Totals
          * we are printing depends on what we set TOLS equal to...

          IF MODE = 'XML' THEN RETURN

          *** If all the products we've printed info for so far
          *** have the same Qty Unit of Measure, we can display it.
          *** Otherwise, we'll display asterics to show that the qty
          *** totals are based on products with different Qty UoMs...
          IF TOLS.UM<1,2> = '' THEN QUM = TOLS.UM<1> ELSE QUM = '**'

          *** Only write the "bucket" ODBC information if they have solar
          IF HAS.ODBC THEN
             *** If the report is printing in Summary or Summary by
             *** Product format, and we're not printing a Grand Total
             *** right now...
             IF DETAIL[1,6] # 'Detail' AND TOL.DESC[1,12] # "Grand Totals" THEN
                *** We wont print a separate total line for the
                *** Product Ranks...
                IF NOT(PRT.RANKS)THEN
                   REC.NUM += 1
                   REC      = ""
                   REC<1>   = LAST.BRK
                   REC<2>   = ICONV(TOLS<1,1>,"MR0")
                   REC<3>   = ICONV(TOLS<1,2>,"MR0")
                   REC<4>   = ICONV(TOLS<1,6>,"MR0")
                   REC<5>   = ICONV(TOLS<1,5>,"MR0")
                   REC<6>   = ICONV(TOLS<1,7>,"MR0")
                   REC<7>   = ICONV(OCONV(TOLS<1,3>,"MR2"),"MR2")
                   REC<11>  = QUM

                   IF GROUP.RANKS THEN
                      TOT.REC.RANKS = LAST.RANKS
                      CONVERT VM TO ',' IN TOT.REC.RANKS
                      REC = INSERT(REC,2;TOT.REC.RANKS)
                   END

                   IF SORTBY[1,7] # 'Prod GL' THEN
                      READV LAST.LDESC FROM PLNEFILE,LAST.BRK,1 ELSE
                         LAST.LDESC = ''
                      END
                      REC = INSERT(REC,2;LAST.LDESC)
                   END

                   WRITE REC ON ODBCFILE,REC.NUM "R%7"
                END
             END
          END

          *** If Tag Onhd is less than 0, print out a value of 0
          IF TOLS<1,2> < 0 THEN
             TOLS<1,2> = 0
          END

          PRINT.STRING = ""
          TOTAL.STRING = ""
          COL.DATA = ""
          SEP.CHAR = ""
          COL.DATA<5> = TOLS<1,1> "R2,"
          COL.DATA<6> = TOLS<1,2> "R,"
          COL.DATA<7> = TOLS<1,6> "R,"
          COL.DATA<8> = TOLS<1,5> "R,"
          IF SHOW.VPRC THEN
             COL.DATA<9> = TOLS<1,7> "R2,"
          END
          COL.DATA<11> = QUM

          EXT.CT = DCOUNT(EXT.BASE,AM)
          FOR ECT = 1 TO EXT.CT
             COL = EXT.BASE<ECT,2>
             * add to column if there is an ext column
             IF COL # 0 THEN
                COL.DATA<COL> = OCONV(TOLS.EXT<COL>,"MR2")  "R2,"
             END
          NEXT ECT

          * If doing 'Summary by Branch...', make sure to pass the branch.
          IF INDEX(DETAIL,'by Branch',1) AND TOL.DESC[1,12]#"Grand Totals" THEN
             COL.DATA<70> = LAST.BRK
             COL.DATA<77> = LAST.BR.DESC
             COL.DATA<78> = LAST.COST.CENTER
          END

          * Extended total amount for tags.
          COL.DATA<69> = OCONV(TOLS<1,3>,"MR2")          "R2,"

          *** If the report format is Detail OR Summary by Product...
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             IF PRINTING.GTOTALS THEN
                SEP.CHAR = "="
             END ELSE
                SEP.CHAR = "-"
             END
          END

          REPORT.LAYOUT.V.PRINT.STR COL.DATA,SEP.CHAR,COL.RECORD,TOTAL.STRING,PRINT.STRING
          * This is necessary because there is no 'fixed' data that can be
          * replaced with the total title
          LEADIN.STRING = TOL.DESC
          GOSUB REPL.LSTR
          IF TOTAL.STRING # '' THEN
             PRINT TOTAL.STRING
          END
          PRINT PRINT.STRING

          RETURN
*-------------------------------------------------------------------------*
REPL.LSTR:* Add totals string to string that is returned from variable
          * column routine.
          REMAINING.LENGTH = LEN(PRINT.STRING)-LEN(LEADIN.STRING)
          IF REMAINING.LENGTH > 0 THEN
             PRINT.STRING = LEADIN.STRING:PRINT.STRING[REMAINING.LENGTH]
          END ELSE
             PRINT.STRING = LEADIN.STRING
          END
          RETURN
*-------------------------------------------------------------------------*
SEL.IDS:  * Select data for our report...

          *** If the User has specified Price or Buy Lines to report
          *** our data for...

          IF SLIST#'' AND SLCT = 'Product' THEN
             IDS = RAISE(SLIST)
             SELECT IDS
          END ELSE IF SLIST # '' AND (SLCT = 'Price Line' OR SLCT = 'Buy Line') THEN
             IDS = ''
             CT  = DCOUNT(SLIST,VM)
             FOR J = 1 TO CT
                NEXT.LINE = SLIST<1,J>
                *** Get all valid product for this Price/Buy Line...
                IF SLCT[1,1] = 'P' THEN
                   ** Note: We can't read the PNs from the PROD.SEQ record
                   ** because deleted products are excluded from these
                   ** records.  They are not excluded from the PROD.DYNAM
                   ** so if we use the PROD.SEQ records we will get
                   ** inconsistent results
                   EXE = 'SELECT PRODUCT WITH LINE = "':NEXT.LINE:'"'
                   EXECUTE EXE CAPTURING MSG
                   READLIST ID2 ELSE ID2 = ''

                END ELSE
                   EXE = 'SELECT PRODUCT WITH BUY.LINE = "':NEXT.LINE:'"'
                   EXECUTE EXE CAPTURING MSG
                   READLIST ID2 ELSE ID2 = ''
                END
                IF IDS = '' THEN
                   IDS = ID2
                END ELSE
                   IF ID2 THEN
                      ID2.CNT = DCOUNT(ID2,AM)
                      *** Strip out duplicate part numbers
                      FOR XX = 1 TO ID2.CNT
                         LOCATE ID2<XX> IN IDS SETTING POS ELSE
                            IDS<-1> = ID2<XX>
                         END
                      NEXT XX
                   END
                 IF DCOUNT(IDS,AM) > 100000 THEN EXIT
                END
             NEXT J
             *** If we have more than 100,000 products, we'll try and
             *** shorten the list...
             IF DCOUNT(IDS,AM) > 100000 THEN
                IDS = ''
                *** Get a list of the products that are active....
                PRDD.PN.SELECT
                LOOP
                   READNEXT PN ELSE EXIT
                   *** Set the Line Attribute - Price or Buy...
                   IF SLCT[1,1] = 'P' THEN ATTB = 9 ELSE ATTB = 12

                   READV LINE FROM PRDFILE,PN,ATTB THEN
                      LOCATE LINE IN SLIST<1> SETTING POS THEN
                         IDS<-1> = PN
                      END
                   END
                REPEAT
                SELECT IDS
             END ELSE
                SELECT IDS
             END
          END ELSE
             *** Get a list of the products that are active....
             PRDD.PN.SELECT
          END

          GOSUB CREATE.TEMPFILE

          RETURN
*-------------------------------------------------------------------------*
CREATE.TEMPFILE:* Create a Tempfile for our report data so that we can
                * get it sorted properly...

          UT.TEMPFILE.CREATE FLNM.ID,TEMPFILE
          *** Loop through the list of products we picked up before we
          *** got here...
          LOOP
             READNEXT PN ELSE EXIT
             MATREAD PRD FROM PRDFILE,PN ELSE MAT PRD = ''
             GLCODE = PRD(2)

             *** Go make sure the product should really be on the report...
             GOSUB FILTER.PRODUCT

             *** If it didn't pass our filters then skip it...
             IF NOT(PRODUCT.OK) THEN CONTINUE

             IF SORTBY = 'Price Line' OR SORTBY = 'Buy Line' THEN
                *** Find Seq for product
                IF SLCT[1,3] = 'Buy' THEN
                   GET.BLINE.IDS ,LINES,BUY.LN
                END ELSE
                   GET.LINE.IDS ,LINES,PRC.LN
                END
                SORT.CODE = TRIM(PRD(20))
                IF SORT.CODE AND NUM(SORT.CODE) THEN
                   SORT.CODE += 0
                END
                IF SORT.CODE THEN
                   SEQ = SORT.CODE
                END ELSE
                   LOCATE PN IN LINES<1> SETTING SEQ ELSE SEQ = 1
                END
             END
             *** Set sortby for each product
             BEGIN CASE
             CASE SORTBY = 'Price Line'
                SBY = PRC.LN "L#10":'~':SEQ "R%9"
             CASE SORTBY = 'Buy Line'
                SBY = BUY.LN "L#10":'~':SEQ "R%9"
             CASE SORTBY = 'Prod GL Code'
                SBY      = GLCODE "L#10":'~'

                IF SLCT  = 'Price Line' OR SLCT = 'Sell Group' THEN
                   SBY  := PRD(9) "L#10"
                END ELSE IF SLCT[1,3] = 'Buy' THEN
                   SBY  := PRD(12) "L#10"
                END
             CASE SORTBY = 'Sell Group'
                SBY = SEL.GRP "L#10"
             CASE SORTBY = 'Buy Group'
                SBY = BUY.GRP "L#10"
             CASE SORTBY = 'Prod ID'
                SBY = PN "L#10"
             END CASE

             *** PER was not being reset for each PN.  Reset here before
             *** the branch.
             PER = ''
             TMP.REC = ''
             FOR BRN = 1 TO BR.CT
                BR   = BRCHS<1,BRN>

                *** Check that the product is active in the current branch
                READV NADA FROM PRDDFILE,PN:'*':BR,0 ELSE CONTINUE

                *** If the User has selected on specific Product Ranks, but
                *** we are not grouping our data according to Product Ranks
                *** we'll need to go validate the ranks right now...
                IF NOT(GROUP.RANKS) AND RANKS THEN
                   *** Get all the ranks for this product at the current
                   *** branch...
                   PRDC.BR.GET.VAL BR,PN,1,PROD.RANKS
                   *** if there aren't any ranks returned we don't want to
                   *** use resources to process.
                   RANKED.OK  = YES
                   TEMP.RANKS = ''
                   GOSUB GET.PRODUCT.RANKS

                   *** If one of the product's ranks was not a match for
                   *** what the User selected, we'll skip this product...
                   IF NOT(RANKED.OK) THEN CONTINUE
                END

                *** Go get the OnHand Qtys for the current product at the
                *** the current Branch...
                GOSUB GET.QTYS

                *** If it didn't pass the location/qty filters we won't
                *** include it on the report...
                IF NOT(INCLUDE.PRODUCT) THEN CONTINUE

                *** If we're page breaking on Consignment
                *** Vendors/Customers...
                IF ENTBK THEN
                   FOR XX = 1 TO CONSIGN.CT
                      TAG.QTY  = 0
                      DISP.QTY = 0

                      *** If we're Page Breaking on Consignment Vendors...
                      IF ENTBK = 'V' THEN
                         VCN.QTY = CONSIGNMENT.QTY<XX>; STK.QTY = 0; REV.QTY = 0
                      END ELSE
                         *** Otherwise we're page Breaking on Consignement
                         *** Customers...
                         STK.QTY = CONSIGNMENT.QTY<XX>; REV.QTY = 0
                      END

                      *** Set up Consignment Entity part of our Tempfile
                      *** Record...
                      TEMP.ID    = NAME<XX>:'~':TMP.CN<XX>:'~'
                      EXT.BASE   = RAISE(CONSIGN.EXT<XX>)
                      DFLT.PER   = CONSIGN.DPER<XX>
                      DFLT.ALPHA = CONSIGN.DALPHA<XX>
                      PER        = CONSIGN.PER<XX>
                      PER.UM     = CONSIGN.UM<XX>

                      GOSUB ADD.REC
                   NEXT XX
                *** Otherwise we're in here cause we're grouping products
                *** by their ranks...
                END ELSE
                   IF NOT(INCLUDE.PRODUCT) THEN CONTINUE

                   TEMP.ID = ''

                   GOSUB ADD.REC
                END
             NEXT BRN
          REPEAT

          RETURN
*-------------------------------------------------------------------------*
ADD.REC:  *** Create a record in our Tempfile for the current product
          *** at the current Branch...
          *** If we need to group our data by product ranks...
          IF GROUP.RANKS THEN
             *** Get all the ranks for this product at the current
             *** branch...
             PRDC.BR.GET.VAL BR,PN,1,PROD.RANKS
             *** If the User has selected certain Product Ranks make
             *** sure this product has all the right ones...
             IF RANKS THEN
                RANKED.OK  = YES
                TEMP.RANKS = ''

                GOSUB GET.PRODUCT.RANKS

                *** If one of the product's ranks was not a match for
                *** what the User selected, we'll skip this product...
                IF NOT(RANKED.OK) THEN RETURN
             END ELSE
                TEMP.RANKS = RAISE(PROD.RANKS)
             END

             *** Do this so we can make it part of our Tempfile Id...
             CONVERT AM TO '!' IN TEMP.RANKS

             TEMP.ID := TEMP.RANKS:'~'
          END

          PDESC       = PRD(1)<1,1>
          CONVERT '~' TO ' ' IN PDESC

          TEMP.ID    := SBY

          *** If our report is in Detail OR 'Summary by...' Format...
          TEMP.ID := '~':PDESC:'~':PN
          BEGIN CASE
             CASE INDEX(DETAIL,'by Branch',1)
                TEMP.ID  = BR "R#4":'~':TEMP.ID:'~':BR
             CASE DETAIL = 'Detail' OR DETAIL = 'Summary'
                TEMP.ID := '~':BR
          END CASE

          READ TMP.REC FROM TEMPFILE,TEMP.ID ELSE TMP.REC = ''

          TMP.REC<1> += TAG.QTY / DFLT.PER
          TMP.REC<2> += STK.QTY / DFLT.PER
          TMP.REC<3> += DISP.QTY / DFLT.PER
          TMP.REC<4> += REV.QTY / DFLT.PER
          TMP.REC<5> += VCN.QTY / DFLT.PER

          BASN.CT = DCOUNT(EXT.BASE,AM)
          FOR BCT = 1 TO BASN.CT
             TMP.REC<7,BCT,1> += EXT.BASE<BCT,1>
             TMP.REC<7,BCT,2>  = EXT.BASE<BCT,2>
          NEXT BCT

          * The extended tag amount should always be written so it can be
          * summed into the total in summary reports
          TMP.REC<16> = TAG.EXT.AMT

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             *** We only need to store this stuff if we're
             *** printing a Detail or Summary by Product Report...
             TMP.REC<8>  = PER
             TMP.REC<9>  = PER.UM
             TMP.REC<10> = DFLT.PER
             TMP.REC<11> = DFLT.ALPHA
             TMP.REC<12> = LOWER(UNIT.BASE)

             IF DETAIL[1,6] = 'Detail' THEN
                TMP.REC<13> = LOWER(TAG.IDS)
                TMP.REC<14> = LOWER(TAG.QTYS)
                TMP.REC<15> = LOWER(TAG.COSTS)

                TMP.REC<21> = FIFO.IDS
                TMP.REC<22> = FIFO.QTYS
                TMP.REC<23> = FIFO.COSTS
                TMP.REC<24> = FIFO.DTS
             END

             TMP.REC<17> = CCN.LIST
             TMP.REC<18> = CCN.QTYS
             TMP.REC<19> = VCN.LIST
             TMP.REC<20> = VCN.QTYS
          END ELSE
             LOCATE DFLT.ALPHA IN TMP.REC<11> SETTING APOS ELSE
                TMP.REC<11,APOS> = DFLT.ALPHA
             END
          END

          WRITE TMP.REC ON TEMPFILE,TEMP.ID

          RETURN
*-------------------------------------------------------------------------*
GET.PRODUCT.RANKS: *** Get the Product Ranks for this product and validate
                   *** against any ranks the User has selected...
          IF TRIM(PROD.RANKS,VM) = "" THEN
             RANKED.OK = NO
             RETURN
          END

          FOUND.A.MATCH = NO
          FOR RNUM  = 1 TO RANK.NUM.CT
             SELECTED.RANKS = RAISE(RANKS<1,RNUM>)
             IF SELECTED.RANKS<1,1> = '' THEN CONTINUE

             PRD.RANK = PROD.RANKS<1,RNUM>
             LOCATE PRD.RANK IN SELECTED.RANKS<1> SETTING RNKPOS THEN
                *** If it's a match, we can add it to our temp list.
                TEMP.RANKS<RNUM> = PRD.RANK
                FOUND.A.MATCH    = YES
             END ELSE
                TEMP.RANKS<RNUM> = '*'
                *** If they only want products that match 'All'
                *** Product Rank Selections (vs. 'Any')...
                IF ANY.ALL = 'All' THEN
                   *** If we find one that isn't a match, we can't
                   *** put this product on the report...
                   RANKED.OK = NO
                   EXIT
                END
             END

          NEXT RNUM

          *** If there were no matches then we don't want this product
          *** on the report...
          IF NOT(FOUND.A.MATCH) THEN RANKED.OK = NO

          RETURN
*-------------------------------------------------------------------------*
FILTER.PRODUCT: *** Make sure the product should really be on the report...
          PRODUCT.OK = NO

          *** Check the Override account on the product
          IF INV.ACCTS # "" THEN
             READV PL.INV.ACCT FROM PLNEFILE,PRD(9),24 ELSE PL.INV.ACCT=''
             IF PRD(89) = '' AND PL.INV.ACCT = '' AND NOT(INC.PRI.INV) THEN RETURN
             IF NOT(PRD(89) = '' AND PL.INV.ACCT = '' AND INC.PRI.INV) THEN
                IF PRD(89) = "" THEN
                   LOCATE PL.INV.ACCT IN INV.ACCTS<1> SETTING POS ELSE RETURN
                END ELSE
                   LOCATE PRD(89) IN INV.ACCTS<1> SETTING POS ELSE RETURN
                END
             END
          END

          *** If our Dynamic Kit flag is set for this product...
          IF PRD(106) THEN GOSUB CLR.KIT

          *** If this product is a 'Misc. Charge', a 'Comment' or a
          *** 'Lot Item', skip it...
          IF PRD(3)  = 3 OR PRD(3) = 6 OR PRD(3) = 9 THEN RETURN
          *** If this product isn't a 'Stock' item, and the User wants
          *** to 'Exclude' Nonstocks, skip it...
          IF STK.ITEM = 'No' AND EXCL.NS[1,1] = 'E'      THEN RETURN
          *** If this product is a 'Stock' item, and the User 'Only'
          *** wants Nonstocks, skip it...
          IF PRD(3)  = 1 AND EXCL.NS[1,1] = 'O'      THEN RETURN
          *** If this product is a Kit, skip it...
          IF PRD(53) # ''                            THEN RETURN

          PRC.LN = PRD(9)
          BUY.LN = PRD(12)
          PRD.BR.GET.VAL BRCHS<1,1>,PN,24,SEL.GRP
          SEL.GRP = SEL.GRP<1,1,1>
          PRD.BR.GET.VAL BRCHS<1,1>,PN,23,BUY.GRP
          BUY.GRP = BUY.GRP<1,1,1>
          IF SLIST # '' THEN
             BEGIN CASE
             *** Filter against product list
             CASE SLCT = 'Product'
                LOCATE PN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = PN
             *** Filter against any Price Lines that have been selected...
             CASE SLCT = 'Price Line'
                LOCATE PRC.LN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = PRC.LN
             *** Filter against any Buy Lines selected
             CASE SLCT = 'Buy Line'
                LOCATE BUY.LN IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = BUY.LN
             *** Filter against any Sell Groups that have been selected...
             CASE SLCT = 'Sell Group'
                LOCATE SEL.GRP IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = SEL.GRP
             *** Filter against any Buy Groups that have been selected...
             CASE SLCT = 'Buy Group'
                LOCATE BUY.GRP IN SLIST<1> SETTING XX ELSE RETURN
                SRT.BRK = BUY.GRP
             END CASE
          END

          PRODUCT.OK = YES

          RETURN
*-------------------------------------------------------------------------*
INIT:     *** Initialize the data we'll need for this Phantom to work...

          ERR.MSG        = ''
          MODE           = MISC.DATA<1,5>
          OUTBOUND.EMAIL = MISC.DATA<1,6>

          BASIS.LIST     = RAISE(BASIS.LIST)

          *** Get the number of columns, desc, and lengths
          REPORT.PREFIX = 'INV.VALUE'
          *** no summary version
          REPORT.V.COL.GET.DATA REPORT.PREFIX,DRPT<56>,COL.RECORD,'D'
          IF COL.RECORD = '' THEN
             ERR.MSG = 'Missing report data for Inventory Valuation. Contact your support representative'
             RETURN
          END

          IF MODE = 'XML' THEN
             GET.NEW.ID 'NEXT.INV.WKSHT.ID',OUT.ID,,,,YES
             OUT.ID = 'InventoryModel-':OUT.ID:'.xml'

             UT.OPEN.FILE 'XML.FORMS',XMLFILE,ERR.MSG
             IF ERR.MSG THEN RETURN

             READ ITEM.RESP FROM XMLFILE,'INV.MODEL.ITEM.TEMPLATE' ELSE
                ERR.MSG = 'Unable to Locate Item Template'
                RETURN
             END

             READ BODY.RESP FROM XMLFILE,'INV.MODEL.XLS' ELSE
                ERR.MSG = 'Unable to Locate Excel Template'
                RETURN
             END

             READ FOOTER.RESP FROM XMLFILE,'INV.MODEL.FOOTER' ELSE
                FOOTER.RESP = ''
             END

             READ INV.PARAMS FROM CTRLFILE,'INV.MODEL.PARAMS' ELSE
                INV.PARAMS = ''
             END
             DEMAND.ROUNDING = OCONV(INV.PARAMS<1>,'MR2')
             TARGET.TURNS = INV.PARAMS<2>
             SF.RANK = INV.PARAMS<3>
             READV DAYS.DEMAND FROM CTRLFILE,'MIN.ORD.CYCLE',1 ELSE
                DAYS.DEMAND = 7
             END
             READV HIT.CUTOFF FROM CTRBFILE,'MIN.BR.HITS~':BR,1  ELSE
                HIT.CUTOFF = ''
             END
          END

          UT.OPEN.COMMON.FILE 'PROD.LOT', HNDL
          IF HNDL > 0 THEN
             DLOTFILE = FILES(HNDL)
          END ELSE
             PRINT BELL:;
             SYS.LOG.CMT 'Cannot open PROD.LOT.','INV.PHR.INV.VALUE'
             CSTAT "'Cannot open PROD.LOT from 'INV.PHR.INV.VALUE'"
             RETURN
          END

          *** Break out our Price/Buy Line data...
          SLIST = RAISE(SEL.DATA<1,1>)
          SLCT  = SEL.DATA<1,2>

          * Break out our additional data...
          VCSGN      = ADDL.DATA<1,1,1>
          CCSGN      = ADDL.DATA<1,1,2>
          CCN        = ADDL.DATA<1,1,3>
          VPBK       = ADDL.DATA<1,1,4>
          CPBK       = ADDL.DATA<1,1,5>
          SHOW.VPRC  = ADDL.DATA<1,1,7>
          HIST.YEARS = ADDL.DATA<1,1,8>
          INCL.ADJ   = ADDL.DATA<1,1,9>

          * If page break requested on consignment customer or vendor
          * set variable ENTBK appropriately
          BEGIN CASE
          CASE VPBK AND NOT(CCN);       ENTBK = 'V'
          CASE CPBK AND NOT(CCN);       ENTBK = 'C'
          CASE OTHERWISE;               ENTBK = ''
          END CASE

          * Make sure that if the costing date was "" we use today.
          IF NOT(CAOD) THEN CAOD = DATE()

          AS.OF.DT         = OCONV(AOD,'D4/')
          CAS.OF.DT        = OCONV(CAOD,'D4/')
          PRC.DATE         = CAOD
          INV.DT           = AOD + 1
          DETAIL           = DET.OPTS<1,1>
          REPORT.FORMAT    = DETAIL
          SHOW.PN          = DET.OPTS<1,2>
          ANY.ALL          = MISC.DATA<1,1>
          INV.ACCTS        = MISC.DATA<1,3>
          INV.ACCTS        = RAISE(INV.ACCTS)
          AVG.CST          = MISC.DATA<1,4>
          BEG.DT           = ICONV('01/01/':AS.OF.DT[7,4],'D')
          SEL.CCN          = ''
          TEST.AVG         = ''
          IF CCN THEN SEL.CCN = CCN

          * Parse our data out from our Include/Exclude parameter...
          EXCL.NS  = INC.EXC.DATA<1,1>
          NEG      = INC.EXC.DATA<1,2>
          SUP.ZERO = INC.EXC.DATA<1,3>

          IF RANKS THEN
             RANK.NUM.CT   = DCOUNT(RANKS<1>,VM)
          END ELSE
             * There 5 possible Rank Numbers (methods)...
             RANK.NUM.CT   = 5
          END

          GROUP.RANKS      = GROUP.RANK.DATA<1,1>
          SUBT.ON.SORTBY   = GROUP.RANK.DATA<1,2>
          TSORTBY          = SORTBY

          * If the user has selected to show the Rank column, then show
          * ranks.
          RANK.COL = NO
          LOCATE '74' IN COL.RECORD<1> SETTING POS THEN
             RANK.COL = YES
          END

          IF DETAIL[1,6] = 'Detail' AND (RANKS OR GROUP.RANKS OR RANK.COL) THEN
             DISPLAY.RANKS = YES
          END ELSE
             DISPLAY.RANKS = NO
          END

          * If no qty types were specified, set to ALL by default
          IF IQTYPES = '' THEN
             QTYPES = 'S':VM:'F':VM:'O':VM:'R':VM:'L':VM:'T'
             IF VCSGN = 'Include' THEN QTYPES<1,-1> = 'V'
             IF CCSGN = 'Include' THEN QTYPES<1,-1> = 'C'
          END ELSE
             QTYPES = IQTYPES
          END

          IF VCSGN = 'Only' THEN QTYPES = 'V'
          IF CCSGN = 'Only' THEN QTYPES = 'C'

          LOCATE 'V' IN QTYPES<1> SETTING DUM THEN
             FND.VQTYPE = YES
          END ELSE
             FND.VQTYPE = NO
          END

          NEG   = NEG[1,1]

          BR.CT = DCOUNT(BRCHS,VM)
          BRS   = BR

          BCNT = DCOUNT(BASIS.LIST,AM)
          IF BCNT > 1 THEN
             GBDESC = "**Multiple**"
          END ELSE
             GLOBAL.BNAME.GET BASIS.LIST<1,1,1>,GBDESC
          END

          BEGIN CASE
          CASE GBDESC = "COGS-COST";            KPT = 3
          CASE GBDESC = "DFLT-COST";            KPT = 4
          CASE OTHERWISE;                       KPT = 1
          END CASE

          * Build some dashed lines for when we need to print totals...

          IF VCSGN[1,1] = 'I' OR VCSGN[1,1] = 'O' THEN
             SINGLE.LINES  = '  ':STR('-',11):' ':STR('-',9):' ':STR('-',9)
             SINGLE.LINES := ' ':STR('-',9):' ':STR('-',9):SPACE(24):STR('-',13)
          END ELSE
             SINGLE.LINES  = '  ':STR('-',11):' ':STR('-',9):' ':STR('-',9)
             SINGLE.LINES := ' ':STR('-',9):SPACE(24):STR('-',13)
          END
          DOUBLE.LINES = SINGLE.LINES
          CONVERT '-' TO '=' IN DOUBLE.LINES

          * See if they are storing serial numbers by location.
          READV SLC.FLAG FROM CTRLFILE,'RF.LOC.BY.SERS',1 ELSE SLC.FLAG=''
          READV SCONV FROM CTRLFILE,'SERIAL.CONV.NEEDED',1 ELSE SCONV = ''
          * If a conversion still needs to run then serials are
          * being stored the opposite of what is in RF.LOC.BY.SERS
          IF SCONV THEN SLC.FLAG = NOT(SLC.FLAG)

          CCN.LIST  = ''
          CCN.QTYS  = ''
          VCN.LIST  = ''
          VCN.QTYS  = ''

          PTOLS.EXT = ""
          RTOLS.EXT = ""
          LTOLS.EXT = ""
          ETOLS.EXT = ""
          BTOLS.EXT = ""
          GTOLS.EXT = ""

          * This is a check to see if we are including primary inventory
          * in our selection.  Products that are primary will have a NULL
          * value in inventory account override field on the product and
          * price line.
          INC.PRI.INV = NO
          LOCATE 'INVTY' IN GL.AUTO<1> SETTING POS THEN
             INVTY.ACCT = GL.AUTO<2,POS>
             LOCATE INVTY.ACCT IN INV.ACCTS<1> SETTING POS2 THEN
                INC.PRI.INV = YES
             END
          END

          IF MODE = 'XML' THEN
             IF OUTBOUND.EMAIL THEN
                OPENSEQ 'MSG-OUT',OUT.ID TO OUTFILE ELSE NULL
             END ELSE
                GET.NEW.ID 'NEXT.DOC',HOLD.ID,,,RPTFILE

                OPENSEQ '&HOLD&',HOLD.ID TO OUTFILE ELSE NULL

                HOLD.LN.CT = 0
                RPT        = ''
                RPT<1>     = OUT.ID
                RPT<2>     = 100
                RPT<3>     = 60
                RPT<4>     = USER.ID
                RPT<5>     = DATE()
                RPT<6>     = INT(TIME())
                RPT<11>    = DATE()
                RPT<12>    = INT(TIME())
                RPT<36>    = USER.ID
             END

             UT.REP.STR BODY.RESP,'&INV.BR&',BRS
             UT.REP.STR BODY.RESP,'&INVTY.DT&',AS.OF.DT
             UT.REP.STR BODY.RESP,'&GBASIS&',GBDESC
             UT.REP.STR BODY.RESP,'&DEMAND.ROUNDING&',DEMAND.ROUNDING
             UT.REP.STR BODY.RESP,'&TARGET.TURNS&',TARGET.TURNS
             UT.REP.STR BODY.RESP,'&DAYS.DEMAND&',DAYS.DEMAND
             UT.REP.STR BODY.RESP,'&HIT.CUTOFF&',HIT.CUTOFF

             LN.CT = DCOUNT(BODY.RESP,AM)
             FOR LN = 1 TO LN.CT
                WRITESEQ BODY.RESP<LN> ON OUTFILE ELSE EXIT
                IF NOT(OUTBOUND.EMAIL) THEN
                   HOLD.LN.CT += 1
                END
             NEXT LN
          END
          RETURN
*-------------------------------------------------------------------------*
SETUP.ODBC:  * Setup the report information so that it's recognized as an
             * ODBC data source.
          DRPT<40> = "1"
          DRPT<41> = ''

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
*** Setup the column justifications and widths.
             DRPT<41,1>  = "L#35"
             DRPT<41,2>  = "L#10"; *Line Id
             DRPT<41,3>  = "L#25"; *Line Description
             DRPT<41,4>  = "R#13"
             DRPT<41,5>  = "R#10"
             DRPT<41,6>  = "R#10"
             DRPT<41,7>  = "R#10"
             DRPT<41,8>  = "R#11"
             DRPT<41,9>  = "R#10"
             DRPT<41,10> = "L#5"
             DRPT<41,11> = "R#13"
             DRPT<41,12> = "L#6"
             DRPT<41,13> = "R#5"
             DRPT<41,14> = "L#10"
             DRPT<41,15> = "L#6"
             DRPT<41,16> = "R#5"

*** Setup the column headings for the report.
             DRPT<45>    = ""
             DRPT<45,1>  = "Product Description"
             DRPT<45,2>  = SLCT
             DRPT<45,3>  = "Description"
             DRPT<45,4>  = "Stock Onhand"
             DRPT<45,5>  = "Tag Onhand"
             DRPT<45,6>  = "R/F/O Onhand"
             DRPT<45,7>  = "Display Onhand"
             DRPT<45,8>  = "Vendor Consign"
             DRPT<45,9>  = "Unit Value"
             DRPT<45,10> = "UM"
             DRPT<45,11> = "Extended Value"
             DRPT<45,12> = "Qty UM"
             DRPT<45,13> = "Br"
             DRPT<45,14> = "G/L Code"
             DRPT<45,15> = "PN"
             DRPT<45,16> = "Seq"

*** Setup which columns should show totals on a break line

             DRPT<42>    = ""
             *** Stock OnHand
             DRPT<42,4>  = YES
             *** Tag OnHand
             DRPT<42,5>  = YES
             *** R/F/O OnHand
             DRPT<42,6>  = YES
             *** Disp OnHand
             DRPT<42,7>  = YES
             *** Ext Value
             DRPT<42,11> = YES

             *** If they're putting the product ranks on the report
             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF DISPLAY.RANKS THEN
                DRPT<41> = INSERT(DRPT<41>,1,4;"L#11")
                DRPT<42> = INSERT(DRPT<42>,1,4;0)
                DRPT<45> = INSERT(DRPT<45>,1,4;"Product Ranks")
             END

             *** If they're running consignment and breaking on the entity
             *** add the entity column to the front of the report and
             *** push all our other report values up at the same time...
             IF ENTBK THEN
                DRPT<41> = INSERT(DRPT<41>,1,1;"L#20")
                DRPT<42> = INSERT(DRPT<42>,1,1;0)
                DRPT<45> = INSERT(DRPT<45>,1,1;"Entity")
             END

*** Set up the items we should be breaking on...
             DRPT<43>    = ""

             *** Set the break on to either the line or gl type column.
             IF SORTBY[1,1] = "L" THEN
                BRK.POS = 2
             END ELSE
                *** If Product Ranks are being displayed, the GL Code
                *** will be in column 14...
                IF DISPLAY.RANKS THEN BRK.POS = 14 ELSE BRK.POS = 13
             END

             *** If we're pg. breaking on consignment entity, all our
             *** data columns will be moved over one...
             IF ENTBK THEN
                BRK.POS += 1
             END

             DRPT<43,BRK.POS> = YES

             *** Set the break for the consignment entity...
             IF ENTBK THEN
                DRPT<43,1> = YES
             END

             *** A break for the Product Ranks if Group by Product Ranks
             *** option is set to yes...
             IF GROUP.RANKS THEN
                IF ENTBK THEN DRPT<43,5> = YES ELSE DRPT<43,4> = YES
             END

*** Setup the initial sortby of the report.
             SORT.COLS = ''
             BEGIN CASE
             CASE GROUP.RANKS AND ENTBK AND SORTBY[1,7] # 'Prod GL'
                SORT.COLS<1> = 1;  *Entity
                SORT.COLS<2> = 5;  *Product Ranks
                SORT.COLS<3> = 3;  *Line
                SORT.COLS<4> = 17; *Seq
             CASE GROUP.RANKS AND ENTBK AND SORTBY[1,7] = 'Prod GL'
                SORT.COLS<1> = 1;  *Entity
                SORT.COLS<2> = 5;  *Product Ranks
                SORT.COLS<3> = 14; *GL Code
                SORT.COLS<4> = 3;  *Line
                SORT.COLS<5> = 17; *Seq
             CASE (GROUP.RANKS OR ENTBK) AND SORTBY[1,7] # 'Prod GL'
                IF ENTBK THEN
                   SORT.COLS<1> = 1;  *Entity
                   SORT.COLS<2> = 3;  *Line
                END ELSE
                   SORT.COLS<1> = 3;  *Product Ranks
                   SORT.COLS<2> = 2;  *Line
                END
                SORT.COLS<3>    = 17; *Seq
             CASE (GROUP.RANKS OR ENTBK) AND SORTBY[1,7] = 'Prod GL'
                IF ENTBK THEN
                   SORT.COLS<1> = 1;  *Entity
                   SORT.COLS<3> = 3;  *Line
                END ELSE
                   SORT.COLS<1> = 3;  *Product Ranks
                   SORT.COLS<3> = 2;  *Line
                END
                SORT.COLS<2>    = 14; *GL Code
                SORT.COLS<4>    = 17; *Seq
             CASE OTHERWISE
                IF SORTBY[1,7]  # 'Prod GL' THEN
                   SORT.COLS<1> = 2;  *Line
                   SORT.COLS<2> = 17; *Seq
                END ELSE
                   SORT.COLS<1> = 14; *GL Code
                   SORT.COLS<3> = 2;  *Line
                   SORT.COLS<4> = 17; *Seq
                END
             END CASE

             DRPT<47>  = ''
             COLUMN.CT = DCOUNT(SORT.COLS,AM)
             FOR CC = 1 TO COLUMN.CT
                DRPT<47,CC> = SORT.COLS<CC>:SVM:"A"
             NEXT CC

*** Setup the columns that will be "bucketed" for selection
             DRPT<49>    = ""
             DRPT<49,1>  = YES
             DRPT<49,2>  = YES
             DRPT<49,14> = YES
             IF GROUP.RANKS THEN
                DRPT<49> = INSERT(DRPT<49>,1,4;YES)
             END
             IF ENTBK THEN
                DRPT<49> = INSERT(DRPT<49>,1,1;YES)
             END

          END ELSE

             *** We'll only be displaying the Stock OnHand, Tag OnHand,
             *** R.F.O OnHand, Disp OnHand and Extended Value columns for
             *** the Summary format...

*** Setup the column justifications and widths.
             DRPT<41,1> = "L#10"
             DRPT<41,2> = "R#13"
             DRPT<41,3> = "R#10"
             DRPT<41,4> = "R#10"
             DRPT<41,5> = "R#10"
             DRPT<41,6> = "R#13"

*** Setup which columns should show totals when we've done a break...
             DRPT<42>    = ""
             DRPT<42,3>  = YES
             DRPT<42,4>  = YES
             DRPT<42,5>  = YES
             DRPT<42,6>  = YES

*** Since this is summary there are not any break ons.
             DRPT<43> = ""

*** Setup the column headings for the report.
             DRPT<45> = ""
             IF SORTBY[1,7] # "Prod GL" THEN
                DRPT<45,1>  = SLCT
             END ELSE
                DRPT<45,1>  = "G/L Code"
             END
             DRPT<45,2> = "Stock Onhand"
             DRPT<45,3> = "Tag Onhand"
             DRPT<45,4> = "R/F/O Onhand"
             DRPT<45,5> = "Display Onhand"
             DRPT<45,6> = "Extended Value"

             *** Setup the sortby on the summary report.
             DRPT<47> = 1:SVM:"A"

             *** If they're putting the product ranks on the report
             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF GROUP.RANKS THEN
                DRPT<41> = INSERT(DRPT<41>,1,2;"L#11")
                DRPT<42> = INSERT(DRPT<42>,1,2;0)
                DRPT<43> = INSERT(DRPT<43>,1,2;YES)
                DRPT<45> = INSERT(DRPT<45>,1,2;"Product Ranks")
             END

             *** Insert our Line Description column...
             IF SORTBY[1,7] # 'Prod GL' THEN
                DRPT<41> = INSERT(DRPT<41>,1,2;"L#25")
                DRPT<42> = INSERT(DRPT<42>,1,2;0)
                DRPT<43> = INSERT(DRPT<43>,1,2;0)
                DRPT<45> = INSERT(DRPT<45>,1,2;"Description")
             END
          END

          *** Set up the number column formats on the report...
          DRPT<44>    = ""
          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             DRPT<44,4>  = "MR2"
             DRPT<44,5>  = "MR2"
             DRPT<44,6>  = "MR2"
             DRPT<44,7>  = "MR2"
             DRPT<44,8>  = "MR2"
             DRPT<44,9>  = "MR3"
             DRPT<44,11> = "MR2"
             DRPT<44,15> = "MR0"
          END ELSE
             DRPT<44,2>  = "MR0"
             DRPT<44,3>  = "MR0"
             DRPT<44,4>  = "MR0"
             DRPT<44,5>  = "MR0"
             DRPT<44,6>  = "MR2"
             *** Move our data up to accomodate for the Line Description...
             IF SORTBY[1,7] # 'Prod GL' THEN
                DRPT<44>  = INSERT(DRPT<44>,1,2;" ")
             END
          END

          *** Insert our info into our report arrays, pushing all our
          *** other report values up at the same time...
          IF DISPLAY.RANKS THEN
             BEGIN CASE
             CASE DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B'
                DRPT<44> = INSERT(DRPT<44>,1,4;" ")
             *** Summary format and sorted by 'Line'...
             CASE SORTBY[LEN(SORTBY)-4,4] = 'Line'
                DRPT<44> = INSERT(DRPT<44>,1,3;" ")
             *** Summary format and sorted by 'GL Code'...
             CASE OTHERWISE
                DRPT<44> = INSERT(DRPT<44>,1,2;" ")
             END CASE
          END

          IF ENTBK THEN
             DRPT<44> = INSERT(DRPT<44>,1,1;" ")
          END

          *** Setup default column specific data based on the selections
          *** made by the user for branch, start and end date.
          DRPT<46>    = ""
          DRPT<46,1>  = BRCHS<1,1>
          DRPT<46,2>  = BRCHS<1,1>
          DRPT<46,3>  = AOD
          DRPT<46,4>  = AOD

          RETURN
*-------------------------------------------------------------------------*
UPD.SUMS: *** Update the summary buckets for the specified items
          IF NOT(HAS.ODBC) THEN RETURN

          *** Product Number...
          IF BCKTS<1> THEN
             *** Check whether we've already added values for this product.
             LOCATE BCKTS<1> IN BUCKETS<1> SETTING POS ELSE
                BUCKETS<1,POS> = BCKTS<1>
             END
             BUCKETS<2,POS> = ADDS(BUCKETS<2,POS>,LOWER(LOWER(SUMRY)))
          END

          *** Line...
          IF BCKTS<2> THEN
             *** Check whether we've already added values for this line...
             LOCATE BCKTS<2> IN BUCKETS<3> SETTING POS ELSE
                BUCKETS<3,POS> = BCKTS<2>
             END
             BUCKETS<4,POS> = ADDS(BUCKETS<4,POS>,LOWER(LOWER(SUMRY)))
          END

          *** G/L Code...
          IF BCKTS<3> THEN
             *** Check whether we've already added values for this G/L Code
             LOCATE BCKTS<3> IN BUCKETS<5> SETTING POS ELSE
                BUCKETS<5,POS> = BCKTS<3>
             END
             BUCKETS<6,POS> = ADDS(BUCKETS<6,POS>,LOWER(LOWER(SUMRY)))
          END

          *** Product Ranks...
          IF DETAIL[1,6] = 'Detail' THEN
             IF BCKTS<4> THEN
                *** Check whether we've already added values for these
                *** Product Ranks...
                LOCATE BCKTS<4> IN BUCKETS<7> SETTING POS ELSE
                   BUCKETS<7,POS> = BCKTS<4>
                END
                BUCKETS<8,POS> = ADDS(BUCKETS<8,POS>,LOWER(LOWER(SUMRY)))
             END
          END

          RETURN
*-------------------------------------------------------------------------*
CREATE.DICTS:   * Create the dictionaries for the ODBC report file.

          WRITE 'Creating Dictionaries...' ON PHSTFILE,PID$

          *** Setup a "dummy" report writer data set to auto-create the
          *** dictionaries available for the users from this report.
          WORK.ITEM = ""

          *** Setup that this report was run off of the product file.
          WORK.ITEM<2>    = "PRODUCT"

          IF DETAIL # 'Summary' AND DETAIL[1,12] # 'Summary by B' THEN
             *** Set up the known dictionaries for the respective
             *** columns on the report.
             LINE.COL = 2
             PN.COL   = 14
             IF ENTBK THEN
                LINE.COL += 1
                PN.COL   += 1
             END
             IF DISPLAY.RANKS THEN
                LINE.COL += 1
             END

             WORK.ITEM<4,LINE.COL>  = "LINE"
             WORK.ITEM<4,PN.COL>    = "@ID"

             *** Setup the initial column names.
             WORK.ITEM<7,1>  = "Product Description"
             WORK.ITEM<7,2>  = SLCT
             WORK.ITEM<7,3>  = "Description"
             WORK.ITEM<7,4>  = "Stock Onhand"
             WORK.ITEM<7,5>  = "Tag Onhand"
             WORK.ITEM<7,6>  = "R/F/O Onhand"
             WORK.ITEM<7,7>  = "Display Onhand"
             WORK.ITEM<7,8>  = "Unit Value"
             WORK.ITEM<7,9>  = "UM"
             WORK.ITEM<7,10>  = "Extended Value"
             WORK.ITEM<7,11> = "Qty UM"
             WORK.ITEM<7,12> = "Br"
             WORK.ITEM<7,13> = "G/L Code"
             WORK.ITEM<7,14> = "PN"
             WORK.ITEM<7,15> = "Seq"

             *** Setup the widths on each column.
             WORK.ITEM<6,1>  = "35"
             WORK.ITEM<6,2>  = "10"
             WORK.ITEM<6,3>  = "15"
             WORK.ITEM<6,4>  = "13"
             WORK.ITEM<6,5>  = "10"
             WORK.ITEM<6,6>  = "10"
             WORK.ITEM<6,7>  = "10"
             WORK.ITEM<6,8>  = "11"
             WORK.ITEM<6,9>  = "3"
             WORK.ITEM<6,10> = "13"
             WORK.ITEM<6,11> = "5"
             WORK.ITEM<6,12> = "3"
             WORK.ITEM<6,13> = "10"
             WORK.ITEM<6,14> = "6"
             WORK.ITEM<6,15> = "6"

             *** Setup the number formats, only for numeric columns.
             WORK.ITEM<26,4>  = "MR2"
             WORK.ITEM<26,5>  = "MR2"
             WORK.ITEM<26,6>  = "MR2"
             WORK.ITEM<26,7>  = "MR2"
             WORK.ITEM<26,8>  = "MR3"
             WORK.ITEM<26,10> = "MR2"
             WORK.ITEM<26,15> = "MR0"

             *** Insert our info into our report arrays, pushing all our
             *** other report values up at the same time...
             IF DISPLAY.RANKS THEN
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,4;" ")
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,4;"11")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,4;"Product Ranks")
             END

             IF ENTBK THEN
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,1;" ")
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,1;"20")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,1;"Entity")
             END
          END ELSE
             *** Setup the known dictionaries for the respective columns on
             *** the report.
             IF SORTBY[1,7] # "Prod GL" THEN
                WORK.ITEM<4,1> = "LINE"
                WORK.ITEM<7,1> = SLCT
             END ELSE
                WORK.ITEM<7,1> = "G/L Code"
             END

             *** Setup the initial column names.
             WORK.ITEM<7,2> = "Stock Onhand"
             WORK.ITEM<7,3> = "Tag Onhand"
             WORK.ITEM<7,4> = "R/F/O Onhand"
             WORK.ITEM<7,5> = "Display Onhand"
             WORK.ITEM<7,6> = "Extended Value"

             *** Setup the widths on each column.
             WORK.ITEM<6,1>  = "10"
             WORK.ITEM<6,2>  = "13"
             WORK.ITEM<6,3>  = "10"
             WORK.ITEM<6,4>  = "10"
             WORK.ITEM<6,5>  = "10"
             WORK.ITEM<6,6>  = "13"

             *** Setup the number formats, only for numeric columns.
             WORK.ITEM<26,2> = "MR0"
             WORK.ITEM<26,3> = "MR0"
             WORK.ITEM<26,4> = "MR0"
             WORK.ITEM<26,5> = "MR0"
             WORK.ITEM<26,6> = "MR2"

             IF SORTBY[1,7] # 'Prod GL' THEN
                WORK.ITEM<6>  = INSERT(WORK.ITEM<6>,1,2;"25")
                WORK.ITEM<7>  = INSERT(WORK.ITEM<7>,1,2;"Description")
                WORK.ITEM<26> = INSERT(WORK.ITEM<26>,1,2;" ")
             END
          END

          *** Flag the dictionary creation to create all related file
          *** dictionaries for this report.
          WORK.ITEM<50,3> = YES
          ODBC.SET.DICTS FILE.ID, WORK.ITEM

          RETURN
*-------------------------------------------------------------------------*
WRITE.BCKTS:   * Write the summary information to the files.

          WRITE 'Creating Summaries...    ' ON PHSTFILE,PID$

          BCKTFILE  = PN.ODBCFILE
          BFILE.ID  = FILE.ID:'.PN'
          BCKT.DATA = RAISE(BUCKETS<1>)
          BCKT.VALS = RAISE(BUCKETS<2>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          BCKTFILE  = PL.ODBCFILE
          BFILE.ID  = FILE.ID:'.':FILE.LINE
          BCKT.DATA = RAISE(BUCKETS<3>)
          BCKT.VALS = RAISE(BUCKETS<4>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          BCKTFILE  = GL.ODBCFILE
          BFILE.ID  = FILE.ID:'.G/L_CODE'
          BCKT.DATA = RAISE(BUCKETS<5>)
          BCKT.VALS = RAISE(BUCKETS<6>)
          GOSUB WRT.DATA
          GOSUB BCKT.DICTS

          *** Since Product Ranks are Branch specific, we can only
          *** have subtotals if the report is in Detail format...
          IF DETAIL[1,6] = 'Detail' THEN
             *** Product Rank Totals...
             BCKTFILE  = RANK.ODBCFILE
             BFILE.ID  = FILE.ID:'.PROD.RANK'
             BCKT.DATA = RAISE(BUCKETS<7>)
             BCKT.VALS = RAISE(BUCKETS<8>)
             GOSUB WRT.DATA
             GOSUB BCKT.DICTS
          END

          RETURN
*-------------------------------------------------------------------------*
WRT.DATA: * Actually perform the bucket writes.
          BCKT.CT = DCOUNT(BCKT.DATA,AM)

          FOR BPOS = 1 TO BCKT.CT
             WRITE RAISE(BCKT.VALS<BPOS>) ON BCKTFILE,BCKT.DATA<BPOS>
          NEXT BPOS

          RETURN
*-------------------------------------------------------------------------*
BCKT.DICTS:   * Create the dictionaries for the ODBC summary files.
          WRITE 'Creating Dictionaries...' ON PHSTFILE,PID$

          *** Setup a "dummy" report writer data set to auto-create the
          *** dictionaries available for the users from this report.
          WORK.ITEM = ""

          *** Setup that this report was run off of the product file.
          WORK.ITEM<2>    = "PRODUCT"

          *** Setup the initial column names.
          WORK.ITEM<7,1>  = "Stock Onhand"
          WORK.ITEM<7,2>  = "Tag Onhand"
          WORK.ITEM<7,3>  = "R/F/O Onhand"
          WORK.ITEM<7,4>  = "Display Onhand"
          WORK.ITEM<7,5>  = "Unit Value"
          WORK.ITEM<7,6>  = "Extended Value"

          *** Setup the widths on each column.
          WORK.ITEM<6,1>  = "14"
          WORK.ITEM<6,2>  = "14"
          WORK.ITEM<6,3>  = "14"
          WORK.ITEM<6,4>  = "14"
          WORK.ITEM<6,5>  = "14"
          WORK.ITEM<6,6>  = "16"

          *** Setup the number formats, only for numeric columns.
          WORK.ITEM<26,1> = "MR2"
          WORK.ITEM<26,2> = "MR2"
          WORK.ITEM<26,3> = "MR2"
          WORK.ITEM<26,4> = "MR2"
          WORK.ITEM<26,5> = "MR3"
          WORK.ITEM<26,6> = "MR2"

          ODBC.SET.DICTS BFILE.ID, WORK.ITEM

          RETURN
*-------------------------------------------------------------------------*
GET.ANZR: *** Create an Annualizer (multiplier for calculating annualized
          *** data)...

          IF PRD(26)<1,1> > START.DATE THEN
             IF PRD(26)<1,1> > END.DATE THEN
                ANNUALIZER = 0
             END ELSE
                ANNUALIZER = 365 / (END.DATE - PRD(26)<1,1> + 1)
             END
          END ELSE
             ANNUALIZER = 365 / (END.DATE - START.DATE + 1)
          END

          RETURN
*-------------------------------------------------------------------------*
GET.OC:   *** Get the order cycle for the product
          IS.WHSE = NO

          WBRS = WHSE.LIST(BR,PCGID)
          WBRS = WBRS<1>
          LOCATE BR IN WBRS<1> BY 'AL' SETTING POS ELSE
             WBRS=INSERT(WBRS,1,POS;BR)
          END

          PBRS = BUY.FOR.BR(BR,PCGID)
          WHOF = WHSE.OF(BR,PCGID)
          BB   = PURCH.BRS(BR,PCGID)

          IF PBRS # BR AND PBRS # '' THEN
             IS.WHSE = YES
          END

          IF WBRS # BR AND WBRS # PBRS AND WBRS # '' THEN
             IF WHOF = BR THEN IS.WHSE = YES
          END

          IF WHOF = BR THEN IS.WHSE = YES

          IF IS.WHSE THEN
             OC    = GET.CYCLE(PRD(12),BB,PRD(18))
          END ELSE
             WBRS  = WHSE.BRS(BR,PCGID)
             WHSE  = WBRS<1,1>

             CID = 'AUTO.XFER.DFLT~':WHSE:'.':BR
             READV OC FROM CTRBFILE,CID,1 ELSE OC = ''
             OC = OC<1,1>
          END

          RETURN
*-------------------------------------------------------------------------*
!TSMITH~08/24/12~16:31
