Updated 1-21-17
POS file formats


This page is for folks that wish to write 3rd party software to enhance my Cash Register software.

Please note that many things in the data files are restricted, for example passwords, sales figures, transaction numbers, taxes paid, PIN numbers, etc. If others know how to access those things then they would be able to write software to change those things. For example software could be written to to reduce the taxes collected for a day so that a store could pay less tax than is actually owed. This would then make the Cash Register software untrustworthy and therefore useless.

However if the data you need for your software is not accessible from the code below you can ask me to add it. If it is restricted I will politely refuse and if it is not I will add it to the listings below.

These file formats are in QuickBasic. In some instances I will also post formats that are in VisualBasic.

NOTE: In the code samples below the file name is set with FL$ = "KEYSHOP" Make sure that you use the file name of your register files when using the code.


Employees
Network path
Pole display
Stock table

Employees
The following QuickBASIC code will read the employee names and display them on the screen.

Remember to open the .POS file as SHARED in the GLOBAL folder if the Cash Register program is being used over a network.

COLOR 15, 1: CLS
DIM EMP$(56)
FL$ = "KEYSHOP"
OPEN FL$ + ".POS" FOR RANDOM AS 1 LEN = 80
FOR E = 0 TO 1
   FOR A = 1 TO 3: FIELD 1, (A - 1) * 24 AS NULL$, 24 AS E$(A): NEXT
   FOR A = 0 TO 9
      GET 1, A + 11 + E * 291
      FOR B = 1 TO 3
         C = A * 3 + B: IF C < 29 THEN EMP$(C + E * 28) = E$(B)
NEXT B, A, E
FOR A = 1 TO 19
   FOR B = 0 TO 2
      C = A + B * 19
      IF C < 57 THEN
         LOCATE A, B * 26 + 1
         PRINT USING "##.\                     \"; C; EMP$(C);
      END IF
NEXT B, A

Network path
The following QuickBASIC code will read the PATH and folder type and display them on the screen.

Read this data from the LOCAL folder.

GBL = 0 = This is a LOCAL folder.

GBL = 1 = This is the GLOBAL folder.

COLOR 15, 1: CLS
FL$ = "KEYSHOP"
OPEN FL$ + ".POS" FOR RANDOM AS 1 LEN = 80
   FIELD 1, 40 AS A$, 1 AS B$, 1 AS G$: GET 1, 8: REG$ = B$
   PATH$ = LTRIM$(RTRIM$(A$)): IF INSTR(A$, CHR$(0)) THEN PATH$ = "": REG$ = ""
   GBL = SGN(ASC(G$))
CLOSE
PRINT PATH$
PRINT GBL

Pole display
The POS program outputs data to the file POLE.DAT that is read by the pole display or the BIGPOLE.EXE program. You can use the POLE.DAT file to create your own display device or for any other purpose.

The program below will read the data from the POLE.EXE file and display it on the screen.

The POLE.DAT file is read from the LOCAL folder.

COLOR 15, 1: CLS
OPEN "POLE.DAT" FOR RANDOM ACCESS READ WRITE SHARED AS 1 LEN = 20
FIELD 1, 18 AS NUL$, 2 AS Z$: FIELD 1, 20 AS A$
FIELD 1, 15 AS STOCK$, 1 AS RD$, 2 AS TYPE$, 2 AS DIFF$
FIELD 1, 2 AS NOD$, 4 AS NET$, 4 AS TAX$, 4 AS TOTAL$, 1 AS UP$
P$ = "\" + SPACE$(18) + "\"
12 B$ = INKEY$: IF B$ = CHR$(27) THEN CLOSE : END
GET 1, 3: DIFFERENT = CVI(DIFF$)
IF DIFF <> DIFFERENT THEN
   DIFF = DIFFERENT
   GET 1, 1: LOCATE 1, 1: PRINT USING P$; A$;
   GET 1, 2: LOCATE 2, 1: PRINT USING P$; A$;
   GET 1, 3: LOCATE 3, 1: PRINT USING P$; STOCK$;
   TYPES = VAL(TYPE$): LOCATE 4, 1
   IF TYPES = 0 THEN PRINT SPACE$(20)
   IF TYPES = 1 THEN PRINT USING P$; "PURCHASE";
   IF TYPES = 2 THEN PRINT USING P$; "RETURN / PAYOUT";
   RD = ASC(RD$): LOCATE 5, 1
   IF RD = 0 THEN PRINT SPACE$(20)
   IF RD = 1 THEN PRINT USING P$; "DISCOUNT / REDUCTION";
   IF RD = 2 THEN PRINT USING P$; "PRICE CHANGE";
