1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE MultiParamTypeClasses #-}
5 {-# LANGUAGE ScopedTypeVariables #-}
6 {-# LANGUAGE StandaloneDeriving #-}
7 {-# LANGUAGE TupleSections #-}
8 {-# LANGUAGE TypeFamilies #-}
9 module Hcompta.Filter where
11 -- import Control.Applicative (pure, (<$>), (<*>))
13 import qualified Data.Fixed
14 import qualified Data.Foldable
15 -- import Data.Foldable (Foldable(..))
16 import qualified Data.Functor.Compose
17 -- import qualified Data.List
18 import Data.Map.Strict (Map)
19 import qualified Data.Map.Strict as Data.Map
20 import qualified Data.Monoid
21 -- import Data.Monoid (Monoid(..))
22 import Data.Text (Text)
23 -- import qualified Data.Text as Text
24 -- import qualified Data.Time.Calendar as Time
25 -- import Data.Traversable (Traversable(..))
26 import Data.Typeable ()
27 import Prelude hiding (filter)
28 import Text.Regex.Base ()
29 import Text.Regex.TDFA ()
30 import Text.Regex.TDFA.Text ()
32 import qualified Data.List.NonEmpty as NonEmpty
33 -- import Data.List.NonEmpty (NonEmpty(..))
34 import Hcompta.Lib.Interval (Interval)
35 import qualified Hcompta.Lib.Interval as Interval
36 import qualified Hcompta.Lib.Regex as Regex
37 import Hcompta.Lib.Regex (Regex)
38 -- import qualified Hcompta.Lib.TreeMap as TreeMap
39 -- import Hcompta.Lib.TreeMap (TreeMap)
40 import qualified Hcompta.Amount as Amount
41 import qualified Hcompta.Amount.Unit as Amount.Unit
42 import qualified Hcompta.Date as Date
43 import Hcompta.Date (Date)
44 import qualified Hcompta.Account as Account
45 import Hcompta.Account (Account)
46 -- import qualified Hcompta.Date as Date
47 import qualified Hcompta.Balance as Balance
48 import qualified Hcompta.GL as GL
50 -- * Requirements' interface
55 unit_text :: a -> Text
57 instance Unit Amount.Unit where
58 unit_text = Amount.Unit.text
60 instance Unit Text where
66 ( Ord (Amount_Quantity a)
67 , Show (Amount_Quantity a)
68 , Show (Amount_Unit a)
69 , Unit (Amount_Unit a)
73 type Amount_Quantity a
74 amount_unit :: a -> Amount_Unit a
75 amount_quantity :: a -> Amount_Quantity a
77 instance Amount Amount.Amount where
78 type Amount_Unit Amount.Amount = Amount.Unit
79 type Amount_Quantity Amount.Amount = Amount.Quantity
80 amount_quantity = Amount.quantity
81 amount_unit = Amount.unit
83 instance (Amount a, GL.Amount a)
84 => Amount (Amount.Sum a) where
85 type Amount_Unit (Amount.Sum a) = Amount_Unit a
86 type Amount_Quantity (Amount.Sum a) = Amount_Quantity a
87 amount_quantity = amount_quantity . Amount.sum_balance
88 amount_unit = amount_unit . Amount.sum_balance
92 class Amount (Posting_Amount p)
95 posting_account :: p -> Account
96 posting_amounts :: p -> Map (Amount_Unit (Posting_Amount p)) (Posting_Amount p)
98 -- ** Class 'Transaction'
100 class Posting (Transaction_Posting t)
101 => Transaction t where
102 type Transaction_Posting t
103 transaction_date :: t -> Date
104 transaction_description :: t -> Text
105 transaction_postings :: t -> Map Account [Transaction_Posting t]
106 transaction_tags :: t -> Map Text [Text]
108 -- ** Class 'Balance'
110 class Amount (Balance_Amount b)
112 type Balance_Amount b
113 balance_account :: b -> Account
114 balance_amount :: b -> Balance_Amount b
115 balance_positive :: b -> Maybe (Balance_Amount b)
116 balance_negative :: b -> Maybe (Balance_Amount b)
118 instance (Amount a, Balance.Amount a)
119 => Balance (Account, Amount.Sum a) where
120 type Balance_Amount (Account, Amount.Sum a) = a
121 balance_account = fst
122 balance_amount (_, amt) =
124 Amount.Sum_Negative n -> n
125 Amount.Sum_Positive p -> p
126 Amount.Sum_Both n p -> Balance.amount_add n p
127 balance_positive = Amount.sum_positive . snd
128 balance_negative = Amount.sum_negative . snd
132 class Amount (GL_Amount r)
135 gl_account :: r -> Account
137 gl_amount_positive :: r -> Maybe (GL_Amount r)
138 gl_amount_negative :: r -> Maybe (GL_Amount r)
139 gl_amount_balance :: r -> GL_Amount r
140 gl_sum_positive :: r -> Maybe (GL_Amount r)
141 gl_sum_negative :: r -> Maybe (GL_Amount r)
142 gl_sum_balance :: r -> GL_Amount r
144 instance (Amount a, GL.Amount a)
145 => GL (Account, Date, Amount.Sum a, Amount.Sum a) where
146 type GL_Amount (Account, Date, Amount.Sum a, Amount.Sum a) = a
147 gl_account (x, _, _, _) = x
148 gl_date (_, x, _, _) = x
149 gl_amount_positive (_, _, x, _) = Amount.sum_positive x
150 gl_amount_negative (_, _, x, _) = Amount.sum_negative x
151 gl_amount_balance (_, _, x, _) = Amount.sum_balance x
152 gl_sum_positive (_, _, _, x) = Amount.sum_positive x
153 gl_sum_negative (_, _, _, x) = Amount.sum_negative x
154 gl_sum_balance (_, _, _, x) = Amount.sum_balance x
156 -- * Newtypes to avoid overlapping instances
160 instance Functor Scalar where
161 fmap f (Scalar x) = Scalar (f x)
166 = Simplified { simplified :: Either p Bool }
168 instance Functor Simplified where
169 fmap _f (Simplified (Right b)) = Simplified (Right b)
170 fmap f (Simplified (Left x)) = Simplified (Left $ f x)
172 -- | Conjonctive ('&&') 'Monoid'.
173 instance Monoid p => Monoid (Simplified p) where
174 mempty = Simplified (Right True)
175 mappend (Simplified x) (Simplified y) =
178 (Right bx , Right by ) -> Right (bx && by)
179 (Right True , Left _fy ) -> y
180 (Right False, Left _fy ) -> x
181 (Left _fx , Right True ) -> x
182 (Left _fx , Right False) -> y
183 (Left fx , Left fy ) -> Left $ fx `mappend` fy
186 test :: p -> x -> Bool
187 simplify :: p -> Maybe x -> Simplified p
188 simplify p _x = Simplified $ Left p
191 :: (Foldable t, Test p x, Monoid x)
194 Data.Foldable.foldMap
195 (\x -> if test p x then x else mempty)
197 -- ** Type 'Test_Text'
201 | Test_Text_Exact Text
202 | Test_Text_Regex Regex
203 deriving (Eq, Show, Typeable)
205 instance Test Test_Text Text where
208 Test_Text_Any -> True
209 Test_Text_Exact m -> (==) m x
210 Test_Text_Regex m -> Regex.match m x
212 -- ** Type 'Test_Ord'
221 deriving (Data, Eq, Show, Typeable)
223 instance Functor Test_Ord where
226 Test_Ord_Lt o -> Test_Ord_Lt (f o)
227 Test_Ord_Le o -> Test_Ord_Le (f o)
228 Test_Ord_Gt o -> Test_Ord_Gt (f o)
229 Test_Ord_Ge o -> Test_Ord_Ge (f o)
230 Test_Ord_Eq o -> Test_Ord_Eq (f o)
231 Test_Ord_Any -> Test_Ord_Any
232 instance (Ord o, o ~ x)
233 => Test (Test_Ord o) (Scalar x) where
236 Test_Ord_Lt o -> (<) x o
237 Test_Ord_Le o -> (<=) x o
238 Test_Ord_Gt o -> (>) x o
239 Test_Ord_Ge o -> (>=) x o
240 Test_Ord_Eq o -> (==) x o
242 instance (Ord o, o ~ x)
243 => Test (Test_Ord o) (Interval x) where
245 let l = Interval.low i in
246 let h = Interval.high i in
248 Test_Ord_Lt o -> case compare (Interval.limit h) o of
250 EQ -> Interval.adherence h == Interval.Out
252 Test_Ord_Le o -> Interval.limit h <= o
253 Test_Ord_Gt o -> case compare (Interval.limit l) o of
255 EQ -> Interval.adherence l == Interval.Out
257 Test_Ord_Ge o -> Interval.limit l >= o
258 Test_Ord_Eq o -> Interval.limit l == o && Interval.limit h == o
261 -- ** Type 'Test_Interval'
264 = Test_Interval_In (Interval (Interval.Unlimitable x))
265 deriving (Eq, Ord, Show)
266 --instance Functor Test_Interval where
267 -- fmap f (Test_Interval_In i) = Test_Interval_In (fmap (fmap f) i)
268 instance (Ord o, o ~ x)
269 => Test (Test_Interval o) (Scalar (Interval.Unlimitable x)) where
270 test (Test_Interval_In p) (Scalar x) =
271 Interval.locate x p == EQ
272 instance (Ord o, o ~ x)
273 => Test (Test_Interval o) (Interval (Interval.Unlimitable x)) where
274 test (Test_Interval_In p) i = Interval.into i p
276 -- ** Type 'Test_Num_Abs'
280 = Test_Num_Abs (Test_Ord n)
281 deriving (Data, Eq, Show, Typeable)
283 instance (Num n, Ord x, n ~ x)
284 => Test (Test_Num_Abs n) x where
285 test (Test_Num_Abs f) x = test f (Scalar (abs x))
287 -- ** Type 'Test_Bool'
293 | And (Test_Bool p) (Test_Bool p)
294 | Or (Test_Bool p) (Test_Bool p)
296 deriving instance Eq p => Eq (Test_Bool p)
297 instance Functor Test_Bool where
299 fmap f (Bool x) = Bool (f x)
300 fmap f (Not t) = Not (fmap f t)
301 fmap f (And t0 t1) = And (fmap f t0) (fmap f t1)
302 fmap f (Or t0 t1) = Or (fmap f t0) (fmap f t1)
303 -- | Conjonctive ('And') 'Monoid'.
304 instance Monoid (Test_Bool p) where
307 instance Foldable Test_Bool where
308 foldr _ acc Any = acc
309 foldr f acc (Bool p) = f p acc
310 foldr f acc (Not t) = Data.Foldable.foldr f acc t
311 foldr f acc (And t0 t1) = Data.Foldable.foldr f (Data.Foldable.foldr f acc t0) t1
312 foldr f acc (Or t0 t1) = Data.Foldable.foldr f (Data.Foldable.foldr f acc t0) t1
313 instance Traversable Test_Bool where
314 traverse _ Any = pure Any
315 traverse f (Bool x) = Bool <$> f x
316 traverse f (Not t) = Not <$> traverse f t
317 traverse f (And t0 t1) = And <$> traverse f t0 <*> traverse f t1
318 traverse f (Or t0 t1) = Or <$> traverse f t0 <*> traverse f t1
319 instance Test p x => Test (Test_Bool p) x where
321 test (Bool p) x = test p x
322 test (Not t) x = not $ test t x
323 test (And t0 t1) x = test t0 x && test t1 x
324 test (Or t0 t1) x = test t0 x || test t1 x
326 simplify Any _ = Simplified $ Right True
327 simplify (Bool p) x =
329 case simplified (simplify p x) of
330 Left p' -> Left (Bool p')
334 case simplified (simplify t x) of
335 Left p' -> Left (Not $ p')
337 simplify (And t0 t1) x =
339 case (simplified $ simplify t0 x, simplified $ simplify t1 x) of
340 (Right b0, Right b1) -> Right (b0 && b1)
341 (Right b0, Left p1) -> if b0 then Left p1 else Right False
342 (Left p0, Right b1) -> if b1 then Left p0 else Right False
343 (Left p0, Left p1) -> Left (And p0 p1)
344 simplify (Or t0 t1) x =
346 case (simplified $ simplify t0 x, simplified $ simplify t1 x) of
347 (Right b0, Right b1) -> Right (b0 || b1)
348 (Right b0, Left p1) -> if b0 then Right True else Left p1
349 (Left p0, Right b1) -> if b1 then Right True else Left p0
350 (Left p0, Left p1) -> Left (Or p0 p1)
352 bool :: Test p x => Test_Bool p -> x -> Bool
354 bool (Bool p) x = test p x
355 bool (Not t) x = not $ test t x
356 bool (And t0 t1) x = test t0 x && test t1 x
357 bool (Or t0 t1) x = test t0 x || test t1 x
359 -- ** Type 'Test_Unit'
362 = Test_Unit Test_Text
363 deriving (Eq, Show, Typeable)
365 instance Unit u => Test Test_Unit u where
366 test (Test_Unit f) = test f . unit_text
368 -- ** Type 'Test_Account'
371 = [Test_Account_Section]
373 data Test_Account_Section
374 = Test_Account_Section_Any
375 | Test_Account_Section_Many
376 | Test_Account_Section_Text Test_Text
377 deriving (Eq, Show, Typeable)
379 instance Test Test_Account Account where
381 comp f (NonEmpty.toList acct)
383 comp :: [Test_Account_Section] -> [Account.Name] -> Bool
385 comp [Test_Account_Section_Many] _ = True
390 Test_Account_Section_Any -> True
391 Test_Account_Section_Many -> True
392 Test_Account_Section_Text m -> test m n
394 comp so@(s:ss) no@(n:ns) =
396 Test_Account_Section_Any -> comp ss ns
397 Test_Account_Section_Many -> comp ss no || comp so ns
398 Test_Account_Section_Text m -> test m n && comp ss ns
401 -- ** Type 'Test_Amount'
409 { test_amount_quantity :: Test_Quantity (Amount_Quantity a)
410 , test_amount_unit :: Test_Unit
411 } deriving (Typeable)
412 deriving instance Amount a => Eq (Test_Amount a)
413 deriving instance Amount a => Show (Test_Amount a)
416 => Test (Test_Amount a) a where
417 test (Test_Amount fq fu) amt =
418 test fu (amount_unit amt) &&
419 test fq (Scalar (amount_quantity amt))
421 -- ** Type 'Test_Date'
424 = Test_Date_UTC (Test_Ord Date)
425 | Test_Date_Year (Test_Interval Integer)
426 | Test_Date_Month (Test_Interval Int)
427 | Test_Date_DoM (Test_Interval Int)
428 | Test_Date_Hour (Test_Interval Int)
429 | Test_Date_Minute (Test_Interval Int)
430 | Test_Date_Second (Test_Interval Data.Fixed.Pico)
432 deriving instance Show (Test_Date)
434 instance Test Test_Date Date where
435 test (Test_Date_UTC f) d = test f $ Scalar d
436 test (Test_Date_Year f) d = test f $ Scalar $ Interval.Limited $ Date.year d
437 test (Test_Date_Month f) d = test f $ Scalar $ Interval.Limited $ Date.month d
438 test (Test_Date_DoM f) d = test f $ Scalar $ Interval.Limited $ Date.dom d
439 test (Test_Date_Hour f) d = test f $ Scalar $ Interval.Limited $ Date.hour d
440 test (Test_Date_Minute f) d = test f $ Scalar $ Interval.Limited $ Date.minute d
441 test (Test_Date_Second f) d = test f $ Scalar $ Interval.Limited $ Date.second d
443 instance Test Test_Date (Interval (Interval.Unlimitable Date)) where
444 test (Test_Date_UTC f) d = test (Interval.Limited <$> f) d
445 test (Test_Date_Year f) d = maybe False (test f) $ Interval.fmap (fmap Date.year) d
446 test (Test_Date_Month f) d = maybe False (test f) $ Interval.fmap (fmap Date.month) d
447 test (Test_Date_DoM f) d = maybe False (test f) $ Interval.fmap (fmap Date.dom) d
448 test (Test_Date_Hour f) d = maybe False (test f) $ Interval.fmap (fmap Date.hour) d
449 test (Test_Date_Minute f) d = maybe False (test f) $ Interval.fmap (fmap Date.minute) d
450 test (Test_Date_Second f) d = maybe False (test f) $ Interval.fmap (fmap Date.second) d
452 -- ** Type 'Test_Tag'
455 = Test_Tag_Name Test_Text
456 | Test_Tag_Value Test_Text
458 deriving instance Show (Test_Tag)
460 instance Test Test_Tag (Text, Text) where
461 test (Test_Tag_Name f) (x, _) = test f x
462 test (Test_Tag_Value f) (_, x) = test f x
464 -- ** Type 'Test_Posting'
467 => Test_Posting posting
468 = Test_Posting_Account Test_Account
469 | Test_Posting_Amount (Test_Amount (Posting_Amount posting))
470 | Test_Posting_Unit Test_Unit
473 -- Description Comp_String String
475 -- Account_Tag Comp_String String (Maybe (Comp_String, String))
476 -- Account_Balance Comp_Num Comp_Num_Absolute Amount
477 -- Depth Comp_Num Int
481 -- Tag Comp_String Tag.Name (Maybe (Comp_String, Tag.Value))
482 deriving instance Posting p => Eq (Test_Posting p)
483 deriving instance Posting p => Show (Test_Posting p)
486 => Test (Test_Posting p) p where
487 test (Test_Posting_Account f) p =
488 test f $ posting_account p
489 test (Test_Posting_Amount f) p =
490 Data.Foldable.any (test f) $ posting_amounts p
491 test (Test_Posting_Unit f) p =
492 Data.Foldable.any (test f . amount_unit) $ posting_amounts p
494 newtype Cross t = Cross t
495 instance (Transaction t, Transaction_Posting t ~ p, Posting p)
496 => Test (Test_Transaction t) (Cross p) where
499 (Test_Transaction_Description _) -> True
500 (Test_Transaction_Posting f) -> test f p
501 (Test_Transaction_Date _) -> True -- TODO: use posting_date
502 (Test_Transaction_Tag _) -> False -- TODO: use posting_tags
504 -- ** Type 'Test_Transaction'
507 => Test_Transaction t
508 = Test_Transaction_Description Test_Text
509 | Test_Transaction_Posting (Test_Posting (Transaction_Posting t))
510 | Test_Transaction_Date (Test_Bool Test_Date)
511 | Test_Transaction_Tag (Test_Bool Test_Tag)
513 deriving instance Transaction t => Show (Test_Transaction t)
515 instance Transaction t
516 => Test (Test_Transaction t) t where
517 test (Test_Transaction_Description f) t =
518 test f $ transaction_description t
519 test (Test_Transaction_Posting f) t =
520 Data.Foldable.any (test f) $
521 Data.Functor.Compose.Compose $
522 transaction_postings t
523 test (Test_Transaction_Date f) t =
524 test f $ transaction_date t
525 test (Test_Transaction_Tag f) t =
527 Data.Map.foldrWithKey
528 (\n -> mappend . Data.Monoid.Any .
529 Data.Foldable.any (test f . (n,)))
530 (Data.Monoid.Any False) $
533 -- ** Type 'Test_Balance'
537 = Test_Balance_Account Test_Account
538 | Test_Balance_Amount (Test_Amount (Balance_Amount b))
539 | Test_Balance_Positive (Test_Amount (Balance_Amount b))
540 | Test_Balance_Negative (Test_Amount (Balance_Amount b))
542 deriving instance Balance b => Eq (Test_Balance b)
543 deriving instance Balance b => Show (Test_Balance b)
546 => Test (Test_Balance b) b where
547 test (Test_Balance_Account f) b =
548 test f $ balance_account b
549 test (Test_Balance_Amount f) b =
550 test f $ balance_amount b
551 test (Test_Balance_Positive f) b =
552 Data.Foldable.any (test f) $
554 test (Test_Balance_Negative f) b =
555 Data.Foldable.any (test f) $
562 = Test_GL_Account Test_Account
563 | Test_GL_Amount_Positive (Test_Amount (GL_Amount r))
564 | Test_GL_Amount_Negative (Test_Amount (GL_Amount r))
565 | Test_GL_Amount_Balance (Test_Amount (GL_Amount r))
566 | Test_GL_Sum_Positive (Test_Amount (GL_Amount r))
567 | Test_GL_Sum_Negative (Test_Amount (GL_Amount r))
568 | Test_GL_Sum_Balance (Test_Amount (GL_Amount r))
570 deriving instance GL r => Eq (Test_GL r)
571 deriving instance GL r => Show (Test_GL r)
574 => Test (Test_GL r) r where
575 test (Test_GL_Account f) r =
576 test f $ gl_account r
577 test (Test_GL_Amount_Positive f) r =
578 Data.Foldable.any (test f) $
580 test (Test_GL_Amount_Negative f) r =
581 Data.Foldable.any (test f) $
583 test (Test_GL_Amount_Balance f) r =
584 test f $ gl_amount_balance r
585 test (Test_GL_Sum_Positive f) r =
586 Data.Foldable.any (test f) $
588 test (Test_GL_Sum_Negative f) r =
589 Data.Foldable.any (test f) $
591 test (Test_GL_Sum_Balance f) r =
592 test f $ gl_sum_balance r