cmMoney : Begin
Money;
End;
cmArchive : Begin
Arc_;
End;
else
ClearEvent(Event);
end;
ClearEvent(Event)
End;
2.6.4.4 ПРОГРАММИРОВАНИЕ ДИАЛОГОВЫХ ЗАПРОСОВ
В обработчике событий TSklVks.HandleEvent я предусмотрел вызов нескольких процедур, с помощью которых реализуются конкретные действия. Начну с процедуры Prih. Ее задача - ввести данные о новом приходе. Очевидно, что данные будут храниться в файле, поэтому опишу сразу структуру данных. Данные выглядят в виде записи, которые состоят из полей. Например структура данных для приходов выглядит следующим образом :
(* Структура приходов *)
Type PPP = record
Number : LongInt; { номер прихода}
FirmName : String[22]; { фирма поставщик }
Date : String[10]; { дата прихода }
Kurs : Real; { курс доллара ЦБ РФ на день прихода}
TotalItems : LongInt; { кол-во пришедших наименований}
SebSumma : Real; { общая сумма прихода }
End;
(* Структура приходов для наименования}
Type PPPItems = Record
Number : LongInt; {ноиер наименования }
PrihodNum : LongInt; {номер прихода}
SkladNum : LongInt; {номер наименования на складе}
Name : String[lpname]; {наименование}
Rasf : String[lprasf]; {расфасовка}
Box : Integer; {кол-во штук в упаковке}
Kolvo : LongInt; {кол-во поступившего наименования}
SebPrice : Real; {себестоимость наименования}
Price : Real; {стоимость наименования}
SellTimes : Boolean; {зарезервировано}
End;
Эти строки находятся в модуле SKLTYPES.PAS. В нем же находится описание всех структур данных, а так же их связь с файлами и основной программой :
Var {ТИП ДАННЫХ}
PrihodData : PPP;
PrihodItemsData : PPPItems;
....{ТИП ДАННЫХ В ФАЙЛЕ}
PrihodFile : File of PPP;
PrihodItemsFile : File of PPPItems;
....
Теперь, надо ввести данные. Для этого сначала появляется панель диалога для ввода даты прихода, курса прихода и реквизитов фирмы поставщика. Ее я создавал при помощи небольшой программы Dialog Designer 4.0. При создании панелей диалога при помощи это программы генерируется модульный файл в котором реализован код для отображения окна, его закрытия, так же содержится обработчик событий.
Ниже приведу текст модуля, cодержащего код для создания диалогового окна.
unit PRIHOD1;
interface
uses Drivers, Objects, Views, Dialogs, Validate;
type
PrihodDataRec = record
Field1 : String[10];
Field3 : String[13];
Field2 : String[22];
end;
PPrihodDataRec = ^PrihodDataRec;
{ TPrihodDataDialog }
PPrihodDataDialog = ^TPrihodDataDialog;
TPrihodDataDialog = object(TDialog)
constructor Init;
constructor Load(var S: TStream);
procedure HandleEvent(var Event: TEvent); virtual;
procedure Store(var S: TStream);
function Valid(Command : word): boolean; virtual;
destructor Done; virtual;
end;
Var
PRD : PrihodDataRec;
Const
RPrihodDataDialog : TStreamRec = (
ObjType: 12345; {= 100 here!!}
VmtLink: Ofs(Typeof(TPrihodDataDialog)^);
Load : @TPrihodDataDialog.Load;
Store : @TPrihodDataDialog.Store);
implementation
{ TPrihodDataDialog }
constructor TPrihodDataDialog.Init;
var
R: TRect;
Control : PView;
begin
R.Assign(13, 3, 66, 17);
inherited Init(R, 'Приход / Ввод данных');
Flags := Flags and not wfMove;
R.Assign(24, 3, 36, 4);
Control := New(PInputLine, Init(R, 10));
Insert(Control);
PInputLine(Control)^.Validator := New(PPXPictureValidator, Init('{##}/{##}/{####}', true));
R.Assign(5, 3, 24, 4);
Insert(New(PLabel, Init(R, 'Дата : ', Control)));
R.Assign(24, 5, 39, 6);
Control := New(PInputLine, Init(R, 13));
Insert(Control);
PInputLine(Control)^.Validator := New(PPXPictureValidator, Init('*#[.#][#]', False));
R.Assign(5, 5, 24, 6);
Insert(New(PLabel, Init(R, 'Курс прихода : ', Control)));
R.Assign(24, 7, 48, 8);
Control := New(PInputLine, Init(R, 22));
Insert(Control);
R.Assign(5, 7, 24, 8);
Insert(New(PLabel, Init(R, 'Фирма поставщик : ', Control)));
R.Assign(7, 10, 18, 12);
Control := New(PButton, Init(R, '~С~брос', cmCancel, bfNormal));
Insert(Control);
R.Assign(23, 10, 41, 12);
Control := New(PButton, Init(R, '~Н~аименования', cmOK, bfDefault));
Insert(Control);
R.Assign(37, 3, 51, 4);
Control := New(PStaticText, Init(R, '[ ДД/ММ/ГГГГ ]'));
Insert(Control);
SelectNext(False);
end;
constructor TPrihodDataDialog.Load(var S: TStream);
begin
inherited Load(S);
end;
procedure TPrihodDataDialog.HandleEvent(var Event: TEvent);
begin
(*---
if Event.What = evMessage then
case Event.Command of
end; --*)
inherited HandleEvent(Event);
(*---
if Event.What = evMessage then
case Event.Command of
end; --*)
end;
procedure TPrihodDataDialog.Store(var S: TStream);
begin
inherited Store(S);
end;
function TPrihodDataDialog.Valid(Command : word): boolean;
var
Result : boolean;
begin
Result := inherited Valid(Command);
Valid := Result;
end;
destructor TPrihodDataDialog.Done;
begin
inherited Done;
end;
end.
Для отображения этого диалогового окна в процедуре PRIH испльзовался следующий код :
PRD.Field1:=ShowDate; { Дата прихода - текущая дата}
Str(GetKurs:-1:2,PRD.Field3); { Курс прихода - текущий курс }
FillChar(PRD.Field2[1],22,' '); {Онулить фирму поставщика}
c1:=ExecuteDialog(New(PPrihodDataDialog,Init),@PRD); { Ввести на экран панель диалога}
После ввода курса, даты и фирмы поставщика нужно ввести собственно наименования. Для этого я создал диалоговое окно (см. модуль PRIH2.PAS), где вводится информации о наименовании (название, расфасовка, стоимость, себестоимость и т.д.). (см. рис. 3)
(рис.3)
Введя данные, их надо сохранить. Чтобы это сделать надо сначала открыть файл, затем в его конец записать данные и в конце этот файл закрыть. Это делается приблизительно так ( подробно см. файл FIRMA.PAS) :
OpenPrihodFile; { Процедура открытия файла см.модуль SklFiles.Pas}
Seek(PrihodFile,FileSize(PrihodFile)-1);
Write(PrihodFile,PrihodData);
ClosePrihodFile; { см. модуль SklFiles.Pas}
Если нажимается кнопка СБРОС, то вызывается процедура сброса прихода в которой все задействованные файлы данных усекаются до предыдущей длина (в начале процедуры PRIH запоминаются текущие длины файлов) при помощи стандартной процедуры TRUNCATE.
После успешного ввода данных появляется запрос о печати приходной накладной. Печать осуществляется стандартными средствами (см. процедуры PrintPrihodNakl и PrintEndOfPrihodNakl в модуле SklUnit). С помощью константы LST, компьютер связывается с печатающим устройством на LPT1, затем процедурами Writeln(LST,‘строка для печати’) данные выводятся на принтер. Все введенные данные помещаются в файлы данных : SKLAD.001 (Товар на складе) , PRIHOD.001 (Заголовки приходов), PRIHOD.002 (Пришедшие наименования). Затем пользователь получает доступ к следующим командам меню : Данные-Склад, Данные-Приходы, Печать-Прайс-Лист, Печать-Отчет о наличии товара на складе, Клиент-Продажа. Основной из вышеперечисленных команд является Клиент-Про После успешного ввода данных появляется запрос о печати приходной накладной. Печать осуществляется стандартными средствами (см. процедуры PrintPrihodNakl и PrintEndOfPrihodNakl в модуле SklUnit). С помощью константы LST, компьютер связывается с печатающим устройством на LPT1, затем процедурами Writeln(LST,‘строка для печати’) данные выводятся на принтер. Все введенные данные помещаются в файлы данных : SKLAD.001 (Товар на складе) , PRIHOD.001 (Заголовки приходов), PRIHOD.002 (Пришедшие наименования). Затем пользователь получает доступ к следующим командам меню : Данные-Склад, Данные-Приходы, Печать-Прайс-Лист, Печать-Отчет о наличии товара на складе, Клиент-Продажа. Основной из вышеперечисленных команд является Клиент-Продажа. При помощи этой команды пользователь должен выписать накладную по заказу клиента. Реализовал эту команду так : Появляется диалоговое окно со списком наименований, имеющихся на складе (В нем пользователь выбирает продаваемые наименования и их количества). Затем при нажатии на кнопку ОПЛАТА появляется окно со списком всех выбранных наименований (накладная) в котором предоставляется возможность отредактировать введенные данные (изменить количество каждого наименования, его стоимость и курс доллара). Далее после нажатия на ОПЛАТА на экране появляется диалоговое окно для ввода следующей информации : реквизиты клиента, форма сделки (реализация), округление суммы накладной, дата накладной. После нажатия на ВЫПИСАТЬ НАКЛАДНУЮ у пользователя программы спрашивает, нужно ли печатать накладную и если товар дается клиенту не на реализацию, то спрашивается, нужно ли печатать приходный кассовый ордер. Все введенные данные помещаются в файлы данных : SDELKA.001 (Заголовки сделок), SDELKA.002 (Наименования сделок), в файле SKLAD.001 делаются соответствующие изменения по количеству оставшегося товара.
Для реализации диалогового окна со списком я создал коллекцию строк наименований имеющихся на складе, причем если товара на складе не осталось то это наименование не помещаеся в коллекцию.
Data1:=New(PStrSor,Init(10,1)); {Указатель на коллекцию }
For N:=0 to FileSize(DataFile)-1 do {Каждую позицию проверить, если нулевая то не вносить в коллекцию)
begin {1}
Seek(DataFile,n);
Read(DataFile,Data); {Считываем данные}
val(Data.ProductOst,tempccc,code);
Str(Data1^.Count+1,Numm);
if tempccc=0 then goto ccc; { Если нулевая позиция, то не вносить в коллекцию }
o[0]:=chr(57);
FillChar(o[1],57,' ');
with data do
begin {Создание строки типа : Номер, Наименование, Расфасовка}
move(Numm[1],o[2],Length(Numm));
move(ProductName[1],o[succ(lpnum-1)+4],Length(Data.ProductName));
move(ProductRasf[1],o[succ(lpnum+lpname)+5],Length(Data.ProductRasf));
move(ProductNumber[1],o[50],Length(Data.ProductNumber));
end;
Data1^.Insert(NewStr(o)); {Помещаю ее в коллекцию строк}
ccc:
end; {1}
Для реализации списка строк в Turbo Vision предусмотрен объект TListBox. Это объект создает специальное окно скроллера с указателем на текущий элемент. Наименования я поместил в вышеописанную коллекцию строк, указатель на которую передал объекту с помощью метода TListBox.NewList.
Var
SCR : PScrollBar ;
LIST : PLitstBox;
.....
Begin
.....
RR.Assign(50,05,51,17); {Координаты скроллера}
Scr:=New(PscrollBar,Init(RR)); {Указатель на полосу скроллера}
Insert(Scr); {Создаем скроллер}
R.Assign(03,05,50,17); {Координаты окна со списком}
List:=New(PMyListBox,Init(R,1,scr)); {Указатель на окно со списком}
List^.NewList(Data1); {Связывание окна со коллекцией строк}
Insert(list); {Создаем окно со сп Insert(list); {Создаем окно со списком }
....
End;
Диалоговое окно выбора наименований выглядит следующим образом :
Перейду теперь к описанию процедуры Данные-Сделки. При выборе соответствующего пункта меню на экране почвляется диалоговое окно выбора периода для просмотра сделок. В нем нужно ввести нижнюю и верхнюю границы периода.
После ввода при нажатии на кнопку ПОКАЗАТЬ ЗА ПЕРИОД разворачивается окно со списком сделок (накладных) относящихся к введенному периоду. Это реализованно следующим образом. При соэдании коллекции строк с накладными сравниваютя дата сделки, нижняя и верхняя границы периода при помощи написанной прцедуоы DATECOMP из модуля DATES.PAS. Но для того, чтобы накладные распологались по порядку (по дате) мне пришлось перекрыть метод сортировки коллекции (см. модуль SKLSTR.PAS). После того, как создано диалоговое окно, пользователю предоставляется возможность распечатать выбранную накладную, соответствующую накладную по себестоимости и если накладная оплачена, то и приходный кассовый ордер.
2.6.4.5 ПРЕОБРАЗОВАНИЕ ДАННЫХ ПРИ ВЫВОДЕ ДОКУМЕНТОВ НА ПЕЧАТАЮЩЕЕ УСТРОЙСТВО.
В основном, вывод осуществляется стандартными процедурами Borland Pascal. Но например для печати приходного кассового ордера мне пришлось создать функцию для перевода суммы в слова (123 рубля в сто двадцать три рубля). Эта функция находится в модуле NUMSTR.PAS. Или для большей читабельности документов написал функцию для преобразования строки типа 1000000.00 в строку типа 1,000,000.00. Эта функция так же находится в модуле NUMSTR.PAS.
2.6.4.6 АРХИВИРОВАНИЕ ДАННЫХ .
Для того, чтобы в списке сделок не появлялись уже не нужные данные я написал подпрограмму для архивирования данных. При выборе команды меню АРХИВ-РАБОТА С АРХИВОМ на экране появляется диалоговое окно с двумя списками строк. В первом списке (СКЛАД) распологаются периоды сделок (месяц, год), которые доступны пользователю для просмотра, а во втором смиске (АРХИВ) сделки которые находятся в АРХИВЕ. Для архивации периода сделок использовал следующий алгоритм: При выборе периода для архивации, программа проверяет каждую сделку на принадлежность к этому периоду. Ecли сделка попадает а него, то поле SDELKA.ARCHIVE устанавливается в TRUE. При выборе периода для деархивации прграмма устанавливает поля SDELKA.ARCHIVE соответствующих сделок в FALSE. Подпрограмма архивации также увеличивает быстродействие программмы за счет уменьшения количества выводимых сделок.
2.6.4.7 ВВОД УСТАНОВОК .
Для того, чтобы предотвратить использование данной программы лицами не имеющим разрешения ее использование был создана подпрограммы для ввода пароля. Пароль (и все установки) хранится в конфигурационном файле FIRMA.DAT, который имеет следующую структуру :
Type {Структура данных для конфигурационного файла}
ConfigFileStructure = record
CassName : String[22]; { ФИО Кассира}
BuhName : String[30]; { ФИО бухгалтера}
GenName : String[30]; { ФИО коммерческого директора}
UserName : String[30]; { ФИ UserName : String[30]; { ФИО пользователя}
Signature : String[10]; { Сюда пишется дата BIOS'а }
Reserved1 : String[10]; { }
Reserved2 : String[10]; {Зарезервировано }
Reserved3 : String[20]; { }
Password : String[10]; {Пароль}
FirmName : String[60]; {Реквизиты фирмы}
Kurs : Real; {Курс доллара}
end;
При начальном запуске пароль не установлен. При необходимости из меню УСТАНОВКИ-ПАРОЛЬ можно установить пароль. Опишу реальзацию подпрограмму для его установки. Создается диалоговое окно со строкой ввода. Для того, чтобы привводе пароля на экране не отображались вводимые символы, был перекрыт метод TInputLine.HandelEvent так, что бы каждый введенный символ помещался в отдельную строку, а отображался ‘*’ (звездочкой). Для детального просмотра алгоритма см. модуль PASSWORD.PAS. После введения пароля и нажатия на кнопку OK появляется диалоговое окно для проверки введенного пароля. Если оба введеных пароля совпадают, то он записывается в файл FIRMA.DAT в поле Password. Если они не совпадают, то пароль не устанавливается.
Анологично реализованы подпрограммы для ввода реквизитов и курса доллара.
2.6.4.8. ПАРОЛИРОВАНИЕ.
Для того, чтобы предотвратить доступ к данным я решил создать пароль, который запрашивался бы каждый раз при запуске программы. Для этого в записи ConfigFileStructure и добавил поле PassWord типа String[10];
Type
ConfigFileStructure = record
....
Password : String[10];
....
End;
Затем в меню УСТАНОВКИ добавил подменю УСТАНОВКИ-ПАРОЛЬ и связал
с ним комманду cmDostup, которая вызывает процедуру ввода пароля Dostup
из модуля SETUP.PAS. Эта процедура реализована так: На экране появляется
диалоговое окно со строкой ввода :
Введите пароль. В ней вводится пароль и при нажатии на кнопку OK
появляется следующее окно для проверки введенного пароля. Если во втором
окне введенный пароль соответствует введенному паролю в первом окне, то
этот пароль кодируется и записывается в файл FIRMA.DAT. Кодирование
происходит следующим образом. Каждый символ пароля умножается на число
(58+номер символа)*2. При запуске программы появляется диалоговое окно со
сторокой вводаВведитепароль. Если введенный пароль в закодированном
виде соответствует установленному закодированному паролю, то программа
продолжает функционировать , а если не соответсвуют, то выводится
сообщение о не правильно введенном пароле и программа прекращает
функционировать.
2.6.4.9. СТРУКТУРА БАЗ ДАННЫХ (БД).
Теперь рассмотрю структуру баз данных. Структура не является сложной, однако
она может содержать все данные, необходимые для данной системой и связь между разными БД осуществляется довольно простым путем.
Основной БД является БД, которая содержит информацию о наименованиях, имеющихся на складе (о количестве, о стоимости и т.д.). Она имеет следующую структуру :
Type
DataType = record { Структура складских данных}
ProductNumber : String[5]; {Номер}
ProductName : String[22]; {Наименование}
ProductRasf : String[10]; {Расфасовка}
PieceInBox : String[5]; {Кол-во штук в упаковке}
ProductPrice : String[12]; { Себестоимость }
ProductKol : String[12]; {Поставленное количество}
ProductFirm : String[22]; {Фирма поставщик}
ProductDate : String[l10]; {Дата прихода}
ProductOST : String[12]; {Осталось на складе}
ProductKurs : Real; { Курс прихода }
ProductSellPrice : Real; { Стоимость }
ProductVozvrat : LongInt; {Возврат}
end;
Var
Data : DataType;
DataFile : File of DataType; {Файл SKLAD.001}
Для записи (считывания) данных в (из) нее используется следующий алгоритм:
1. Открыть файл данных.
2. Поместить указатель положения в файле на нужную запись.
3. Для записи данных - каждому полю записи Data присвоить соответствующие данные, затем их записать в файл данных Write(DataFile,Data). Для считывания данных - Считать данные из файла Read(DataFile,Data);
4. Закрыть файл данных.
Более интересной структурой является структура сделок. :
Type {Заголовок сделки}
SdelkaInfo = record
SdelkaNumber : LongInt; {Номер сделки }
Reserved : Word;
Archive : Boolean; {Флаг архива}
ItemIndex : LongInt; {Индекс в наименованиях}