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
35 import qualified Data.Text as T
37 import qualified Hcompta as H
42 { amount_unit :: !Unit
43 , amount_quantity :: !Quantity
44 } deriving (Data, Eq, Ord, Show, Typeable)
46 -- type instance H.UnitFor Amount = Unit
47 -- type instance H.QuantityFor Amount = H.Polarized Quantity
49 instance NFData Amount where
50 rnf (Amount q u) = rnf q `seq` rnf u
51 -- instance H.Amount Amount
54 type instance H.Unit H.:@ Amount = Unit
55 instance H.GetI H.Unit Amount where
57 instance H.SetI H.Unit Amount where
58 setI amount_unit a = a{amount_unit}
60 type instance H.Quantity H.:@ Amount = Quantity
61 instance H.GetI H.Quantity Amount where
62 getI = amount_quantity
63 instance H.SetI H.Quantity Amount where
64 setI amount_quantity a = a{amount_quantity}
67 instance H.Zeroable Amount where
68 zero = Amount "" H.zero
69 instance H.Nullable Amount where
70 null = H.null . amount_quantity
71 -- NOTE: the 'Amount'’s 'amount_quantity' MUST already be rounded
72 -- at 'Amount'’s 'amount_style'’s 'style_amount_precision'.
73 instance H.Signable Amount where
74 sign = H.sign . amount_quantity
80 , amount_quantity = H.zero
83 amount_style :: Style_Amounts -> Amount -> Style_Amount
84 amount_style styles = style_amount_find styles . amount_unit
87 type Quantity = Decimal
89 quantity_round :: Word8 -> Quantity -> Quantity
90 quantity_round = Data.Decimal.roundTo
95 deriving (Data, Eq, IsString, Ord, Show, Typeable)
96 instance H.Zeroable Unit where
98 instance H.Nullable Unit where
99 null (Unit x) = T.null x
101 instance H.Unit Unit where
103 textUnit (Unit t) = t
105 instance NFData Unit where
108 -- ** Example 'Unit's
114 -- | <https://en.wikipedia.org/wiki/Swiss_franc Swiss franc> unit of currency.
116 unit_chf = Unit "CHF"
118 -- | <https://en.wikipedia.org/wiki/Yuan Yuan> unit of currency.
122 -- | <https://en.wikipedia.org/wiki/Euro Euro> unit of currency.
126 -- | <https://en.wikipedia.org/wiki/Pound_sterling Pound sterling> unit of currency.
130 -- | <https://en.wikipedia.org/wiki/Indian_rupee Indian rupee> unit of currency.
134 -- | <https://en.wikipedia.org/wiki/Japanese_yen Japanese yen> unit of currency.
138 -- | <https://en.wikipedia.org/wiki/Russian_ruble Russian ruble> unit of currency.
140 -- NOTE: Ꝑ (U+A750) is used as a replacement latin letter,
141 -- because GHC currently chokes on ₽ (U+20BD),
142 -- which is the recently (2014/02) assigned Unicode code-point
143 -- for this currency.
147 -- | <https://en.wikipedia.org/wiki/United_States_dollar United States dollar> unit of currency.
151 -- * Type 'Style_Amount'
154 { style_amount_fractioning :: !(S.Maybe Style_Amount_Fractioning)
155 , style_amount_grouping_integral :: !(S.Maybe Style_Amount_Grouping)
156 , style_amount_grouping_fractional :: !(S.Maybe Style_Amount_Grouping)
157 , style_amount_unit_side :: !(S.Maybe LR)
158 , style_amount_unit_spaced :: !(S.Maybe Style_Amount_Spacing)
159 -- TODO: , style_amount_sign_plus :: S.Maybe Bool
160 } deriving (Data, Eq, Ord, Show, Typeable)
161 instance NFData Style_Amount where
162 rnf (Style_Amount f gi gf ui up) =
168 instance Semigroup Style_Amount where
169 (<>) = style_amount_union
170 instance Monoid Style_Amount where
171 mempty = style_amount
174 style_amount :: Style_Amount
177 { style_amount_fractioning = Nothing
178 , style_amount_grouping_integral = Nothing
179 , style_amount_grouping_fractional = Nothing
180 , style_amount_unit_side = Nothing
181 , style_amount_unit_spaced = Nothing
190 { style_amount_fractioning=f
191 , style_amount_grouping_integral=gi
192 , style_amount_grouping_fractional=gf
193 , style_amount_unit_side=side
194 , style_amount_unit_spaced=spaced
197 { style_amount_fractioning=f'
198 , style_amount_grouping_integral=gi'
199 , style_amount_grouping_fractional=gf'
200 , style_amount_unit_side=side'
201 , style_amount_unit_spaced=spaced'
207 { style_amount_fractioning = S.maybe f' (const f) f
208 , style_amount_grouping_integral = S.maybe gi' (const gi) gi
209 , style_amount_grouping_fractional = S.maybe gf' (const gf) gf
210 , style_amount_unit_side = S.maybe side' (const side) side
211 , style_amount_unit_spaced = S.maybe spaced' (const spaced) spaced
214 -- ** Type 'Style_Amount_Fractioning'
215 type Style_Amount_Fractioning
218 -- ** Type 'Style_Amount_Grouping'
219 data Style_Amount_Grouping
220 = Style_Amount_Grouping Char [Int]
221 deriving (Data, Eq, Ord, Show, Typeable)
222 instance NFData Style_Amount_Grouping where
223 rnf (Style_Amount_Grouping s d) = rnf s `seq` rnf d
225 -- ** Type 'Style_Amount_Precision'
226 type Style_Amount_Precision
229 -- ** Type 'Style_Amount_Spacing'
230 type Style_Amount_Spacing
235 deriving (Data, Eq, Ord, Show, Typeable)
236 instance NFData LR where
240 -- ** Type 'Style_Amounts'
241 newtype Style_Amounts
242 = Style_Amounts (Map Unit Style_Amount)
243 deriving (Data, Eq, NFData, Ord, Show, Typeable)
245 -- type instance H.UnitFor Amounts = Unit
246 -- type instance H.QuantityFor Amounts = Quantity
248 instance Semigroup Style_Amounts where
249 Style_Amounts x <> Style_Amounts y =
250 Style_Amounts (Map.unionWith (flip (<>)) x y)
251 instance Monoid Style_Amounts where
252 mempty = Style_Amounts mempty
254 instance H.Zeroable Style_Amounts where
255 zero = Style_Amounts mempty
256 instance H.Sumable Style_Amounts (Unit, Style_Amount) where
257 Style_Amounts ss += (u, s) = Style_Amounts $ Map.insertWith (flip (<>)) u s ss
259 unStyle_Amounts :: Style_Amounts -> Map Unit Style_Amount
260 unStyle_Amounts (Style_Amounts fp) = fp
262 style_amount_find :: Style_Amounts -> Unit -> Style_Amount
263 style_amount_find (Style_Amounts s) u = Map.findWithDefault mempty u s
265 -- *** Example 'Style_Amounts'
266 style_amounts :: Style_Amounts
267 style_amounts = Style_Amounts $ Map.fromList
268 [ (unit_scalar,) Style_Amount
269 { style_amount_fractioning = Just '.'
270 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
271 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
272 , style_amount_unit_side = Just R
273 , style_amount_unit_spaced = Just False
275 , (unit_chf,) Style_Amount
276 { style_amount_fractioning = Just ','
277 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
278 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
279 , style_amount_unit_side = Just R
280 , style_amount_unit_spaced = Just False
282 , (unit_cny,) Style_Amount
283 { style_amount_fractioning = Just ','
284 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
285 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
286 , style_amount_unit_side = Just R
287 , style_amount_unit_spaced = Just False
289 , (unit_eur,) Style_Amount
290 { style_amount_fractioning = Just ','
291 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
292 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
293 , style_amount_unit_side = Just R
294 , style_amount_unit_spaced = Just False
296 , (unit_gbp,) Style_Amount
297 { style_amount_fractioning = Just '.'
298 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
299 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
300 , style_amount_unit_side = Just L
301 , style_amount_unit_spaced = Just False
303 , (unit_inr,) Style_Amount
304 { style_amount_fractioning = Just ','
305 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping '.' [3]
306 , style_amount_grouping_integral = Just $ Style_Amount_Grouping '.' [3]
307 , style_amount_unit_side = Just R
308 , style_amount_unit_spaced = Just False
310 , (unit_jpy,) Style_Amount
311 { style_amount_fractioning = Just '.'
312 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
313 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
314 , style_amount_unit_side = Just L
315 , style_amount_unit_spaced = Just False
317 , (unit_rub,) Style_Amount
318 { style_amount_fractioning = Just '.'
319 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
320 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
321 , style_amount_unit_side = Just L
322 , style_amount_unit_spaced = Just False
324 , (unit_usd,) Style_Amount
325 { style_amount_fractioning = Just '.'
326 , style_amount_grouping_fractional = Just $ Style_Amount_Grouping ',' [3]
327 , style_amount_grouping_integral = Just $ Style_Amount_Grouping ',' [3]
328 , style_amount_unit_side = Just L
329 , style_amount_unit_spaced = Just False
333 -- ** Type 'Styled_Amount'
334 type Styled_Amount t = (Style_Amount, t)
336 styled_amount :: Style_Amounts -> Amount -> Styled_Amount Amount
337 styled_amount styles amt = (amount_style styles amt, amt)
340 newtype Amounts = Amounts (Map Unit Quantity)
341 deriving (Data, Eq, NFData, Ord, Show, Typeable
342 , H.Addable, H.Negable, H.Subable)
343 unAmounts :: Amounts -> Map Unit Quantity
344 unAmounts (Amounts a) = a
345 instance H.Zeroable Amounts where
346 zero = Amounts H.zero
347 instance H.Nullable Amounts where
348 null (Amounts x) = H.null x
349 instance Semigroup Amounts where
350 Amounts x <> Amounts y = Amounts (Map.unionWith (flip (H.+)) x y)
351 instance Monoid Amounts where
352 mempty = Amounts mempty
355 type instance MT.Element Amounts = Amount
356 -- instance H.Amounts Amounts
358 -- instance H.Get (H.Balance_Amounts Unit Quantity) Amounts where
359 -- get (Amounts a) = a