Add Support for Rewriting Multipler Postings Into Different Commodities (#557)
When generating a new posting as a multiple of an existing posting, support conversion to a different commodity. For example, postings in hours can be used to generate postings in USD. Automatic transactions generated from rewrite rules use the commodity, amount style, and transaction price if the rewrite defines a commodity.
This commit is contained in:
parent
35f77ee5e4
commit
d39040c634
@ -60,12 +60,15 @@ More:
|
|||||||
$ hledger rewrite -- [QUERY] --add-posting "ACCT AMTEXPR" ...
|
$ hledger rewrite -- [QUERY] --add-posting "ACCT AMTEXPR" ...
|
||||||
$ hledger rewrite -- ^income --add-posting '(liabilities:tax) *.33'
|
$ hledger rewrite -- ^income --add-posting '(liabilities:tax) *.33'
|
||||||
$ hledger rewrite -- expenses:gifts --add-posting '(budget:gifts) *-1"'
|
$ hledger rewrite -- expenses:gifts --add-posting '(budget:gifts) *-1"'
|
||||||
|
$ hledger rewrite -- ^income --add-posting '(budget:foreign currency) *0.25 JPY; diversify'
|
||||||
```
|
```
|
||||||
|
|
||||||
Argument for `--add-posting` option is a usual posting of transaction with an
|
Argument for `--add-posting` option is a usual posting of transaction with an
|
||||||
exception for amount specification. More precisely you can use `'*'` (star
|
exception for amount specification. More precisely, you can use `'*'` (star
|
||||||
symbol) in place of currency to indicate that that this is a factor for an
|
symbol) before the amount to indicate that that this is a factor for an
|
||||||
amount of original matched posting.
|
amount of original matched posting. If the amount includes a commodity name,
|
||||||
|
the new posting amount will be in the new commodity; otherwise, it will be in
|
||||||
|
the matched posting amount's commodity.
|
||||||
|
|
||||||
#### Re-write rules in a file
|
#### Re-write rules in a file
|
||||||
|
|
||||||
|
|||||||
@ -151,7 +151,7 @@ instance Num Amount where
|
|||||||
|
|
||||||
-- | The empty simple amount.
|
-- | The empty simple amount.
|
||||||
amount, nullamt :: Amount
|
amount, nullamt :: Amount
|
||||||
amount = Amount{acommodity="", aquantity=0, aprice=NoPrice, astyle=amountstyle}
|
amount = Amount{acommodity="", aquantity=0, aprice=NoPrice, astyle=amountstyle, amultiplier=False}
|
||||||
nullamt = amount
|
nullamt = amount
|
||||||
|
|
||||||
-- | A temporary value for parsed transactions which had no amount specified.
|
-- | A temporary value for parsed transactions which had no amount specified.
|
||||||
|
|||||||
@ -55,7 +55,7 @@ import Hledger.Query
|
|||||||
-- ping $1.00
|
-- ping $1.00
|
||||||
-- <BLANKLINE>
|
-- <BLANKLINE>
|
||||||
-- <BLANKLINE>
|
-- <BLANKLINE>
|
||||||
-- >>> runModifierTransaction Any (ModifierTransaction "ping" ["pong" `post` amount{acommodity="*", aquantity=3}]) nulltransaction{tpostings=["ping" `post` usd 2]}
|
-- >>> runModifierTransaction Any (ModifierTransaction "ping" ["pong" `post` amount{amultiplier=True, aquantity=3}]) nulltransaction{tpostings=["ping" `post` usd 2]}
|
||||||
-- 0000/01/01
|
-- 0000/01/01
|
||||||
-- ping $2.00
|
-- ping $2.00
|
||||||
-- pong $6.00
|
-- pong $6.00
|
||||||
@ -107,7 +107,7 @@ tdates t = tdate t : concatMap pdates (tpostings t) ++ maybeToList (tdate2 t) wh
|
|||||||
postingScale :: Posting -> Maybe Quantity
|
postingScale :: Posting -> Maybe Quantity
|
||||||
postingScale p =
|
postingScale p =
|
||||||
case amounts $ pamount p of
|
case amounts $ pamount p of
|
||||||
[a] | acommodity a == "*" -> Just $ aquantity a
|
[a] | amultiplier a -> Just $ aquantity a
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
runModifierPosting :: Posting -> (Posting -> Posting)
|
runModifierPosting :: Posting -> (Posting -> Posting)
|
||||||
@ -117,10 +117,12 @@ runModifierPosting p' = modifier where
|
|||||||
, pdate2 = pdate2 p
|
, pdate2 = pdate2 p
|
||||||
, pamount = amount' p
|
, pamount = amount' p
|
||||||
}
|
}
|
||||||
amount' =
|
amount' = case postingScale p' of
|
||||||
case postingScale p' of
|
Nothing -> const $ pamount p'
|
||||||
Nothing -> const $ pamount p'
|
Just n -> \p -> withAmountType (head $ amounts $ pamount p') $ pamount p `divideMixedAmount` (1/n)
|
||||||
Just n -> \p -> pamount p `divideMixedAmount` (1/n)
|
withAmountType amount (Mixed as) = case acommodity amount of
|
||||||
|
"" -> Mixed as
|
||||||
|
c -> Mixed [a{acommodity = c, astyle = astyle amount, aprice = aprice amount} | a <- as]
|
||||||
|
|
||||||
renderPostingCommentDates :: Posting -> Posting
|
renderPostingCommentDates :: Posting -> Posting
|
||||||
renderPostingCommentDates p = p { pcomment = comment' }
|
renderPostingCommentDates p = p { pcomment = comment' }
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import Hledger.Utils
|
|||||||
|
|
||||||
|
|
||||||
-- characters that may not be used in a non-quoted commodity symbol
|
-- characters that may not be used in a non-quoted commodity symbol
|
||||||
nonsimplecommoditychars = "0123456789-+.@;\n \"{}=" :: [Char]
|
nonsimplecommoditychars = "0123456789-+.@*;\n \"{}=" :: [Char]
|
||||||
|
|
||||||
quoteCommoditySymbolIfNeeded s | any (`elem` nonsimplecommoditychars) (T.unpack s) = "\"" <> s <> "\""
|
quoteCommoditySymbolIfNeeded s | any (`elem` nonsimplecommoditychars) (T.unpack s) = "\"" <> s <> "\""
|
||||||
| otherwise = s
|
| otherwise = s
|
||||||
|
|||||||
@ -158,10 +158,11 @@ data Commodity = Commodity {
|
|||||||
instance NFData Commodity
|
instance NFData Commodity
|
||||||
|
|
||||||
data Amount = Amount {
|
data Amount = Amount {
|
||||||
acommodity :: CommoditySymbol,
|
acommodity :: CommoditySymbol,
|
||||||
aquantity :: Quantity,
|
aquantity :: Quantity,
|
||||||
aprice :: Price, -- ^ the (fixed) price for this amount, if any
|
aprice :: Price, -- ^ the (fixed) price for this amount, if any
|
||||||
astyle :: AmountStyle
|
astyle :: AmountStyle,
|
||||||
|
amultiplier :: Bool -- ^ amount is a multipier for AutoTransactions
|
||||||
} deriving (Eq,Ord,Typeable,Data,Generic)
|
} deriving (Eq,Ord,Typeable,Data,Generic)
|
||||||
|
|
||||||
instance NFData Amount
|
instance NFData Amount
|
||||||
|
|||||||
@ -371,30 +371,39 @@ signp = do
|
|||||||
return $ case sign of Just '-' -> "-"
|
return $ case sign of Just '-' -> "-"
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
|
||||||
|
multiplierp :: TextParser m Bool
|
||||||
|
multiplierp = do
|
||||||
|
multiplier <- optional $ oneOf ("*" :: [Char])
|
||||||
|
return $ case multiplier of Just '*' -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
leftsymbolamountp :: Monad m => JournalStateParser m Amount
|
leftsymbolamountp :: Monad m => JournalStateParser m Amount
|
||||||
leftsymbolamountp = do
|
leftsymbolamountp = do
|
||||||
sign <- lift signp
|
sign <- lift signp
|
||||||
|
m <- lift multiplierp
|
||||||
c <- lift commoditysymbolp
|
c <- lift commoditysymbolp
|
||||||
sp <- lift $ many spacenonewline
|
sp <- lift $ many spacenonewline
|
||||||
(q,prec,mdec,mgrps) <- lift numberp
|
(q,prec,mdec,mgrps) <- lift numberp
|
||||||
let s = amountstyle{ascommodityside=L, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
let s = amountstyle{ascommodityside=L, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||||
p <- priceamountp
|
p <- priceamountp
|
||||||
let applysign = if sign=="-" then negate else id
|
let applysign = if sign=="-" then negate else id
|
||||||
return $ applysign $ Amount c q p s
|
return $ applysign $ Amount c q p s m
|
||||||
<?> "left-symbol amount"
|
<?> "left-symbol amount"
|
||||||
|
|
||||||
rightsymbolamountp :: Monad m => JournalStateParser m Amount
|
rightsymbolamountp :: Monad m => JournalStateParser m Amount
|
||||||
rightsymbolamountp = do
|
rightsymbolamountp = do
|
||||||
|
m <- lift multiplierp
|
||||||
(q,prec,mdec,mgrps) <- lift numberp
|
(q,prec,mdec,mgrps) <- lift numberp
|
||||||
sp <- lift $ many spacenonewline
|
sp <- lift $ many spacenonewline
|
||||||
c <- lift commoditysymbolp
|
c <- lift commoditysymbolp
|
||||||
p <- priceamountp
|
p <- priceamountp
|
||||||
let s = amountstyle{ascommodityside=R, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
let s = amountstyle{ascommodityside=R, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}
|
||||||
return $ Amount c q p s
|
return $ Amount c q p s m
|
||||||
<?> "right-symbol amount"
|
<?> "right-symbol amount"
|
||||||
|
|
||||||
nosymbolamountp :: Monad m => JournalStateParser m Amount
|
nosymbolamountp :: Monad m => JournalStateParser m Amount
|
||||||
nosymbolamountp = do
|
nosymbolamountp = do
|
||||||
|
m <- lift multiplierp
|
||||||
(q,prec,mdec,mgrps) <- lift numberp
|
(q,prec,mdec,mgrps) <- lift numberp
|
||||||
p <- priceamountp
|
p <- priceamountp
|
||||||
-- apply the most recently seen default commodity and style to this commodityless amount
|
-- apply the most recently seen default commodity and style to this commodityless amount
|
||||||
@ -402,7 +411,7 @@ nosymbolamountp = do
|
|||||||
let (c,s) = case defcs of
|
let (c,s) = case defcs of
|
||||||
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) prec})
|
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) prec})
|
||||||
Nothing -> ("", amountstyle{asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps})
|
Nothing -> ("", amountstyle{asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps})
|
||||||
return $ Amount c q p s
|
return $ Amount c q p s m
|
||||||
<?> "no-symbol amount"
|
<?> "no-symbol amount"
|
||||||
|
|
||||||
commoditysymbolp :: TextParser m CommoditySymbol
|
commoditysymbolp :: TextParser m CommoditySymbol
|
||||||
|
|||||||
@ -49,6 +49,80 @@
|
|||||||
>>>2
|
>>>2
|
||||||
>>>=0
|
>>>=0
|
||||||
|
|
||||||
|
# Add postings in another commodity
|
||||||
|
../../bin/hledger-rewrite -f-
|
||||||
|
<<<
|
||||||
|
2017/04/24 * 09:00-09:25
|
||||||
|
(assets:unbilled:client1) 0.42h
|
||||||
|
|
||||||
|
2017/04/25 * 10:00-11:15
|
||||||
|
(assets:unbilled:client1) 1.25h
|
||||||
|
|
||||||
|
2017/04/25 * 14:00-15:32
|
||||||
|
(assets:unbilled:client2) 1.54h
|
||||||
|
|
||||||
|
; billing rules
|
||||||
|
= ^assets:unbilled:client1
|
||||||
|
(assets:to bill:client1) *100.00 CAD
|
||||||
|
|
||||||
|
= ^assets:unbilled:client2
|
||||||
|
(assets:to bill:client2) *150.00 CAD
|
||||||
|
>>>
|
||||||
|
2017/04/24 * 09:00-09:25
|
||||||
|
(assets:unbilled:client1) 0.42h
|
||||||
|
(assets:to bill:client1) 42.00 CAD
|
||||||
|
|
||||||
|
2017/04/25 * 10:00-11:15
|
||||||
|
(assets:unbilled:client1) 1.25h
|
||||||
|
(assets:to bill:client1) 125.00 CAD
|
||||||
|
|
||||||
|
2017/04/25 * 14:00-15:32
|
||||||
|
(assets:unbilled:client2) 1.54h
|
||||||
|
(assets:to bill:client2) 231.00 CAD
|
||||||
|
|
||||||
|
>>>2
|
||||||
|
>>>=0
|
||||||
|
|
||||||
|
|
||||||
|
# Add postings with prices
|
||||||
|
../../bin/hledger-rewrite -f- -B
|
||||||
|
<<<
|
||||||
|
2017/04/24 * 09:00-09:25
|
||||||
|
(assets:unbilled:client1) 0.42h
|
||||||
|
|
||||||
|
2017/04/25 * 10:00-11:15
|
||||||
|
(assets:unbilled:client1) 1.25h
|
||||||
|
|
||||||
|
2017/04/25 * 14:00-15:32
|
||||||
|
(assets:unbilled:client2) 1.54h
|
||||||
|
|
||||||
|
; billing rules
|
||||||
|
= ^assets:unbilled:client1
|
||||||
|
assets:to bill:client1 *1.00 hours @ $100.00
|
||||||
|
income:consulting:client1
|
||||||
|
|
||||||
|
= ^assets:unbilled:client2
|
||||||
|
assets:to bill:client2 *1.00 hours @ $150.00
|
||||||
|
income:consulting:client2
|
||||||
|
>>>
|
||||||
|
2017/04/24 * 09:00-09:25
|
||||||
|
(assets:unbilled:client1) 0.42h
|
||||||
|
assets:to bill:client1 $42.00
|
||||||
|
income:consulting:client1
|
||||||
|
|
||||||
|
2017/04/25 * 10:00-11:15
|
||||||
|
(assets:unbilled:client1) 1.25h
|
||||||
|
assets:to bill:client1 $125.00
|
||||||
|
income:consulting:client1
|
||||||
|
|
||||||
|
2017/04/25 * 14:00-15:32
|
||||||
|
(assets:unbilled:client2) 1.54h
|
||||||
|
assets:to bill:client2 $231.00
|
||||||
|
income:consulting:client2
|
||||||
|
|
||||||
|
>>>2
|
||||||
|
>>>=0
|
||||||
|
|
||||||
# Add absolute bank processing fee
|
# Add absolute bank processing fee
|
||||||
../../bin/hledger-rewrite -f- assets:bank and 'amt:<0' --add-posting 'expenses:fee $5' --add-posting 'assets:bank $-5'
|
../../bin/hledger-rewrite -f- assets:bank and 'amt:<0' --add-posting 'expenses:fee $5' --add-posting 'assets:bank $-5'
|
||||||
<<<
|
<<<
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user