From be1889195715a5cc1fe084209336c213cf5e9636 Mon Sep 17 00:00:00 2001 From: Jesse Visser Date: Sun, 5 Nov 2023 15:49:00 +0100 Subject: [PATCH] Enhancement: Add option to select in/out field during import (#1788) --- .../components/modals/ImportTransactions.js | 165 +++++++++++++++--- upcoming-release-notes/1788.md | 6 + 2 files changed, 144 insertions(+), 27 deletions(-) create mode 100644 upcoming-release-notes/1788.md diff --git a/packages/desktop-client/src/components/modals/ImportTransactions.js b/packages/desktop-client/src/components/modals/ImportTransactions.js index e9f27f5c80a..2417553515b 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactions.js +++ b/packages/desktop-client/src/components/modals/ImportTransactions.js @@ -193,11 +193,22 @@ function getInitialMappings(transactions) { ), ); + let inOutField = key( + fields.find( + ([name, value]) => + name !== dateField && + name !== amountField && + name !== payeeField && + name !== notesField, + ), + ); + return { date: dateField, amount: amountField, payee: payeeField, notes: notesField, + inOut: inOutField, }; } @@ -222,7 +233,14 @@ function parseAmount(amount, mapper) { return value; } -function parseAmountFields(trans, splitMode, flipAmount, multiplierAmount) { +function parseAmountFields( + trans, + splitMode, + inOutMode, + outValue, + flipAmount, + multiplierAmount, +) { const multiplier = parseFloat(multiplierAmount) || 1.0; if (splitMode) { @@ -240,6 +258,16 @@ function parseAmountFields(trans, splitMode, flipAmount, multiplierAmount) { inflow, }; } + if (inOutMode) { + return { + amount: + parseAmount(trans.amount, n => + trans.inOut === outValue ? Math.abs(n) * -1 : Math.abs(n), + ) * multiplier, + outflow: null, + inflow: null, + }; + } return { amount: parseAmount(trans.amount, n => (flipAmount ? n * -1 : n)) * multiplier, @@ -255,6 +283,8 @@ function Transaction({ parseDateFormat, dateFormat, splitMode, + inOutMode, + outValue, flipAmount, multiplierAmount, }) { @@ -269,6 +299,8 @@ function Transaction({ let { amount, outflow, inflow } = parseAmountFields( transaction, splitMode, + inOutMode, + outValue, flipAmount, multiplierAmount, ); @@ -320,13 +352,24 @@ function Transaction({ ) : ( - - {amount} - + <> + {inOutMode && ( + + {transaction.inOut} + + )} + + {amount} + + )} ); @@ -457,11 +500,43 @@ function MultiplierOption({ ); } +function InOutOption({ + inOutMode, + outValue, + disabled, + onToggle, + onChangeText, +}) { + return ( + + + {inOutMode + ? 'in/out identifier' + : 'Select column to specify if amount goes in/out'} + + {inOutMode && ( + + )} + + ); +} + function FieldMappings({ transactions, mappings, onChange, splitMode, + inOutMode, hasHeaderRow, }) { if (transactions.length === 0) { @@ -537,16 +612,30 @@ function FieldMappings({ ) : ( - - - onChange('amount', name)} - hasHeaderRow={hasHeaderRow} - firstTransaction={transactions[0]} - /> - + <> + {inOutMode && ( + + + onChange('inOut', name)} + hasHeaderRow={hasHeaderRow} + firstTransaction={transactions[0]} + /> + + )} + + + onChange('amount', name)} + hasHeaderRow={hasHeaderRow} + firstTransaction={transactions[0]} + /> + + )} @@ -569,6 +658,8 @@ export default function ImportTransactions({ modalProps, options }) { let [filetype, setFileType] = useState(null); let [fieldMappings, setFieldMappings] = useState(null); let [splitMode, setSplitMode] = useState(false); + let [inOutMode, setInOutMode] = useState(false); + let [outValue, setOutValue] = useState(''); let [flipAmount, setFlipAmount] = useState(false); let [multiplierEnabled, setMultiplierEnabled] = useState(false); let { accountId, onImported } = options; @@ -684,6 +775,8 @@ export default function ImportTransactions({ modalProps, options }) { let isSplit = !splitMode; setSplitMode(isSplit); + setInOutMode(false); + setFlipAmount(false); // Run auto-detection on the fields to try to detect the fields // automatically @@ -749,6 +842,8 @@ export default function ImportTransactions({ modalProps, options }) { let { amount } = parseAmountFields( trans, splitMode, + inOutMode, + outValue, flipAmount, multiplierAmount, ); @@ -757,7 +852,7 @@ export default function ImportTransactions({ modalProps, options }) { break; } - let { inflow, outflow, ...finalTransaction } = trans; + let { inflow, outflow, inOut, ...finalTransaction } = trans; finalTransactions.push({ ...finalTransaction, date, @@ -812,6 +907,9 @@ export default function ImportTransactions({ modalProps, options }) { { name: 'Notes', width: 'flex' }, ]; + if (inOutMode) { + headers.push({ name: 'In/Out', width: 90, style: { textAlign: 'left' } }); + } if (splitMode) { headers.push({ name: 'Outflow', width: 90, style: { textAlign: 'right' } }); headers.push({ name: 'Inflow', width: 90, style: { textAlign: 'right' } }); @@ -873,6 +971,8 @@ export default function ImportTransactions({ modalProps, options }) { dateFormat={dateFormat} fieldMappings={fieldMappings} splitMode={splitMode} + inOutMode={inOutMode} + outValue={outValue} flipAmount={flipAmount} multiplierAmount={multiplierAmount} /> @@ -905,6 +1005,7 @@ export default function ImportTransactions({ modalProps, options }) { onChange={onUpdateFields} mappings={fieldMappings} splitMode={splitMode} + inOutMode={inOutMode} hasHeaderRow={hasHeaderRow} /> @@ -1018,19 +1119,29 @@ export default function ImportTransactions({ modalProps, options }) { setFlipAmount(!flipAmount)} > Flip amount {filetype === 'csv' && ( - - Split amount into separate inflow/outflow columns - + <> + + Split amount into separate inflow/outflow columns + + setInOutMode(!inOutMode)} + onChangeText={setOutValue} + /> + )}