tldm-universe/CMS/BP.CNV/CMSD.INV.MASTER
2024-09-10 15:25:06 -04:00

844 lines
27 KiB
Plaintext
Executable File

SUBROUTINE CMSD.INV.MASTER(IMA.DATA)
*----------------------------------------------------------------------
*PGM NAME: CMSD.INV.MASTER
*PURPOSE: PERFORM ALL INVENTORY UPDATES - RETURN PASS/FAIL RESULT
*AUTHOR: BARKSDALE
*CREATED: 08/12/94
*----------------------------------------------------------------------
*
* BEWARE!!! This program performs all updates which affect quantity
* available and physical quantity to inventory including transaction
* detail and summarized activity (historical record-keeping) for any
* inventory item. It is called from a significant number of programs
* system-wide as well as from itself (recursive). Please exercise
* EXTREME CAUTION when making additions or changes to code contained
* herein!
*
*--explanation of reject reason codes:
* 1 - quantity is zero or null
* 2 - invalid date
* 3 - cannot assemble non-assembled items
* 4 - transactions not valid against assembled items
* 5 - invalid warehouse
* 6 - transfers must be reductions to FROM warehouse (negative qty)
* 7 - transfers must have valid TO warehouse
* 8 - inventory item not on file
* 9 - insufficient quantity available
* 10 - item has backorders waiting for release
* 11 - cannot assemble sufficient quantity
*
*--include data
$INCLUDE GEN.COMMON
$INCLUDE IMA
$INCLUDE IVT
$INCLUDE INV
$INCLUDE IA
$INCLUDE IC
*
*--initialize tables
READ WHSE.CODES FROM GEN.KEYS,"WAREHOUSE.CODES" ELSE WHSE.CODES=""
READ TYPE.CODES FROM GEN.KEYS,"INVTRANS.TYPE" ELSE TYPE.CODES=""
*
*--initialize other variables
READU NEXT.JRNL FROM GEN.KEYS,"NEXT.INV.JOURNAL" THEN
NEXT.JRNL=NEXT.JRNL+1
END ELSE
NEXT.JRNL=1
END
WRITE NEXT.JRNL ON GEN.KEYS,"NEXT.INV.JOURNAL"
EQU TRUE TO 1
EQU FALSE TO 0
*
*----
* Check to see if IMA.DATA contains all the necessary data to
* perform update to inventory and verify validity of several
* of the fields.
*----
REJECT=FALSE
REJECT.REASON=""
CHANGED.FIELDS=""
*----
* Log error in case of ultimate failure to update in any capacity
* by the calling program. For example:
* - this program was unable to read the inventory item
* - the calling program is not defining IMA.DATA properly
*----
IF IMA.DATA<IMA$FUNCTION>="REPORT.ERROR" THEN GO 100000
READ IREC FROM INVENTORY,IMA.DATA<IMA$ITEM> THEN
IF IREC<INV$ORDER.CODE>="N" THEN
INXFLAG=TRUE
END ELSE
INXFLAG=FALSE
END
IF IREC<INV$ASSEMBLED>="Y" THEN
ASMFLAG=TRUE
END ELSE
ASMFLAG=FALSE
END
IF IMA.DATA<IMA$QUANTITY>="" OR IMA.DATA<IMA$QUANTITY>=0 THEN
IF IMA.DATA<IMA$FUNCTION>="PO.ADD" OR IMA.DATA<IMA$FUNCTION>="PO.DELETE" ELSE
*--zero or null would be valid only if change PO E.T.A.
REJECT=TRUE
REJECT.REASON=1
END
END
IF IMA.DATA<IMA$DATE>="" THEN
REJECT=TRUE
REJECT.REASON=2
END
*----
* check to see if valid function
*----
BEGIN CASE
CASE IMA.DATA<IMA$FUNCTION>="CHECK.AVAILABLE"
CASE IMA.DATA<IMA$FUNCTION>="COMMIT"
CASE IMA.DATA<IMA$FUNCTION>="UNCOMMIT"
CASE IMA.DATA<IMA$FUNCTION>="ADD.BACKORDER"
CASE IMA.DATA<IMA$FUNCTION>="DELETE.BACKORDER"
CASE IMA.DATA<IMA$FUNCTION>="RELEASE.BACKORDER"
CASE IMA.DATA<IMA$FUNCTION>="ASSEMBLE"
IF ASMFLAG ELSE
REJECT=TRUE;*--may only assemble assembled items
REJECT.REASON=3
END
CASE IMA.DATA<IMA$FUNCTION>="ASM.ADD"
CASE IMA.DATA<IMA$FUNCTION>="PO.ADD"
CASE IMA.DATA<IMA$FUNCTION>="PO.DELETE"
CASE IMA.DATA<IMA$FUNCTION>="PO.RECEIVE"
CASE IMA.DATA<IMA$FUNCTION>="COST.UPDATE"
CASE IMA.DATA<IMA$FUNCTION>="RETURN.GOOD"
CASE IMA.DATA<IMA$FUNCTION>="RETURN.DAMAGED"
CASE IMA.DATA<IMA$FUNCTION>="RETURN.DEFECTIVE"
CASE IMA.DATA<IMA$FUNCTION>="REPORT.ERROR"
CASE 1
LOCATE(IMA.DATA<IMA$FUNCTION>,TYPE.CODES,1;FND) ELSE REJECT=1
IF ASMFLAG THEN
IF IMA.DATA<IMA$PROCESS>="CMSD.INVTRANS" OR IMA.DATA<IMA$PROCESS>="CMSD.IVT.MULTI" THEN
REJECT=TRUE;*--transactions invalid for assembled item
REJECT.REASON=4
END
END
END CASE
IF IMA.DATA<IMA$WAREHOUSE>="" THEN
REJECT=TRUE
REJECT.REASON=5
END
LOCATE(IMA.DATA<IMA$WAREHOUSE>,WHSE.CODES,1;FND) ELSE
REJECT=TRUE
REJECT.REASON=5
END
IF IMA.DATA<IMA$FUNCTION>="TRF" THEN
IF IMA.DATA<IMA$QUANTITY> > 0 THEN
REJECT=TRUE
REJECT.REASON=6
END
IF IMA.DATA<IMA$TO.WHSE>="" THEN
REJECT=TRUE
REJECT.REASON=7
END
LOCATE(IMA.DATA<IMA$TO.WHSE>,WHSE.CODES,1;FND) ELSE
REJECT=TRUE
REJECT.REASON=7
END
END
END ELSE
REJECT=TRUE
REJECT.REASON=8
END
IF REJECT THEN GO 100000
*
*--initialize variables
MINUS=-1
ITEM=IMA.DATA<IMA$ITEM>
QTY=IMA.DATA<IMA$QUANTITY>
YEAR=OCONV(IMA.DATA<IMA$DATE>,"DY")
MONTH=OCONV(IMA.DATA<IMA$DATE>,"DM")'R%2'
ACTION=IMA.DATA<IMA$FUNCTION>
IF ACTION="ASM.ADD" THEN ACTION="ASM"
TYPE=ACTION
TDATE=IMA.DATA<IMA$DATE>
USER=IMA.DATA<IMA$USER>
TREF=IMA.DATA<IMA$REFERENCE>
FLOC=IMA.DATA<IMA$WAREHOUSE>
TLOC=IMA.DATA<IMA$TO.WHSE>
CMNT=IMA.DATA<IMA$COMMENT>
WHSE=IMA.DATA<IMA$WAREHOUSE>
ENTRY.DATE=DATE()
EFFPD=YEAR[3,2]:"-":MONTH
TREC=''; IREC=''; AREC=''
IKEY=ITEM
TKEY=EFFPD:'|':ITEM
AKEY=YEAR:"|":WHSE:"|":ITEM
TYPWHS=TYPE:"|":WHSE:"|":TDATE
*
*--------------------main program section-----------------------------
BEGIN CASE
CASE ACTION="CHECK.AVAILABLE"
*----
* This is a request to see if sufficient quantity is available to
* fulfill a request for an item. It returns a pass/fail result
* and is informational only - ABSOLUTELY NO UPDATE to inventory
* will occur as a result of this requested action. The purpose for
* using this program to simply check availability of an item is to
* insure that the verification of availability follows the same
* procedural steps as an actual update to inventory and thereby
* yields more consistent results.
*----
IF QTY < 0 ELSE
*--if negative quantity then result is always "PASS"
IF INXFLAG ELSE
*--if inexhaustible supply then result is always "PASS"
IF ASMFLAG THEN
GOSUB 7000
IF REJECT THEN REJECT.REASON=11
END ELSE
QTY=QTY*MINUS
GOSUB 1000
IF REJECT THEN REJECT.REASON=9
END
END
END
CASE ACTION="COMMIT"
*----
* This is a request to commit inventory. Quantity should always be
* a positive number. An attempt will be made to reduce
* INV$QTY.AVAILABLE for item. If quantity available is insufficient
* to commit inventory the following other actions will be attempted:
* - If INV$ORDER.CODE = "N" then item has an inexhaustible supply
* and sufficient quantity will be added to fulfill request.
* - If INV$ASSEMBLED = "Y" then item is assembled from components.
* An attempt will be made to assemble enough to cover requested
* quantity.
* If quantity remains insufficient to fulfill request or backorders
* already exist for this item then request will be rejected and
* the result "FAIL" will be returned to calling program.
* "PHYSICAL" quantity remains unaffected regardless of result.
*----
IF INXFLAG THEN GOSUB 6000 ;*--inexhaustible supply
IF ASMFLAG THEN GOSUB 7000 ;*--assembled item
QTY=QTY*MINUS
GOSUB 1000;*--deduct from available quantity
CASE ACTION="UNCOMMIT"
*----
* This is a request to release inventory which has previously been
* committed. Quantity should always be a positive number.
* INV$QTY.AVAILABLE will be increased. No attempt will be made to
* undo updates resulting from inexhaustible supply or assembled
* inventory processes.
* Result is always "PASS" to a request to uncommit.
* "PHYSICAL" quantity remains unaffected.
*----
GOSUB 1000;*--add back to available quantity
CASE ACTION="ADD.BACKORDER"
*----
* This is a request to backorder inventory. Quantity should always
* be a positive number. INV$QTY.BACKORDERED will be increased.
* Result is always "PASS" to a request to backorder.
* "PHYSICAL" quantity remains unaffected.
*----
GOSUB 2000;*--add to backorder quantity
CASE ACTION="DELETE.BACKORDER"
*----
* This is a request to reduce quantity on backorder. Quantity should
* always be a positive number. INV$QTY.BACKORDERED will be reduced.
* Result is always "PASS" to a request to delete a backorder.
* "PHYSICAL" quantity remains unaffected.
*----
QTY=QTY*MINUS
GOSUB 2000;*--reduce backorder quantity
CASE ACTION="RELEASE.BACKORDER"
*----
* This indicates backorder is being released to ship. Quantity should
* always be a positive number. Both INV$QTY.AVAILABLE and
* INV$QTY.BACKORDERED will be reduced.
* "PHYSICAL" quantity remains unaffected.
*----
IF INXFLAG THEN GOSUB 6000;*--inexhaustible supply
IF ASMFLAG THEN GOSUB 7000;*--assembled item
QTY=QTY*MINUS
GOSUB 1000;*--reduce quantity available
IF NOT(REJECT) THEN GOSUB 2000;*--reduce backorder quantity
CASE ACTION="ASSEMBLE"
*----
* This is a request to assemble a specified quantity of an item
* from its component parts. A "PASS" result indicates a reduction
* to "PHYSICAL" and "AVAILABLE" quantity for each of the component
* items and an offsetting increase to the assembled item itself.
* A "FAIL" result indicates at least one of the component items
* was on backorder or had insufficient supply to fulfill request.
*----
GOSUB 7000
CASE ACTION="PO.ADD"
*----
* This is a request to add to the ON ORDER quantity of an item.
* Sub-valued data relevant to PO within WHSE will be updated.
* All other quantity fields will be unaffected.
*----
GOSUB 8000
CASE ACTION="PO.DELETE"
*----
* This action will do exactly the same as above except the quantity
* will be subtracted from the ON ORDER number for this PO
*----
QTY=QTY*MINUS
GOSUB 8000
CASE ACTION="PO.RECEIVE"
*----
* This action is similar to PO.DELETE accept that ETA will not be
* updated.
*----
QTY=QTY*MINUS
GOSUB 9000
CASE ACTION="COST.UPDATE"
*----
* This action signifies an update to the INV.COST file which is
* used to calculate unit cost. No quantity fields on the inventory
* record are updated
*----
GOSUB 10000
CASE ACTION="RETURN.GOOD"
CASE ACTION="RETURN.DAMAGED"
CASE ACTION="RETURN.DEFECTIVE"
CASE ACTION="SLS"
*----
* This is a request to record the actual shipment of product to a
* customer. INV$QTY.REDUCTIONS will be increased, and appropriate
* entries will be recorded in INV.TRANS and INV.ACTIVITY files to
* reflect change in "PHYSICAL" quantity.
* INV$QTY.AVAILABLE is unaffected by this action.
*----
QTY=QTY*MINUS
GOSUB 3000;*--create transaction
GOSUB 4000;*--reduce physical quantity
GOSUB 5000;*--update activity (summary)
CASE ACTION="TRF"
*----
* This is a request to transfer quantity from one warehouse to
* another. It requires that the quantity to be transferred MUST
* be available in the FROM warehouse. No attempt is made to
* assemble sufficient quantity or to add quantity due to the
* inexhaustibility of the item being transferred.
* A quantity adjustment will be made to 2 (two) warehouses.
* Both "PHYSICAL" and "AVAILABLE" quantity will be updated.
*----
GOSUB 1000;*--reduce available quantity - FROM warehouse
IF NOT(REJECT) THEN GOSUB 3000;*--create transaction - FROM warehouse
IF NOT(REJECT) THEN GOSUB 4000;*--reduce physical - FROM warehouse
IF NOT(REJECT) THEN GOSUB 5000;*--update activity - FROM warehouse
WHSE=TLOC
AKEY=YEAR:"|":WHSE:"|":ITEM
QTY=QTY*MINUS
IF NOT(REJECT) THEN GOSUB 1000;*--increase available - TO warehouse
IF NOT(REJECT) THEN GOSUB 3000;*--create transaction - TO warehouse
IF NOT(REJECT) THEN GOSUB 4000;*--increase physical - TO warehouse
IF NOT(REJECT) THEN GOSUB 5000;*--update activity - TO warehouse
CASE 1
*----
* All other actions are valid inventory transactions which
* will affect both the physical and available quantities.
*----
IF QTY < 0 AND INXFLAG THEN GOSUB 6000 ;*--inexhaustible supply
GOSUB 1000;*--update available quantity
IF NOT(REJECT) THEN GOSUB 3000;*--create transaction
IF NOT(REJECT) THEN GOSUB 4000;*--update physical quantity
IF NOT(REJECT) THEN GOSUB 5000;*--update activity (summary)
END CASE
GO 100000 ;*--end of program
*---------------------------------------------------------------------
*
1000 *---update inventory quantity available
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;"AR") ELSE
IREC=INSERT(IREC,INV$WAREHOUSE,FND;WHSE)
IREC=INSERT(IREC,INV$QTY.INCREASES,FND;0)
IREC=INSERT(IREC,INV$QTY.REDUCTIONS,FND;0)
IREC=INSERT(IREC,INV$QTY.AVAILABLE,FND;0)
IREC=INSERT(IREC,INV$QTY.BACKORDERED,FND;0)
IREC=INSERT(IREC,INV$QTY.ON.ORDER,FND;"")
IREC=INSERT(IREC,INV$EST.TIME.ARRIVAL,FND;"")
IREC=INSERT(IREC,INV$PO.NUMBER,FND;"")
IREC=INSERT(IREC,INV$ACTIVITY.YEARS,FND;"")
IREC=INSERT(IREC,INV$LEAD.TIME,FND;"")
IREC=INSERT(IREC,INV$REORDER.POINT,FND;0)
IREC=INSERT(IREC,INV$SAFETY.STOCK,FND;0)
IREC=INSERT(IREC,INV$EST.MONTHLY.USAGE,FND;0)
IREC=INSERT(IREC,INV$PROJECTED.OS.DATE,FND;"")
IREC=INSERT(IREC,INV$PRIMARY.LOCATION,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION1,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION2,FND;"")
END
IF QTY < 0 THEN
LOCATE(ACTION,TYPE.CODES,1;IDX) ELSE
IF IREC<INV$QTY.AVAILABLE,FND> >= ABS(QTY) ELSE
REJECT=TRUE;*--insufficient quantity to fulfill request
REJECT.REASON=9
RETURN
END
QOB=IREC<INV$QTY.BACKORDERED,FND>
IF QOB > 0 AND ACTION#"RELEASE.BACKORDER" THEN
REJECT=TRUE;*--waiting backorders take priority over new orders
REJECT.REASON=10
RETURN
END
END
END
IREC<INV$QTY.AVAILABLE,FND>=IREC<INV$QTY.AVAILABLE,FND>+QTY
IF ACTION="CHECK.AVAILABLE" ELSE
*--do not WRITE if only checking availability
IF IREC<INV$QTY.AVAILABLE,FND> < 0 THEN
CRT @(0,22):CL:BEEP:"Warning! Quantity available for ":ITEM:" is negative - <return>":
*ANY=""; INPUT ANY
CHANGED.FIELDS=CHANGED.FIELDS:"-A"
END ELSE
CHANGED.FIELDS=CHANGED.FIELDS:"A"
END
WRITE IREC ON INVENTORY,ITEM
IF ACTION="COMMIT" OR ACTION="UNCOMMIT" THEN
CHANGED.FIELDS=CHANGED.FIELDS:"U"
END
END
END
RETURN
*
2000 *---update backorder quantity (INVENTORY)
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;"AR") THEN
IREC<INV$QTY.BACKORDERED,FND>=IREC<INV$QTY.BACKORDERED,FND>+QTY
END ELSE
IREC=INSERT(IREC,INV$WAREHOUSE,FND;WHSE)
IREC=INSERT(IREC,INV$QTY.INCREASES,FND;0)
IREC=INSERT(IREC,INV$QTY.REDUCTIONS,FND;0)
IREC=INSERT(IREC,INV$QTY.AVAILABLE,FND;0)
IREC=INSERT(IREC,INV$QTY.BACKORDERED,FND;0)
IREC=INSERT(IREC,INV$QTY.ON.ORDER,FND;"")
IREC=INSERT(IREC,INV$EST.TIME.ARRIVAL,FND;"")
IREC=INSERT(IREC,INV$PO.NUMBER,FND;"")
IREC=INSERT(IREC,INV$ACTIVITY.YEARS,FND;"")
IREC=INSERT(IREC,INV$LEAD.TIME,FND;"")
IREC=INSERT(IREC,INV$REORDER.POINT,FND;0)
IREC=INSERT(IREC,INV$SAFETY.STOCK,FND;0)
IREC=INSERT(IREC,INV$EST.MONTHLY.USAGE,FND;0)
IREC=INSERT(IREC,INV$PROJECTED.OS.DATE,FND;"")
IREC=INSERT(IREC,INV$PRIMARY.LOCATION,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION1,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION2,FND;"")
IREC<INV$QTY.BACKORDERED,FND>=QTY
END
IF IREC<INV$QTY.BACKORDERED,FND> < 0 THEN
CRT @(0,22):CL:BEEP:"Warning! Quantity backordered for ":ITEM:" is negative - <return>":
*ANY=""; INPUT ANY
CHANGED.FIELDS=CHANGED.FIELDS:"-B"
END ELSE
CHANGED.FIELDS=CHANGED.FIELDS:"B"
END
WRITE IREC ON INVENTORY,ITEM
END
RETURN
*
3000 *---update inventory transactions (INV.TRANS)
READU TREC FROM INV.TRANS,TKEY THEN
BEGIN CASE
CASE TYPE='SLS'
LOCATE(TYPWHS,TREC,IVT$SORT.KEY;WH) THEN
TREC<IVT$QUANTITY,WH>=TREC<IVT$QUANTITY,WH>+QTY
TREC<IVT$TIME,WH>=TIME()
END ELSE
GOSUB 3100
END
CASE TYPE='RTS'
LOCATE(TYPWHS,TREC,IVT$SORT.KEY;WH) THEN
IF TREC<IVT$DATE,WH>=TDATE THEN
TREC<IVT$QUANTITY,WH>=TREC<IVT$QUANTITY,WH>+QTY
TREC<IVT$TIME,WH>=TIME()
END ELSE
GOSUB 3100
END
END ELSE
GOSUB 3100
END
CASE TYPE='INX'
LOCATE(TYPWHS,TREC,IVT$SORT.KEY;WH) THEN
TREC<IVT$QUANTITY,WH>=TREC<IVT$QUANTITY,WH>+QTY
TREC<IVT$TIME,WH>=TIME()
END ELSE
GOSUB 3100
END
CASE TYPE='ASM'
FOUND=FALSE
LOCATE(TYPWHS,TREC,IVT$SORT.KEY;WH) THEN
LOOP
UNTIL TREC<IVT$SORT.KEY,WH>#TYPWHS OR FOUND DO
IF TREC<IVT$COMMENT,WH>=CMNT THEN
TREC<IVT$QUANTITY,WH>=TREC<IVT$QUANTITY,WH>+QTY
TREC<IVT$TIME,WH>=TIME()
FOUND=TRUE
END
WH=WH+1
REPEAT
IF NOT(FOUND) THEN GOSUB 3100
END ELSE
GOSUB 3100
END
CASE 1
GOSUB 3100
END CASE
END ELSE
TREC<IVT$TYPE>=TYPE
TREC<IVT$QUANTITY>=QTY
TREC<IVT$DATE>=TDATE
TREC<IVT$TIME>=TIME()
TREC<IVT$OPERATOR>=USER
TREC<IVT$REFERENCE.NUMBER>=TREF
TREC<IVT$FROM.LOCATION>=FLOC
TREC<IVT$TO.LOCATION>=TLOC
TREC<IVT$COMMENT>=CMNT
TREC<IVT$ENTRY.DATE>=ENTRY.DATE
TREC<IVT$SORT.KEY>=TYPWHS
END
WRITE TREC ON INV.TRANS,TKEY
RETURN
*
3100 *----insert new transaction----
LOCATE(TDATE,TREC,IVT$DATE;WH;'DR') THEN NULL
TREC=INSERT(TREC,IVT$TYPE,WH;TYPE)
TREC=INSERT(TREC,IVT$QUANTITY,WH;QTY)
TREC=INSERT(TREC,IVT$DATE,WH;TDATE)
TREC=INSERT(TREC,IVT$TIME,WH;TIME())
TREC=INSERT(TREC,IVT$OPERATOR,WH;USER)
TREC=INSERT(TREC,IVT$REFERENCE.NUMBER,WH;TREF)
TREC=INSERT(TREC,IVT$FROM.LOCATION,WH;FLOC)
TREC=INSERT(TREC,IVT$TO.LOCATION,WH;TLOC)
TREC=INSERT(TREC,IVT$COMMENT,WH;CMNT)
TREC=INSERT(TREC,IVT$ENTRY.DATE,WH;ENTRY.DATE)
TREC=INSERT(TREC,IVT$SORT.KEY,WH;TYPWHS)
RETURN
*
4000 *---update shelf quantity (INVENTORY)
IF QTY > 0 THEN
IATTR=INV$QTY.INCREASES
END ELSE
IATTR=INV$QTY.REDUCTIONS
END
READU IREC FROM INVENTORY,IKEY THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;'AR') THEN
IREC<IATTR,FND>=IREC<IATTR,FND>+ABS(QTY)
LOCATE(YEAR,IREC,INV$ACTIVITY.YEARS,FND;WH;'DR') ELSE
IREC=INSERT(IREC,INV$ACTIVITY.YEARS,FND,WH;YEAR)
END
END ELSE
IREC=INSERT(IREC,INV$WAREHOUSE,FND;WHSE)
IREC=INSERT(IREC,INV$QTY.INCREASES,FND;0)
IREC=INSERT(IREC,INV$QTY.REDUCTIONS,FND;0)
IREC=INSERT(IREC,INV$QTY.AVAILABLE,FND;0)
IREC=INSERT(IREC,INV$QTY.BACKORDERED,FND;0)
IREC=INSERT(IREC,INV$QTY.ON.ORDER,FND;"")
IREC=INSERT(IREC,INV$EST.TIME.ARRIVAL,FND;"")
IREC=INSERT(IREC,INV$PO.NUMBER,FND;"")
IREC=INSERT(IREC,INV$ACTIVITY.YEARS,FND;"")
IREC=INSERT(IREC,INV$LEAD.TIME,FND;"")
IREC=INSERT(IREC,INV$REORDER.POINT,FND;0)
IREC=INSERT(IREC,INV$SAFETY.STOCK,FND;0)
IREC=INSERT(IREC,INV$EST.MONTHLY.USAGE,FND;0)
IREC=INSERT(IREC,INV$PROJECTED.OS.DATE,FND;"")
IREC=INSERT(IREC,INV$PRIMARY.LOCATION,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION1,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION2,FND;"")
IREC<IATTR,FND>=ABS(QTY)
END
PHYSICAL=IREC<INV$QTY.INCREASES,FND>-IREC<INV$QTY.REDUCTIONS,FND>
IF PHYSICAL < 0 THEN
CRT @(0,22):CL:BEEP:"Warning! Physical quantity for ":ITEM:" is negative - <return>":
*ANY=""; INPUT ANY
CHANGED.FIELDS=CHANGED.FIELDS:"-P"
END ELSE
CHANGED.FIELDS=CHANGED.FIELDS:"P"
END
WRITE IREC ON INVENTORY,IKEY
END
RETURN
*
5000 *---update INV.ACTIVITY
HOLD.QTY=QTY ;*--save original value because we might change its sign
BEGIN CASE
CASE TYPE='SLS'
QTY=QTY*MINUS
ATTR=IA$SALES
CASE TYPE='RTS'
ATTR=IA$RETURNS
CASE QTY > 0
ATTR=IA$INCREASES
CASE QTY < 0
QTY=QTY*MINUS
ATTR=IA$REDUCTIONS
END CASE
READU AREC FROM INV.ACTIVITY,AKEY THEN
LOCATE(EFFPD,AREC,IA$PERIOD;WH;'DL') THEN
AREC<ATTR,WH>=AREC<ATTR,WH>+QTY
END ELSE
AREC=INSERT(AREC,IA$PERIOD,WH;EFFPD)
AREC=INSERT(AREC,IA$SALES,WH;0)
AREC=INSERT(AREC,IA$RETURNS,WH;0)
AREC=INSERT(AREC,IA$INCREASES,WH;0)
AREC=INSERT(AREC,IA$REDUCTIONS,WH;0)
AREC<ATTR,WH>=AREC<ATTR,WH>+QTY
END
END ELSE
GOSUB 5100 ;*--determine beginning qty
AREC<IA$PERIOD>=EFFPD
AREC<IA$BEGIN.QTY>=BEGQTY
AREC<IA$SALES>=0
AREC<IA$RETURNS>=0
AREC<IA$INCREASES>=0
AREC<IA$REDUCTIONS>=0
AREC<ATTR>=AREC<ATTR>+QTY
END
WRITE AREC ON INV.ACTIVITY,AKEY
QTY=HOLD.QTY ;*--restore qty variable to original value
RETURN
*
5100 *---determine warehouse beginning quantity for new year
BEGQTY=0
DONE=FALSE
CHECK.YEAR=YEAR
LOOP
UNTIL DONE DO
KEY=CHECK.YEAR:"|":WHSE:"|":ITEM
READ REC FROM INV.ACTIVITY,KEY THEN
BEGQTY=REC<IA$BEGIN.QTY>-SUM(REC<IA$SALES>)+SUM(REC<IA$RETURNS>)+SUM(REC<IA$INCREASES>)-SUM(REC<IA$REDUCTIONS>)
DONE=TRUE
END ELSE
IF CHECK.YEAR > 1970 THEN
CHECK.YEAR=CHECK.YEAR-1
END ELSE
BEGQTY=0
DONE=TRUE
END
END
REPEAT
RETURN
*
6000 *--inexhaustible supply update
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;"AR") THEN
QOH=IREC<INV$QTY.AVAILABLE,FND>
END ELSE
QOH=0
END
IF QTY > 0 THEN
QOH=QOH-QTY
END ELSE
QOH=QOH+QTY
END
IF QOH < 0 THEN
QTY.NEEDED=ABS(QOH)
PASS.DATA=IMA.DATA
PASS.DATA<IMA$FUNCTION>="INX"
PASS.DATA<IMA$QUANTITY>=QTY.NEEDED
PASS.DATA<IMA$COMMENT>="Automatic Supply Increase"
CALL CMSD.INV.MASTER(PASS.DATA) ;*--recursive call
END
END
RETURN
*
7000 *--assembled item update
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;"AR") THEN
QOH=IREC<INV$QTY.AVAILABLE,FND>
END ELSE
QOH=0
END
*----
* The following lines of code are designed to insure the correct
* quantity is assembled. If action is ASSEMBLE then we are talking
* about the manual process (from CMSD.ASSEMBLE.ITEMS) otherwise
* we are simply doing an ASM transaction for the difference
* between what is already available and what has been requested.
*----
IF ACTION="ASSEMBLE" THEN
QOH=0
END
QOH=QOH-QTY
IF QOH < 0 THEN
QTY.NEEDED=ABS(QOH)
MAX.AVAIL=999999999999
CCT=DCOUNT(IREC<INV$COMPONENT.ITEMS>,VM)
FOR C=1 TO CCT UNTIL MAX.AVAIL=0
CKEY=IREC<INV$COMPONENT.ITEMS,C>
CQTY=IREC<INV$COMPONENT.QTY,C>
READU CREC FROM INVENTORY,CKEY THEN
IF CREC<INV$ASSEMBLED>="Y" THEN MAX.AVAIL=0
IF CREC<INV$ORDER.CODE>#"N" THEN
LOCATE(WHSE,CREC,INV$WAREHOUSE;FND;"AR") THEN
MAX.QTY=INT(CREC<INV$QTY.AVAILABLE,FND>/CQTY)
IF MAX.QTY < MAX.AVAIL THEN MAX.AVAIL=MAX.QTY
END ELSE
MAX.AVAIL=0
END
END
END ELSE
MAX.AVAIL=0
END
NEXT C
IF ACTION="CHECK.AVAILABLE" THEN
*--no updates will be performed
FOR C=1 TO CCT
*--release component items which have been locked
RELEASE INVENTORY,IREC<INV$COMPONENT.ITEMS,C>
NEXT C
IF MAX.AVAIL < QTY.NEEDED THEN REJECT=1
RETURN
END
IF MAX.AVAIL >= QTY.NEEDED THEN
PASS.DATA=IMA.DATA
PASS.DATA<IMA$FUNCTION>="ASM"
PASS.DATA<IMA$COMMENT>="Assembled into ":ITEM
FOR C=1 TO CCT
*----
* Loop through component items and call CMSD.INV.MASTER
* to perform updates to inventory. As items remain locked
* (preventing any updates) as a result of above evaluation,
* and sufficient quantity has already been verified,
* PASS.DATA will always return a "PASS" result back to this
* program so IMA$RESULT does not need to be checked.
* SPECIAL NOTE!!! This is a recursive call of a program
* from within itself. Exercise EXTREME CAUTION when changing!
*----
CITEM=IREC<INV$COMPONENT.ITEMS,C>
CQTY=IREC<INV$COMPONENT.QTY,C>
PASS.DATA<IMA$ITEM>=CITEM
PASS.DATA<IMA$QUANTITY>=QTY.NEEDED*CQTY*MINUS
CALL CMSD.INV.MASTER(PASS.DATA) ;*--recursive call
NEXT C
PASS.DATA=IMA.DATA
PASS.DATA<IMA$FUNCTION>="ASM.ADD"
PASS.DATA<IMA$QUANTITY>=QTY.NEEDED
PASS.DATA<IMA$COMMENT>="Assembled from Components"
CALL CMSD.INV.MASTER(PASS.DATA) ;*--recursive call
END ELSE
FOR C=1 TO CCT
*--release component items which have been locked
RELEASE INVENTORY,IREC<INV$COMPONENT.ITEMS,C>
NEXT C
REJECT=TRUE;*--cannot assemble enough to fulfill request
REJECT.REASON=11
RETURN
END
END ELSE
*----
* there is no need to assemble any items - sufficient
* quantity is already available
*----
END
END
RETURN
*
8000 *--update PO related fields (PO.ADD & PO.DELETE)
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;'AR') THEN
LOCATE(TREF,IREC,INV$PO.NUMBER,FND;WH;'AR') THEN
IREC<INV$QTY.ON.ORDER,FND,WH>=IREC<INV$QTY.ON.ORDER,FND,WH>+QTY
IREC<INV$EST.TIME.ARRIVAL,FND,WH>=TDATE
*--verify ON ORDER is not zero else delete
IF IREC<INV$QTY.ON.ORDER,FND,WH>=0 THEN
IREC=DELETE(IREC,INV$PO.NUMBER,FND,WH)
IREC=DELETE(IREC,INV$QTY.ON.ORDER,FND,WH)
IREC=DELETE(IREC,INV$EST.TIME.ARRIVAL,FND,WH)
END
END ELSE
IREC=INSERT(IREC,INV$PO.NUMBER,FND,WH;TREF)
IREC=INSERT(IREC,INV$QTY.ON.ORDER,FND,WH;QTY)
IREC=INSERT(IREC,INV$EST.TIME.ARRIVAL,FND,WH;TDATE)
END
END ELSE
IREC=INSERT(IREC,INV$WAREHOUSE,FND;WHSE)
IREC=INSERT(IREC,INV$QTY.INCREASES,FND;0)
IREC=INSERT(IREC,INV$QTY.REDUCTIONS,FND;0)
IREC=INSERT(IREC,INV$QTY.AVAILABLE,FND;0)
IREC=INSERT(IREC,INV$QTY.BACKORDERED,FND;0)
IREC=INSERT(IREC,INV$QTY.ON.ORDER,FND;QTY)
IREC=INSERT(IREC,INV$EST.TIME.ARRIVAL,FND;TDATE)
IREC=INSERT(IREC,INV$PO.NUMBER,FND;TREF)
IREC=INSERT(IREC,INV$ACTIVITY.YEARS,FND;"")
IREC=INSERT(IREC,INV$LEAD.TIME,FND;"")
IREC=INSERT(IREC,INV$REORDER.POINT,FND;0)
IREC=INSERT(IREC,INV$SAFETY.STOCK,FND;0)
IREC=INSERT(IREC,INV$EST.MONTHLY.USAGE,FND;0)
IREC=INSERT(IREC,INV$PROJECTED.OS.DATE,FND;"")
IREC=INSERT(IREC,INV$PRIMARY.LOCATION,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION1,FND;"")
IREC=INSERT(IREC,INV$ALTERNATE.LOCATION2,FND;"")
END
WRITE IREC ON INVENTORY,ITEM
CHANGED.FIELDS=CHANGED.FIELDS:"O"
END
RETURN
*
9000 *--update PO related fields (PO.RECEIVE)
READU IREC FROM INVENTORY,ITEM THEN
LOCATE(WHSE,IREC,INV$WAREHOUSE;FND;'AR') THEN
LOCATE(TREF,IREC,INV$PO.NUMBER,FND;WH;'AR') THEN
IREC<INV$QTY.ON.ORDER,FND,WH>=IREC<INV$QTY.ON.ORDER,FND,WH>+QTY
* Verify ON ORDER is greater than zero else delete
* NOTE: PO.RECEIVE of more than ordered quantity (overruns)
* will leave nothing on order for specified PO.
*----
IF IREC<INV$QTY.ON.ORDER,FND,WH> LE 0 THEN
IREC=DELETE(IREC,INV$PO.NUMBER,FND,WH)
IREC=DELETE(IREC,INV$QTY.ON.ORDER,FND,WH)
IREC=DELETE(IREC,INV$EST.TIME.ARRIVAL,FND,WH)
END
WRITE IREC ON INVENTORY,ITEM
CHANGED.FIELDS=CHANGED.FIELDS:"O"
END ELSE
*--nothing on order so nothing to do
REJECT=TRUE
END
END ELSE
*--nothing on order so nothing to do
REJECT=TRUE
END
END
RETURN
*
10000 *--update INV.COST record
COST=FIELD(CMNT,"|",2)
SOURCE=FIELD(CMNT,"|",1)
CKEY=WHSE:"|":ITEM
CREC=""
READU CREC FROM INV.COST,CKEY THEN
IF CREC<IC$UNIT.COST,1>=COST THEN
CREC<IC$DATE.RECEIVED,1>=TDATE
CREC<IC$QTY.RECEIVED,1>=CREC<IC$QTY.RECEIVED,1>+QTY
CREC<IC$SOURCE,1>=SOURCE
CREC<IC$REFERENCE,1>=TREF
END ELSE
CREC=INSERT(CREC,IC$DATE.RECEIVED,1;TDATE)
CREC=INSERT(CREC,IC$QTY.RECEIVED,1;QTY)
CREC=INSERT(CREC,IC$UNIT.COST,1;COST)
CREC=INSERT(CREC,IC$SOURCE,1;SOURCE)
CREC=INSERT(CREC,IC$REFERENCE,1;TREF)
END
END ELSE
CREC<IC$DATE.RECEIVED>=TDATE
CREC<IC$QTY.RECEIVED>=QTY
CREC<IC$UNIT.COST>=COST
CREC<IC$SOURCE>=SOURCE
CREC<IC$REFERENCE>=TREF
END
WRITE CREC ON INV.COST,CKEY
RETURN
*
100000 *--return to calling program
IF REJECT THEN
IMA.DATA<IMA$RESULT>="FAIL"
END ELSE
IMA.DATA<IMA$RESULT>="PASS"
END
IMA.DATA<IMA$PROCESS.DATE>=DATE()
IMA.DATA<IMA$PROCESS.TIME>=TIME()
IMA.DATA<IMA$FAIL.REASON>=REJECT.REASON
IMA.DATA<IMA$CHANGES>=CHANGED.FIELDS
WRITE IMA.DATA ON INV.JOURNAL,NEXT.JRNL
RELEASE INVENTORY,IMA.DATA<IMA$ITEM>
RETURN