root / LicensesMercure / UFMenu.pas @ 1
Historique | Voir | Annoter | Télécharger (14,185 ko)
1 | 1 | avalancogn | unit UFMenu;
|
---|---|---|---|
2 | |||
3 | interface
|
||
4 | |||
5 | uses
|
||
6 | Windows, Forms, Messages, SysUtils, Variants, Classes, Graphics, Controls, |
||
7 | Dialogs, StdCtrls, Buttons, PBNumEdit, DB, DBTables, mysql, LbCipher, |
||
8 | DateUtils, JvComponent, JvComputerInfoEx, JvComponentBase; |
||
9 | |||
10 | type
|
||
11 | TFMenu = class(TForm)
|
||
12 | PBOrders: TPBNumEdit; |
||
13 | BBOrders: TBitBtn; |
||
14 | PBAjinomoto: TPBNumEdit; |
||
15 | BBAjinomoto: TBitBtn; |
||
16 | PBKeys: TPBNumEdit; |
||
17 | BBKeys: TBitBtn; |
||
18 | GBKeys: TGroupBox; |
||
19 | GBOrders: TGroupBox; |
||
20 | GBAjinomoto: TGroupBox; |
||
21 | GBAdmin: TGroupBox; |
||
22 | MLog: TMemo; |
||
23 | TOrder: TTable; |
||
24 | TAjinomoto: TTable; |
||
25 | TLicense: TTable; |
||
26 | TOrders: TTable; |
||
27 | GBINRA: TGroupBox; |
||
28 | BBINRA: TBitBtn; |
||
29 | GBInvoices: TGroupBox; |
||
30 | BBInvoices: TBitBtn; |
||
31 | TInvoices: TTable; |
||
32 | TINRA: TTable; |
||
33 | TLicenses: TTable; |
||
34 | BBAnnual: TBitBtn; |
||
35 | TEducation: TTable; |
||
36 | ComputerInfo: TJvComputerInfoEx; |
||
37 | TAgreement: TTable; |
||
38 | GBAgreement: TGroupBox; |
||
39 | BBAgreement: TBitBtn; |
||
40 | BBUSB: TBitBtn; |
||
41 | BBManual: TBitBtn; |
||
42 | LManual: TLabel; |
||
43 | LUSB: TLabel; |
||
44 | LAnnual: TLabel; |
||
45 | EAdmin: TEdit; |
||
46 | EUsername: TEdit; |
||
47 | EPassword: TEdit; |
||
48 | procedure FormCreate(Sender: TObject);
|
||
49 | procedure FormShow(Sender: TObject);
|
||
50 | procedure FormClose(Sender: TObject; var Action: TCloseAction); |
||
51 | procedure BBOrdersClick(Sender: TObject);
|
||
52 | procedure BBAjinomotoClick(Sender: TObject);
|
||
53 | procedure BBKeysClick(Sender: TObject);
|
||
54 | procedure BBInvoicesClick(Sender: TObject);
|
||
55 | procedure BBINRAClick(Sender: TObject);
|
||
56 | procedure BBAnnualClick(Sender: TObject);
|
||
57 | procedure BBAgreementClick(Sender: TObject);
|
||
58 | procedure BBUSBClick(Sender: TObject);
|
||
59 | procedure BBManualClick(Sender: TObject);
|
||
60 | private
|
||
61 | { D?clarations priv?es }
|
||
62 | t: Text; |
||
63 | public
|
||
64 | { D?clarations publiques }
|
||
65 | DateLimite: TDateTime; |
||
66 | function MD5Str(Digest: TMD5Digest): String; |
||
67 | function EducationKey(Version, FirstName, LastName, Company, VolumeSerialNumber, FinalDate: String): String; |
||
68 | function CompleteKey(LicenseNumber: Integer; Version, FirstName, LastName, Company, VolumeSerialNumber, FinalDate: String): String; |
||
69 | end;
|
||
70 | |||
71 | var
|
||
72 | FMenu: TFMenu; |
||
73 | |||
74 | const
|
||
75 | SHARED_DRIVE = '\\pnas1.stockage.inra.fr\rennes-pegase\root';
|
||
76 | SHARED_FOLDER = '\SG-prj-Inraporc\Licenses\';
|
||
77 | SMTP_SERVER = 'smtp.inra.fr';
|
||
78 | IMAP_SERVER = 'imap.inra.fr';
|
||
79 | LDAP_SERVER = 'ldap-authentification.inra.fr';
|
||
80 | //EXCHANGE_USERNAME = 'INRA\avalancogne';
|
||
81 | //EXCHANGE_PASSWORD = 'Al1Messac35!';
|
||
82 | EXCHANGE_SHARED_MAILBOX = 'inraporc-rennes';
|
||
83 | MAIL_INRAPORC = 'inraporc-rennes@inra.fr';
|
||
84 | MAIL_INRA_TRANSFERT_1 = 'chrystele.eiler@inra.fr';
|
||
85 | MAIL_INRA_TRANSFERT_2 = 'franck.leguerhier@inra.fr';
|
||
86 | MAIL_AJINOMOTO = 'Corrent_Etienne@eli.ajinomoto.com';
|
||
87 | MYSQL_HOST = 'inraporc.inra.fr';
|
||
88 | MYSQL_USER = 'inraporc';
|
||
89 | MYSQL_PASSWD = 'jydjvmav';
|
||
90 | MYSQL_DB = 'inraporc';
|
||
91 | MYSQL_PORT = 3306;
|
||
92 | SCP_HOST = 'inraporc.inra.fr';
|
||
93 | SCP_HOSTKEY = '64:29:24:0b:24:7b:05:e1:6a:5e:36:d3:81:47:08:f9';
|
||
94 | SCP_LOGIN = 'valanc';
|
||
95 | SCP_PASSWD = 'al1:7a!?';
|
||
96 | SCP_PATH = '/var/www/html/inraporc/Uploads/';
|
||
97 | |||
98 | var
|
||
99 | con: PMYSQL; // connection handler
|
||
100 | res: PMYSQL_RES; // result
|
||
101 | row: PMYSQL_ROW; // row
|
||
102 | que: AnsiString; // query
|
||
103 | cur: Integer; // current record
|
||
104 | SLOrder, SLAjinomoto, SLLicense: TStrings; |
||
105 | |||
106 | implementation
|
||
107 | |||
108 | uses
|
||
109 | UFOrder, UFInvoice, UFINRA, UFAjinomoto, UFAgreement, UFKeys, UFAnnual, UFUSB, |
||
110 | UFManual, UFConnect; |
||
111 | |||
112 | {$R *.dfm}
|
||
113 | |||
114 | type
|
||
115 | WindowsString = type AnsiString(1252); |
||
116 | |||
117 | procedure TFMenu.FormCreate(Sender: TObject);
|
||
118 | begin
|
||
119 | Top := Forms.Screen.WorkAreaHeight - Height; |
||
120 | Left := 0;
|
||
121 | end;
|
||
122 | |||
123 | procedure TFMenu.FormShow(Sender: TObject);
|
||
124 | begin
|
||
125 | AssignFile(t, SHARED_DRIVE + SHARED_FOLDER + 'Logs\Errors.log');
|
||
126 | if FileExists(SHARED_DRIVE + SHARED_FOLDER + 'Logs\Errors.log') |
||
127 | then
|
||
128 | try
|
||
129 | Append(t); |
||
130 | except
|
||
131 | MessageDlg('Le fichier de log ne peut pas ?tre ouvert.' + sLineBreak + 'L''application est probablement d?j? lanc?e.', mtError, [mbOK], 0); |
||
132 | Application.Terminate; |
||
133 | end
|
||
134 | else
|
||
135 | try
|
||
136 | Rewrite(t); |
||
137 | except
|
||
138 | MessageDlg('Le fichier de log ne peut pas ?tre cr??.' + sLineBreak + 'V?rifier les droits d''acc?s.', mtError, [mbOK], 0); |
||
139 | Application.Terminate; |
||
140 | end;
|
||
141 | |||
142 | FConnect := TFConnect.Create(Self); |
||
143 | if FConnect.ShowModal <> mrOk then |
||
144 | begin
|
||
145 | FConnect.Release; |
||
146 | Application.Terminate; |
||
147 | end;
|
||
148 | FConnect.Release; |
||
149 | |||
150 | Writeln(t, '*************************');
|
||
151 | Writeln(t, 'BEGIN ', DateTimeToStr(Now));
|
||
152 | Writeln(t, 'Computer IP address is ', ComputerInfo.Identification.IPAddress);
|
||
153 | EUsername.Text := ComputerInfo.Identification.LocalUserName; |
||
154 | Writeln(t, 'Local user name is ', EUsername.Text);
|
||
155 | MLog.Lines.Add('User is ' + EAdmin.Text);
|
||
156 | SLOrder := TStringList.Create; |
||
157 | SLAjinomoto := TStringList.Create; |
||
158 | SLLicense := TStringList.Create; |
||
159 | MLog.Lines.Add('Opening dBase tables...');
|
||
160 | // Probl?me avec Windows Vista (fichier PDOXUSRS.NET)
|
||
161 | TLicense.DBSession.NetFileDir := SHARED_DRIVE + SHARED_FOLDER; |
||
162 | TOrder.TableName := SHARED_DRIVE + SHARED_FOLDER + TOrder.TableName; |
||
163 | try
|
||
164 | TOrder.Active := True; |
||
165 | except
|
||
166 | MLog.Lines.Add('Table ' + TOrder.TableName + ' could not be opened !'); |
||
167 | end;
|
||
168 | TLicense.TableName := SHARED_DRIVE + SHARED_FOLDER + TLicense.TableName; |
||
169 | try
|
||
170 | TLicense.Active := True; |
||
171 | except
|
||
172 | MLog.Lines.Add('Table ' + TLicense.TableName + ' could not be opened !'); |
||
173 | end;
|
||
174 | TEducation.TableName := SHARED_DRIVE + SHARED_FOLDER + TEducation.TableName; |
||
175 | try
|
||
176 | TEducation.Active := True; |
||
177 | except
|
||
178 | MLog.Lines.Add('Table ' + TEducation.TableName + ' could not be opened !'); |
||
179 | end;
|
||
180 | TOrders.TableName := SHARED_DRIVE + SHARED_FOLDER + TOrders.TableName; |
||
181 | try
|
||
182 | TOrders.Active := True; |
||
183 | except
|
||
184 | MLog.Lines.Add('Table ' + TOrders.TableName + ' could not be opened !'); |
||
185 | end;
|
||
186 | TInvoices.TableName := SHARED_DRIVE + SHARED_FOLDER + TInvoices.TableName; |
||
187 | try
|
||
188 | TInvoices.Active := True; |
||
189 | except
|
||
190 | MLog.Lines.Add('Table ' + TInvoices.TableName + ' could not be opened !'); |
||
191 | end;
|
||
192 | TINRA.TableName := SHARED_DRIVE + SHARED_FOLDER + TINRA.TableName; |
||
193 | try
|
||
194 | TINRA.Active := True; |
||
195 | except
|
||
196 | MLog.Lines.Add('Table ' + TINRA.TableName + ' could not be opened !'); |
||
197 | end;
|
||
198 | TAjinomoto.TableName := SHARED_DRIVE + SHARED_FOLDER + TAjinomoto.TableName; |
||
199 | try
|
||
200 | TAjinomoto.Active := True; |
||
201 | except
|
||
202 | MLog.Lines.Add('Table ' + TAjinomoto.TableName + ' could not be opened !'); |
||
203 | end;
|
||
204 | TLicenses.TableName := SHARED_DRIVE + SHARED_FOLDER + TLicenses.TableName; |
||
205 | try
|
||
206 | TLicenses.Active := True; |
||
207 | except
|
||
208 | MLog.Lines.Add('Table ' + TLicenses.TableName + ' could not be opened !'); |
||
209 | end;
|
||
210 | TAgreement.TableName := SHARED_DRIVE + SHARED_FOLDER + TAgreement.TableName; |
||
211 | try
|
||
212 | TAgreement.Active := True; |
||
213 | except
|
||
214 | MLog.Lines.Add('Table ' + TAgreement.TableName + ' could not be opened !'); |
||
215 | end;
|
||
216 | // Check DLL
|
||
217 | libmysql_fast_load(nil);
|
||
218 | case libmysql_status of |
||
219 | LIBMYSQL_UNDEFINED: MLog.Lines.Add('libmysql_load() has not yet been called !');
|
||
220 | LIBMYSQL_MISSING: MLog.Lines.Add('libmySQL.dll could not be located !');
|
||
221 | LIBMYSQL_INCOMPATIBLE: MLog.Lines.Add('libmySQL.dll was found but is not compatible !');
|
||
222 | end;
|
||
223 | if libmysql_status <> LIBMYSQL_READY then Exit; |
||
224 | MLog.Lines.Add('Initializing connection handler...');
|
||
225 | con := mysql_init(nil);
|
||
226 | if con = nil |
||
227 | then
|
||
228 | begin
|
||
229 | MLog.Lines.Add('Insufficient memory to initialize connection handler !');
|
||
230 | Exit; |
||
231 | end;
|
||
232 | MLog.Lines.Add('Connecting to database...');
|
||
233 | if mysql_real_connect(con, MYSQL_HOST, MYSQL_USER, MYSQL_PASSWD, MYSQL_DB, MYSQL_PORT, nil, 0) = nil |
||
234 | then
|
||
235 | begin
|
||
236 | MLog.Lines.Add(mysql_error(con)); |
||
237 | Exit; |
||
238 | end;
|
||
239 | que := 'SELECT `Key` FROM `commandes`';
|
||
240 | MLog.Lines.Add(que); |
||
241 | if mysql_query(con, PAnsiChar(que)) <> 0 |
||
242 | then
|
||
243 | MLog.Lines.Add(mysql_error(con)) |
||
244 | else
|
||
245 | begin
|
||
246 | res := mysql_use_result(con); |
||
247 | if res = nil |
||
248 | then
|
||
249 | MLog.Lines.Add(mysql_error(con)) |
||
250 | else
|
||
251 | begin
|
||
252 | row := mysql_fetch_row(res); |
||
253 | while row <> nil do |
||
254 | begin
|
||
255 | SLOrder.Add(row^[0]);
|
||
256 | row := mysql_fetch_row(res); |
||
257 | end;
|
||
258 | mysql_free_result(res); |
||
259 | end;
|
||
260 | end;
|
||
261 | PBOrders.AsInteger := SLOrder.Count; |
||
262 | que := 'SELECT `Key` FROM `ajinomoto`';
|
||
263 | MLog.Lines.Add(que); |
||
264 | if mysql_query(con, PAnsiChar(que)) <> 0 |
||
265 | then
|
||
266 | MLog.Lines.Add(mysql_error(con)) |
||
267 | else
|
||
268 | begin
|
||
269 | res := mysql_store_result(con); |
||
270 | if res = nil |
||
271 | then
|
||
272 | MLog.Lines.Add(mysql_error(con)) |
||
273 | else
|
||
274 | begin
|
||
275 | row := mysql_fetch_row(res); |
||
276 | while row <> nil do |
||
277 | begin
|
||
278 | SLAjinomoto.Add(row^[0]);
|
||
279 | row := mysql_fetch_row(res); |
||
280 | end;
|
||
281 | mysql_free_result(res); |
||
282 | end;
|
||
283 | end;
|
||
284 | PBAjinomoto.AsInteger := SLAjinomoto.Count; |
||
285 | que := 'SELECT `Key` FROM `licences`';
|
||
286 | MLog.Lines.Add(que); |
||
287 | if mysql_query(con, PAnsiChar(que)) <> 0 |
||
288 | then
|
||
289 | MLog.Lines.Add(mysql_error(con)) |
||
290 | else
|
||
291 | begin
|
||
292 | res := mysql_store_result(con); |
||
293 | if res = nil |
||
294 | then
|
||
295 | MLog.Lines.Add(mysql_error(con)) |
||
296 | else
|
||
297 | begin
|
||
298 | row := mysql_fetch_row(res); |
||
299 | while row <> nil do |
||
300 | begin
|
||
301 | SLLicense.Add(row^[0]);
|
||
302 | row := mysql_fetch_row(res); |
||
303 | end;
|
||
304 | mysql_free_result(res); |
||
305 | end;
|
||
306 | end;
|
||
307 | PBKeys.AsInteger := SLLicense.Count; |
||
308 | BBOrders.Enabled := (PBOrders.AsInteger > 0) and TOrder.Active and TLicense.Active and TOrders.Active; |
||
309 | BBInvoices.Enabled := TOrder.Active and TLicense.Active and TInvoices.Active; |
||
310 | BBINRA.Enabled := TLicense.Active and TINRA.Active;
|
||
311 | BBAjinomoto.Enabled := (PBAjinomoto.AsInteger > 0) and TLicense.Active and TAjinomoto.Active; |
||
312 | BBKeys.Enabled := (PBKeys.AsInteger > 0) and TLicense.Active and TOrder.Active and TEducation.Active and TLicenses.Active; |
||
313 | BBAgreement.Enabled := TLicense.Active and TAgreement.Active;
|
||
314 | BBAnnual.Enabled := TLicense.Active; |
||
315 | BBUSB.Enabled := TAjinomoto.Active and TEducation.Active;
|
||
316 | BBManual.Enabled := TLicense.Active; |
||
317 | |||
318 | if MonthOf(Date) < 9 |
||
319 | then
|
||
320 | DateLimite := EndOfTheYear(Date) |
||
321 | else
|
||
322 | DateLimite := EndOfTheYear(IncYear(Date)); |
||
323 | DateLimite := Trunc(DateLimite); // Suppression de la partie heure
|
||
324 | MLog.Lines.Add('Final date is ' + DateToStr(DateLimite));
|
||
325 | end;
|
||
326 | |||
327 | procedure TFMenu.FormClose(Sender: TObject; var Action: TCloseAction); |
||
328 | var
|
||
329 | i: Integer; |
||
330 | begin
|
||
331 | SLOrder.Free; |
||
332 | SLAjinomoto.Free; |
||
333 | SLLicense.Free; |
||
334 | MLog.Lines.Add('Closing server connection...');
|
||
335 | if con <> nil then mysql_close(con); |
||
336 | MLog.Lines.Add('Closing dBase tables...');
|
||
337 | TOrder.Active := False; |
||
338 | TLicense.Active := False; |
||
339 | TEducation.Active := False; |
||
340 | TOrders.Active := False; |
||
341 | TInvoices.Active := False; |
||
342 | TINRA.Active := False; |
||
343 | TAjinomoto.Active := False; |
||
344 | TLicenses.Active := False; |
||
345 | TAgreement.Active := False; |
||
346 | for i := 0 to MLog.Lines.Count - 1 do |
||
347 | Writeln(t, MLog.Lines[i]); |
||
348 | Writeln(t, 'END ', DateTimeToStr(Now));
|
||
349 | Flush(t); |
||
350 | CloseFile(t); |
||
351 | end;
|
||
352 | |||
353 | procedure TFMenu.BBOrdersClick(Sender: TObject);
|
||
354 | begin // Type 0 |
||
355 | cur := 0;
|
||
356 | FOrder := TFOrder.Create(Self); |
||
357 | if FOrder.GetRequest
|
||
358 | then
|
||
359 | FOrder.ShowModal; |
||
360 | FOrder.Release; |
||
361 | BBOrders.Enabled := PBOrders.AsInteger > 0;
|
||
362 | end;
|
||
363 | |||
364 | procedure TFMenu.BBInvoicesClick(Sender: TObject);
|
||
365 | begin
|
||
366 | FInvoice := TFInvoice.Create(Self); |
||
367 | FInvoice.ShowModal; |
||
368 | FInvoice.Release; |
||
369 | end;
|
||
370 | |||
371 | procedure TFMenu.BBINRAClick(Sender: TObject);
|
||
372 | begin // Type 1 |
||
373 | FINRA := TFINRA.Create(Self); |
||
374 | FINRA.ShowModal; |
||
375 | FINRA.Release; |
||
376 | end;
|
||
377 | |||
378 | procedure TFMenu.BBAjinomotoClick(Sender: TObject);
|
||
379 | begin // Type 2 |
||
380 | cur := 0;
|
||
381 | FAjinomoto := TFAjinomoto.Create(Self); |
||
382 | if FAjinomoto.GetRequest
|
||
383 | then
|
||
384 | FAjinomoto.ShowModal; |
||
385 | FAjinomoto.Release; |
||
386 | BBAjinomoto.Enabled := PBAjinomoto.AsInteger > 0;
|
||
387 | end;
|
||
388 | |||
389 | procedure TFMenu.BBAgreementClick(Sender: TObject);
|
||
390 | begin
|
||
391 | FAgreement := TFAgreement.Create(Self); |
||
392 | FAgreement.ShowModal; |
||
393 | FAgreement.Release; |
||
394 | end;
|
||
395 | |||
396 | procedure TFMenu.BBKeysClick(Sender: TObject);
|
||
397 | begin
|
||
398 | cur := 0;
|
||
399 | FKeys := TFKeys.Create(Self); |
||
400 | if FKeys.GetRequest
|
||
401 | then
|
||
402 | FKeys.ShowModal; |
||
403 | FKeys.Release; |
||
404 | BBKeys.Enabled := PBKeys.AsInteger > 0;
|
||
405 | end;
|
||
406 | |||
407 | procedure TFMenu.BBManualClick(Sender: TObject);
|
||
408 | begin
|
||
409 | FManual := TFManual.Create(Self); |
||
410 | FManual.ShowModal; |
||
411 | FManual.Release; |
||
412 | end;
|
||
413 | |||
414 | procedure TFMenu.BBUSBClick(Sender: TObject);
|
||
415 | begin
|
||
416 | FUSB := TFUSB.Create(Self); |
||
417 | FUSB.ShowModal; |
||
418 | FUSB.Release; |
||
419 | end;
|
||
420 | |||
421 | procedure TFMenu.BBAnnualClick(Sender: TObject);
|
||
422 | begin
|
||
423 | FAnnual := TFAnnual.Create(Self); |
||
424 | FAnnual.ShowModal; |
||
425 | FAnnual.Release; |
||
426 | end;
|
||
427 | |||
428 | function TFMenu.MD5Str(Digest: TMD5Digest): String; |
||
429 | var
|
||
430 | i: Byte; |
||
431 | sb: TStringBuilder; |
||
432 | const
|
||
433 | Digits: array[0..15] of Char = |
||
434 | ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); |
||
435 | begin
|
||
436 | sb := TStringBuilder.Create; |
||
437 | for i := 0 to 15 do |
||
438 | sb.Append(Digits[(Digest[I] shr 4) and $0f]).Append(Digits[Digest[I] and $0f]); |
||
439 | sb.Insert(24, '-').Insert(16, '-').Insert(8, '-'); |
||
440 | Result := sb.ToString; |
||
441 | FreeAndNil(sb); |
||
442 | end;
|
||
443 | |||
444 | function TFMenu.EducationKey(Version, FirstName, LastName, Company, VolumeSerialNumber, FinalDate: String): String; |
||
445 | var
|
||
446 | sb: TStringBuilder; |
||
447 | Digest: TMD5Digest; |
||
448 | begin
|
||
449 | sb := TStringBuilder.Create; |
||
450 | sb.Append(Format('InraPorc version %s : ', [Version[1]])); |
||
451 | sb.Append('licence limit?e ? l''?ducation ');
|
||
452 | sb.Append(Format('sur le volume %s ', [VolumeSerialNumber]));
|
||
453 | sb.Append(Format('accord?e ? %s %s (%s) ', [FirstName, LastName, Company]));
|
||
454 | sb.Append(Format('valable jusqu''au %s', [FinalDate]));
|
||
455 | StringHashMD5(Digest, WindowsString(sb.ToString)); |
||
456 | FreeAndNil(sb); |
||
457 | Result := MD5Str(Digest); |
||
458 | end;
|
||
459 | |||
460 | function TFMenu.CompleteKey(LicenseNumber: Integer; Version, FirstName, LastName, Company, VolumeSerialNumber, FinalDate: String): String; |
||
461 | var
|
||
462 | sb: TStringBuilder; |
||
463 | Digest: TMD5Digest; |
||
464 | begin
|
||
465 | sb := TStringBuilder.Create; |
||
466 | sb.Append(Format('InraPorc version %s : ', [Version[1]])); |
||
467 | sb.Append(Format('licence num?ro %d ', [LicenseNumber]));
|
||
468 | sb.Append(Format('sur le volume %s ', [VolumeSerialNumber]));
|
||
469 | sb.Append(Format('accord?e ? %s %s (%s) ', [FirstName, LastName, Company]));
|
||
470 | sb.Append(Format('valable jusqu''au %s', [FinalDate]));
|
||
471 | StringHashMD5(Digest, WindowsString(sb.ToString)); |
||
472 | FreeAndNil(sb); |
||
473 | Result := MD5Str(Digest); |
||
474 | end;
|
||
475 | |||
476 | end. |