;csv: amount-setting notes, doc improvements from reddit discussion

https://www.reddit.com/r/plaintextaccounting/comments/nxu1ss/hledger_parsing_csv_with_negative_amount_in_debit/
This commit is contained in:
Simon Michael 2021-06-11 16:30:43 -10:00
parent 665fec83cd
commit b81f8f768d
2 changed files with 42 additions and 36 deletions

View File

@ -974,9 +974,10 @@ transactionFromCsvRecord sourcepos rules record = t
-- If there's multiple non-zeros, or no non-zeros but multiple zeros, it throws an error. -- If there's multiple non-zeros, or no non-zeros but multiple zeros, it throws an error.
getAmount :: CsvRules -> CsvRecord -> Text -> Bool -> Int -> Maybe MixedAmount getAmount :: CsvRules -> CsvRecord -> Text -> Bool -> Int -> Maybe MixedAmount
getAmount rules record currency p1IsVirtual n = getAmount rules record currency p1IsVirtual n =
-- Warning, many tricky corner cases here. -- Warning! Many tricky corner cases here.
-- docs: hledger_csv.m4.md #### amount -- Keep synced with:
-- tests: hledger/test/csv.test ~ 13, 31-34 -- hledger_csv.m4.md -> CSV FORMAT -> "amount", "Setting amounts",
-- hledger/test/csv.test -> 13, 31-34
let let
unnumberedfieldnames = ["amount","amount-in","amount-out"] unnumberedfieldnames = ["amount","amount-in","amount-out"]
@ -990,6 +991,7 @@ getAmount rules record currency p1IsVirtual n =
assignments = [(f,a') | f <- fieldnames assignments = [(f,a') | f <- fieldnames
, Just v <- [T.strip . renderTemplate rules record <$> hledgerField rules record f] , Just v <- [T.strip . renderTemplate rules record <$> hledgerField rules record f]
, not $ T.null v , not $ T.null v
-- XXX maybe ignore rule-generated values like "", "-", "$", "-$", "$-" ? cf CSV FORMAT -> "amount", "Setting amounts",
, let a = parseAmount rules record currency v , let a = parseAmount rules record currency v
-- With amount/amount-in/amount-out, in posting 2, -- With amount/amount-in/amount-out, in posting 2,
-- flip the sign and convert to cost, as they did before 1.17 -- flip the sign and convert to cost, as they did before 1.17
@ -1012,6 +1014,7 @@ getAmount rules record currency p1IsVirtual n =
assignments'' of assignments'' of
[] -> Nothing [] -> Nothing
[(f,a)] | "-out" `T.isSuffixOf` f -> Just (maNegate a) -- for -out fields, flip the sign [(f,a)] | "-out" `T.isSuffixOf` f -> Just (maNegate a) -- for -out fields, flip the sign
-- XXX unless it's already negative ? back compat issues / too confusing ?
[(_,a)] -> Just a [(_,a)] -> Just a
fs -> error' . T.unpack . T.unlines $ [ -- PARTIAL: fs -> error' . T.unpack . T.unlines $ [ -- PARTIAL:
"multiple non-zero amounts or multiple zero amounts assigned," "multiple non-zero amounts or multiple zero amounts assigned,"

View File

@ -3581,20 +3581,18 @@ a default account name will be chosen (like "expenses:unknown" or "income:unknow
##### amount ##### amount
`amountN` sets posting N's amount. `amountN` sets the Nth posting's amount.
If the CSV uses separate fields for inflows and outflows, you can By assigning to `amount1`, `amount2`, ... etc. you can generate up to 99 postings.
use `amountN-in` and `amountN-out` instead.
By assigning to `amount1`, `amount2`, ... etc. you can generate anywhere
from 0 to 99 postings.
There is also an older, unnumbered form of these names, suitable for If the CSV uses separate fields for debits and credits (inflows and outflows), you can
2-posting transactions, which sets both posting 1's and (negated) posting 2's amount: use `amountN-in` and `amountN-out` instead.
`amount`, or `amount-in` and `amount-out`. Note hledger assumes both of these fields are unsigned, and will automatically negate the "-out" value.
This is still supported If the fields are signed, see ["Setting amounts"](#setting-amounts) below.
because it keeps pre-hledger-1.17 csv rules files working,
and because it can be more succinct, There is also an unnumbered form of these names: `amount`, or `amount-in` and `amount-out`.
and because it converts posting 2's amount to cost if there's a This is supported to keep pre-hledger-1.17 CSV rules files working (and for occasional convenience).
[transaction price](#transaction-prices), which can be useful. It is suitable only for two-posting transactions; it sets both posting 1's and posting 2's amount.
Posting 2's amount will be negated, and also converted to cost if there's a [transaction price](#transaction-prices).
If you have an existing rules file using the unnumbered form, you If you have an existing rules file using the unnumbered form, you
might want to use the numbered form in certain conditional blocks, might want to use the numbered form in certain conditional blocks,
@ -4041,31 +4039,37 @@ Here are the ways to set a posting's amount:
Assign (via a [fields list](#fields) or a [field assignment](#field-assignment)) to `amountN`. Assign (via a [fields list](#fields) or a [field assignment](#field-assignment)) to `amountN`.
This sets the Nth posting's amount. N is usually 1 or 2 but can go up to 99. This sets the Nth posting's amount. N is usually 1 or 2 but can go up to 99.
2. **If the CSV has separate Debit and Credit amount fields:**\ 2. **If the CSV has separate amount fields for debit & credit (in & out):**\
a. **If both fields are unsigned:**\
Assign to `amountN-in` and `amountN-out`. Assign to `amountN-in` and `amountN-out`.
This sets posting N's amount to whichever of these has a non-zero value, This sets posting N's amount to whichever of these has a non-zero value,
guessing an appropriate sign. and negates the "-out" value.
- **If hledger guesses the wrong sign:**\ b. **If either field is signed (can contain a minus sign):**\
Prepend a minus sign to flip it. Eg: Use a [conditional rule](#if-block) to flip the sign (of non-empty values).
Since hledger always negates amountN-out, if it was already negative,
we must undo that by negating once more (but only if the field is non-empty):
```rules ```rules
fields date, description, amount-in, amount-out fields date, description, amount1-in, amount1-out
amount-out -%amount-out if %amount1-out [1-9]
amount1-out -%amount1-out
``` ```
- **If both fields contain a non-zero value:**\ c. **If both fields, or neither field, can contain a non-zero value:**\
The `amountN-in`/`amountN-out` rules require that each CSV record has a non-zero value in exactly one of the two fields, hledger normally expects exactly one of the fields to have a non-zero value.
so that hledger knows which to choose. So these would all be rejected: Eg, the `amountN-in`/`amountN-out` rules would reject values like these:
```csv ```csv
"", "" "" , ""
"0", "0" "0" , "0"
"1", "none" "1" , "none"
``` ```
If your CSV has amount values like this, use [conditional rules](#if-block) instead. So, use [conditional rules](#if-block) to set the amount from the appropriate field.
For example, to make hledger to choose the value containing non-zero digits: Eg, these rules would make it use only the value containing non-zero digits,
handling the above:
```rules ```rules
fields date, description, in, out fields date, description, in, out
@ -4075,10 +4079,9 @@ Here are the ways to set a posting's amount:
amount1 %out amount1 %out
``` ```
3. **Using the old numberless syntax:**\ 3. **If you are stuck with hledger <1.17, or you want posting 2's amount converted to cost:**\
Assign to `amount` (or to `amount-in` and `amount-out`). Use the old numberless syntax, which sets amount1 and amount2:
This sets posting 1's and posting 2's amounts (and converts posting 2's amount to cost). assign to `amount` (or to `amount-in` and `amount-out`).
This is supported for backwards compatibility (and occasional convenience).
4. **If the CSV has the balance instead of the transaction amount:**\ 4. **If the CSV has the balance instead of the transaction amount:**\
Assign to `balanceN`, which sets posting N's amount indirectly via a Assign to `balanceN`, which sets posting N's amount indirectly via a