1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
5 {-# LANGUAGE MultiParamTypeClasses #-}
6 {-# LANGUAGE NamedFieldPuns #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE TupleSections #-}
9 {-# LANGUAGE TypeFamilies #-}
10 {-# LANGUAGE TypeOperators #-}
11 {-# OPTIONS_GHC -fno-warn-orphans #-}
12 module Hcompta.LCC.Amount where
14 import Control.DeepSeq
16 import Data.Char (Char)
18 import Data.Decimal (Decimal, roundTo)
19 import Data.Eq (Eq(..))
20 import Data.Function (($), (.), const, flip)
21 import Data.Map.Strict (Map)
22 import Data.Monoid (Monoid(..))
23 import Data.Ord (Ord(..))
24 import Data.Semigroup (Semigroup(..))
25 import Data.Strict.Maybe
26 import Data.String (IsString)
27 import Data.Text (Text)
28 import Data.Typeable ()
29 import Data.Word (Word8)
30 import Prelude (Int, seq)
31 import Text.Show (Show(..))
32 import qualified Data.Map.Strict as Map
33 import qualified Data.MonoTraversable as MT
34 import qualified Data.Strict as S
36 import qualified Hcompta as H
41 { amount_unit :: !Unit
42 , amount_quantity :: !Quantity
43 } deriving (Data, Eq, Ord, Show, Typeable)
44 instance NFData Amount where
45 rnf (Amount q u) = rnf q `seq` rnf u
46 instance H.Amount Amount
48 type instance H.Unit H.:@ Amount = Unit
49 instance H.GetI H.Unit Amount where
51 instance H.SetI H.Unit Amount where
52 setI_ _ amount_unit a = a{amount_unit}
54 type instance H.Quantity H.:@ Amount = Quantity
55 instance H.GetI H.Quantity Amount where
56 getI_ _ = amount_quantity
57 instance H.SetI H.Quantity Amount where
58 setI_ _ amount_quantity a = a{amount_quantity}
60 instance H.Zero Amount where
61 quantity_zero = Amount H.unit_empty H.quantity_zero
62 quantity_null = H.quantity_null . amount_quantity
63 -- NOTE: the 'Amount'’s 'amount_quantity' MUST already be rounded
64 -- at 'Amount'’s 'amount_style'’s 'style_amount_precision'.
65 quantity_sign = H.quantity_sign . amount_quantity
71 , amount_quantity = H.quantity_zero
74 amount_style :: Style_Amounts -> Amount -> Style_Amount
75 amount_style styles = style_amount_find styles . amount_unit
78 type Quantity = Decimal
80 quantity_round :: Word8 -> Quantity -> Quantity
81 quantity_round = Data.Decimal.roundTo
86 deriving (Data, Eq, IsString, Ord, Show, Typeable)
87 instance H.Unit Unit where
89 unit_text (Unit t) = t
90 instance NFData Unit where
97 unit_scalar = H.unit_empty
99 -- | <https://en.wikipedia.org/wiki/Swiss_franc Swiss franc> unit of currency.
101 unit_chf = Unit "CHF"
103 -- | <https://en.wikipedia.org/wiki/Yuan Yuan> unit of currency.
107 -- | <https://en.wikipedia.org/wiki/Euro Euro> unit of currency.
111 -- | <https://en.wikipedia.org/wiki/Pound_sterling Pound sterling> unit of currency.
115 -- | <https://en.wikipedia.org/wiki/Indian_rupee Indian rupee> unit of currency.
119 -- | <https://en.wikipedia.org/wiki/Japanese_yen Japanese yen> unit of currency.
123 -- | <https://en.wikipedia.org/wiki/Russian_ruble Russian ruble> unit of currency.
125 -- NOTE: Ꝑ (U+A750) is used as a replacement latin letter,
126 -- because GHC currently chokes on ₽ (U+20BD),
127 -- which is the recently (2014/02) assigned Unicode code-point
128 -- for this currency.
132 -- | <https://en.wikipedia.org/wiki/United_States_dollar United States dollar> unit of currency.
136 -- * Type 'Style_Amount'
139 { style_amount_fractioning :: !(S.Maybe Style_Amount_Fractioning)
140 , style_amount_grouping_integral :: !(S.Maybe Style_Amount_Grouping)
141 , style_amount_grouping_fractional :: !(S.Maybe Style_Amount_Grouping)
142 , style_amount_unit_side :: !(S.Maybe LR)
143 , style_amount_unit_spaced :: !(S.Maybe Style_Amount_Spacing)
144 -- TODO: , style_amount_sign_plus :: S.Maybe Bool
145 } deriving (Data, Eq, Ord, Show, Typeable)
146 instance NFData Style_Amount where
147 rnf (Style_Amount f gi gf ui up) =
153 instance Semigroup Style_Amount where
154 (<>) = style_amount_union
155 instance Monoid Style_Amount where
156 mempty = style_amount
159 style_amount :: Style_Amount
162 { style_amount_fractioning = Nothing
163 , style_amount_grouping_integral = Nothing
164 , style_amount_grouping_fractional = Nothing
165 , style_amount_unit_side = Nothing
166 , style_amount_unit_spaced = Nothing
175 { style_amount_fractioning=f
176 , style_amount_grouping_integral=gi
177 , style_amount_grouping_fractional=gf
178 , style_amount_unit_side=side
179 , style_amount_unit_spaced=spaced
182 { style_amount_fractioning=f'
183 , style_amount_grouping_integral=gi'
184 , style_amount_grouping_fractional=gf'
185 , style_amount_unit_side=side'
186 , style_amount_unit_spaced=spaced'
192 { style_amount_fractioning = S.maybe f' (const f) f
193 , style_amount_grouping_integral = S.maybe gi' (const gi) gi
194 , style_amount_grouping_fractional = S.maybe gf' (const gf) gf
195 , style_amount_unit_side = S.maybe side' (const side) side
196 , style_amount_unit_spaced = S.maybe spaced' (const spaced) spaced
199 -- ** Type 'Style_Amount_Fractioning'
200 type Style_Amount_Fractioning
203 -- ** Type 'Style_Amount_Grouping'
204 data Style_Amount_Grouping
205 = Style_Amount_Grouping Char [Int]
206 deriving (Data, Eq, Ord, Show, Typeable)
207 instance NFData Style_Amount_Grouping where
208 rnf (Style_Amount_Grouping s d) = rnf s `seq` rnf d
210 -- ** Type 'Style_Amount_Precision'
211 type Style_Amount_Precision
214 -- ** Type 'Style_Amount_Spacing'
215 type Style_Amount_Spacing
220 deriving (Data, Eq, Ord, Show, Typeable)
221 instance NFData LR where
225 -- ** Type 'Style_Amounts'
226 newtype Style_Amounts
227 = Style_Amounts (Map Unit Style_Amount)
228 deriving (Data, Eq, NFData, Ord, Show, Typeable)
229 unStyle_Amounts :: Style_Amounts -> Map Unit Style_Amount
230 unStyle_Amounts (Style_Amounts fp) = fp
231 instance Semigroup Style_Amounts where
232 Style_Amounts x <> Style_Amounts y =
233 Style_Amounts (Map.unionWith (flip (<>)) x y)
234 instance Monoid Style_Amounts where
235 mempty = Style_Amounts mempty
239 style_amount_cons :: (Unit, Style_Amount) -> Style_Amounts -> Style_Amounts
240 style_amount_cons (u, s) (Style_Amounts ss) =
242 Map.insertWith (flip (<>)) u s ss
244 style_amount_find :: Style_Amounts -> Unit -> Style_Amount
245 style_amount_find (Style_Amounts s) u = Map.findWithDefault mempty u s
247 -- *** Example 'Style_Amounts'
248 style_amounts :: Style_Amounts
249 style_amounts = Style_Amounts $ Map.fromList
250 [ (unit_scalar,) Style_Amount
251 { style_amount_fractioning = Just '.'
252 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
253 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
254 , style_amount_unit_side = Just R
255 , style_amount_unit_spaced = Just False
257 , (unit_chf,) Style_Amount
258 { style_amount_fractioning = Just ','
259 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
260 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
261 , style_amount_unit_side = Just R
262 , style_amount_unit_spaced = Just False
264 , (unit_cny,) Style_Amount
265 { style_amount_fractioning = Just ','
266 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
267 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
268 , style_amount_unit_side = Just R
269 , style_amount_unit_spaced = Just False
271 , (unit_eur,) Style_Amount
272 { style_amount_fractioning = Just ','
273 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
274 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
275 , style_amount_unit_side = Just R
276 , style_amount_unit_spaced = Just False
278 , (unit_gbp,) Style_Amount
279 { style_amount_fractioning = Just '.'
280 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
281 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
282 , style_amount_unit_side = Just L
283 , style_amount_unit_spaced = Just False
285 , (unit_inr,) Style_Amount
286 { style_amount_fractioning = Just ','
287 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
288 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
289 , style_amount_unit_side = Just R
290 , style_amount_unit_spaced = Just False
292 , (unit_jpy,) Style_Amount
293 { style_amount_fractioning = Just '.'
294 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
295 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
296 , style_amount_unit_side = Just L
297 , style_amount_unit_spaced = Just False
299 , (unit_rub,) Style_Amount
300 { style_amount_fractioning = Just '.'
301 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
302 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
303 , style_amount_unit_side = Just L
304 , style_amount_unit_spaced = Just False
306 , (unit_usd,) Style_Amount
307 { style_amount_fractioning = Just '.'
308 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
309 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
310 , style_amount_unit_side = Just L
311 , style_amount_unit_spaced = Just False
315 -- ** Type 'Styled_Amount'
316 type Styled_Amount t = (Style_Amount, t)
318 styled_amount :: Style_Amounts -> Amount -> Styled_Amount Amount
319 styled_amount styles amt = (amount_style styles amt, amt)
322 newtype Amounts = Amounts (Map Unit Quantity)
323 deriving (Data, Eq, NFData, Ord, Show, Typeable
324 , H.Addable, H.Negable, H.Subable)
325 unAmounts :: Amounts -> Map Unit Quantity
326 unAmounts (Amounts a) = a
327 instance H.Zero Amounts where
328 quantity_zero = Amounts H.quantity_zero
329 instance Semigroup Amounts where
330 Amounts x <> Amounts y =
331 Amounts (Map.unionWith (flip H.quantity_add) x y)
332 instance Monoid Amounts where
333 mempty = Amounts mempty
336 type instance MT.Element Amounts = Amount
337 instance H.Amounts Amounts
339 instance H.Get (H.Balance_Amounts Unit Quantity) Amounts where