4 people like it.
Like the snippet!
Multi-currency report
Multi-currency report (generated as HTML) based on example given at the start of chapter one of Kent Beck's Test-Driven Development by Example book.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
type Money = private { Amount:decimal; Currency:Currency }
with
static member ( * ) (lhs:Money,rhs:decimal) =
{ lhs with Amount=lhs.Amount * rhs }
static member ( + ) (lhs:Money,rhs:Money) =
if lhs.Currency <> rhs.Currency then invalidOp "Currency mismatch"
{ lhs with Amount=lhs.Amount + rhs.Amount}
override money.ToString() = sprintf "%M%s" money.Amount money.Currency
and Currency = string
type RateTable = { To:Currency; From:Map<Currency,decimal> }
let exchangeRate (rates:RateTable) cy =
if rates.To = cy then 1.0M else rates.From.[cy]
let convertCurrency (rates:RateTable) money =
let rate = exchangeRate rates money.Currency
{ Amount=money.Amount / rate; Currency=rates.To }
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
|
type Report = { Rows:Row list; Total:Money }
and Row = { Position:Position; Total:Money }
and Position = { Instrument:string; Shares:int; Price:Money }
let generateReport rates positions =
let rows =
[for position in positions ->
let total = position.Price * decimal position.Shares
{ Position=position; Total=total } ]
let total =
rows
|> Seq.map (fun row -> convertCurrency rates row.Total)
|> Seq.reduce (+)
{ Rows=rows; Total=total }
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
|
let toHtml (report:Report) =
html [
head [ title %"Multi-currency report" ]
body [
table <|
("style"%="border-collapse:collapse;") ::
("cellpadding"%="8") ::
thead [
tr [th %"Instrument"; th %"Shares"; th %"Price"; th %"Total"]
] ::
tbody [
for row in report.Rows ->
let p = row.Position
tr [td %p.Instrument; td %p.Shares; td %p.Price; td %row.Total]
] ::
[ tfoot [
tr [td ("colspan"%="3"::"align"%="right"::[strong %"Total"])
td %report.Total]
]]
]
]
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
let USD amount = { Amount=amount; Currency="USD" }
let CHF amount = { Amount=amount; Currency="CHF" }
let positions =
[{Instrument="IBM"; Shares=1000; Price=USD( 25M)}
{Instrument="Novartis"; Shares= 400; Price=CHF(150M)}]
let inUSD = { To="USD"; From=Map.ofList ["CHF",1.5M] }
let positionsInUSD = generateReport inUSD positions
positionsInUSD |> toHtml |> Html.toString
|
type Money =
private {Amount: decimal;
Currency: Currency;}
override ToString : unit -> string
static member ( + ) : lhs:Money * rhs:Money -> Money
static member ( * ) : lhs:Money * rhs:decimal -> Money
Full name: Script.Money
Money.Amount: decimal
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.decimal
--------------------
type decimal = System.Decimal
Full name: Microsoft.FSharp.Core.decimal
--------------------
type decimal<'Measure> = decimal
Full name: Microsoft.FSharp.Core.decimal<_>
Multiple items
Money.Currency: Currency
--------------------
type Currency = string
Full name: Script.Currency
val lhs : Money
val rhs : decimal
val rhs : Money
Money.Currency: Currency
val invalidOp : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.invalidOp
val money : Money
override Money.ToString : unit -> string
Full name: Script.Money.ToString
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
type Currency = string
Full name: Script.Currency
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
type RateTable =
{To: Currency;
From: Map<Currency,decimal>;}
Full name: Script.RateTable
RateTable.To: Currency
RateTable.From: Map<Currency,decimal>
Multiple items
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IEnumerable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
member Add : key:'Key * value:'Value -> Map<'Key,'Value>
member ContainsKey : key:'Key -> bool
override Equals : obj -> bool
member Remove : key:'Key -> Map<'Key,'Value>
...
Full name: Microsoft.FSharp.Collections.Map<_,_>
--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val exchangeRate : rates:RateTable -> cy:Currency -> decimal
Full name: Script.exchangeRate
val rates : RateTable
val cy : Currency
val convertCurrency : rates:RateTable -> money:Money -> Money
Full name: Script.convertCurrency
val rate : decimal
type Report =
{Rows: Row list;
Total: Money;}
Full name: Script.Report
Report.Rows: Row list
type Row =
{Position: Position;
Total: Money;}
Full name: Script.Row
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
Report.Total: Money
Multiple items
Row.Position: Position
--------------------
type Position =
{Instrument: string;
Shares: int;
Price: Money;}
Full name: Script.Position
Row.Total: Money
type Position =
{Instrument: string;
Shares: int;
Price: Money;}
Full name: Script.Position
Position.Instrument: string
Position.Shares: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Position.Price: Money
val generateReport : rates:RateTable -> positions:seq<Position> -> Report
Full name: Script.generateReport
val positions : seq<Position>
val rows : Row list
val position : Position
val total : Money
module Seq
from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.map
val row : Row
val reduce : reduction:('T -> 'T -> 'T) -> source:seq<'T> -> 'T
Full name: Microsoft.FSharp.Collections.Seq.reduce
val toHtml : report:Report -> Html
Full name: Script.toHtml
val report : Report
val html : (Html list -> Html)
Full name: Script.html
val head : (Html list -> Html)
Full name: Script.head
val title : (Html list -> Html)
Full name: Script.title
val body : (Html list -> Html)
Full name: Script.body
val table : (Html list -> Html)
Full name: Script.table
val thead : (Html list -> Html)
Full name: Script.thead
val tr : (Html list -> Html)
Full name: Script.tr
val th : (Html list -> Html)
Full name: Script.th
val tbody : (Html list -> Html)
Full name: Script.tbody
val p : Position
Row.Position: Position
val td : (Html list -> Html)
Full name: Script.td
val tfoot : (Html list -> Html)
Full name: Script.tfoot
val strong : (Html list -> Html)
Full name: Script.strong
val USD : amount:decimal -> Money
Full name: Script.USD
val amount : decimal
val CHF : amount:decimal -> Money
Full name: Script.CHF
val positions : Position list
Full name: Script.positions
val inUSD : RateTable
Full name: Script.inUSD
val ofList : elements:('Key * 'T) list -> Map<'Key,'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.ofList
val positionsInUSD : Report
Full name: Script.positionsInUSD
type Html =
| Elem of string * Html list
| Attr of string * string
| Text of string
override ToString : unit -> string
static member toString : elem:Html -> string
Full name: Script.Html
static member Html.toString : elem:Html -> string
More information