/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StMoney.pas
ViewVC logotype

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StMoney.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 44024 byte(s)
Added tpsystools component
1 // Upgraded to Delphi 2009: Sebastian Zierer
2
3 (* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is TurboPower SysTools
17 *
18 * The Initial Developer of the Original Code is
19 * TurboPower Software
20 *
21 * Portions created by the Initial Developer are Copyright (C) 1996-2002
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * ***** END LICENSE BLOCK ***** *)
27
28 {*********************************************************}
29 {* SysTools: StMoney.pas 4.04 *}
30 {*********************************************************}
31 {* SysTools: Currency and Money Related Classes *}
32 {*********************************************************}
33
34 {$include StDefine.inc}
35
36 unit StMoney;
37
38 interface
39
40 uses
41 Windows, SysUtils, Classes,
42
43 StConst, StBase, StStrms, StDecMth, StIniStm;
44
45
46 type
47 {
48 ; Layout of currency entries
49 [ISOCode]
50 Name=Country-Currency Name
51 ISOName=<ISO 4217 3 Letter Currency ID>
52 ISOCode=<ISO 4217 3 Digit Currency Number>
53 UnitMajor=<Major Currency Name>
54 UnitMinor=<Minor Currency Name>
55 Ratio=<ratio of minor currency to major>
56 }
57
58 TStCurrency = class(TObject)
59 { representation of a national currency, based on ISO 4217 specification }
60 private
61 FName: String;
62 FISOCode: String;
63 FISOName: String;
64 FRatio: Integer;
65 FUnitMajor: String;
66 FUnitMinor: String;
67
68 public
69 { Persistence and streaming methods }
70 procedure LoadFromList(List : TStrings);
71 procedure SaveToList(List : TStrings);
72
73 { properties }
74 property ISOCode: String
75 read FISOCode write FISOCode;
76 property ISOName: String
77 read FISOName write FISOName;
78 property Name: String
79 read FName write FName;
80 property Ratio: Integer
81 read FRatio write FRatio;
82 property UnitMajor: String
83 read FUnitMajor write FUnitMajor;
84 property UnitMinor: String
85 read FUnitMinor write FUnitMinor;
86 end;
87
88 TStCurrencyList = class (TObject)
89 { collection of national currencies }
90 private
91 FItems: TStringList;
92 protected {private}
93 function GetCount: Integer;
94 function GetCurrency(const ISOName : String): TStCurrency;
95 function GetItem(Index : Integer): TStCurrency;
96 procedure SetCurrency(const ISOName : String; Value: TStCurrency);
97 procedure SetItem(Index : Integer; Value: TStCurrency);
98
99 procedure FreeCurrencyByIndex(Index : Integer);
100 public
101 constructor Create;
102 destructor Destroy; override;
103
104 { Access and Update Methods }
105 procedure Add(ACurrency : TStCurrency);
106 procedure Clear;
107 function Contains(ACurrency : TStCurrency): Boolean;
108 function ContainsName(const ISOName : String): Boolean;
109 procedure Delete(const ISOName: String);
110 function IndexOf(const ISOName : String) : Integer;
111
112 { Persistence and streaming methods }
113 procedure LoadFromFile(const AFileName: TFileName);
114 procedure LoadFromStream(AStream: TStream);
115 procedure SaveToFile(const AFileName: TFileName);
116 procedure SaveToStream(AStream: TStream);
117
118 { properties }
119 property Count : Integer
120 read GetCount;
121 property Currencies[const ISOName : String]: TStCurrency
122 read GetCurrency write SetCurrency;
123 property Items[Index : Integer] : TStCurrency
124 read GetItem write SetItem; default;
125 end;
126
127
128 {
129 Conversion Methods
130 ===================
131 When converting money of one currency into money of another currency, three
132 conversion methods are commonly encountered:
133
134 1)
135 "Triangular": the source currency amount is converted to an intermediate
136 currency amount, then the intermediate currency amount is converted to
137 the target amount.
138
139 Note: This is the method required by members of the European Monetary
140 Union (EMU), for converting among national currencies that are transitioning
141 to the Euro; the Euro should be used as the Intermediate currency for such
142 conversions.
143
144 2)
145 "Multiply" the source currency amount is multiplied by a conversion Rate
146 to obtain the target currency amount.
147
148 3)
149 "Divide" the source currency amount is divided by a conversion Rate to
150 obtain the target currency amount.
151 }
152 TStConversionType = (ctUnknown, ctTriangular, ctMultiply, ctDivide);
153
154 TStGetRateUpdateEvent = procedure (Sender: TObject; NewRate : TStDecimal;
155 var NewDate : TDateTime) of object;
156 {
157 ; Layout of exchange entries
158 [SRC:TRG]
159 source=SRC
160 target=TRG
161 ; empty/ignored if not a triangular exchange
162 intermediate=XXX
163 rate=xxx
164 ; error if tri and intermediate not set
165 type=<tri|mul|div>
166 date=<date>
167 }
168
169 TStExchangeRate = class (TObject)
170 { particular Exchange Rate between two currencies }
171 private
172 FRate: TStDecimal;
173 FSource: String;
174 FTarget : String;
175 FIntermediate : String;
176 FConversionType : TStConversionType;
177 FDateUpdated : TDateTime;
178 FOnGetRateUpdate: TStGetRateUpdateEvent;
179 procedure SetRate(const Value: TStDecimal);
180 public
181 constructor Create;
182 destructor Destroy; override;
183
184 { Access and Update Methods }
185 procedure Assign(ARate : TStExchangeRate);
186 procedure Clear;
187 procedure Convert(Amount, Result: TStDecimal);
188 function Equals(aRate : TStExchangeRate) : Boolean;
189 function IsValid : Boolean;
190 function SameSourceAndTarget(aRate : TStExchangeRate) : Boolean;
191 procedure Update;
192
193 { Persistence and streaming methods }
194 procedure LoadFromList(List : TStrings);
195 procedure SaveToList(List : TStrings);
196
197 { properties }
198 property ConversionType : TStConversionType
199 read FConversionType write FConversionType;
200 property DateUpdated : TDateTime
201 read FDateUpdated write FDateUpdated;
202 property Intermediate : String
203 read FIntermediate write FIntermediate;
204 property Rate : TStDecimal
205 read FRate write SetRate;
206 property Source : String
207 read FSource write FSource;
208 property Target : String
209 read FTarget write FTarget;
210
211 { events }
212 property OnGetRateUpdate : TStGetRateUpdateEvent
213 read FOnGetRateUpdate write FOnGetRateUpdate;
214 end;
215
216 TStExchangeRateList = class (TObject)
217 { collection of currency conversions (TStExchangeRate) }
218 private
219 FRates : TStringList;
220 protected {private}
221 procedure DeleteRate(Index: Integer);
222 function GetCount: Integer;
223 function GetRate(const Source, Target: String): TStExchangeRate;
224 function GetItem(Index: Integer): TStExchangeRate;
225 function MakeEntry(const Source, Target: String): String; virtual;
226
227 procedure ConvertPrim(const aSource, aTarget : string;
228 aAmount : TStDecimal;
229 aAllowTriangular : boolean);
230 public
231 constructor Create;
232 destructor Destroy; override;
233
234 { Access and Update Methods }
235 procedure Add(ARate : TStExchangeRate);
236 procedure AddByValues(const Source, Target, Intermediate: String;
237 Rate: Double; ConversionType: TStConversionType; DateUpdated: TDateTime);
238 procedure Assign(AList : TStExchangeRateList);
239 procedure Clear;
240 function Contains(ARate : TStExchangeRate) : Boolean;
241 function ContainsByName(const Source, Target : String) : Boolean;
242 procedure Convert(const Source, Target : String;
243 Amount, Result : TStDecimal);
244 procedure Delete(ARate : TStExchangeRate);
245 procedure DeleteByName(const Source, Target : String);
246 procedure UpdateRate(const Source, Target : String; Rate : TStDecimal);
247
248 { Persistence and streaming methods }
249 procedure LoadFromFile(const AFileName: TFileName);
250 procedure LoadFromStream(AStream: TStream);
251 procedure SaveToFile(const AFileName: TFileName);
252 procedure SaveToStream(AStream: TStream);
253
254 { properties }
255 property Count : Integer
256 read GetCount;
257 { Returns the number of exchange rates in this table. }
258 property Items[Index : Integer] : TStExchangeRate
259 read GetItem;
260 { access to all of the exchange rates in the collection by numeric index }
261 property Rates[const Source, Target : String] : TStExchangeRate
262 read GetRate;
263 { access to all of the exchange rates in the collection by Source and Target }
264 end;
265
266 TStMoney = class (TObject)
267 { representation of an amount of Currency and operations on same }
268 private
269 FAmount : TStDecimal;
270 FCurrency : String;
271 FExchangeRates : TStExchangeRateList;
272
273 function GetAsFloat: Double;
274 function GetAsString: String;
275 procedure SetAmount(const Value: TStDecimal);
276 procedure SetAsFloat(const Value: Double);
277 procedure SetAsString(const Value: String);
278 procedure Validate(Source, Operand, Result: TStMoney);
279 function ValidateCurrencies(Source, Dest: TStMoney) : Boolean;
280 public
281 constructor Create;
282 destructor Destroy; override;
283 procedure Assign(AMoney : TStMoney);
284
285 { basic math operations }
286 procedure Abs(Result : TStMoney);
287 procedure Add(Addend, Sum : TStMoney);
288 procedure Divide(Divisor : Double; Quotient : TStMoney);
289 procedure DivideByDecimal(Divisor : TStDecimal; Quotient : TStMoney);
290 procedure Multiply(Multiplier : Double; Product : TStMoney);
291 procedure MultiplyByDecimal(Multiplier : TStDecimal; Product : TStMoney);
292 procedure Negate(Result : TStMoney);
293 procedure Subtract(Subtrahend, Remainder : TStMoney);
294
295 { logical comparisons }
296 function Compare(CompareTo : TStMoney): Integer;
297 function IsEqual(AMoney : TStMoney): Boolean;
298 function IsGreaterThan(AMoney : TStMoney): Boolean;
299 function IsGreaterThanOrEqual(AMoney : TStMoney): Boolean;
300 function IsLessThan(AMoney : TStMoney): Boolean;
301 function IsLessThanOrEqual(AMoney : TStMoney): Boolean;
302 function IsNegative: Boolean;
303 function IsNotEqual(AMoney : TStMoney): Boolean;
304 function IsPositive: Boolean;
305 function IsZero: Boolean;
306
307 { Conversion Methods }
308 procedure Convert(const Target : String; Result : TStMoney);
309 procedure Round(Method : TStRoundMethod; Decimals : Integer; Result : TStMoney);
310 { See definition of TStRoundMethod in the StDecMth unit for more
311 information on rounding }
312
313 { properties }
314 property Amount: TStDecimal
315 read FAmount write SetAmount;
316 property AsFloat: Double
317 read GetAsFloat write SetAsFloat;
318 property AsString: String
319 read GetAsString write SetAsString;
320 property Currency: String
321 read FCurrency write FCurrency;
322 property ExchangeRates : TStExchangeRateList
323 read FExchangeRates write FExchangeRates;
324 end;
325
326 implementation
327
328 var
329 ExchBaseDate : TDateTime; // the base date for exchange rates
330
331 { TStCurrency }
332
333 procedure TStCurrency.LoadFromList(List : TStrings);
334 {
335 assign currency properties from a set of <Name>=<Value> pairs
336
337 BuildItem expects data in the form:
338
339 Name=Country-Currency Name
340 ISOName=<ISO 4217 3 Letter Currency ID>
341 ISOCode=<ISO 4217 3 Digit Currency Number>
342 UnitMajor=<Major Currency Name>
343 UnitMinor=<Minor Currency Name>
344 Ratio=<ratio of minor currency to major>
345 }
346 begin
347 if Assigned(List) then begin
348 FName := List.Values['Name'];
349 FISOCode := List.Values['ISOCode'];
350 FISOName := List.Values['ISOName'];
351 FUnitMajor := List.Values['UnitMajor'];
352 FUnitMinor := List.Values['UnitMinor'];
353 FRatio := StrToIntDef(List.Values['Ratio'], 100);
354 end;
355 end;
356
357 procedure TStCurrency.SaveToList(List : TStrings);
358 { write Currency data to <Name>=<Value> pairs for persistence }
359 begin
360 if Assigned(List) then begin
361 List.Clear;
362 List.Add('Name=' + FName);
363 List.Add('ISOCode=' + FISOCode);
364 List.Add('ISOName=' + FISOName);
365 List.Add('UnitMajor=' + FUnitMajor);
366 List.Add('UnitMinor=' + FUnitMinor);
367 List.Add('Ratio=' + IntToStr(FRatio));
368 end;
369 end;
370
371 { TStCurrencyList }
372
373 constructor TStCurrencyList.Create;
374 begin
375 inherited Create;
376 FItems := TStringList.Create;
377 FItems.Sorted := True;
378 FItems.Duplicates := dupIgnore;
379 end;
380
381 destructor TStCurrencyList.Destroy;
382 begin
383 Clear;
384 FItems.Free;
385 inherited Destroy;
386 end;
387
388 procedure TStCurrencyList.Add(ACurrency: TStCurrency);
389 { add a new currency to the list }
390 begin
391 if Assigned(ACurrency) then
392 FItems.AddObject(ACurrency.ISOName, ACurrency);
393 end;
394
395 procedure TStCurrencyList.Clear;
396 { Clear the list of currencies }
397 var
398 i: Integer;
399 begin
400 for i := Pred(FItems.Count) downto 0 do
401 FreeCurrencyByIndex(i);
402 end;
403
404 function TStCurrencyList.Contains(ACurrency: TStCurrency): Boolean;
405 { returns true if there's an entry for such a currency }
406 begin
407 Result := False;
408 if Assigned(ACurrency) then
409 Result := FItems.IndexOf(ACurrency.ISOName) >= 0;
410 end;
411
412 function TStCurrencyList.ContainsName(const ISOName: String): Boolean;
413 { returns true if there's an entry for such a currency ID }
414 begin
415 Result := FItems.IndexOf(ISOName) >= 0;
416 end;
417
418 procedure TStCurrencyList.Delete(const ISOName: String);
419 { delete the requested currency from the list }
420 begin
421 FreeCurrencyByIndex(FItems.IndexOf(ISOName));
422 end;
423
424 procedure TStCurrencyList.FreeCurrencyByIndex(Index: Integer);
425 { release a currency by the requested numeric index in the list }
426 begin
427 { if index in range }
428 if (0 <= Index) and (Index < FItems.Count) then begin
429 { free StCurrency data at that index }
430 (FItems.Objects[Index] as TStCurrency).Free;
431 { delete item from list }
432 FItems.Delete(Index);
433 end;
434 { else, item doesn't exist, so do nothing }
435 end;
436
437 function TStCurrencyList.GetCount : Integer;
438 { just return count of maintained items }
439 begin
440 Result := FItems.Count;
441 end;
442
443 function TStCurrencyList.GetCurrency(const ISOName: String): TStCurrency;
444 {
445 return reference to requested currency item indexed by ISOName
446 returns nil if item doesn't exist
447 }
448 var
449 Index : Integer;
450 begin
451 { find index of item }
452 Index := FItems.IndexOf(ISOName);
453 { return item as a TStCurrency reference, or nil if it wasn't found }
454 if (Index >= 0) then
455 Result := GetItem(Index)
456 else
457 Result := nil;
458 end;
459
460 function TStCurrencyList.GetItem(Index : Integer): TStCurrency;
461 {
462 return reference to requested currency item indexed by position in list
463 returns nil if item doesn't exist
464 }
465 begin
466 if not ((0 <= Index) and (Index < FItems.Count)) then
467 raise EStException.CreateResFmtTP(stscBadIndex, [IntToStr(Index)], 0);
468
469 Result := (FItems.Objects[Index] as TStCurrency);
470 end;
471
472 function TStCurrencyList.IndexOf(const ISOName: String): Integer;
473 {
474 locate index of requested item in list,
475 returns -1 if item doesn't exist
476 }
477 begin
478 Result := FItems.IndexOf(ISOName);
479 end;
480
481 procedure TStCurrencyList.LoadFromFile(const AFileName: TFileName);
482 var
483 FS : TFileStream;
484 begin
485 FS := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
486 try
487 LoadFromStream(FS);
488 finally
489 FS.Free;
490 end;
491 end;
492
493 procedure TStCurrencyList.LoadFromStream(AStream : TStream);
494 var
495 IniStr : TStIniStream;
496 Currencies, Section : TStrings;
497 ACurrency : TStCurrency;
498 i : Integer;
499 begin
500 {clear out the current currency items}
501 Clear;
502
503 IniStr := nil;
504 Currencies := nil;
505 Section := nil;
506 ACurrency := nil;
507 try
508 IniStr := TStIniStream.Create(AStream);
509 Currencies := TStringList.Create;
510 Section := TStringList.Create;
511 { create an "index" of the sections }
512 IniStr.ReadSections(Currencies);
513
514 { read a currency definition }
515 for i := 0 to Pred(Currencies.Count) do begin
516 { get settings as .INI style items }
517 IniStr.ReadSectionValues(Currencies[i], Section);
518
519 { create a new currency item }
520 ACurrency := TStCurrency.Create;
521
522 { set its properties }
523 ACurrency.LoadFromList(Section);
524
525 { add it to the list }
526 FItems.AddObject(ACurrency.ISOName, ACurrency);
527 ACurrency := nil;
528 end;
529 finally
530 IniStr.Free;
531 Section.Free;
532 Currencies.Free;
533 // note: this only does something if either the LoadFromList or
534 // AddObject calls failed
535 ACurrency.Free;
536 end;
537 end;
538
539 procedure TStCurrencyList.SaveToFile(const AFileName: TFileName);
540 var
541 FS : TFileStream;
542 begin
543 if not FileExists(AFileName) then begin
544 FS := TFileStream.Create(AFileName, fmCreate);
545 FS.Free;
546 end;
547
548 FS := TFileStream.Create(AFileName, fmOpenWrite or fmShareDenyNone);
549 try
550 SaveToStream(FS);
551 finally
552 FS.Free;
553 end;
554 end;
555
556 procedure TStCurrencyList.SaveToStream(AStream : TStream);
557 var
558 IniStr : TStIniStream;
559 Strs : TStringList;
560 i : Integer;
561 begin
562 IniStr := nil;
563 Strs := nil;
564 try
565 IniStr := TStIniStream.Create(AStream);
566 Strs := TStringList.Create;
567 for i := 0 to Pred(FItems.Count) do begin
568 { clear the string list to contain the ccy definition }
569 Strs.Clear;
570 { get item properties as string list }
571 (FItems.Objects[i] as TStCurrency).SaveToList(Strs);
572 { add new section to .INI data }
573 IniStr.WriteSection(FItems[i], Strs);
574 end;
575 finally
576 Strs.Free;
577 IniStr.Free;
578 end;
579 end;
580
581 procedure TStCurrencyList.SetCurrency(const ISOName: String;
582 Value: TStCurrency);
583 var
584 Idx : Integer;
585 begin
586 { locate item }
587 Idx := FItems.IndexOf(ISOName);
588 if (Idx >= 0) then
589 SetItem(Idx, Value);
590 end;
591
592 procedure TStCurrencyList.SetItem(Index : Integer;
593 Value: TStCurrency);
594 begin
595 if not ((0 <= Index) and (Index < FItems.Count)) then
596 raise EStException.CreateResFmtTP(stscBadIndex, [IntToStr(Index)], 0);
597
598 if Assigned(Value) then begin
599 { release current currency info }
600 (FItems.Objects[Index] as TStCurrency).Free;
601 { replace with new info }
602 FItems.Objects[Index] := Value;
603 end;
604 end;
605
606
607 { TStMoney }
608
609 constructor TStMoney.Create;
610 begin
611 inherited Create;
612 FAmount := TStDecimal.Create;
613 end;
614
615 destructor TStMoney.Destroy;
616 begin
617 FAmount.Free;
618 inherited Destroy;
619 end;
620
621 procedure TStMoney.Abs(Result : TStMoney);
622 { Returns a new money which has the absolute value of this money's amount. }
623 begin
624 Result.Assign(Self);
625 Result.Amount.Abs;
626 end;
627
628 procedure TStMoney.Add(Addend, Sum : TStMoney);
629 begin
630 Validate(Self, Addend, Sum);
631 Sum.Assign(Self);
632 Sum.Amount.Add(Addend.Amount);
633 end;
634
635 procedure TStMoney.Assign(AMoney : TStMoney);
636 begin
637 if Assigned(AMoney) then begin
638 Amount.Assign(AMoney.Amount);
639 Currency := AMoney.Currency;
640 ExchangeRates := AMoney.ExchangeRates;
641 end;
642 end;
643
644 function TStMoney.Compare(CompareTo : TStMoney): Integer;
645 {
646 Compares this money to the specified money.
647
648 Returns <0 if this money is less than the other money, 0 if they are equal,
649 and >0 if it is greater
650
651 Note: Currencies must also be the same
652 }
653 begin
654 Validate(Self, CompareTo, Self);
655 Result := Amount.Compare(CompareTo.Amount);
656 end;
657
658 procedure TStMoney.Convert(const Target : String; Result : TStMoney);
659 {
660 Converts the value to a different currency, utilizes TStExchangeRateList
661 }
662 begin
663 { check that exchange rates are available }
664 if not Assigned(ExchangeRates) then
665 raise EStException.CreateResTP(stscMoneyNoExchangeRatesAvail, 0);
666
667 { check validity of operands and result }
668 if not Assigned(Result) then
669 raise EStException.CreateResTP(stscMoneyNilResult, 0);
670
671 Result.Assign(Self);
672 ExchangeRates.Convert(Currency, Target, Amount, Result.Amount);
673 end;
674
675 procedure TStMoney.DivideByDecimal(Divisor : TStDecimal; Quotient : TStMoney);
676 { Returns a new money which is the quotient of the money divided by
677 the decimal divisor. }
678 begin
679 if not Assigned(Divisor) then
680 raise EStException.CreateResTP(stscMoneyNilParameter, 0);
681
682 if not Assigned(Quotient) then
683 raise EStException.CreateResTP(stscMoneyNilResult, 0);
684
685 Quotient.Assign(Self);
686 Quotient.Amount.Divide(Divisor);
687 end;
688
689 procedure TStMoney.Divide(Divisor : Double; Quotient : TStMoney);
690 { Returns a new money which is the quotient of the money divided by
691 the floating point divisor. }
692 var
693 DecDiv : TStDecimal;
694 begin
695 DecDiv := TStDecimal.Create;
696 try
697 DecDiv.AssignFromFloat(Divisor);
698 DivideByDecimal(DecDiv, Quotient);
699 finally
700 DecDiv.Free;
701 end;
702 end;
703
704 function TStMoney.GetAsFloat: Double;
705 { return money amount as a Floating point value }
706 begin
707 Result := Amount.AsFloat;
708 end;
709
710 function TStMoney.GetAsString: String;
711 { return money amount as a string }
712 begin
713 Result := Amount.AsString;
714 end;
715
716 function TStMoney.IsEqual(AMoney : TStMoney): Boolean;
717 { Returns true if this money and the specified money are equal }
718 begin
719 Result := Compare(AMoney) = 0;
720 end;
721
722 function TStMoney.IsGreaterThan(AMoney : TStMoney): Boolean;
723 { Returns true if this money's amount is greater than that of the specified money. }
724 begin
725 Result := Compare(AMoney) > 0;
726 end;
727
728 function TStMoney.IsGreaterThanOrEqual(AMoney : TStMoney): Boolean;
729 { Returns true if this money's amount is greater than or equal to the specified money. }
730 begin
731 Result := Compare(AMoney) >= 0;
732 end;
733
734 function TStMoney.IsPositive : Boolean;
735 { Returns true if this money's amount is greater than zero. }
736 begin
737 Result := Amount.IsPositive;
738 end;
739
740 function TStMoney.IsZero: Boolean;
741 { Returns true if this money's amount is equal to zero. }
742 begin
743 Result := Amount.IsZero;
744 end;
745
746 function TStMoney.IsLessThan(AMoney : TStMoney): Boolean;
747 { Returns true if this money's amount is less than that of the specified money. }
748 begin
749 Result := Compare(AMoney) < 0;
750 end;
751
752 function TStMoney.IsLessThanOrEqual(AMoney : TStMoney): Boolean;
753 { Returns true if this money's amount is less than or equal to that of the specified money. }
754 begin
755 Result := Compare(AMoney) <= 0;
756 end;
757
758 function TStMoney.IsNegative: Boolean;
759 { Returns true if this money's amount is less than zero. }
760 begin
761 Result := Amount.IsNegative;
762 end;
763
764 function TStMoney.IsNotEqual(AMoney : TStMoney): Boolean;
765 { Returns true if this money and the specified money are not equal }
766 begin
767 Result := Compare(AMoney) <> 0;
768 end;
769
770 procedure TStMoney.MultiplyByDecimal(Multiplier : TStDecimal;
771 Product : TStMoney);
772 { Returns a new money which is the product of the money and the decimal value. }
773 begin
774 if not Assigned(Multiplier) then
775 raise EStException.CreateResTP(stscMoneyNilParameter, 0);
776
777 if not Assigned(Product) then
778 raise EStException.CreateResTP(stscMoneyNilResult, 0);
779
780 Product.Assign(Self);
781 Product.Amount.Multiply(Multiplier);
782 end;
783
784 procedure TStMoney.Multiply(Multiplier : Double; Product : TStMoney);
785 { Returns a new money which is the product of the money and the floating point value. }
786 var
787 MulDec : TStDecimal;
788 begin
789 MulDec := TStDecimal.Create;
790 try
791 MulDec.AssignFromFloat(Multiplier);
792 MultiplyByDecimal(MulDec, Product);
793 finally
794 MulDec.Free;
795 end;
796 end;
797
798 procedure TStMoney.Negate(Result : TStMoney);
799 { Returns a new money which is the negation of this money's amount. }
800 begin
801 if not Assigned(Result) then
802 raise EStException.CreateResTP(stscMoneyNilResult, 0);
803
804 Result.Assign(Self);
805 Result.Amount.ChangeSign;
806 end;
807
808 procedure TStMoney.Round(Method : TStRoundMethod; Decimals : Integer; Result : TStMoney);
809 {
810 Returns a new money with the rounded value of this money using the specified accuracy.
811 and using the specified rounding method
812
813 See definition of TStRoundMethod in the StDecMth unit for more
814 information on rounding
815 }
816 begin
817 if not Assigned(Result) then
818 raise EStException.CreateResTP(stscMoneyNilResult, 0);
819
820 Result.Assign(Self);
821 Result.Amount.Round(Method, Decimals);
822 end;
823
824 procedure TStMoney.SetAmount(const Value: TStDecimal);
825 begin
826 Amount.Assign(Value);
827 end;
828
829 procedure TStMoney.SetAsFloat(const Value: Double);
830 begin
831 Amount.AssignFromFloat(Value);
832 end;
833
834 procedure TStMoney.SetAsString(const Value: String);
835 begin
836 Amount.AsString := Value;
837 end;
838
839 procedure TStMoney.Subtract(Subtrahend, Remainder : TStMoney);
840 { Returns a new money which is the difference between this money and the given money. }
841 begin
842 Validate(Self, Subtrahend, Remainder);
843 Remainder.Assign(Self);
844 Remainder.Amount.Subtract(Subtrahend.Amount);
845 end;
846
847 function TStMoney.ValidateCurrencies(Source, Dest : TStMoney) : Boolean;
848 begin
849 Result := Source.Currency = Dest.Currency;
850 end;
851
852 procedure TStMoney.Validate(Source, Operand, Result : TStMoney);
853 begin
854 { check validity of operands and result }
855 if not Assigned(Source) or not Assigned(Operand) then
856 raise EStException.CreateResTP(stscMoneyNilParameter, 0);
857
858 if not Assigned(Result) then
859 raise EStException.CreateResTP(stscMoneyNilResult, 0);
860
861 if not ValidateCurrencies(Source, Operand) then
862 raise EStException.CreateResTP(stscMoneyCurrenciesNotMatch, 0);
863 end;
864
865 { TStExchangeRate }
866
867 constructor TStExchangeRate.Create;
868 begin
869 inherited Create;
870 FRate := TStDecimal.Create;
871 Clear;
872 end;
873
874 destructor TStExchangeRate.Destroy;
875 begin
876 FRate.Free;
877 inherited Destroy;
878 end;
879
880 procedure TStExchangeRate.Assign(ARate: TStExchangeRate);
881 begin
882 if Assigned(ARate) then begin
883 Source := ARate.Source;
884 Target := ARate.Target;
885 Intermediate := ARate.Intermediate;
886 ConversionType := ARate.ConversionType;
887 DateUpdated := ARate.DateUpdated;
888 Rate.Assign(ARate.Rate);
889 end else
890 begin
891 Clear;
892 end;
893 end;
894
895 procedure TStExchangeRate.Clear;
896 { clear item fields }
897 begin
898 FSource := '';
899 FTarget := '';
900 FIntermediate := '';
901 FConversionType := ctMultiply;
902 FDateUpdated := ExchBaseDate;
903 FRate.SetToOne;
904 end;
905
906 procedure TStExchangeRate.Convert(Amount, Result: TStDecimal);
907 { convert supplied amount using current ConversionType and Exchange Rate }
908 begin
909 {the parameters must be present}
910 if not Assigned(Amount) or not Assigned(Result) then
911 raise EStException.CreateResTP(stscMoneyNilParameter, 0);
912
913 {the exchange rate must be valid}
914 if not IsValid then
915 raise EStException.CreateResTP(stscMoneyInvalidExchRate, 0);
916
917 {set the result equal to the amount prior to converting it}
918 Result.Assign(Amount);
919
920 case ConversionType of
921 { multiplication conversion }
922 ctMultiply :
923 begin
924 Result.Multiply(Rate);
925 end;
926
927 { division conversion }
928 ctDivide :
929 begin
930 Result.Divide(Rate);
931 end;
932
933 { triangular conversion }
934 ctTriangular :
935 begin
936 {this can't be done by a single exchange rate}
937 raise EStException.CreateResTP(stscMoneyInvalidTriangleExchange, 0);
938 end;
939
940 else
941 raise EStException.CreateResTP(stscMoneyInvalidExchangeParams, 0);
942 end; { case }
943 end;
944
945 function TStExchangeRate.Equals(aRate: TStExchangeRate): Boolean;
946 {
947 Returns true if this exchange rate and specified exchange rate have
948 identical Exchange types, Source currencies, Target currencies,
949 and conversion Rates or are both Triangular exchanges with the same
950 Source, Target, and Intermediate currencies
951 }
952 var
953 CurrenciesMatch, TypesMatch : Boolean;
954 begin
955 Result := False;
956 if not Assigned(aRate) then Exit;
957
958 { check if currencies match }
959 CurrenciesMatch := (AnsiCompareText(Source, aRate.Source) = 0) and
960 (AnsiCompareText(Target, aRate.Target) = 0);
961
962 { check if exchange types match }
963 TypesMatch := (ConversionType = aRate.ConversionType);
964
965 if TypesMatch and CurrenciesMatch then
966 case ConversionType of
967 ctTriangular : { both triangular }
968 { equal if same intermediate currency }
969 Result := (FIntermediate = aRate.FIntermediate);
970
971 ctMultiply,
972 ctDivide : { both multiply or divide }
973 { equal if same conversion rate }
974 Result := (Rate.Compare(aRate.Rate) = 0);
975 else
976 raise EStException.CreateResTP(stscMoneyInvalidExchangeParams, 0);
977 end; { case }
978 end;
979
980 function TStExchangeRate.IsValid: Boolean;
981 {
982 Checks to see if this exchange rate has its source, target and Rate
983 fields set to non-default values, or if a Triangular exchange, that
984 the intermediate currency is set
985 }
986 begin
987 {assume the exchange rate is invalid}
988 Result := false;
989
990 {the source cannot be empty}
991 if (Source = '') then
992 Exit;
993
994 {the target cannot be empty}
995 if (Target = '') then
996 Exit;
997
998 {the source and target must be different}
999 if (AnsiCompareText(Source, Target) = 0) then
1000 Exit;
1001
1002 {for a multiply/divide conversion, the rate must be > 0.0}
1003 if (ConversionType = ctMultiply) or (ConversionType = ctDivide) then begin
1004 Result := FRate.IsPositive;
1005 Exit;
1006 end;
1007
1008 {for a triangular conversion, the intermediate currency must be set
1009 and cannot be equal to either Source or Target to avoid infinite
1010 loops in TStExchangeList.Convert <g>}
1011 if (ConversionType = ctTriangular) then begin
1012 if (Intermediate = '') then
1013 Exit;
1014 if (AnsiCompareText(Source, Intermediate) = 0) then
1015 Exit;
1016 if (AnsiCompareText(Target, Intermediate) = 0) then
1017 Exit;
1018 Result := true;
1019 Exit;
1020 end;
1021
1022 {otherwise the exchange rate is invalid}
1023 end;
1024
1025 function MakeXChgStr(ConversionType : TStConversionType) : String;
1026 { convert TStConversionType to string for persistence }
1027 begin
1028 case ConversionType of
1029 ctTriangular : Result := 'tri';
1030 ctMultiply : Result := 'mul';
1031 ctDivide : Result := 'div';
1032 else
1033 raise Exception.Create('Unknown conversion type');
1034 end; { case }
1035 end;
1036
1037 function MakeXChg(const XchStr : String) : TStConversionType;
1038 { convert persistence string to TStConversionType }
1039 begin
1040 if (AnsiCompareText(XchStr, 'mul') = 0) then
1041 Result := ctMultiply
1042 else if (AnsiCompareText(XchStr, 'div') = 0) then
1043 Result := ctDivide
1044 else if (AnsiCompareText(XchStr, 'tri') = 0) then
1045 Result := ctTriangular
1046 else begin
1047 raise Exception.Create('Unknown conversion type in INI file');
1048 Result := ctUnknown;
1049 end;
1050 end;
1051
1052 procedure ReplaceCh(var S : String; aFromCh : Char; aToCh : Char);
1053 var
1054 i : integer;
1055 begin
1056 {replace the first occurrence of aFromCh with aToCh in string S}
1057 for i := 0 to length(S) do
1058 if (S[i] = aFromCh) then begin
1059 S[i] := aToCh;
1060 Exit;
1061 end;
1062 end;
1063
1064 procedure TStExchangeRate.LoadFromList(List: TStrings);
1065 {
1066 set item properties from Exchange Rate data
1067 expects data in the format:
1068
1069 source=<source currency>
1070 target=<target currency>
1071 intermediate=<intermediate currency>
1072 rate=<exchange rate>
1073 type=<tri|mul|div>
1074 date=<date of setting>
1075 }
1076 var
1077 Str : String;
1078 DayCount : integer;
1079 ec : integer;
1080 begin
1081 if Assigned(List) then begin
1082 Clear;
1083 FSource := List.Values['source'];
1084 FTarget := List.Values['target'];
1085 FIntermediate := List.Values['intermediate'];
1086 FConversionType := MakeXChg(List.Values['type']);
1087
1088 Str := List.Values['date'];
1089 Val(Str, DayCount, ec);
1090 if (ec <> 0) then
1091 DayCount := 0;
1092 FDateUpdated := ExchBaseDate + DayCount;
1093
1094 Str := List.Values['rate'];
1095 if Str = '' then
1096 FRate.SetToOne
1097 else begin
1098 {the INI file stores rates with a decimal *point*; if the locale
1099 uses something else (eg, a comma) we'll need to switch it for
1100 the AsString property, which obeys the locale}
1101 if ({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator <> '.') then
1102 ReplaceCh(Str, '.', {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
1103 FRate.AsString := Str;
1104 end;
1105 end;
1106 end;
1107
1108 function TStExchangeRate.SameSourceAndTarget(
1109 aRate: TStExchangeRate): Boolean;
1110 {
1111 Tests whether the specified rate has the same source and target currencies.
1112 Returns True of the Source and Target currencies are the same, False otherwise
1113 }
1114 begin
1115 Result := False;
1116 if Assigned(aRate) then
1117 Result := (AnsiCompareText(Source, aRate.Source) = 0) and
1118 (AnsiCompareText(Target, aRate.Target) = 0);
1119 end;
1120
1121 procedure TStExchangeRate.SaveToList(List: TStrings);
1122 { create persistent representation of item }
1123 var
1124 Str : String;
1125 DayCount : integer;
1126 begin
1127 if Assigned(List) then begin
1128 List.Clear;
1129 List.Add('source=' + FSource);
1130 List.Add('target=' + FTarget);
1131 List.Add('intermediate=' + FIntermediate);
1132 Str := FRate.AsString;
1133 if ({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator <> '.') then
1134 ReplaceCh(Str, {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator, '.');
1135 List.Add('rate=' + Str);
1136 List.Add('type=' + MakeXChgStr(FConversionType));
1137 DayCount := trunc(FDateUpdated - ExchBaseDate);
1138 if DayCount < 0 then
1139 DayCount := 0;
1140 List.Add('date=' + IntToStr(DayCount));
1141 end;
1142 end;
1143
1144 procedure TStExchangeRate.SetRate(const Value: TStDecimal);
1145 begin
1146 FRate.Assign(Value);
1147 end;
1148
1149 procedure TStExchangeRate.Update;
1150 { fire update event }
1151 var
1152 NewDate : TDateTime;
1153 begin
1154 if Assigned(FOnGetRateUpdate) then begin
1155 NewDate := DateUpdated;
1156 FOnGetRateUpdate(Self, Rate, NewDate);
1157 DateUpdated := NewDate;
1158 end;
1159 end;
1160
1161
1162 { TStExchangeRateList }
1163 constructor TStExchangeRateList.Create;
1164 begin
1165 inherited Create;
1166 FRates := TStringList.Create;
1167 FRates.Sorted := True;
1168 FRates.Duplicates := dupIgnore;
1169 end;
1170
1171 destructor TStExchangeRateList.Destroy;
1172 begin
1173 Clear;
1174 FRates.Free;
1175 inherited Destroy;
1176 end;
1177
1178 procedure TStExchangeRateList.Add(ARate: TStExchangeRate);
1179 {
1180 Adds the given exchange rate to the list
1181
1182 Since FRates list is set for dupIgnore, if Rate already exists, the
1183 new values will be discarded
1184
1185 To modify an existing rate, use the Rates property or the UpdateRate
1186 method, or delete the existing Rate and re-add it
1187 }
1188 begin
1189 if Assigned(ARate) then
1190 FRates.AddObject(MakeEntry(ARate.Source, ARate.Target), ARate);
1191 end;
1192
1193 procedure TStExchangeRateList.AddByValues(const Source, Target,
1194 Intermediate : String; Rate : Double; ConversionType : TStConversionType;
1195 DateUpdated : TDateTime);
1196 {
1197 Create new rate with provided characteristics and add it to the list
1198
1199 Since FRates list is set for dupIgnore, if Rate already exists, the
1200 new values will be discarded
1201
1202 To modify an existing rate, use the Rates property or the UpdateRate
1203 method, or delete the existing Rate and re-add it
1204 }
1205 var
1206 TempRate : TStExchangeRate;
1207 begin
1208 TempRate := TStExchangeRate.Create;
1209 TempRate.Source := Source;
1210 TempRate.Target := Target;
1211 TempRate.Intermediate := Intermediate;
1212 TempRate.ConversionType := ConversionType;
1213 TempRate.DateUpdated := DateUpdated;
1214 TempRate.Rate.AssignFromFloat(Rate);
1215 Add(TempRate);
1216 end;
1217
1218 procedure TStExchangeRateList.Assign(AList: TStExchangeRateList);
1219 var
1220 i : Integer;
1221 begin
1222 if Assigned(AList) then begin
1223 { if Rate Lists already point to same list then don't do anything }
1224 if FRates = AList.FRates then Exit;
1225
1226 { empty list }
1227 Clear;
1228
1229 { add items from new list }
1230 for i := 0 to Pred(AList.Count) do
1231 Add(AList.Items[i]);
1232 end;
1233 end;
1234
1235 procedure TStExchangeRateList.Clear;
1236 { Clears all of the exchange rates from this table. }
1237 var
1238 i : Integer;
1239 begin
1240 for i := Pred(FRates.Count) downto 0 do begin
1241 DeleteRate(i);
1242 end;
1243 end;
1244
1245 function TStExchangeRateList.Contains(
1246 ARate: TStExchangeRate): Boolean;
1247 {
1248 Returns true if an exchange rate already exists with this rate's source,
1249 target pair.
1250 }
1251 begin
1252 Result := False;
1253 if Assigned(ARate) then
1254 Result := ContainsByName(ARate.Source, ARate.Target);
1255 end;
1256
1257 function TStExchangeRateList.ContainsByName(const Source,
1258 Target: String): Boolean;
1259 {
1260 Returns true if an exchange rate already exists with this one's
1261 source and target ISOName Strings
1262 }
1263 begin
1264 Result := FRates.IndexOf(MakeEntry(Source, Target)) >= 0;
1265 end;
1266
1267 procedure TStExchangeRateList.Convert(const Source, Target: String;
1268 Amount, Result: TStDecimal);
1269 {
1270 convert Amount from Source currency to Target currency,
1271 return new value in Result
1272 }
1273 begin
1274 {Amount and Result must be created}
1275 if (Amount = nil) or (Result = nil) then
1276 raise EStException.CreateResTP(stscMoneyInvalidExchangeParams, 0);
1277
1278 {set the result value equal to the amount being converted}
1279 Result.Assign(Amount);
1280
1281 {convert, allowing triangular exchanges}
1282 ConvertPrim(Source, Target, Result, true);
1283 end;
1284
1285 procedure TStExchangeRateList.ConvertPrim(const aSource, aTarget : string;
1286 aAmount : TStDecimal;
1287 aAllowTriangular : boolean);
1288 var
1289 Rate : TStExchangeRate;
1290 begin
1291 { do we have an entry for a Source->Target conversion? }
1292 if not ContainsByName(aSource, aTarget) then
1293 raise EStException.CreateResFmtTP(stscMoneyNoSuchExchange,
1294 [aSource, aTarget], 0);
1295
1296 {get the exchange rate}
1297 Rate := Rates[aSource, aTarget];
1298
1299 {for a simple multiply or divide conversion, the Rate object can
1300 handle that by itself}
1301 if (Rate.ConversionType = ctMultiply) or
1302 (Rate.ConversionType = ctDivide) then begin
1303 Rate.Convert(aAmount, aAmount);
1304 Exit;
1305 end;
1306
1307 {if a triangular exchange is not allowed, raise an error}
1308 if not aAllowTriangular then
1309 raise EStException.CreateResTP(stscMoneyTriExchUsesTriExch, 0);
1310
1311 {if the exchange rate is not triangular, raise an error}
1312 if (Rate.ConversionType <> ctTriangular) then
1313 raise EStException.CreateResTP(stscMoneyInvalidExchangeParams, 0);
1314
1315 {the conversion is triangular: check the intermediate currency}
1316 if (Rate.Intermediate = '') then
1317 raise EStException.CreateResTP(stscMoneyInvalidExchangeParams, 0);
1318
1319 {check to see if we have the two exchange rates}
1320 if (not ContainsByName(aSource, Rate.Intermediate)) or
1321 (not ContainsByName(Rate.Intermediate, aTarget)) then
1322 raise EStException.CreateResFmtTP(stscMoneyMissingIntermediateRate,
1323 [aSource, aTarget], 0);
1324
1325 {convert the amount from the Source to the Intermediate currency,
1326 and then the result from the Intermediate to the Target currency;
1327 triangular exchanges are *not* allowed to avoid infinite loops}
1328 ConvertPrim(aSource, Rate.Intermediate, aAmount, false);
1329 ConvertPrim(Rate.Intermediate, aTarget, aAmount, false);
1330 end;
1331
1332 procedure TStExchangeRateList.Delete(ARate: TStExchangeRate);
1333 {
1334 delete specified rate from list
1335 fails silently if no matching rate exists in list
1336 }
1337 begin
1338 DeleteByName(ARate.Source, ARate.Target);
1339 end;
1340
1341 procedure TStExchangeRateList.DeleteByName(const Source,
1342 Target: String);
1343 {
1344 delete rate from list as determined by Source and Target
1345 fails silently if no matching rate exists in list
1346 }
1347 var
1348 Idx : Integer;
1349 begin
1350 { find item in list }
1351 Idx := FRates.IndexOf(MakeEntry(Source, Target));
1352
1353 { if it exists, remove it }
1354 if Idx >= 0 then
1355 DeleteRate(Idx);
1356 end;
1357
1358 procedure TStExchangeRateList.DeleteRate(Index : Integer);
1359 { remove Rate from list by index }
1360 { no error checking that Index is in Range, should be done by caller }
1361 begin
1362 (FRates.Objects[Index] as TStExchangeRate).Free;
1363 FRates.Delete(Index);
1364 end;
1365
1366 function TStExchangeRateList.GetCount: Integer;
1367 begin
1368 Result := FRates.Count;
1369 end;
1370
1371 function TStExchangeRateList.GetItem(Index: Integer): TStExchangeRate;
1372 { return Exchange rate by index }
1373 begin
1374 if not ((0 <= Index) and (Index < FRates.Count)) then
1375 raise EStException.CreateResFmtTP(stscBadIndex, [IntToStr(Index)], 0);
1376 Result := (FRates.Objects[Index] as TStExchangeRate);
1377 end;
1378
1379 function TStExchangeRateList.GetRate(const Source,
1380 Target: String): TStExchangeRate;
1381 { return Exchange rate by Source and Target }
1382 var
1383 Idx : Integer;
1384 begin
1385 Idx := FRates.IndexOf(MakeEntry(Source, Target));
1386 if Idx >= 0 then begin
1387 Result := (FRates.Objects[Idx] as TStExchangeRate);
1388 end
1389 else
1390 raise EStException.CreateResFmtTP(stscMoneyNoSuchExchange, [Source, Target], 0);
1391 end;
1392
1393 procedure TStExchangeRateList.LoadFromFile(const AFileName: TFileName);
1394 var
1395 FS : TFileStream;
1396 begin
1397 FS := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
1398 try
1399 LoadFromStream(FS);
1400 finally
1401 FS.Free;
1402 end;
1403 end;
1404
1405 procedure TStExchangeRateList.LoadFromStream(AStream: TStream);
1406 { build Rate list from stream of Rate data }
1407 var
1408 i : Integer;
1409 IniStrm : TStIniStream;
1410 Entries, Sections : TStringList;
1411 CurRate : TStExchangeRate;
1412 begin
1413 IniStrm := nil;
1414 Entries := nil;
1415 Sections := nil;
1416 CurRate := nil;
1417 try
1418 IniStrm := TStIniStream.Create(AStream);
1419 Entries := TStringList.Create;
1420 Sections := TStringList.Create;
1421 { create "index" of sections }
1422 IniStrm.ReadSections(Sections);
1423
1424 { iterate sections }
1425 for i := 0 to Pred(Sections.Count) do begin
1426 { get settings as a list of <Name>=<Value> pairs }
1427 IniStrm.ReadSectionValues(Sections[i], Entries);
1428
1429 { build new rate item from settings }
1430 CurRate := TStExchangeRate.Create;
1431 CurRate.LoadFromList(Entries);
1432
1433 { add to list }
1434 Add(CurRate);
1435 CurRate := nil;
1436 end;
1437 finally
1438 Sections.Free;
1439 Entries.Free;
1440 IniStrm.Free;
1441 CurRate.Free;
1442 end;
1443 end;
1444
1445 function TStExchangeRateList.MakeEntry(const Source, Target : String) : String;
1446 { format conversion entry header from Source and Target }
1447 begin
1448 Result := Source + ':' + Target;
1449 end;
1450
1451 procedure TStExchangeRateList.SaveToFile(const AFileName: TFileName);
1452 var
1453 FS : TFileStream;
1454 begin
1455 if not FileExists(AFileName) then begin
1456 FS := TFileStream.Create(AFileName, fmCreate);
1457 FS.Free;
1458 end;
1459
1460 FS := TFileStream.Create(AFileName, fmOpenWrite or fmShareDenyNone);
1461 try
1462 SaveToStream(FS);
1463 finally
1464 FS.Free;
1465 end;
1466 end;
1467
1468 procedure TStExchangeRateList.SaveToStream(AStream: TStream);
1469 { persist list of Rate data to a stream }
1470 var
1471 i : Integer;
1472 IniStrm : TStIniStream;
1473 Entries : TStringList;
1474 CurRate : TStExchangeRate;
1475 begin
1476 IniStrm := nil;
1477 Entries := nil;
1478 try
1479 IniStrm := TStIniStream.Create(AStream);
1480 Entries := TStringList.Create;
1481 { for each maintained Rate item }
1482 for i := 0 to Pred(FRates.Count) do begin
1483
1484 { get reference to the Rate }
1485 CurRate := (FRates.Objects[i] as TStExchangeRate);
1486
1487 { make entries for Rate }
1488 CurRate.SaveToList(Entries);
1489
1490 { write entries as a new section to INI stream }
1491 IniStrm.WriteSection(MakeEntry(CurRate.Source, CurRate.Target),
1492 Entries);
1493 end;
1494 finally
1495 Entries.Free;
1496 IniStrm.Free;
1497 end;
1498 end;
1499
1500 procedure TStExchangeRateList.UpdateRate(const Source,
1501 Target: String; Rate: TStDecimal);
1502 {
1503 Modifies the exchange rate specified by the source and target
1504 assumes rate already exists, use Add or AddByValues to add new rates
1505 }
1506 var
1507 Idx : Integer;
1508 begin
1509 if not Assigned(Rate) then
1510 raise EStException.CreateResTP(stscMoneyNilParameter, 0);
1511
1512 Idx := FRates.IndexOf(MakeEntry(Source, Target));
1513 if Idx >= 0 then begin { conversion already exists for source and target }
1514 { update Rate to reflect new rate }
1515 (FRates.Objects[Idx] as TStExchangeRate).Rate.Assign(Rate);
1516 end
1517 { else no such rate }
1518 end;
1519
1520 initialization
1521 ExchBaseDate := EncodeDate(1980, 1, 1);
1522 end.
1523

  ViewVC Help
Powered by ViewVC 1.1.20