Глава 9:Система электронных заказов

 Некоторое время назад я занимался программированием на КОБОЛе для большой машины (mainframe), переболел этим и почти забыл вид этих здоровых железок, как вдруг неожиданный контракт на крупную сумму заставил меня вспомнить прошлые дела.

Несколько клиентов просили меня написать подсистему, способную общаться с большим компьютером. У всех требования сводились к получению возможности посылки запроса на большую машину и приема ответа для обновления файла заказов на персональном компьютере.


В главе я привожу пример приложения, в котором использованы обобщенные методики. Можно разработать универсальный инструментарий для решения некоторых из такого рода задач, так как на каждом этапе разработки используются списки полей, длины полей, протоколы преобразований и стандартные форматы. Однако, мне кажется, что при чрезмерном обобщении есть опасность потерять основную идею. Если вам потребуется писать по приложению в неделю, тогда есть смысл придумать нечто универсальное.

Я выбрал в качестве примера электронную систему заказов, так как она вполне может оказаться той первой задачей, с которой вам придется иметь дело. Обычный сценарий работы подразумевает посылку на большой компьютер файла с данными из .DBF-файла и ожидание ответа. Большие машины обычно выполняют запись и чтение в формате стандартного КОБОЛа, и обмен данными является ключевым участком всего приложения.



Когда я только взялся за разработку, то предполагал, что самой трудной частью будет написание программы связи с большим компьютером. На самом деле этот участок оказался самым легким: сделать это было невозможно, так что я даже и не пытался. Я просто использовал коммерческий пакет ProComm PLUS и нанял парня, который написал программу связи (script) примерно за пару часов. Если вы решите стать экспертом по программному обеспечению поддержки телекоммуникаций, то вам придется провести несколько месяцев в уединении на острове с неограниченным бюджетом для оплаты телефонной связи. В конце такого добровольного заключения вы будете знать достаточно, чтобы понять, что гораздо проще нанять программиста, который за 200 долл. сделает эту работу вместо вас.

Больше всего времени у меня заняла разработка формата файлов для посылки на большую машину. В этой главе вы увидите как это реализуется средствами функций низкого уровня доступа.

Проектирование программы

Большие компьютеры не способны читать файлы в формате xBASE. Они работают с файлами последовательной иерархической структуры (Hierarchical Sequential File Structure HSAM). В системе заказов на покупки, построенной на xBASE, вы имеете дело с файлом заголовков, по одному заголовку на каждый заказ, определяющему общую информацию о заказе. Кроме того, у вас имеется файл с полными данными по заказам. Каждому заказу соответствует по одной записи в файле данных. Поле номера заказа связывает два файла вместе. В нашем примере мы предполагаем, что такая система у вас уже имеется.

В мире КОБОЛа оба типа записей находятся в одном файле, и им предшествует символьный идентификатор (1-3 байта), определяющий тип записи. Это позволяет считать запись в буфер, определить ее тип и перенести в другой буфер (подобно структуре на Си), который интерпретирует поля должным образом. В итоге файл заказов может выглядеть примерно так:

01PO#0000103/12/910008795002000000
02PO#0000100001ITEM#01001295001295
02PO#0000100003ITEM#02002500007500
01PO#0000203/15/910002590001000000
02PO#0000200002ITEM#01001295002590


Для меня это выглядит вполне обычно. В КОБОЛе вы можете иметь несколько структур записи в одном файле, и, поэтому, заголовки не располагаются в верхней части файла, как это имеет место в формате xBASE. Надо сказать, что правительство регулярно складирует миллионы бобин с лентами, содержащими данные по переписке, за счет налогоплательщиков, но при этом не хранит дескрипторы файлов КОБОЛа и, следовательно, прочитать данные невозможно. Напишите жалобу вашему конгрессмену!

Чтобы считать наш файл вам необходимо иметь дескриптор, описывающий структуры записей в соответствии с их типами. Для приведенного выше примера дескриптор выглядит так:(Табл. 9-1)