END IF
GET 1, 4: NODECIMAL = CVI(NOD$)
IF NODECIMAL = 100 THEN PP$ = "##########" ELSE PP$ = "#######.##"
IF UP$ = "1" THEN
   LOCATE 6, 1: PRINT "  NET SALES"; USING PP$; CVL(NET$) / 100 * NODECIMAL
   LOCATE 7, 1: PRINT "        TAX"; USING PP$; CVL(TAX$) / 100 * NODECIMAL
   LOCATE 8, 1: PRINT "TOTAL SALES"; USING PP$; CVL(TOTAL$) / 100 * NODECIMAL
END IF
LOCATE 10, 1: PRINT "PRESS [ESC] TO QUIT"
GOTO 12

Here is some Q & A from a recent post about the POLE.DAT file.

Q1) Line 1: Product description Line 2: Quanty X Single Price = Total

A2) Not neccessally. Record #1 contains the text that is supposed to be displayed on the top line of the pole display and record #2 contains the text that is supposed to be displayed on the second line of the pole display. For example before a transaction record #1 will contain "WELCOME TO...." and the second line will contain the store name.

Q2) What is "RD"? (used for, all possible values, etc) The real value is the ASCII code of the string RD?

A2) RD tells you if the data in records 1 and 2 are from a "discount / reduction" or a "price change." The REMOTE.EXE program uses the value of RD to decide if the information displayed from records 1 and 2 should be highlighted in a different color. Possible values are 0, 1, or 2.

Q3) What is "DIFF"? (used for, all possible values, etc)

A3) Every time the information in POLE.DAT is updated the value in DIFF is incremented by 1. Therefore when the value in DIFF changes it is time to update the pole display. Possible values are 0 to 32768 at which point the value is reset to 0.

Q4) What exactly does the CVL() function do, that it is used on the Net Total, Tax Rate, and Total? (Decoding of some sort? Is this an exclusively QBASIC function?) AND What is the CVI() function?

A4) I have no idea if QB is the only language that has the CVL( ) and CVI( ) functions because QB is the only language I know. However the deal is that QB can store numberic values in a compressed format. This serves two functions. First all the numbers of the same type (interger or single precision, etc.) all can be stored in the same length string (all intergers in 2 bytes, all long intergers in 4 bytes, etc.) which is really useful in random access files. The second function is that the numbers are much smaller to store. Remember that I come from a time before 100 gigabyte hard drives when a 360k floppy drive was the most you could hope for in storage. But anyway, following is some code that will replace the CVL( ) and CVI( ) commands.

CVL( )
A$ Is the number you are trying to convert.

X = 0
FOR A = 1 TO 4
E = ASC(MID$(A$, A, 1))
G = 256 ^ (A - 1)
X = X + E * G
NEXT A

CVI( )
A$ Is the number you are trying to convert.

X = 0
FOR A = 1 TO 2
E = ASC(MID$(A$, A, 1))
G = 256 ^ (A - 1)
X = X + E * G
NEXT A

Q5) What are the possible values for decimal/no decimal and what does each case mean?

A5) NOD can equal either 1 or 100. 100 means that there are no decimals displayed in numeric values. 1 means that decimals are displayed. This is only a concern with Net, Tax, and Total because values in records #1 and #2 are are handled by the POS program. The values that will be returned from Net, Tax, and Total will not have decimals when decoded by the program code above so to properly display the values this code is used.

PRINT USING "#####.##"; A / 100 * NOD

Q6) What is the "UP" value used for?

A6) When UP = 1 it means that the Net, Tax, and Total has been updated and should be redisplayed. Your program must reset the value of UP to zero in the POLE.DAT file.


Stock table
The stock table contains all of the information on the merchandise that is sold a store. The stock table is kept in two separate files.

The main file ends in the extension .TBL and contains all of the information in the stock table.

The first record in the stock table .TBL file contains the version number and the number of lines that are in the stock table

Remember to open the .POS file and the .TBL file as SHARED in the GLOBAL folder if the Cash Register program is being used over a network.

The following QuickBASIC code will read the stock table a line at a time and display the contends of each record on the screen.

