1 ! OUTBILL.BAS [1,100] - PROGRAM TO PRODUCE BILLS FOR BIOMEDICAL COMPUTATION
	! BILLS PRODUCED ARE FOR SPECIFIED PROJECTS:
	! CHARGES ARE GENERATED AND LISTED ON DAILY BASIS FOR A SPECIFIED MONTH
	! CHARGES ARE FOR PROGRAMMER, CONNECT, AND STORAGE TIME
	!
	! RUNS UNDER BA5
50	 !
	 ! FUNCTION DEFINITIONS
	 ! 
50 DEF FNRM(A,B)=A-INT(A/B)*B
60 DEF FNSU(PJ,DA,PG)=(PJ*32+DA)*11+PG
80 DEF FNV(S0$)=VAL(SBS$(S0$,1,3)+SBS$(S0$,5,2)+SBS$(S0$,8,2)+SBS$(S0$,11,2)) : ! LOG TIME W/O :
90 DEF FNF$(X)=FRMT$(X,10,2) : ! FORMAT # TO 2 DIGITS TO RT OF ., SIG. DIGS. ONLY, & RJS IT IN FIELD OF 10
100	 !
	 ! VARIABLE DEFINITIONS, DIMENSIONS
	 ! 
110 DIM R$[60]V ! RECORD STRING FOR LOGFILES, CONNECT FILES, STORAGE FILES
111 DIM L$[60]V  ! INPUT VARIABLE FOR USER DESCRIPTION FILES
115 DIM FF$[50]V ! FOR MASTER FILE NAME
120 DIM AN$[60]V ! ACCOUNT NAME (DEPT)
125 DIM NF$[9]V  ! USER DESCRIPTION FILE NAME
130 DIM UN$[9]V, UN& ! USER NAME(S), # OF USER NAMES FOR ACCOUNT
135 DIM ST$[9]V ! NAME OF STORAGE FILE FOR EACH USER
140 DIM DP$[4]V ! MASK FOR MATCHING ACCOUNT TO LOG FILE CODING
150 DIM M$[3](12),M&(12) ! MONTH NAMES, # OF DAYS IN MONTH
170 DIM H(31,4) ! HOURS PER DAY FOR 6 USERS, 3 CATEGORIES EACH (PGMR, CONNTECT, STORAGE, ADJ PGMR)
180 DIM C&(3) ! CHARGES FOR EACH CATEGORY
185 DIM E(4) ! CONVERSION FACTORS FOR TIME INTO HOURS
190 DIM PF(10)	! PROGRAMMER FACTORS
195 DIM S$[16]V ! DUMMY STRING
199 DIM #16,PR%(21742)
200 P1=2 : P2=6 ! LOWER, UPPER BOUNDS ON PROGRAMMER SUBSCRIPTS
210 OVERLAY "PROJNUM/RT"
300	 !
	 ! USERS ACCOUNTS INFO
	 !
	 ! 	EACH DATA STMT DESCRIBES THE USER AS FOLLOWS
	 ! 		NAME OF DEPT (OR OF USER)
	 ! 		NAME OF BLOCK STORAGE FILE
	 ! 		MASK DESCRIBING ITS CODE IN PROGRAMMER LOG FILE
	 ! 		# OF ACCTS (USER NAMES) USER HAS
	 ! 		LIST OF ACCTS (USER NAMES) FOR USER
	 ! 
310	! PROGRAMMER SENIORITY FACTORS
	! PROGRAMMER NUMBERS AS FOLLOWS
	!	1 = UNUSED
	!	2 = BONNIE
	!	3 = DEANA
	!	4 = LARRY
	!	5 = BEVERLY
	!	6 = DAN
320 DATA 1,.7,1.5,.7,1
330 FOR I=P1 TO P2 : READ PF(I) : NEXT I
430	M$(1)="JAN" : M&(1)=31 :
	M$(2)="FEB" : M&(2)=29 :
	M$(3)="MAR" : M&(3)=31 :
	M$(4)="APR" : M&(4)=30 :
	M$(5)="MAY" : M&(5)=31 :
	M$(6)="JUN" : M&(6)=30 :
	M$(7)="JUL" : M&(7)=31 :
	M$(8)="AUG" : M&(8)=31 :
	M$(9)="SEP" : M&(9)=30 :
	M$(10)="OCT" : M&(10)=31 :
	M$(11)="NOV" : M&(11)=30 :
	M$(12)="DEC" : M&(12)=31	! NAMES OF MONTHS, DAYS IN MONTH