В нашем примере первая запись имеет тип [01] заголовок, для заказа #00001 от 12 марта 1991 на общую сумму $87.95. Две следующие записи типа [02] описывают заказ, который включает один предмет #01 и два предмета #02. Обратите внимание, что все записи имеют одинаковую длину (в нашем примере 34 байта), так как они входят в один и тот же файл и для доведения записи типа 01 до нужной длины используется заполнитель (необязательно использовать нули, но они более наглядны чем пробелы, а я собираюсь показать принцип работы наглядно).

Обычно программы связи предпочитают иметь дело с пакетным заголовком (batch header record), предшествующем всем записям, и, вы правильно догадались, пакетным концевиком (batch trailer record), завершающим передачу. Это позволяет компьютеру определить можно ли начать работу.

После того как вы послали один из таких файлов на большой компьютер, он некоторое время его помусолит и пошлет назад ответ: у нас есть то, у нас нет этого, я не знаю, что такое ITEM#02 и т. п. Вы считываете файл подтверждения приема, обновляете заказ по информации от большого компьютера и готово.

Что нужно сделать

Если все настолько просто, почему так много программистов для xBASE впадает в панику при необходимости сделать что-либо подобное? (Меня неоднократно спрашивали как решаются такие задачи и могу сказать, что слово паника вполне адекватно описывает состояние спрашивающих). Собственно говоря, с такой или похожей проблемой приходится сталкиваться всем, кто имеет дело с системами электронных заказов:


1)Индексные ключи системы на большом компьютере слегка отличаются от тех, что используются в вашей работающей системе учета заказов.


2) Некоторые из полей, которые требуются для указания в пакетном заголовке, не существуют в ваших файлах.


3) В вашем файле заголовков нет некоторых полей, необходимых в заголовке файла заказов для большой машины.


4) Используются различные системы кодирования (например, они используют "1" для указания отсутствия товара, а вы используете "O/S" (Out of Stock).


5) Почти наверняка они не работают с дробными числами.


6) Разумеется, вы получите в качестве ответа на ваш запрос файл в формате HSAM КОБОЛа, что означает что вам необходимо не только писать в файл такого формата, но и обеспечить его чтение.


Не стоит беспокоиться. Подобно большинству проблем, возникающих у программистов, эти поддаются решению. Все что нужно - это свести до минимума блуждание в тупиках.

Пункт 1 означает, что вам всего лишь нужно преобразовать ваши ключевые выражения при отправке и конвертировать в исходный вид при приеме. Пункт 2 потребует изменения структуры файла. Пункт 3 подразумевает добавление недостающих полей в файл заголовков. Проблема по пункту 4 решается трансляцией значений соответствующего поля в обоих направлениях. Пункт 5 потребует использования:

TRANSFORM(numfield*100,[@L ######])

для каждого числового поля с десятичной частью. Пункт 6 решается довольно просто: посмотрите на программу CONFIRM.PRG.

Вот и все. Предлагаемая вашему вниманию программа может быть довольно близка к тому, что вам придется написать при столкновении с такой задачей.

Заказы на покупку собираются в пакет во время передачи, так что пользователь может посмотреть данные перед построением пакета.

Для построения экрана просмотра я использовал SET SKIP и BROWSE. У меня есть некоторое предубеждение против использования BROWSE из-за его медлительности. Но в данном случае это то, что нужно.

Пользователь выбирает заказы для посылки, пользуясь меню, построенным командой DEFINE POPUP MULTI. Ctrl-Enter снимает выделение, Enter назначает выделение, а клавиши управления курсором распространяют выделение на последующие / предыдущие записи. Т. е., если вы выделите запись 1, нажмете Shift и перейдете к записи 2, она также окажется выделенной. Я надеюсь пользователи привыкнут к этому, особенно потому, что такой интерфейс очень недорого написать. Нажатие ESCAPE заканчивает процесс выделения.

И, наконец, появляется панель управления, позволяющая пользователю снова перейти в режим выбора, выполнить передачу или отменить выполнение. Этот режим построен на основе цикла, в котором выбор <edit> возвращает управление в начало цикла, <transmit>обеспечивает выход из цикла, а <cancel> также выводит из цикла, но при этом переменная Done принимает значение .Т..

Программа имитирует передачу данных. Я включил команду, запускающую ProComm PLUS с файлом сценария "MyScript". Вы посылаете файл с именем ORDERS, ожидаете некоторое время и получаете файл с именем CONFIRM. Сценарий представляет сокращенный вариант поставляемого с ProComm PLUS, но вы всегда можете нанять кого-нибудь, кто знает эту работу лучше вас и сделает ее быстрее.

Для того, чтобы программа могла читать записи, оканчивающиеся CHR(13)+CHR(10), я использую переменную Testing. Просмотреть результаты можно командой MODI FILE, а возврат к рабочему варианту выполняется присваиванием Testing=.F..

Спецификации

Структуры файлов Purchord.Dbf, Poitem.Dbf и Settings.Dbf приведены в Табл. 9-2. Структуры файлов большой машины приведены в Табл. 9-3. Структура файла подтверждения от большой машины приведена в Табл. 9-4. Код всей программы приведен в Листинге 9-1.

Как это работает

Система предполагает, что у вас уже имеется система приема заказов. Опция View используется для планирования передачи данных. Заказы вводятся в файл ORDER с использованием функций низкого уровня доступа. Если переменная Testing имеет значение .Т. (а вам придется присвоить ей такое значение, если только вы не найдете большой компьютер, согласный обработать ваши файлы), то создается парный ему файл с именем ORDER.TXT, и вы можете посмотреть результаты работы. Аналогично, наш имитатор читает файл CONFIRM как ASCII-файл, в котором каждая запись оканчивается возвратом каретки в том случае, если у нас установлен режим тестирования. При реальном использовании ни один из файлов не будет иметь окончаний в виде CrLf.

В реальной жизни заполненные строки станут записями по приему и будут исключены из массива заказов. Частично заполненные строки делятся на две части: одна для частичной отправки имеющегося заказа и другая для задержанной отправки по гарантированным заказам при поступлении товара. В нашей системе мы не обрабатываем разделенные поставки и не удаляем выполненные или отмененные заказы. Мы просто печатаем данные и оставляем их в файле.

При выборе из меню режима <Transmit> программа просит вас построить список заказов из предлагаемого меню со списком номеров заказов, после чего создает файл ORDERS и передает его. Я использую вызов программы ProComm PLUS и передаю ей в качестве параметра имя файла сценария; настоятельно рекомендую вам аналогичную методику. При получении ответа в виде файла CONFIRM я использую TEXTMERGE для печати.

Заключение

Приведенная программа дает пример гибкости языка FoxPro 2.0. Это основное свойство большинства языков, но у FoxPro оно действительно доставляет удовольствие.

Листинг 9-1(часть1)

* Program-ID....: Orders.PRG
* Purpose.......: Drives electronic ordering system SET TALK OFF
SET BELL OFF
SET SAFETY OFF
SET STATUS OFF
SET ESCAPE OFF

CLOSE ALL
CLEAR ALL

Testing = .T.

ON ERROR DO ERROR WITH ERROR(), PROGRAM(), LINENO()

USE SETTINGS IN A
USE PURCHORD IN B ORDER ORDERNUM
USE POITEM IN C ORDER PO_ITEM

SET SYSMENU TO
SET SYSMENU AUTOMATIC

DEFINE PAD One OF _MSYSMENU PROMPT "Menu options" COLOR SCHEME 3
ON PAD One OF _MSYSMENU ACTIVATE POPUP menuoption

DEFINE POPUP menuoption MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF menuoption PROMPT "View Purchase Orders" ;
KEY CTRL+V, "^^V"
DEFINE BAR 2 OF menuoption PROMPT "Transmit" ;
KEY CTRL+T, "^^T"
DEFINE BAR 3 OF menuoption PROMPT "Help" ;
KEY CTRL+H, "^^H"
DEFINE BAR 4 OF menuoption PROMPT "Quit" ;
KEY CTRL+Q, "^^Q"
ON SELECTION BAR 1 OF menuoption DO BrowsePO
ON SELECTION BAR 2 OF menuoption DO Transmit
ON SELECTION BAR 3 OF menuoption DO PO_Help
ON SELECTION BAR 4 OF menuoption Done = .T.

Done = .F.

DO WHILE NOT Done

ACTIVATE MENU _MSYSMENU PAD One

ENDDO

CLOSE DATABASES
SET SYSMENU TO DEFAULT
ON ERROR

RETURN


PROCEDURE PO_Help

DEFINE WINDOW HELPWINDOW FROM 6,10 TO 12, 70 ;
DOUBLE SHADOW ;
TITLE [How this works]

ACTIVATE WINDOW HELPWINDOW
CRLF = CHR(13) + CHR(10)

HTEXT =[ This system collects purchase orders that can be filled ] +;
[from the wholesaler using electronic ordering. It lets ] +;
[you select the P/Os to be included, then sends them in ] +;
[out the format prescribed by the wholesaler. ] +;
+ CRLF + [ ] + REPL([-],WCOLS()-4) + CRLF +;
[ You can view individual purchase orders, but not modify ] +;
[them, in this prototype. To transmit, fill in the fields ] +;
[in the TRANSMIT screen, then select the P/Os to be included ]+;
[and select CONTINUE.] +;
+ CRLF +[ ] +PADC( [*** END OF HELP MESSAGE ***],WCOLS()-2) +[ ]

@ 0,1 EDIT HTEXT NOMODIFY SIZE WROWS(), WCOLS()-2
ON KEY LABEL ENTER KEYBOARD [{Ctrl+W}]
READ
ON KEY LABEL ENTER
RELEASE WINDOW HELPWINDOW

RETURN
* Program-ID....: BROWSEPO.PRG
* Purpose.......: Browse of current purchase order detail

SET SYSMENU OFF
SET BORDER TO DOUBLE

SELECT purchord
SET SKIP TO
SET RELATION TO

RELEASE POPUP ORD
DEFINE POPUP ORD FROM 3,5 SHADOW ;
TITLE [Pick a P/O to view] ;
FOOTER [-+-select • ESC-exit]

I = 1
SCAN
DEFINE BAR I OF ORD PROMPT ORDERNUM + [ ¦ ] + CUSTOMER
I = I + 1
ENDSCAN

SET CONFIRM ON
ON SELECTION POPUP ORD DEACTIVATE POPUP ORD

DEFINE WINDOW FRAME FROM 2,1 TO 21, 76 DOUBLE ;
FOOTER [Press ESC key to continue] ;
TITLE [Selected purchase order] ;
COLOR N/W, W+/R, W+/B, GR+/B, GR+/B, N/W, W+/R, N/W, N/W, N/W

DEFINE WINDOW WIDEWINDOW FROM 2,1 TO 21, 76 ;
NONE ;
COLOR N/W, W+/R, W+/B, GR+/B, GR+/B, N/W, W+/R, N/W, N/W, N/W

SET RELATION TO ordernum INTO poitem
SET SKIP TO poitem

DO WHILE .T.

ACTIVATE POPUP ORD
mprompt = LEFT ( PROMPT() , 6 )

IF EMPTY ( mprompt )
EXIT
ENDIF

SEEK mprompt

ON KEY LABEL F10 KEYBOARD [{Ctrl+W}] && F10 or ESCAPE works...

ACTIVATE WINDOW FRAME
BROWSE NODELETE NOAPPEND ;
WINDOW WIDEWINDOW IN WINDOW FRAME ;
FIELDS ;
ordernum , ;
POITEM.itemno :H=[Item] :W=.F. , ;
POITEM.quantity :H=[ Qty] :V=Calc() , ;
POITEM.unitprice :H=[ Price] :V=Calc() , ;
POITEM.extension :H=[Extended] :W=.F. , ;
POITEM.weight :H=[Weight] :V=WCalc() , ;
POITEM.extweight :H=[ Tot Wt] :W=.F. , ;
POITEM.status :H=[Status] :V=StatCodes() :P=[!!!] ;
FOR ordernum = mprompt ;
WHEN ShowStatus()

ON KEY LABEL F10
DEACTIVATE WINDOW FRAME

ENDDO

RELEASE WINDOW WIDEWINDOW

SELECT purchord
SET SKIP TO
SET RELATION TO


RETURN

FUNCTION Calc
REPLACE NEXT 1 POITEM.Extension WITH POITEM.Quantity * POITEM.UnitPrice

FUNCTION WCalc
REPLACE NEXT 1 POITEM.ExtWeight WITH POITEM.Quantity * POITEM.Weight



FUNCTION StatCodes

IF POITEM.Status $ [ /BOA/BOP/OSC/SAO/ ]
RETURN
ENDIF

DEFINE POPUP STATCODES FROM 14,25 IN SCREEN SHADOW
DEFINE BAR 1 OF STATCODES PROMPT [\] + PADC([Status codes],20)
DEFINE BAR 2 OF STATCODES PROMPT [\-]
DEFINE BAR 3 OF STATCODES PROMPT PADC([Back Ordered All ],20)
DEFINE BAR 4 OF STATCODES PROMPT PADC([Back Ordered-Partial],20)
DEFINE BAR 5 OF STATCODES PROMPT PADC([Out of Stock-Cancel ],20)
DEFINE BAR 6 OF STATCODES PROMPT PADC([Shipped as Ordered ],20)
ON SELECTION POPUP STATCODES DEACTIVATE POPUP STATCODES
ACTIVATE POPUP STATCODES
mprompt = PROMPT()
RELEASE POPUP StatCodes

IF EMPTY ( mprompt )
RETURN
ENDIF

REPLACE NEXT 1 POITEM.Status WITH ;
IIF ( [Back] $ mprompt , [BOA] , ;
IIF ( [Par] $ mprompt , [BOP] , ;
IIF ( [Can] $ mprompt , [OSC] , ;
IIF ( [Shi] $ mprompt , [SAO] , [ ] ) ) ) )

RETURN

FUNCTION ShowStatus

WAIT CLEAR

DO CASE
CASE POITEM.STATUS = [BOA]
msg = [Back Ordered All]
CASE POITEM.STATUS = [BOP]
msg = [Back Ordered Partial]
CASE POITEM.STATUS = [OSC]
msg = [Out of Stock - Cancelled]
CASE POITEM.STATUS = [SAO]
msg = [Shipped as Ordered]
OTHERWISE
msg = []
ENDCASE

IF NOT EMPTY ( msg )
WAIT WINDOW msg NOWAIT
ENDIF

RETURN .T.

* Program=-ID....: TRANSMIT.PRG
* Purpose........: Builds file ORDERS for transmission to mainframe.

IF TYPE ( [Testing] ) = [U]
Testing = .F.
ENDIF

SET SYSMENU OFF

PUBLIC SELECTED(1)

STORE 0 TO OneCount , TwoCount, ThreeCount
STORE 0 TO TotWeight, TotLines, TotCount , TotValue, TotQty, TotPOS

STORE [......] TO m.CustID
m.TodaysDate = SUBSTR ( DTOC ( DATE() , 1 ) , 3 ) && YYMMDD

CRLF = CHR(13) + CHR(10)
NewPage = CHR(12)

SELECT PURCHORD

DO SETTINGS && FROM .SPR

IF Done
RETURN
ENDIF


SELECT PURCHORD
GO TOP

SET BORDER TO DOUBLE

RELEASE ORDERLIST
DIMENSION ORDERLIST ( RECCOUNT() )

DEFINE POPUP ORDERS FROM 1,3 MULTI MARGIN TITLE [P/Os to include]

I = 1
SCAN
DEFINE BAR I OF ORDERS PROMPT ORDERNUM + [ ¦ ] + DTOC ( ORDERDATE )
OrderList ( I ) = OrderNum
I = I + 1
ENDSCAN

DO WHILE .T.

@ 1, 51 CLEAR TO 24, 79
@ 23, 0 TO 23, 79
@ 24, 0 SAY ;
PADC( [-+ or SHIFT+arrows to mark, SHIFT+-+ to unmark, ESC when done], 80)

ON SELECTION POPUP ORDERS DO Picker
ACTIVATE POPUP ORDERS
@ 23, 0 CLEAR

SHOW POPUP ORDERS

m.action = 1
@ 12, 46 TO 14, 75 DOUBLE
@ 13, 48 GET m.action PICTURE [@*H Edit;Transmit;Cancel]
READ CYCLE
@ 12, 46 CLEAR TO 14, 75

DO CASE

CASE m.action = 1

LOOP

CASE m.action = 2

EXIT

CASE m.action = 3

CLEAR
=Alarm()
WAIT WINDOW [Cancelled by operator]
Done = .T.
RETURN

ENDCASE

ENDDO

RELEASE POPUP ORDERS
CLEAR

WAIT WINDOW [Building electronic order] NOWAIT

DO BUILDPO

WAIT WINDOW [Transmitting electronic order] NOWAIT

DO SENDPO

WAIT WINDOW [Printing confirmation report] NOWAIT

DO CONFIRM

WAIT WINDOW [** DONE ** ] TIMEOUT 1

Done = .T.

RETURN


FUNCTION Picker

J = 0
FOR I = 1 TO RECCOUNT()
IF MRKBAR ( [ORDERS] , I )
J = J + 1
IF J > 20
=Alarm()
WAIT WINDOW ;
[Only 20 orders can be combined in one transmission] TIMEOUT 1
EXIT
ENDIF
DIMENSION SELECTED ( J )
SELECTED ( J ) = OrderList ( I )
ENDIF
ENDFOR

OrderCount = J

RETURN



FUNCTION BuildPO

IF FILE([ORDERS.])
DELETE FILE ORDERS.
ENDIF

fh = FCREATE ( [ORDERS.] )
IF fh = -1
=Alarm()
WAIT WINDOW [Problem creating ORDERS file - cancelling] TIMEOUT 1
Done = .T.
RETURN TO MASTER
ENDIF

IF Testing

IF FILE([ORDERS.TXT])
DELETE FILE ORDERS.TXT
ENDIF

ft = FCREATE ( [ORDERS.TXT] )
IF ft = -1
=Alarm()
WAIT WINDOW [Problem creating ORDERS PREVIEW file - cancelling] ;
TIMEOUT 1
Done = .T.
RETURN TO MASTER
ENDIF

ENDIF

=RECORD001()

FOR I = 1 TO ALEN ( Selected )
SELECT PURCHORD
SEEK SELECTED(I)
=RECORD002()
ENDFOR

=RECORD004()

RETURN



FUNCTION RECORD001

* (1) Make needed transformations here

m.CustID = LEFT(SETTINGS.CUSTOMERID,6)
m.PIN = SETTINGS.PIN
m.Cycle = LEFT(SETTINGS.ORDERCYCLE,1)
m.Message = SETTINGS.BACKORDERS


* (2) Insert required spaces while building record structure

Record001 = ;
[001] ;
+ m.CustID ;
+ m.TodaysDate ;
+ m.PIN ;
+ m.Cycle + SPACE(3) ;
+ m.Message

* (3) Pad to desired length with trailing blanks

RECORD001 = LEFT ( RECORD001 + SPACE(50) , 60 )

=FWRITE ( fh , RECORD001 )

IF Testing
* (4) Write out a CR-delimited file to assist in debugging
=FWRITE ( ft , RECORD001 + CRLF )
ENDIF

OneCount = OneCount + 1

RETURN



FUNCTION RECORD002

* (1) Make needed transformations here

m.OrderNum = PURCHORD.ORDERNUM + SPACE(2)
m.OrderDate = SUBSTR ( DTOC ( OrderDate , 1 ) , 3 ) && YYMMDD
m.Status = ;
IIF ( STATUS = [P] , [PRIORITY ] , ;
IIF ( STATUS = [C] , [CREDIT HOLD] , ;
IIF ( STATUS = [O] , [CONSOLIDATE] , ;
[ ] ) ) )

* (2) Insert required spaces while building record structure

Record002 = ;
[002] ;
+ m.CustID ;
+ m.TodaysDate ;
+ m.OrderNum + SPACE( 5) ;
+ m.OrderDate ;
+ m.Status

* (3) Pad to desired length with trailing blanks

RECORD002 = LEFT ( RECORD002 + SPACE(50) , 60 )

=FWRITE ( fh , RECORD002 )

IF Testing
* (4) Write out a CR-delimited file to assist in debugging
=FWRITE ( ft , RECORD002 + CRLF )
ENDIF

TwoCount = TwoCount + 1

SELECT POITEM
SCAN FOR ORDERNUM = PURCHORD.ORDERNUM
=RECORD003()
ENDSCAN

RETURN



FUNCTION RECORD003

* (1) Make needed transformations here

* m.OrderNum = PURCHORD.ORDERNUM + SPACE(2)
m.ItemNo = POITEM.ITEMNO
m.Quantity = TRANSFORM ( POITEM.QUANTITY, [@L #####] )
m.UnitPrice = TRANSFORM ( POITEM.UNITPRICE*100, [@L ##########] )
m.Weight = TRANSFORM ( POITEM.WEIGHT*100, [@L #####] )

* (2) Insert required spaces while building record structure

Record003 = ;
[003] ;
+ m.CustID ;
+ m.TodaysDate ;
+ m.OrderNum + SPACE(5) ;
+ m.ItemNo ;
+ m.Quantity ;
+ m.UnitPrice ;
+ m.Weight

* (3) Pad to desired length with trailing blanks

RECORD003 = LEFT ( RECORD003 + SPACE(50) , 60 )

=FWRITE ( fh , RECORD003 )

IF Testing
* (4) Write out a CR-delimited file to assist in debugging
=FWRITE ( ft , RECORD003 + CRLF )
ENDIF

ThreeCount = ThreeCount + 1

RETURN



FUNCTION RECORD004

* (1) Make needed transformations here

OneCount = TRANSFORM ( OneCount , [@L #####] )
TwoCount = TRANSFORM ( TwoCount , [@L #####] )
ThreeCount = TRANSFORM ( ThreeCount , [@L #####] )

* (2) Insert required spaces while building record structure

Record004 = ;
[004] ;
+ m.CustID ;
+ m.TodaysDate ;
+ Onecount ;
+ Twocount ;
+ Threecount


* (3) Pad to desired length with trailing blanks

RECORD004 = LEFT ( RECORD004 + SPACE(50) , 60 )

=FWRITE ( fh , RECORD004 )

IF Testing
* (4) Write out a CR-delimited file to assist in debugging
=FWRITE ( ft , RECORD004 + CRLF )
ENDIF

RETURN


* Program-ID.....: SENDPO.PRG
* Purpose........: Communicate with mainframe.
* Method.........: Sends file ORDERS, waits for file CONFIRM
* : Uses PROCOMM script file.

! FoxSwap PCPLUS MyScript

RETURN


* Program-ID...: CONFIRM.PRG
* Purpose......: Prints confirmation report and updates purchase orders.

STORE 0 TO TotWeight, TotLines, TotCount , TotValue, TotQty, TotPOS

fh = FOPEN ( [CONFIRM] )
IF fh = -1
=Alarm()
WAIT WINDOW [Error opening confirmation file ] TIMEOUT 1
RETURN
ENDIF

m.Ponum = [ ]

DO WHILE NOT FEOF ( fh )

 Testing=.F. и передает его. Я использую вызов программы ProComm PLUS и передаю ей в качестве параметра имя файла сценария; настоятельно рекомендую вам аналогичную методику. При получении ответа в виде файла =FWRITE ( ft , RECORD002 + CRLF )