1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE MultiParamTypeClasses #-}
5 {-# LANGUAGE StandaloneDeriving #-}
6 {-# LANGUAGE TypeFamilies #-}
7 module Hcompta.Model.Filter where
9 import Prelude hiding (filter)
10 import Control.Applicative (pure, (<$>), (<*>))
12 import qualified Data.Fixed
13 import qualified Data.Foldable
14 import Data.Foldable (Foldable(..))
15 import qualified Data.Functor.Compose
16 import Data.Traversable (Traversable(..))
17 import Data.Monoid (Monoid(..))
18 import Data.Typeable ()
19 import Data.Text (Text)
20 -- import qualified Data.Text as Text
21 -- import qualified Data.Map.Strict as Data.Map
22 import Data.Map.Strict (Map)
23 import Text.Regex.TDFA ()
24 import Text.Regex.Base ()
25 import Text.Regex.TDFA.Text ()
27 import qualified Data.List.NonEmpty as NonEmpty
28 -- import Data.List.NonEmpty (NonEmpty(..))
29 import qualified Hcompta.Lib.Regex as Regex
30 import Hcompta.Lib.Regex (Regex)
31 import qualified Hcompta.Model.Date as Date
32 import Hcompta.Model.Date (Date)
33 import qualified Hcompta.Model.Account as Account
34 import Hcompta.Model.Account (Account)
35 -- import qualified Hcompta.Model.Date as Date
36 import qualified Hcompta.Calc.Balance as Calc.Balance
38 -- * Requirements' interface
43 unit_text :: a -> Text
48 ( Ord (Amount_Quantity a)
49 , Show (Amount_Quantity a)
50 , Show (Amount_Unit a)
51 , Unit (Amount_Unit a)
54 type Amount_Quantity a
56 amount_unit :: a -> Amount_Unit a
57 amount_quantity :: a -> Amount_Quantity a
59 instance (Amount a, Calc.Balance.Amount a)
60 => Amount (Calc.Balance.Amount_Sum a) where
61 type Amount_Quantity (Calc.Balance.Amount_Sum a) = Amount_Quantity a
62 type Amount_Unit (Calc.Balance.Amount_Sum a) = Amount_Unit a
63 amount_quantity = amount_quantity . Calc.Balance.amount_sum_balance
64 amount_unit = amount_unit . Calc.Balance.amount_sum_balance
68 class Amount (Posting_Amount p)
71 posting_account :: p -> Account
72 posting_amounts :: p -> Map (Amount_Unit (Posting_Amount p)) (Posting_Amount p)
74 -- ** Class 'Transaction'
76 class Posting (Transaction_Posting t)
77 => Transaction t where
78 type Transaction_Posting t
79 transaction_date :: t -> Date
80 transaction_description :: t -> Text
81 transaction_postings :: t -> Map Account [Transaction_Posting t]
85 class Amount (Balance_Amount b)
88 balance_account :: b -> Account
89 balance_amount :: b -> Balance_Amount b
91 instance (Amount a, Calc.Balance.Amount a)
92 => Balance (Account, Calc.Balance.Amount_Sum a) where
93 type Balance_Amount (Account, Calc.Balance.Amount_Sum a) = a
95 balance_amount = Calc.Balance.amount_sum_balance . snd
100 test :: p -> x -> Bool
103 :: (Foldable t, Test p x, Monoid x)
106 Data.Foldable.foldMap
107 (\x -> if test p x then x else mempty)
109 -- ** Type 'Test_Text'
112 = Test_Text_Exact Text
113 | Test_Text_Regex Regex
114 deriving (Eq, Show, Typeable)
116 instance Test Test_Text Text where
119 Test_Text_Exact m -> (==) m x
120 Test_Text_Regex m -> Regex.match m x
122 -- ** Type 'Test_Ord'
131 deriving (Data, Eq, Show, Typeable)
133 instance (Ord o, o ~ x)
134 => Test (Test_Ord o) x where
137 Test_Ord_Lt o -> (<) x o
138 Test_Ord_Le o -> (<=) x o
139 Test_Ord_Gt o -> (>) x o
140 Test_Ord_Ge o -> (>=) x o
141 Test_Ord_Eq o -> (==) x o
143 -- ** Type 'Test_Range'
147 | Test_Range_In (Maybe a) (Maybe a)
150 test_range_all :: Test_Range a
152 Test_Range_In Nothing Nothing
154 instance (Ord o, o ~ x)
155 => Test (Test_Range o) x where
158 Test_Range_Eq o -> (==) x o
159 Test_Range_In (Just a0) (Just a1) -> (<=) a0 x && (<=) x a1
160 Test_Range_In Nothing (Just a1) -> (<=) x a1
161 Test_Range_In (Just a0) Nothing -> (<=) a0 x
162 Test_Range_In Nothing Nothing -> True
163 instance Functor Test_Range where
164 fmap f (Test_Range_Eq a) = Test_Range_Eq (f a)
165 fmap f (Test_Range_In a0 a1) = Test_Range_In (fmap f a0) (fmap f a1)
167 -- ** Type 'Test_Num_Abs'
171 = Test_Num_Abs (Test_Ord n)
172 deriving (Data, Eq, Show, Typeable)
174 instance (Num n, Ord x, n ~ x)
175 => Test (Test_Num_Abs n) x where
176 test (Test_Num_Abs f) x = test f (abs x)
178 -- ** Type 'Test_Bool'
184 | And (Test_Bool p) (Test_Bool p)
185 | Or (Test_Bool p) (Test_Bool p)
187 instance Functor Test_Bool where
189 fmap f (Bool x) = Bool (f x)
190 fmap f (Not t) = Not (fmap f t)
191 fmap f (And t0 t1) = And (fmap f t0) (fmap f t1)
192 fmap f (Or t0 t1) = Or (fmap f t0) (fmap f t1)
193 instance Foldable Test_Bool where
194 foldr _ acc Any = acc
195 foldr f acc (Bool p) = f p acc
196 foldr f acc (Not t) = Data.Foldable.foldr f acc t
197 foldr f acc (And t0 t1) = Data.Foldable.foldr f (Data.Foldable.foldr f acc t0) t1
198 foldr f acc (Or t0 t1) = Data.Foldable.foldr f (Data.Foldable.foldr f acc t0) t1
199 instance Traversable Test_Bool where
200 traverse _ Any = pure Any
201 traverse f (Bool x) = Bool <$> f x
202 traverse f (Not t) = Not <$> traverse f t
203 traverse f (And t0 t1) = And <$> traverse f t0 <*> traverse f t1
204 traverse f (Or t0 t1) = Or <$> traverse f t0 <*> traverse f t1
205 instance Test p x => Test (Test_Bool p) x where
207 test (Bool p) x = test p x
208 test (Not t) x = not $ test t x
209 test (And t0 t1) x = test t0 x && test t1 x
210 test (Or t0 t1) x = test t0 x || test t1 x
212 bool :: Test p x => Test_Bool p -> x -> Bool
214 bool (Bool p) x = test p x
215 bool (Not t) x = not $ test t x
216 bool (And t0 t1) x = test t0 x && test t1 x
217 bool (Or t0 t1) x = test t0 x || test t1 x
219 -- ** Type 'Test_Unit'
222 = Test_Unit Test_Text
223 deriving (Eq, Show, Typeable)
225 instance Unit u => Test Test_Unit u where
226 test (Test_Unit f) = test f . unit_text
228 -- ** Type 'Test_Account'
231 = [Test_Account_Section]
233 data Test_Account_Section
234 = Test_Account_Section_Any
235 | Test_Account_Section_Many
236 | Test_Account_Section_Text Test_Text
237 deriving (Eq, Show, Typeable)
239 instance Test Test_Account Account where
241 comp f (NonEmpty.toList acct)
243 comp :: [Test_Account_Section] -> [Account.Name] -> Bool
245 comp [Test_Account_Section_Many] _ = True
250 Test_Account_Section_Any -> True
251 Test_Account_Section_Many -> True
252 Test_Account_Section_Text m -> test m n
254 comp so@(s:ss) no@(n:ns) =
256 Test_Account_Section_Any -> comp ss ns
257 Test_Account_Section_Many -> comp ss no || comp so ns
258 Test_Account_Section_Text m -> test m n && comp ss ns
261 -- ** Type 'Test_Amount'
269 { test_amount_quantity :: Test_Quantity (Amount_Quantity a)
270 , test_amount_unit :: Test_Unit
271 } deriving (Typeable)
272 deriving instance Amount a => Eq (Test_Amount a)
273 deriving instance Amount a => Show (Test_Amount a)
276 => Test (Test_Amount a) a where
277 test (Test_Amount fq fu) amt =
278 test fu (amount_unit amt) &&
279 test fq (amount_quantity amt)
281 -- ** Type 'Test_Date'
284 = Test_Date_UTC (Test_Ord Date)
285 | Test_Date_Year (Test_Range Integer)
286 | Test_Date_Month (Test_Range Int)
287 | Test_Date_DoM (Test_Range Int)
288 | Test_Date_Hour (Test_Range Int)
289 | Test_Date_Minute (Test_Range Int)
290 | Test_Date_Second (Test_Range Data.Fixed.Pico)
292 deriving instance Show (Test_Date)
294 instance Test Test_Date Date where
295 test (Test_Date_UTC f) d = test f d
296 test (Test_Date_Year f) d = test f $ Date.year d
297 test (Test_Date_Month f) d = test f $ Date.month d
298 test (Test_Date_DoM f) d = test f $ Date.dom d
299 test (Test_Date_Hour f) d = test f $ Date.hour d
300 test (Test_Date_Minute f) d = test f $ Date.minute d
301 test (Test_Date_Second f) d = test f $ Date.second d
303 -- ** Type 'Test_Posting'
306 => Test_Posting posting
307 = Test_Posting_Account Test_Account
308 | Test_Posting_Amount (Test_Amount (Posting_Amount posting))
309 | Test_Posting_Unit Test_Unit
312 -- Description Comp_String String
314 -- Account_Tag Comp_String String (Maybe (Comp_String, String))
315 -- Account_Balance Comp_Num Comp_Num_Absolute Amount
316 -- Depth Comp_Num Int
320 -- Tag Comp_String Tag.Name (Maybe (Comp_String, Tag.Value))
321 deriving instance Posting p => Eq (Test_Posting p)
322 deriving instance Posting p => Show (Test_Posting p)
325 => Test (Test_Posting p) p where
326 test (Test_Posting_Account f) p =
327 test f $ posting_account p
328 test (Test_Posting_Amount f) p =
329 Data.Foldable.any (test f) $ posting_amounts p
330 test (Test_Posting_Unit f) p =
331 Data.Foldable.any (test f . amount_unit) $ posting_amounts p
333 newtype Cross t = Cross t
334 instance (Transaction t, Transaction_Posting t ~ p, Posting p)
335 => Test (Test_Transaction t) (Cross p) where
338 (Test_Transaction_Description _) -> True
339 (Test_Transaction_Posting f) -> test f p
340 (Test_Transaction_Date _) -> True -- TODO: use posting_date
342 -- ** Type 'Test_Transaction'
345 => Test_Transaction t
346 = Test_Transaction_Description Test_Text
347 | Test_Transaction_Posting (Test_Posting (Transaction_Posting t))
348 | Test_Transaction_Date (Test_Bool Test_Date)
350 deriving instance Transaction t => Show (Test_Transaction t)
352 instance Transaction t
353 => Test (Test_Transaction t) t where
354 test (Test_Transaction_Description f) t =
355 test f $ transaction_description t
356 test (Test_Transaction_Posting f) t =
357 Data.Foldable.any (test f) $
358 Data.Functor.Compose.Compose $
359 transaction_postings t
360 test (Test_Transaction_Date f) t =
361 test f $ transaction_date t
363 -- ** Type 'Test_Balance'
367 = Test_Balance_Account Test_Account
368 | Test_Balance_Amount (Test_Amount (Balance_Amount b))
370 deriving instance Balance b => Eq (Test_Balance b)
371 deriving instance Balance b => Show (Test_Balance b)
374 => Test (Test_Balance b) b where
375 test (Test_Balance_Account f) b =
376 test f $ balance_account b
377 test (Test_Balance_Amount f) b =
378 test f $ balance_amount b