440 C&=3 :				 ! # OF CATEGORIES OF CHARGES :
	C&(1)=24.50 :			 ! 	$24.50/HR PROGRAMMER TIME :
	C&(2)=20 :			 ! 	$ 20/HR CONNECT TIME :
	C&(3)=5 :			 ! 	$ 5/100 BLOCK-MONTH STORAGE
450  ! E 				  MAY BE USED AS WORK VARIABLE :
	E(1)=1/3600 :		 ! CONVERT SECONDS TO HOURS :
	E(2)=1/60   :		 ! CONVERT MINUTES TO HOURS :
	E(3)=1	    :		 ! CONVERT HOURS TO HOURS :
	E(4)=24	    :		 ! CONVERT DAYS TO HOURS
460 BC=((C&(3)/100)*12)/365	! DAILY BLOCK CHARGE (1 BLOCK-DAY) :
	S$=FRMT$(BC,8,6)	! MAKE CHARGE $ X.XXXXXX :
	BC=VAL(S$)
600	 ! ASK USER FOR MONTH OF BILLING STATEMENT
610 INPUT "MONTH OF BILLING (MMYY) "; M
620 M&=INT(M/100) : IF M&<1 OR M&>12 THEN  PRINT "BAD MONTH" : GOTO 610
630 Y&=M-(M&*100) ! YEAR OF BILL
635 IF 4*INT(Y&/4)<>Y& THEN M&(2)=28	! CORRECT FOR NON LEAP YEAR
640 IF M&>6	 THEN FY&=Y&+1
		 ELSE FY&=Y&		! FISCAL YEAR IS JULY-JUNE
650 D1=DCEN(STR$(M&)+"/01/"+STR$(Y&))		! FIRST DAY OF MONTH
	D2=D1 + M&(M&)-1			! LAST  DAY OF MONTH
670 ! OPEN #16,M$(M&)+STR$(Y&)+".VPG/BL/RO/SH"
680 INPUT "FILE NAME FOR OUTPUT",FF$:
	OPEN #5,FF$+"/WR/LN:84"
700 INPUT "FILE NAME FOR USERS ",FF$:
	IF TRM$(FF$)="" THEN
		FF$="USERS.NAM/RO/SH/EN:900"
	ELSE FF$=FF$+"/RO/SH/EN:900"
705 OPEN #6,FF$ : PRINT
710 INPUT LINE #6,AN$ : PRINT AN$
720 NF$=PIECE$(AN$,",",1):
	FF$=PIECE$(AN$,",",2):
		AN$=PIECE$(AN$,",",3,60)
740 FOR J=0 TO 31 : FOR K=0 TO 4 : H(J,K)=0 : NEXT K,J
750 OPEN #7,"PRF.PRF/RO/SH/EN:850"
753	INPUT LINE #7,L$
755	IF SBS$(L$,3)=NF$+".PRF" THEN 770
757	GOTO 753
760 INPUT LINE #7,L$
770 IF LEFT(L$,1)=";" THEN 760
	ELSE IF LEFT(L$,1)="*" THEN 800
780 DP$=L$
790 GOSUB 1000 ! GET PROGRAMMER TIME
	GOTO 760
800 INPUT LINE #7,L$
810 IF LEFT(L$,1)=";" THEN 800
	ELSE IF LEFT(L$,1)="*" THEN 830
820 UN$=L$:
	GOSUB 1200 ! GET CONNECT TIME
	GOTO 800
830 INPUT LINE #7,L$:
	IF LEFT(L$,1)=";" THEN 830
	ELSE IF LEFT(L$,1)="*" THEN 850
840 ST$=L$ : K&=M&(M&)+1:
	GOSUB 1300 ! GET BLOCK STORAGE
	GOTO 830
	! IF MISSING STORAGE INFO, USE PREVIOUS DAY'S INFO
850 FOR L=2 TO M&(M&)
860 IF H(L,3)=0 THEN H(L,3)=H(L-1,3)
870 NEXT L
880 CLOSE 7 : GOSUB 2000
890 GOTO 710
900 CLOSE
910 PRINT
920 EXIT
1000	! SUBROUTINE TO ACCUMULATE PROGRAMMER TIME
	! FOR ONE USER FROM ACCUMULATED VIRTUAL ARRAY
	! ON ENTRY:
	!	MMMYY.VPG OPEN ON LUN 16 WHERE YY IS FISCAL YEAR
	!		AND MMM IS 3 CHAR MONTH NAME
	!	PR% IS VIRTUAL ARRAY OF PROGRAMMER TIME AS DEFINED
	!		IN PROGMON.BAS
	!	DP$ HAS MASK FOR THIS USER
	!	M& IS MONTH BEING BILLED FOR
	RETURN
