The purpose of this PW application is to create a batch journal entry file based upon the records in a report file.
As you can imagine, this process was extremely time consuming; it took the four A/P clerks almost two days each to complete the process. In addition, the process invited the introduction of errors at every step. To complicate matters even further, the A/P clerks needed to follow a set of business rules when creating the journal entries.
Using this routine, however, the clerks take less than half a day to review the exception reports and record any payables. The A/P entries that this application generates are written to an interface file, which is uploaded to the DTRHA's general ledger host and executed as a batch job.
:cfo3:user:interfaces:rnp.datOn the second line, enter the following:
m:\pdata\rnp\rnp.txt/ovwNotice that we need to specify "txt" instead of "dat" in the second line!
Now it's time to do the processing with Paradox. Minimize CEO-Connection, launch Paradox for Windows, and make m:\pdata\rnp your working directory. Open the form RNP.FSL and push the Begin button. You will be prompted as follows:
During the course of the processing, two reports will be printed: (1) ackDel.rsl, which lists all items deleted because their ACKs meet the specifications in table ackDel.db; and (2) dateDel.rsl, which lists all items deleted because the "received" month is either greater or lesser than the specified month. The latter report will have to be reviewed by the A/P clerks for additional payables. In any event, both reports will print on the Finance laser.
When the processing is finished, you will get a message telling you what the net amount of the entries is (i.e., without the offset). Add this number to the totals at the bottom of the other two reports, and you should wind up with the total amount of the EDS-generated RNP report. If you don't, then notify Bill Kossmann as soon as possible.
All that remains is to get the journal entries into CFO. To do this, perform the following steps:
m:\pdata\rnp\c030.cfoOn the second line, enter the following:
:cfo3:user:interfaces:c030.cfo/ovw
Let's build the pushButton code as the file "button.txt."; it is imported to PW's IDE using the "Edit | Paste From" menu item. Indeed, all of our code will be imported to PW in this manner (simply create a new method and paste the corresponding text file into it). Here's a work-saving tip: establish an alias ":litProg:" that points to the directory holding the text files. When you want to import them, simply click on the ":litProg:" alias and then select the files you need (it's simpler than typing the path name or using the browser).
<button.txt>= <standard comment> method pushButton (var eventInfo event) <declare pushButton variables> <confirm start of processing> <build and show a popUpMenu> <calculate the date> <confirm the month selection> <call the other methods> <tell the user we're done> endMethod
<standard comment>= (U-> U-> U-> U-> U->) ;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;// RNP Written by Bill Kossmann 96/12/22 ;// ;// Here's how to access the program documentation: ;// ;// 1) use a Web browser to open file "m:\pdata\noweb\rnp.htm", or, ;// 2) look in the red binder labelled "Paradox for Windows: Source ;// code documentation" (it's located in Bill Kossmann's office). ;// ;// If you make changes to the code, the noweb source is located in file ;// "m:\pdata\noweb\rnp.nw". Remember, bad things will happen to you if ;// you change the code but you don't change the documentation! ;// ;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While we're at it, we might as well define what we're going to do when we encounter an error. First, we'll beep at the user, display the error using PW's standard error dialog box, and clear the status bar. Then, we'll set lErrors to "True" so that subsequent methods cannot start (as you'll see in subsequent methods, we check the value of lErrors when a method starts). Finally, we'll hide the text object progressTextObject and return to the form.
<do error processing>= (U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U->) beep() errorShow() message("") lErrors = True progressTextObject.visible = False return
<declare pushButton variables>= (U->) var dt dateTime pMenu popUpMenu endVar
Definesdt
,pMenu
(links are to index).
Our global variables need to be declared as well. lErrors is a logical variable whose value is set to "False" when we begin processing. As long as we have no errors, its value won't change. However, if we encounter an error, we'll change its value to "True". Since all methods test the value of lErrors before they start processing, this provides us with a means to gracefully shut down the program.
The other string variables (with one exception) are used in date calculations. We'll use them to calculate the month the user wants to process, and we'll also need them when we start writing the journal entries. The string variable sTotal is a reconciling total that we calculate later.
<globvars.txt>= var lErrors logical sLastDay string sMonth string sTotal string sYearMonth string endVar
DefineslErrors
,sLastDay
,sMonth
,sTotal
,sYearMonth
(links are to index).
<confirm start of processing>= (U->) lErrors = False if msgQuestion("Confirm", "Create monthend accruals from the RNP file now?") <> "Yes" then msgInfo("Status", "Cancelled by user command.") return endIf
Note that we specifically empty the pMenu even before it's built. It's a good idea to do this, even if you can't find this suggestion in the ObjectPAL manuals. The reason is, if your process fails, then under some situations the menu remains in memory. When you press the button again without closing the form, you'll wind up with all the menu items displayed twice. The menu will still work, but it's as ugly as sin and really confuses the user.
After the menu is built, "show()" it to the user and write the user's choice to variable sMonth.
<build and show a popUpMenu>= (U->) pMenu.empty() pMenu.addStaticText("Accrual is for:") pMenu.addSeparator() pMenu.addText("January") pMenu.addText("February") pMenu.addText("March") pMenu.addText("April") pMenu.addText("May") pMenu.addText("June") pMenu.addText("July") pMenu.addText("August") pMenu.addText("September") pMenu.addText("October") pMenu.addText("November") pMenu.addText("December") sMonth = pMenu.show()
We'll use a long switch structure to calculate the last day of the month as well as the year-month combination. All the months except February are easy: the last day is either 30 or 31. We get the year-month combination by calculating the string value of the current year of the PC's clock and prepending it to the month (the year and month are separated by a slash).
The last date in February is a bit different. We have to give PW a date string in dateTime format, so we'll arbitrarily choose 6:00am on the first day of the first month of the current year. We'll then use the isLeapYear function to determine if this is a leap year. If it is, the last day is the 29th; if it's not, then it must be the 28th.
If the user clicks anywhere except a month or if Esc is pressed, a null string is returned and we indicate to the user that it's an invalid choice. If that happens, return to the form.
<calculate the date>= (U->) switch case sMonth = "January" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/01" case sMonth = "February" : sYearMonth = strVal(year(today())) + "/02" dt = dateTime("06:00:00 am " + "01/01/" + subStr(sYearMonth,3,2)) if dt.isLeapYear() then sLastDay = "29" else sLastDay = "28" endIf case sMonth = "March" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/03" case sMonth = "April" : sLastDay = "30" sYearMonth = strVal(year(today())) + "/04" case sMonth = "May" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/05" case sMonth = "June" : sLastDay = "30" sYearMonth = strVal(year(today())) + "/06" case sMonth = "July" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/07" case sMonth = "August" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/08" case sMonth = "September" : sLastDay = "30" sYearMonth = strVal(year(today())) + "/09" case sMonth = "October" : sLastDay = "31" sYearMonth = strVal(year(today())) + "/10" case sMonth = "November" : sLastDay = "30" sYearMonth = strVal(year(today())) + "/11" case sMonth = "December" : sLastDay = "31" sYearMonth = strVal(year(today())-1) + "/12" otherwise: msgStop("Invalid selection", "You must click on a month.") return endSwitch
<confirm the month selection>= (U->) if msgQuestion("Confirm", "Generate entries for " + sMonth + " " + subStr(sYearMonth,1,4) + "?") <> "Yes" then msgInfo("Status", "Process aborted by user command.") return endIf
We'll discuss these methods in later sections. For now, let's just establish the flow of the program:
<call the other methods>= (U->) if lErrors = False then importRnpFile() endIf if lErrors = False then deleteCostCentres() endIf if lErrors = False then deleteNonCurrent() endIf if lErrors = False then writeAccrualEntries() endIf
<tell the user we're done>= (U->) if lErrors = False then progressTextObject.visible = False msgInfo("Status", "The process has finished successfully. The net amount of the journal entries--without the offset--is $" + sTotal + ". Add this amount to the other two reports, and you should arrive at the total of the printed EDS RNP report.") endIf
To build the method, we'll include our "standard" commment and declare our variables. Then we'll empty the table, read the RNP report, and then repopulate the RNP table. To get rid of the items that were returned upon receipt, we'll summarize the table. Here we go:
<import.txt>= <standard comment> method importRnpFile() <declare the importRnpFile() variables> <empty the RNP table> <access the RNP report file> <read the report> <summarize the table> endMethod
After we declare the variable types, we'll initialize liFirstChar as the first character position in the string to search for, and initialize liLastChar as the last character position to search for. We'll set them to "1" because we're searching for the one character that tells we're at the end of a record: a carriage return character.
<declare the importRnpFile() variables>= (<-U) var iCounter smallInt liFirstChar longInt liLastChar longInt qVarImport query sAmount string sArray array[] anyType sLine string sQuantity string tcImport tCursor tsImport textStream endVar liFirstChar = 1 liLastChar = 1
DefinesiCounter
,liFirstChar
,liLastChar
,qVarImport
,sAmount
,sArray
,sLine
,sQuantity
,tcImport
,tsImport
(links are to index).
I've set up the object progressTextObject as an unbound field on the form; its purpose is to display our messages to the user. We could have used the status bar, but we can't set the properties of any text displayed there (messages appearing in our text object will be nicely formatted).
<empty the RNP table>= (<-U) if not tcImport.open("rnp.db") then <do error processing> else progressTextObject.value = "Deleting old data ..." progressTextObject.visible = True tcImport.edit() tcImport.empty() endIf
<access the RNP report file>= (<-U) if not tsImport.open("rnp.txt", "r") then <do error processing> endIf
We'll change the value of the text object so that the user knows what we're up to. All we need to do is convert the current record number to a string and include it in the message. When we're finished, we'll close the tCursor and the textStream.
<read the report>= (<-U) while true liFirstChar = liLastChar if tsImport.advMatch(liFirstChar,liLastChar,chr(13)) then <read the record> <get vendor, ACK, and invoice information> <get rid of the comma in Amount> <is Amount a debit or credit> <convert quantity string to integer> <try to post the record> else quitloop endIf progressTextObject.value = "Successfully imported record number " + strVal(tcImport.nRecords()) endWhile tcImport.close() tsImport.close()
When we finally locate the carriage return character, we'll set the textStream position to the first character of that line and read the entire line into variable sLine. Since we know the record is 157 characters long (including the <CR> and <LF> characters), we'll just read the first 155 characters of the record.
<read the record>= (<-U) then tsImport.setPosition(liFirstChar - 155) tsImport.readChars(sLine,155)
Oh, yes, I almost forgot: the secondary code in the Inventory system is only 5 characters long. We need to append two zeros to make it 7 characters long, otherwise CFO will crap out on us.
<get vendor, ACK, and invoice information>= (<-U) tcImport.insertAfterRecord() tcImport."Facility" = strVal(subStr(sLine,77,1)) tcImport."Primary" = strVal(subStr(sLine,78,2)) + strVal(subStr(sLine,81,5)) tcImport."Secondary" = strVal(subStr(sLine,87,5)) + "00" tcImport."Vendor" = strVal(subStr(sLine,126,30)) tcImport."PO" = strVal(subStr(sLine,1,5)) tcImport."Stock Nbr" = strVal(subStr(sLine,22,10)) tcImport."Item Desc" = strVal(subStr(sLine,31,30)) tcImport."Date Recd" = strVal(subStr(sLine,102,8))
<get rid of the comma in Amount>= (<-U) sAmount = strVal(subStr(sLine,113,11)) sAmount.breakApart(sArray,",") sAmount = "" for iCounter from 1 to sArray.size() sAmount = sAmount + strVal(sArray[iCounter]) endFor
<is Amount a debit or credit>= (<-U) if strVal(subStr(sLine,124,1)) = "-" then tcImport."Amount" = 0 - currency(sAmount) else tcImport."Amount" = currency(sAmount) endIf
<convert quantity string to integer>= (<-U) sQuantity = strVal(subStr(sLine,93,5)) sQuantity.breakApart(sArray,",") sQuantity = "" for iCounter from 1 to sArray.size() sQuantity = sQuantity + strVal(sArray[iCounter]) endFor if strVal(subStr(sLine,96,2)) = " -" then tcImport."Qty Recd" = 0 else if strVal(subStr(sLine,97,1)) = "-" then tcImport."Qty Recd" = 0 - smallInt(subStr(sQuantity,1,sQuantity.size()-1)) else tcImport."Qty Recd" = smallInt(sQuantity) endIf endIf
<try to post the record>= (<-U) if not tcImport.postRecord() then <do error processing> endIf sleep(5)
Let's declare the query and try to execute it. If we fail, then execute our error processing steps. Note that the resulting answer table is rnp.db; that's because we want to overwrite the original table with the summarized table.
<summarize the table>= (<-U) qVarImport = Query answer: :work:rnp.db rnp.db | Facility | Primary | Secondary | Vendor | PO | Stock Nbr | | Check | Check | Check | Check | Check | Check | rnp.db | Item Desc | Date Recd | Amount | Qty Recd | | Check | Check | calc sum as Amount | calc sum as Amount | endQuery if not qVarImport.executeQBE() then <do error processing> endIf message("") sleep(5)
<ackdel.txt>= <standard comment> method deleteCostCentres() <declare deleteCostCentres() variables> <open the query parameter table> <build the query string> <execute the deletion query> <open the report ackDel.rsl> endMethod
In order to report on the deleted records, we'll have to build a query string that deletes all of the records at once. Here we'll set up the query string header sQueryDelCc1, initialize the query body sQueryDelCc2 as a null string, and set up the query string footer sQueryDelCc3. When declaring a query in this manner, we'll need to use the "\ n" characters to terminate a query image record.
<declare deleteCostCentres() variables>= (<-U) var rDelCc report riDelCc reportPrintInfo sQueryDelCc string sQueryDelCc1 string sQueryDelCc2 string sQueryDelCc3 string tcDelCc tCursor endVar sQueryDelCc1 = "Query\n answer: :priv:deleted.db\n\n" + "rnp.db | Facility | Primary | Secondary |\n" sQueryDelCc2 = "" sQueryDelCc3 = "\nendQuery\n"
DefinesrDelCc
,riDelCc
,sQueryDelCc
,sQueryDelCc1
,sQueryDelCc2
,sQueryDelCc3
,tcDelCc
(links are to index).
<open the query parameter table>= (<-U) if not tcDelCc.open("ackDel.db") then <do error processing> endIf
<build the query string>= (<-U) progressTextObject.value = "Deleting specified ACKs" progressTextObject.visible = True scan tcDelCc: sQueryDelCc2 = sQueryDelCc2 + "DELETE | " + strVal(tcDelCc."Facility") + " | " + strVal(tcDelCc."Primary") + " | " + strVal(tcDelCc."Secondary") + " |\n" endScan tcDelCc.close() sQueryDelCc = sQueryDelCc1 + sQueryDelCc2 + sQueryDelCc3
<execute the deletion query>= (<-U) if not executeQbeString(sQueryDelCc) then <do error processing> endIf
<open the report ackDel.rsl>= (<-U U->) progressTextObject.value = "Printing report ackDel.RSL" delayScreenUpdates(Yes) if not rDelCc.open("ackdel.rsl") then <do error processing> else <run the report ackDel.rsl> endif delayScreenUpdates(No) sleep(5)
<run the report ackDel.rsl>= (<-U U->) riDelCc.orient = printLandscape riDelCc.name = "ackdel.rsl" rDelCc.design() rDelCc.period.value = sMonth + " " + sLastDay + ", " + subStr(sYearMonth,1,4) rDelCc.save() rDelCc.print(riDelCc) rDelCc.close()
<datedel.txt>= <standard comment> method deleteNonCurrent() <declare deleteNonCurrent() variables> <calculate the period begin and end dates> <delete the non current records> <sort the deleted.db table> <open the report ackDel.rsl> endMethod
<declare deleteNonCurrent() variables>= (<-U) var qVarDelDate query rDelDate report riDelDate reportPrintInfo sBeginDate string sEndDate string tVarDelDate table endVar
DefinesqVarDelDate
,rDelDate
,riDelDate
,sBeginDate
,sEndDate
,tVarDelDate
(links are to index).
<calculate the period begin and end dates>= (<-U) progressTextObject.value = "Deleting non-current records" sBeginDate = sYearMonth + "/01" sEndDate = sYearMonth + "/" + sLastDay
<delete the non current records>= (<-U) qVarDelDate = Query rnp.db | Date Recd | DELETE | < ~sBeginDate or > ~sEndDate | endQuery if not qVarDelDate.executeQBE() then <do error processing> endIf
<sort the deleted.db table>= (<-U) progressTextObject.value = "Printing report DATEDEL.RSL" if not tVarDelDate.attach(":priv:deleted.db") then <do error processing> else sort tVarDelDate on "Vendor", "Date Recd", "PO" to ":priv:deleted.db" endSort endIf
<open the report dateDel.rsl>= riDelDate.orient = printLandscape riDelDate.name = "datedel.rsl" delayScreenUpdates(Yes) if not rDelDate.open(riDelDate) then <do error processing> else <run the report ackDel.rsl> endif delayScreenUpdates(No) sleep(5)
<run the report dateDel.rsl>= rDelDate.design() rDelDate.period.value = sMonth + " " + sLastDay + ", " + subStr(sYearMonth,1,4) rDelDate.save() rDelDate.print(riDelDate) rDelDate.close()
<write.txt>= <standard comment> method writeAccrualEntries() <declare writeAccrualEntries() variables> <create the group header record> <create the detail records> <create the offset record> endMethod
<declare writeAccrualEntries() variables>= (<-U) var nJeAmt number nOffset number sDesc string tcWrite tCursor tsWrite textStream sAmount string sSign string sVal string sValue string endVar nOffset = 0
DefinesnJeAmt
,nOffset
,sAmount
,sDesc
,sSign
,sVal
,sValue
,tcWrite
,tsWrite
(links are to index).
<create the group header record>= (<-U) if not tsWrite.create("C030.CFO") then <do error processing> else <write the group header record> try tsWrite.commit() onFail <do error processing> endTry endIf
Here's where we define the group header. If you really want to see how the record is laid out, look at the manual "CFO Transaction Manual", pages 350--352, 357, and 378 (the manual is in Bill Kossmann's office). I won't go into the gory details, but suffice it to say that each record must be 80 characters long, and the correct positioning of the information within the record is critical.
Note that some of this stuff is hard-wired, such as the signon record, the transaction and sequence number, and the group number. Our variables sYearMonth and sLastDay come from the pushButton method (they're global variables).
<write the group header record>= (<-U) tsWrite.writeString("*SIGNON*========--------",space(56)) tsWrite.writeString("120 01 A C030 ",subStr(sYearMonth,6,2), sLastDay,subStr(sYearMonth,3,2),"P00",space(49))
When we're all finished with the table, close it--but not before getting the total amount of the "Amount" field and holding it in sTotal. This will be presented to the user when we're finished, in order for the user to reconcile this process to the figures in the Inventory system reports.
<create the detail records>= (<-U) if not tcWrite.open("rnp.db") then <do error processing> endIf scan tcWrite for tcWrite."Amount" <> 0 : <get journal entry information> if nJeAmt > 0 then <calculate a debit overpunch> else <calculate a credit overpunch> endIf <format the journal entry> <write the journal entry> endScan sTotal = strVal(currency(tcWrite.cSum("Amount"))) tcWrite.close()
Here's where we define the detail record. The record layout is in the manual "CFO Transaction Manual", pages 350--352, 357, and 378 (the manual is in Bill Kossmann's office). Note that the variable sAmount is 16 characters long. It's a concatenation of the amount (with leading zeros) and the overpunch character.
Again, note that some of this stuff is hard-wired. That's okay because the batch record definition will never change. Our variables include the global ones we used in the header record, as well as some detail information such as ACK and description.
Because we don't want the user to get bored and reboot the machine, we need to display the number of the journal entry that we're working on by writing to progressTextObject.
<format the journal entry>= (<-U) sAmount = subStr(sValue,2,15) + sSign tsWrite.writeString("122 01 A C030 ",subStr(sYearMonth,6,2), sLastDay,subStr(sYearMonth,3,2), tcWrite."Facility",tcWrite."Primary",tcWrite."Secondary", space(35),"Y ") tsWrite.writeString("122 02 1",space(19),sAmount,space(36)) tsWrite.writeString("122 03",space(12),sDesc,space(10)) progressTextObject.value = "Writing journal entry " + strVal(tcWrite.recNo()) + " of " + strVal(tcWrite.nRecords())
Try to commit every line so that if Paradox chokes, we can isolate the offending record by looking at what it did manage to write.
<write the journal entry>= (<-U) try tsWrite.commit() onFail <do error processing> endTry sleep(5)
Here's where we begin calculating the amount with the overpunch character. If you really want to see how the record is laid out, look at the manual "CFO Transaction Manual", pages 350--352, 357, and 380 (the manual is in Bill Kossmann's office).
First, we'll assign the value of the Amount field to nJeAmt, and calculate the offset amount. Then, we'll create the description, which is a field 51 characters long, left-aligned (AL) and upper-case (CU), with "|" characters acting as separators.
Next we'll format nJeAMt sot that it's a 17-character variable with leading zeros. We want it in pennies (i.e., drop the decimal), so we'll multiply the rounded amount by 100. The last (17th) character of sValue will be used to calculate the overpunch character, so we'll save that information in sVal--it'll be used in a minute.
<get journal entry information>= (<-U) nJeAmt = tcWrite."Amount" nOffset = nOffset - tcWrite."Amount" sDesc = format("W51,AL, CU",tcWrite."Vendor" + " | " + tcWrite."PO" + " | " + tcWrite."Stock Nbr") sValue = format("W17,EZ,S+",(100*(round(nJeAmt,2)))) sVal = subStr(sValue,17,1)
The overpunch character is a representation of both the polarity of the number (positive or negative), and the value of the last character. For example, 1234E represents 123.45 (debit), and 4567Q represents 456.78 (credit). We will calculate the overpunch characters with two switch/endSwitch structures (one for debits and one for credits).
Since we know from nJeAmt whether or not the amount is positive or negative, all we have to do is look at the value of the last character and map it to the appropriate overpunch character:
<calculate a debit overpunch>= (<-U U->) switch case sVal="0" : sSign="{" case sVal="1" : sSign="A" case sVal="2" : sSign="B" case sVal="3" : sSign="C" case sVal="4" : sSign="D" case sVal="5" : sSign="E" case sVal="6" : sSign="F" case sVal="7" : sSign="G" case sVal="8" : sSign="H" case sVal="9" : sSign="I" endSwitch
<calculate a credit overpunch>= (<-U U->) switch case sVal="0" : sSign="}" case sVal="1" : sSign="J" case sVal="2" : sSign="K" case sVal="3" : sSign="L" case sVal="4" : sSign="M" case sVal="5" : sSign="N" case sVal="6" : sSign="O" case sVal="7" : sSign="P" case sVal="8" : sSign="Q" case sVal="9" : sSign="R" endSwitch
<create the offset record>= (<-U) <get the offset information> if nOffset > 0 then <calculate a debit overpunch> else <calculate a credit overpunch> endIf <format the offset information> <write the offset information> try tsWrite.commit() onFail <do error processing> endTry sleep(5) tsWrite.close()
The journal entry description for the offset record will be, for example, "ACCRUAL FOR DECEMBER 1996". The amount of the journal entry is the running total nOffset, so we'll convert it to pennies and grab the last (17th) digit.
<get the offset information>= (<-U) sDesc = format("W30,AL, CU","ACCRUAL FOR " + sMonth + " " + subStr(sYearMonth,1,4)) sValue = format("W17,EZ,S+",(100*(round(nOffset,2)))) sVal = subStr(sValue,17,1)
Again, as with the detail records, we concatenate the 2nd to 15th characters of the amount--remember, the first was a plus sign or a minus sign--with the overpunch character. Then we write the journal entry line, but this time we hard-code the ACK as "141390000000000", which is the Trade Accounts Payable account code.
<format the offset information>= (<-U) sAmount = subStr(sValue,2,15) + sSign tsWrite.writeString("122 01 A C030 ",subStr(sYearMonth,6,2), sLastDay,subStr(sYearMonth,3,2),"141390000000000",space(35),"Y ") tsWrite.writeString("122 02 1",space(19),sAmount,space(36)) tsWrite.writeString("122 03",space(12),sDesc,space(31))
Write the entry to disk from the memory buffer. If it fails, then do the error processing.
<write the offset information>= (<-U) try tsWrite.commit() onFail <do error processing> endTry