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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 44024 byte(s)
Added tpsystools component
1 torben 2671 // 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