1010 GOSUB 8000		! GET PROJECT NUMBER DP% FROM DP$
1020 FOR NI=1 TO M&(M&)
1030 FOR K=P1 TO P2
1040	H(NI,1)=H(NI,1)+PR%(FNSU(DP%,NI,K))/100:
	H(NI,4)=H(NI,4)+PR%(FNSU(DP%,NI,K))*PF(K)/100
1050 NEXT K,NI
1090 RETURN
1200	 !
	 ! CONNECT TIME
	 !
1215 L&=2		! THE 2ND OF EACH USER'S 3 CATEGORIES IS CONNECT TIME
1230		 OPEN #3, UN$+".LOG/LN:56/RN/RO/SH" ! OPEN CONNECT TIME FILE
1240		 FOR K=NRC(3) TO 6 STEP -1	! 1ST 6 RECS ARE HEADINGS
1250			 INPUT LINE #3@K, R$	! CONNECT TIME RECORD
1260			D=DCEN(SBS$(R$,20,8))	! DATE LOGGED OFF
1270			 IF D<D1 THEN 1290	! FILE ORDERED BY DATE; GOT TO DATE EARLIER THAN WANTED
1275		  	 IF D<=D2
			 THEN DR&=VAL(SBS$(R$,23,2))	! DAY OF MONTH
				O1=FNV(SBS$(R$,39,12)) : GOSUB 3200 :
				H(DR&,L&)=H(DR&,L&)+H	! ADD CONNECT TIME, CONVERTED TO HOURS
1280		 NEXT K
1290	 CLOSE 3
1299 RETURN
1300	 !
	 ! ALLOCATED STORAGE BLOCKS
	 ! 
1315	L&=3		! THE 3RD OF EACH USER'S 3 CATEGORIES IS STORAGE BLOCKS
1320	 OPEN #3, ST$+".STO/LN:52/RO/RN/SH"	! OPEN STORAGE BLOCKS FILE
1330	 FOR K=NRC(3) TO 1 STEP -1
1350		 INPUT LINE #3@K, R$		! STORAGE BLOCKS RECORD
1360		D=DCEN(SBS$(R$,1,9))	! DATE
		 IF D<D1
		 THEN 1399		! FILE ORDERED BY DATE; GOT TO DATE EARLIER THAN ONE WANTED
		 ELSE 	 IF D>D2
			 THEN 1390	! DIDN'T GET TO RIGHT DATE SECTION YET
1370		DR&=VAL(SBS$(R$,1,2))	! DAY OF MONTH
1374	IF DR&>=1 THEN H(DR&,L&)=H(DR&,L&)+VAL(SBS$(R$,27,8))	! # OF BLOCKS FOR THAT DAY (ONLY 1 REC OF BLKS PER DAY)
1390		 NEXT K
1399 CLOSE 3 : RETURN
2000	 !
	 ! PRINT BILL FOR EACH PROJECT
	 ! 
2205	 PRINT #5, CHR$(12)
2210	 PRINT #5, TAB(24); "MICHAEL REESE HOSPITAL AND MEDICAL CENTER" :
	 PRINT #5, TAB(24); "      DEPARTMENT OF MEDICAL PHYSICS      " :
	 PRINT #5 : PRINT #5
2220	 PRINT #5, "    BIOMEDICAL COMPUTATION DIVISION";TAB(74); DDAT$(0) :
	 PRINT #5 : PRINT #5
2230	 PRINT #5, "    BILL FOR ";M$(M&);", 19"; STR$(Y&);" -- ";AN$ :
	 PRINT #5, TAB(5);STRING$("-",22+LEN(AN$))
2240	 PRINT #5 : PRINT #5
2250	 PRINT #5, TAB(29); "HOURS";TAB(48);"HOURS"
2260	 PRINT #5, TAB(23); "PROGRAMMER TIME"; TAB(44); "CONNECT TIME"; TAB(64); "STORAGE BLOCKS"
2270	 PRINT #5
2300		 !
		 ! PRINT HOURS
		 ! 
2310	 FOR J=1 TO M&(M&)		! FOR EACH DAY OF THE MONTH
2315		 PRINT #5, TAB(5);M$(M&);J;
2320		 PRINT #5, TAB(20); :
		 IF H(J,4)<>0 THEN  PRINT #5, FRMT$(H(J,4),6,2);	! PROGRAMMING TIME ADJUSTED
		PRINT #5,TAB(30);"(";FRMT$(H(J,1),6,2);")";		!PROGRAMMING TIME UNADJUSTED
2330		 PRINT #5, TAB(44); :
		 IF H(J,2)<>0 THEN  PRINT #5, FNF$(H(J,2));		! CONNECT TIME
