parsing: support D default commodity directive
This commit is contained in:
parent
8429df0f32
commit
78db98366f
@ -797,6 +797,20 @@ You can pull in the content of additional journal files, by writing lines like t
|
|||||||
The `!include` directive may only be used in journal files, and currently
|
The `!include` directive may only be used in journal files, and currently
|
||||||
it may only include other journal files (eg, not timelog files.)
|
it may only include other journal files (eg, not timelog files.)
|
||||||
|
|
||||||
|
##### Default commodity
|
||||||
|
|
||||||
|
You can set a default commodity with a `D` directive in the journal. This
|
||||||
|
will be used for any subsequent amounts with no commodity symbol,
|
||||||
|
including the commodity display settings (left or right symbol, spacing,
|
||||||
|
thousands separator, and precision.)
|
||||||
|
|
||||||
|
; default commodity: british pound, comma thousands separator, two decimal places
|
||||||
|
D £1,000.00
|
||||||
|
|
||||||
|
2010/1/1
|
||||||
|
a 2340.11 ; <- no commodity symbol, so will use the above
|
||||||
|
b
|
||||||
|
|
||||||
#### Default parent account
|
#### Default parent account
|
||||||
|
|
||||||
You can specify a default parent account within a section of the journal with
|
You can specify a default parent account within a section of the journal with
|
||||||
|
|||||||
@ -49,7 +49,7 @@ data Interval = NoInterval | Daily | Weekly | Monthly | Quarterly | Yearly
|
|||||||
|
|
||||||
type AccountName = String
|
type AccountName = String
|
||||||
|
|
||||||
data Side = L | R deriving (Eq,Show,Ord)
|
data Side = L | R deriving (Eq,Show,Read,Ord)
|
||||||
|
|
||||||
data Commodity = Commodity {
|
data Commodity = Commodity {
|
||||||
symbol :: String, -- ^ the commodity's symbol
|
symbol :: String, -- ^ the commodity's symbol
|
||||||
@ -58,7 +58,7 @@ data Commodity = Commodity {
|
|||||||
spaced :: Bool, -- ^ should there be a space between symbol and quantity
|
spaced :: Bool, -- ^ should there be a space between symbol and quantity
|
||||||
comma :: Bool, -- ^ should thousands be comma-separated
|
comma :: Bool, -- ^ should thousands be comma-separated
|
||||||
precision :: Int -- ^ number of decimal places to display
|
precision :: Int -- ^ number of decimal places to display
|
||||||
} deriving (Eq,Show,Ord)
|
} deriving (Eq,Show,Read,Ord)
|
||||||
|
|
||||||
data Amount = Amount {
|
data Amount = Amount {
|
||||||
commodity :: Commodity,
|
commodity :: Commodity,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ where
|
|||||||
|
|
||||||
import Control.Monad.Error
|
import Control.Monad.Error
|
||||||
import Hledger.Data.Utils
|
import Hledger.Data.Utils
|
||||||
import Hledger.Data.Types (Journal)
|
import Hledger.Data.Types (Journal, Commodity)
|
||||||
import Hledger.Data.Journal
|
import Hledger.Data.Journal
|
||||||
import System.Directory (getHomeDirectory)
|
import System.Directory (getHomeDirectory)
|
||||||
import System.FilePath(takeDirectory,combine)
|
import System.FilePath(takeDirectory,combine)
|
||||||
@ -44,12 +44,12 @@ parseJournalWith p f s = do
|
|||||||
-- | Some state kept while parsing a journal file.
|
-- | Some state kept while parsing a journal file.
|
||||||
data JournalContext = Ctx {
|
data JournalContext = Ctx {
|
||||||
ctxYear :: !(Maybe Integer) -- ^ the default year most recently specified with Y
|
ctxYear :: !(Maybe Integer) -- ^ the default year most recently specified with Y
|
||||||
, ctxCommod :: !(Maybe String) -- ^ I don't know
|
, ctxCommodity :: !(Maybe Commodity) -- ^ the default commodity recently specified with D
|
||||||
, ctxAccount :: ![String] -- ^ the current stack of parent accounts specified by !account
|
, ctxAccount :: ![String] -- ^ the current stack of parent accounts specified by !account
|
||||||
} deriving (Read, Show)
|
} deriving (Read, Show)
|
||||||
|
|
||||||
emptyCtx :: JournalContext
|
emptyCtx :: JournalContext
|
||||||
emptyCtx = Ctx { ctxYear = Nothing, ctxCommod = Nothing, ctxAccount = [] }
|
emptyCtx = Ctx { ctxYear = Nothing, ctxCommodity = Nothing, ctxAccount = [] }
|
||||||
|
|
||||||
setYear :: Integer -> GenParser tok JournalContext ()
|
setYear :: Integer -> GenParser tok JournalContext ()
|
||||||
setYear y = updateState (\ctx -> ctx{ctxYear=Just y})
|
setYear y = updateState (\ctx -> ctx{ctxYear=Just y})
|
||||||
@ -57,6 +57,12 @@ setYear y = updateState (\ctx -> ctx{ctxYear=Just y})
|
|||||||
getYear :: GenParser tok JournalContext (Maybe Integer)
|
getYear :: GenParser tok JournalContext (Maybe Integer)
|
||||||
getYear = liftM ctxYear getState
|
getYear = liftM ctxYear getState
|
||||||
|
|
||||||
|
setCommodity :: Commodity -> GenParser tok JournalContext ()
|
||||||
|
setCommodity c = updateState (\ctx -> ctx{ctxCommodity=Just c})
|
||||||
|
|
||||||
|
getCommodity :: GenParser tok JournalContext (Maybe Commodity)
|
||||||
|
getCommodity = liftM ctxCommodity getState
|
||||||
|
|
||||||
pushParentAccount :: String -> GenParser tok JournalContext ()
|
pushParentAccount :: String -> GenParser tok JournalContext ()
|
||||||
pushParentAccount parent = updateState addParentAccount
|
pushParentAccount parent = updateState addParentAccount
|
||||||
where addParentAccount ctx0 = ctx0 { ctxAccount = normalize parent : ctxAccount ctx0 }
|
where addParentAccount ctx0 = ctx0 { ctxAccount = normalize parent : ctxAccount ctx0 }
|
||||||
|
|||||||
@ -169,6 +169,7 @@ journalFile = do journalupdates <- many journalItem
|
|||||||
, liftM (return . addPeriodicTransaction) ledgerPeriodicTransaction
|
, liftM (return . addPeriodicTransaction) ledgerPeriodicTransaction
|
||||||
, liftM (return . addHistoricalPrice) ledgerHistoricalPrice
|
, liftM (return . addHistoricalPrice) ledgerHistoricalPrice
|
||||||
, ledgerDefaultYear
|
, ledgerDefaultYear
|
||||||
|
, ledgerDefaultCommodity
|
||||||
, ledgerIgnoredPriceCommodity
|
, ledgerIgnoredPriceCommodity
|
||||||
, ledgerTagDirective
|
, ledgerTagDirective
|
||||||
, ledgerEndTagDirective
|
, ledgerEndTagDirective
|
||||||
@ -178,20 +179,20 @@ journalFile = do journalupdates <- many journalItem
|
|||||||
journalAddFilePath :: FilePath -> Journal -> Journal
|
journalAddFilePath :: FilePath -> Journal -> Journal
|
||||||
journalAddFilePath f j@Journal{allfilepaths=fs} = j{allfilepaths=fs++[f]}
|
journalAddFilePath f j@Journal{allfilepaths=fs} = j{allfilepaths=fs++[f]}
|
||||||
|
|
||||||
emptyLine :: GenParser Char st ()
|
emptyLine :: GenParser Char JournalContext ()
|
||||||
emptyLine = do many spacenonewline
|
emptyLine = do many spacenonewline
|
||||||
optional $ (char ';' <?> "comment") >> many (noneOf "\n")
|
optional $ (char ';' <?> "comment") >> many (noneOf "\n")
|
||||||
newline
|
newline
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
ledgercomment :: GenParser Char st String
|
ledgercomment :: GenParser Char JournalContext String
|
||||||
ledgercomment = do
|
ledgercomment = do
|
||||||
many1 $ char ';'
|
many1 $ char ';'
|
||||||
many spacenonewline
|
many spacenonewline
|
||||||
many (noneOf "\n")
|
many (noneOf "\n")
|
||||||
<?> "comment"
|
<?> "comment"
|
||||||
|
|
||||||
ledgercommentline :: GenParser Char st String
|
ledgercommentline :: GenParser Char JournalContext String
|
||||||
ledgercommentline = do
|
ledgercommentline = do
|
||||||
many spacenonewline
|
many spacenonewline
|
||||||
s <- ledgercomment
|
s <- ledgercomment
|
||||||
@ -272,14 +273,6 @@ ledgerIgnoredPriceCommodity = do
|
|||||||
restofline
|
restofline
|
||||||
return $ return id
|
return $ return id
|
||||||
|
|
||||||
ledgerDefaultCommodity :: GenParser Char JournalContext JournalUpdate
|
|
||||||
ledgerDefaultCommodity = do
|
|
||||||
char 'D' <?> "default commodity"
|
|
||||||
many1 spacenonewline
|
|
||||||
someamount
|
|
||||||
restofline
|
|
||||||
return $ return id
|
|
||||||
|
|
||||||
ledgerCommodityConversion :: GenParser Char JournalContext JournalUpdate
|
ledgerCommodityConversion :: GenParser Char JournalContext JournalUpdate
|
||||||
ledgerCommodityConversion = do
|
ledgerCommodityConversion = do
|
||||||
char 'C' <?> "commodity conversion"
|
char 'C' <?> "commodity conversion"
|
||||||
@ -317,6 +310,17 @@ ledgerDefaultYear = do
|
|||||||
setYear y'
|
setYear y'
|
||||||
return $ return id
|
return $ return id
|
||||||
|
|
||||||
|
ledgerDefaultCommodity :: GenParser Char JournalContext JournalUpdate
|
||||||
|
ledgerDefaultCommodity = do
|
||||||
|
char 'D' <?> "default commodity"
|
||||||
|
many1 spacenonewline
|
||||||
|
a <- someamount
|
||||||
|
-- someamount always returns a MixedAmount containing one Amount, but let's be safe
|
||||||
|
let as = amounts a
|
||||||
|
when (not $ null as) $ setCommodity $ commodity $ head as
|
||||||
|
restofline
|
||||||
|
return $ return id
|
||||||
|
|
||||||
-- | Try to parse a ledger entry. If we successfully parse an entry,
|
-- | Try to parse a ledger entry. If we successfully parse an entry,
|
||||||
-- check it can be balanced, and fail if not.
|
-- check it can be balanced, and fail if not.
|
||||||
ledgerTransaction :: GenParser Char JournalContext Transaction
|
ledgerTransaction :: GenParser Char JournalContext Transaction
|
||||||
@ -384,10 +388,10 @@ ledgereffectivedate actualdate = do
|
|||||||
edate <- withDefaultYear actualdate ledgerdate
|
edate <- withDefaultYear actualdate ledgerdate
|
||||||
return edate
|
return edate
|
||||||
|
|
||||||
ledgerstatus :: GenParser Char st Bool
|
ledgerstatus :: GenParser Char JournalContext Bool
|
||||||
ledgerstatus = try (do { many1 spacenonewline; char '*' <?> "status"; return True } ) <|> return False
|
ledgerstatus = try (do { many1 spacenonewline; char '*' <?> "status"; return True } ) <|> return False
|
||||||
|
|
||||||
ledgercode :: GenParser Char st String
|
ledgercode :: GenParser Char JournalContext String
|
||||||
ledgercode = try (do { many1 spacenonewline; char '(' <?> "code"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
|
ledgercode = try (do { many1 spacenonewline; char '(' <?> "code"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""
|
||||||
|
|
||||||
ledgerpostings :: GenParser Char JournalContext [Posting]
|
ledgerpostings :: GenParser Char JournalContext [Posting]
|
||||||
@ -404,7 +408,7 @@ ledgerpostings = do
|
|||||||
return $ map (fromparse . parseWithCtx ctx (setPosition pos >> ledgerposting)) ls'
|
return $ map (fromparse . parseWithCtx ctx (setPosition pos >> ledgerposting)) ls'
|
||||||
<?> "postings"
|
<?> "postings"
|
||||||
|
|
||||||
linebeginningwithspaces :: GenParser Char st String
|
linebeginningwithspaces :: GenParser Char JournalContext String
|
||||||
linebeginningwithspaces = do
|
linebeginningwithspaces = do
|
||||||
sp <- many1 spacenonewline
|
sp <- many1 spacenonewline
|
||||||
c <- nonspace
|
c <- nonspace
|
||||||
@ -448,17 +452,17 @@ ledgeraccountname = do
|
|||||||
|
|
||||||
-- | Parse an amount, with an optional left or right currency symbol and
|
-- | Parse an amount, with an optional left or right currency symbol and
|
||||||
-- optional price.
|
-- optional price.
|
||||||
postingamount :: GenParser Char st MixedAmount
|
postingamount :: GenParser Char JournalContext MixedAmount
|
||||||
postingamount =
|
postingamount =
|
||||||
try (do
|
try (do
|
||||||
many1 spacenonewline
|
many1 spacenonewline
|
||||||
someamount <|> return missingamt
|
someamount <|> return missingamt
|
||||||
) <|> return missingamt
|
) <|> return missingamt
|
||||||
|
|
||||||
someamount :: GenParser Char st MixedAmount
|
someamount :: GenParser Char JournalContext MixedAmount
|
||||||
someamount = try leftsymbolamount <|> try rightsymbolamount <|> nosymbolamount
|
someamount = try leftsymbolamount <|> try rightsymbolamount <|> nosymbolamount
|
||||||
|
|
||||||
leftsymbolamount :: GenParser Char st MixedAmount
|
leftsymbolamount :: GenParser Char JournalContext MixedAmount
|
||||||
leftsymbolamount = do
|
leftsymbolamount = do
|
||||||
sign <- optionMaybe $ string "-"
|
sign <- optionMaybe $ string "-"
|
||||||
let applysign = if isJust sign then negate else id
|
let applysign = if isJust sign then negate else id
|
||||||
@ -470,7 +474,7 @@ leftsymbolamount = do
|
|||||||
return $ applysign $ Mixed [Amount c q pri]
|
return $ applysign $ Mixed [Amount c q pri]
|
||||||
<?> "left-symbol amount"
|
<?> "left-symbol amount"
|
||||||
|
|
||||||
rightsymbolamount :: GenParser Char st MixedAmount
|
rightsymbolamount :: GenParser Char JournalContext MixedAmount
|
||||||
rightsymbolamount = do
|
rightsymbolamount = do
|
||||||
(q,p,comma) <- amountquantity
|
(q,p,comma) <- amountquantity
|
||||||
sp <- many spacenonewline
|
sp <- many spacenonewline
|
||||||
@ -480,28 +484,29 @@ rightsymbolamount = do
|
|||||||
return $ Mixed [Amount c q pri]
|
return $ Mixed [Amount c q pri]
|
||||||
<?> "right-symbol amount"
|
<?> "right-symbol amount"
|
||||||
|
|
||||||
nosymbolamount :: GenParser Char st MixedAmount
|
nosymbolamount :: GenParser Char JournalContext MixedAmount
|
||||||
nosymbolamount = do
|
nosymbolamount = do
|
||||||
(q,p,comma) <- amountquantity
|
(q,p,comma) <- amountquantity
|
||||||
pri <- priceamount
|
pri <- priceamount
|
||||||
let c = Commodity {symbol="",side=L,spaced=False,comma=comma,precision=p}
|
defc <- getCommodity
|
||||||
|
let c = fromMaybe Commodity{symbol="",side=L,spaced=False,comma=comma,precision=p} defc
|
||||||
return $ Mixed [Amount c q pri]
|
return $ Mixed [Amount c q pri]
|
||||||
<?> "no-symbol amount"
|
<?> "no-symbol amount"
|
||||||
|
|
||||||
commoditysymbol :: GenParser Char st String
|
commoditysymbol :: GenParser Char JournalContext String
|
||||||
commoditysymbol = (quotedcommoditysymbol <|> simplecommoditysymbol) <?> "commodity symbol"
|
commoditysymbol = (quotedcommoditysymbol <|> simplecommoditysymbol) <?> "commodity symbol"
|
||||||
|
|
||||||
quotedcommoditysymbol :: GenParser Char st String
|
quotedcommoditysymbol :: GenParser Char JournalContext String
|
||||||
quotedcommoditysymbol = do
|
quotedcommoditysymbol = do
|
||||||
char '"'
|
char '"'
|
||||||
s <- many1 $ noneOf ";\n\""
|
s <- many1 $ noneOf ";\n\""
|
||||||
char '"'
|
char '"'
|
||||||
return s
|
return s
|
||||||
|
|
||||||
simplecommoditysymbol :: GenParser Char st String
|
simplecommoditysymbol :: GenParser Char JournalContext String
|
||||||
simplecommoditysymbol = many1 (noneOf nonsimplecommoditychars)
|
simplecommoditysymbol = many1 (noneOf nonsimplecommoditychars)
|
||||||
|
|
||||||
priceamount :: GenParser Char st (Maybe MixedAmount)
|
priceamount :: GenParser Char JournalContext (Maybe MixedAmount)
|
||||||
priceamount =
|
priceamount =
|
||||||
try (do
|
try (do
|
||||||
many spacenonewline
|
many spacenonewline
|
||||||
@ -516,7 +521,7 @@ priceamount =
|
|||||||
-- | Parse a ledger-style numeric quantity and also return the number of
|
-- | Parse a ledger-style numeric quantity and also return the number of
|
||||||
-- digits to the right of the decimal point and whether thousands are
|
-- digits to the right of the decimal point and whether thousands are
|
||||||
-- separated by comma.
|
-- separated by comma.
|
||||||
amountquantity :: GenParser Char st (Double, Int, Bool)
|
amountquantity :: GenParser Char JournalContext (Double, Int, Bool)
|
||||||
amountquantity = do
|
amountquantity = do
|
||||||
sign <- optionMaybe $ string "-"
|
sign <- optionMaybe $ string "-"
|
||||||
(intwithcommas,frac) <- numberparts
|
(intwithcommas,frac) <- numberparts
|
||||||
@ -534,10 +539,10 @@ amountquantity = do
|
|||||||
-- | parse the two strings of digits before and after a possible decimal
|
-- | parse the two strings of digits before and after a possible decimal
|
||||||
-- point. The integer part may contain commas, or either part may be
|
-- point. The integer part may contain commas, or either part may be
|
||||||
-- empty, or there may be no point.
|
-- empty, or there may be no point.
|
||||||
numberparts :: GenParser Char st (String,String)
|
numberparts :: GenParser Char JournalContext (String,String)
|
||||||
numberparts = numberpartsstartingwithdigit <|> numberpartsstartingwithpoint
|
numberparts = numberpartsstartingwithdigit <|> numberpartsstartingwithpoint
|
||||||
|
|
||||||
numberpartsstartingwithdigit :: GenParser Char st (String,String)
|
numberpartsstartingwithdigit :: GenParser Char JournalContext (String,String)
|
||||||
numberpartsstartingwithdigit = do
|
numberpartsstartingwithdigit = do
|
||||||
let digitorcomma = digit <|> char ','
|
let digitorcomma = digit <|> char ','
|
||||||
first <- digit
|
first <- digit
|
||||||
@ -545,7 +550,7 @@ numberpartsstartingwithdigit = do
|
|||||||
frac <- try (do {char '.'; many digit}) <|> return ""
|
frac <- try (do {char '.'; many digit}) <|> return ""
|
||||||
return (first:rest,frac)
|
return (first:rest,frac)
|
||||||
|
|
||||||
numberpartsstartingwithpoint :: GenParser Char st (String,String)
|
numberpartsstartingwithpoint :: GenParser Char JournalContext (String,String)
|
||||||
numberpartsstartingwithpoint = do
|
numberpartsstartingwithpoint = do
|
||||||
char '.'
|
char '.'
|
||||||
frac <- many1 digit
|
frac <- many1 digit
|
||||||
@ -618,7 +623,7 @@ tests_Journal = TestList [
|
|||||||
let -- | compare a parse result with a MixedAmount, showing the debug representation for clarity
|
let -- | compare a parse result with a MixedAmount, showing the debug representation for clarity
|
||||||
assertMixedAmountParse parseresult mixedamount =
|
assertMixedAmountParse parseresult mixedamount =
|
||||||
(either (const "parse error") showMixedAmountDebug parseresult) ~?= (showMixedAmountDebug mixedamount)
|
(either (const "parse error") showMixedAmountDebug parseresult) ~?= (showMixedAmountDebug mixedamount)
|
||||||
assertMixedAmountParse (parsewith someamount "1 @ $2")
|
assertMixedAmountParse (parseWithCtx emptyCtx someamount "1 @ $2")
|
||||||
(Mixed [Amount unknown 1 (Just $ Mixed [Amount dollar{precision=0} 2 Nothing])])
|
(Mixed [Amount unknown 1 (Just $ Mixed [Amount dollar{precision=0} 2 Nothing])])
|
||||||
|
|
||||||
,"postingamount" ~: do
|
,"postingamount" ~: do
|
||||||
|
|||||||
@ -9,6 +9,7 @@ module Hledger.Cli.Commands.Add
|
|||||||
where
|
where
|
||||||
import Hledger.Data
|
import Hledger.Data
|
||||||
import Hledger.Read.Journal (someamount)
|
import Hledger.Read.Journal (someamount)
|
||||||
|
import Hledger.Read.Common (emptyCtx)
|
||||||
import Hledger.Cli.Options
|
import Hledger.Cli.Options
|
||||||
import Hledger.Cli.Commands.Register (registerReport, registerReportAsText)
|
import Hledger.Cli.Commands.Register (registerReport, registerReportAsText)
|
||||||
#if __GLASGOW_HASKELL__ <= 610
|
#if __GLASGOW_HASKELL__ <= 610
|
||||||
@ -92,7 +93,7 @@ getPostings accept historicalps enteredps = do
|
|||||||
then return enteredps
|
then return enteredps
|
||||||
else do
|
else do
|
||||||
amountstr <- askFor (printf "amount %d" n) defaultamount validateamount
|
amountstr <- askFor (printf "amount %d" n) defaultamount validateamount
|
||||||
let amount = fromparse $ parse (someamount <|> return missingamt) "" amountstr
|
let amount = fromparse $ runParser (someamount <|> return missingamt) emptyCtx "" amountstr
|
||||||
let p = nullposting{paccount=stripbrackets account,
|
let p = nullposting{paccount=stripbrackets account,
|
||||||
pamount=amount,
|
pamount=amount,
|
||||||
ptype=postingtype account}
|
ptype=postingtype account}
|
||||||
@ -113,7 +114,7 @@ getPostings accept historicalps enteredps = do
|
|||||||
postingtype _ = RegularPosting
|
postingtype _ = RegularPosting
|
||||||
stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse
|
stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse
|
||||||
validateamount = Just $ \s -> (null s && not (null enteredrealps))
|
validateamount = Just $ \s -> (null s && not (null enteredrealps))
|
||||||
|| isRight (parse (someamount>>many spacenonewline>>eof) "" s)
|
|| isRight (runParser (someamount>>many spacenonewline>>eof) emptyCtx "" s)
|
||||||
|
|
||||||
-- | Prompt for and read a string value, optionally with a default value
|
-- | Prompt for and read a string value, optionally with a default value
|
||||||
-- and a validator. A validator causes the prompt to repeat until the
|
-- and a validator. A validator causes the prompt to repeat until the
|
||||||
|
|||||||
38
tests/default-commodity.test
Normal file
38
tests/default-commodity.test
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# a default commodity defined with the D directive will be used for any
|
||||||
|
# commodity-less amounts in subsequent transactions.
|
||||||
|
#
|
||||||
|
bin/hledger -f- print
|
||||||
|
<<<
|
||||||
|
|
||||||
|
; no default commodity
|
||||||
|
2010/1/1 x
|
||||||
|
a 1000
|
||||||
|
b
|
||||||
|
|
||||||
|
; pound, two decimal places, no thousands separator
|
||||||
|
D £1000.00
|
||||||
|
|
||||||
|
2010/1/1 y
|
||||||
|
a 1000
|
||||||
|
b
|
||||||
|
|
||||||
|
; dollar, no decimal places, comma thousands separator
|
||||||
|
D $1,000
|
||||||
|
|
||||||
|
2010/1/1 z
|
||||||
|
a 1000
|
||||||
|
b
|
||||||
|
|
||||||
|
>>>
|
||||||
|
2010/01/01 x
|
||||||
|
a 1000
|
||||||
|
b -1000
|
||||||
|
|
||||||
|
2010/01/01 y
|
||||||
|
a £1000.00
|
||||||
|
b £-1000.00
|
||||||
|
|
||||||
|
2010/01/01 z
|
||||||
|
a $1,000
|
||||||
|
b $-1,000
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user