DIM TB$(13)
FL$ = "KEYSHOP": COLOR 15, 1: CLS
OPEN FL$ + ".POS" FOR RANDOM AS 1 LEN = 80
FIELD 1, 70 AS A$, 1 AS B$: GET 1, 1
RCC = ASC(B$): CLOSE 1  :'Get number of lines that are supposed to be in the stock table.
IF RCC = 32 THEN RCC = 15
RCC = (RCC - 10) * 1000
OPEN "R", 1, FL$ + ".TBL", 120: FIELD 1, 7 AS A$, 1 AS B$, 1 AS C$: GET 1, 1
RC = (ASC(C$) - 10) * 1000  :'Get number of lines that actually are in the stock table.
IF A$ <> "VERSION" OR B$ <> CHR$(6) THEN PRINT "Not stock table version 6": END
CLOSE 1
IF RCC < RC THEN RC = RCC
OPEN "R", 2, FL$ + ".TBL", 120
FIELD 2, 24 AS TB$(0), 7 AS TB$(1), 1 AS TB$(5), 1 AS TB$(2), 4 AS TB$(3)
FIELD 2, 37 AS NULL$, 4 AS TB$(4), 1 AS TB$(6), 4 AS TB$(7), 18 AS TB$(8)
FIELD 2, 64 AS NULL$, 4 AS TB$(9), 4 AS TB$(10), 4 AS TB$(11), 30 AS TB$(12)
FIELD 2, 106 AS NULL$, 4 AS TB$(13)
FIELD 2, 120 AS TB$
FOR A = 1 TO RC
   GET 2, A + 1
   CLS
   PRINT "RECORD"; A
   PRINT "STOCK NUMBER:"; RIGHT$(TB$(8), 14)    :'Text
   PRINT "DESCRIPTION: "; TB$(0)                :'Text
   PRINT "REGULAR PRICE:"; VAL(TB$(1)) / 100    :'Text
   PRINT "SALE PRICE:"; CVL(TB$(13)) / 100      :'Long interger x 100
   PRINT "TAX RATE NUMBER: "; TB$(2)            :'Text, refers to line in tax chart
   PRINT "CATEGORY NUMBER:"; ASC(TB$(5))        :'ASCII, refers to slot on category list
   PRINT "VENDOR NUMBER:"; ASC(TB$(6))          :'ASCII, refers to slot on vendor list
   PRINT "PIECES SOLD:"; CVL(TB$(3)) / 1000     :'Long interger x 1000
   PRINT "VALUE SOLD:"; CVL(TB$(4)) / 100       :'Long interger x 100
   PRINT "INVENTORY:"; CVL(TB$(7)) / 1000       :'Long interger x 1000
   PRINT "COST:"; CVL(TB$(9)) / 100             :'Long interger x 100
   PRINT "MODEL STOCK:"; CVL(TB$(10))           :'Long interger x 10
   PRINT "WAREHOUSE PACK:"; CVL(TB$(11))        :'Long interger
   PRINT "VENDOR STOCK NUMBER: "; TB$(12)       :'Text
   PRINT : PRINT "PRESS [ENTER] FOR NEXT LINE"
   PRINT : PRINT "PRESS [ESC] TO QUIT"
5  A$ = INKEY$: IF A$ = "" THEN 5
   IF A$ = CHR$(27) THEN END
NEXT
CLOSE
END


The other stock table file ends with the extension .IDX and contains an index of all the stock numbers in the stock table in condensed form. Since the .IDX file is relatively small the POS program can read it and keep the entire file in memory to use to quickly locate any item in the .TBL file.

This means that any program that makes a change in any item's stock number or moves, adds, or deletes any lines in the .TBL file must then re-index the .IDX file otherwise the POS program will no longer be able to find items in the .TBL file.

The following program will re-index the stock table. Remember that if you are using this program over a network that you must open the files as SHARED in the GLOBAL folder.

COLOR 15, 1: CLS
ON ERROR GOTO ERRR: KILL PATH$ + FL$ + ".IDX": ON ERROR GOTO 0
FL$ = "KEYSHOP": COLOR 15, 1: CLS
OPEN FL$ + ".TBL" FOR RANDOM AS 2 LEN = 80
FIELD 2, 70 AS A$, 1 AS B$: GET 2, 1
RCC = ASC(B$): CLOSE 2: 'Get number of lines that are supposed to be in the stock table.
IF RCC = 32 THEN RCC = 15
RC = (RCC - 10) * 1000
OPEN "R", 2, FL$ + ".TBL", 120
FIELD 2, 46 AS NULL$, 18 AS STOCKNUMBER$
OPEN FL$ + ".IDX" FOR RANDOM AS 1 LEN = 2: FIELD 1, 2 AS P$
FOR A = 1 TO RC
   LOCATE 12, 57, 0: PRINT ABS(A - RC);
   GET 2, A + 1
   STOCKNUMBER# = VAL(STOCKNUMBER$)
   IF STOCKNUMBER# < 1000000000 THEN
      P = STOCKNUMBER#
   ELSE
      P = VAL(MID$(LTRIM$(STR$(STOCKNUMBER#)), 3, 9))
   END IF
   LSET P$ = MKI$(P MOD 65536 - 32768): PUT 1, A
NEXT: CLOSE 2
END
ERRR: RESUME NEXT: RETURN

Click on [BACK] on your web browser to return to the previous page, or
PREVIOUS PAGE