2340		 PRINT #5, TAB(64); :
		 IF H(J,3)<>0 THEN 	 PRINT #5, RJS$(STR$(H(J,3)),10)	! STORAGE BLOCKS
				 ELSE 	 PRINT #5
2360	H(0,1)=H(0,1)+H(J,1)	! TOTAL # OF PROGRAMMER HRS
	H(0,4)=H(0,4)+H(J,4)	! TOTAL # OF ADJUSTED PROGRAMMER HRS
2370	H(0,2)=H(0,2)+H(J,2)		:	 ! TOTAL # OF CONNECT HRS
2380	 IF H(J,3)>0
	 THEN 	H(0,3)=H(0,3) + (INT(H(J,3)/100)+1)*100 * BC :
	 ! KEEP RUNNING TOTAL OF DAILY BLOCK CHARGES :
	 ! BLOCKS ARE CHARGES IN UNITS OF 100, SO ROUND UP TO NEXT 100
2399	 NEXT J
2400		 !
		 ! FIGURE CHARGES FOR ENTIRE MONTH; PRINT THEM
		 ! 
2410	 PRINT #5
2420	 PRINT #5, TAB(5);"TOTAL HOURS"; TAB(20); :
		 IF H(0,1)<>0 THEN  PRINT #5, FRMT$(H(0,4),6,2);	! PROGRAMMER TIME ADJUSTED
		 PRINT #5,TAB(30);"(";FRMT$(H(0,1),6,2);")";		! PROGRAMMER TIME UNADJUSTED
2422	 PRINT #5, TAB(44); :
		 IF H(0,2)<>0 THEN  PRINT #5, FNF$(H(0,2)) ELSE  PRINT #5	! CONNECT TIME
2425	 PRINT #5
2430	 PRINT #5, TAB(23); "PROGRAMMER TIME"; TAB(44); "CONNECT TIME"; TAB(64); "STORAGE BLOCKS"
2440	 PRINT #5, TAB(24); "@ $";C&(1);"/ HR"; TAB(44);"@ $";C&(2);"/ HR"; TAB(59);"@ $";C&(3);"/ 100 BLOCK-MONTH"
2442	 PRINT #5, TAB(57); "= @ $";BC;"/ BLOCK-DAY"
2445	 PRINT #5
2448	H=0 ! GRAND TOTAL OF CHARGES
2450	 PRINT #5, TAB(5);"$ CHARGE"; TAB(24); :
	 IF H(0,4)<>0
	 THEN 	S$=FNF$(C&(1)*H(0,4)) :
		 PRINT #5, S$; :
		 H=VAL(S$)	! GRAND TOTAL		! PROGRAMMER CHARGES
2452	 PRINT #5, TAB(44); :
	 IF H(0,2)<>0
	 THEN 	S$=FNF$(C&(2)*H(0,2)) :
		 PRINT #5, S$; :
		 H=H+VAL(S$)	! GRAND TOTAL		: ! CONNECT CHARGES
2454	 PRINT #5, TAB(64); :
	 IF H(0,3)<>0
	 THEN 	S$=FNF$(H(0,3)) :
		 PRINT #5, S$ :
		H=H+VAL(S$)
	 ELSE 	 PRINT #5			! STORAGE CHARGES (RUNNING TOTAL KEPT BY STMT 2380
2460	 PRINT #5 : PRINT #5 :
	 PRINT #5, TAB(34); "-----" :
	 PRINT #5, TAB(34); "TOTAL $";FRMT$(H,7,2)
2470	 PRINT #5, TAB(34); "-----"
2500	!
	! INDICATE MINIMUM CHARGE, ETC.
	!
2510 PRINT #5 : PRINT #5
2520	PRINT #5,TAB(5);"THE MINIMUM MONTHLY CHARGE IS $";FF$;".00."; :
		PRINT #5,"  IF THE ABOVE TOTAL IS LESS THAN THE"
2530	PRINT #5,TAB(5);"MINIMUM THEN PLEASE REMIT THE MINIMUM.  IF"; :
		PRINT #5," THE TOTAL IS MORE THAN THE MINIMUM"
2540	PRINT #5,TAB(5);"THEN PLEASE REMIT THE TOTAL."
2599 RETURN
3200	 !
	 ! CONVERT TIME TO # OF HOURS
	 ! 
3201	 ! WORKS ON O1, WHICH IS SET BY CALLING STMT
	 ! RETURNS H
3202	 !
	 ! USES O1, O2, L
3220 H=0 :
	 IF O1=0 THEN 3299
3240 FOR L=1 TO 4
3250	O2=INT(O1/100)
3260	H=H+(O1-O2*100)*E(L)
3270	O1=O2
3280 NEXT L
3299 RETURN 
