From caca2497ea4e310df89899a8fef3107a83355b70 Mon Sep 17 00:00:00 2001 From: Neil <55785687+carkom@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:51:51 +0000 Subject: [PATCH] Add ability to import categories from CSV (#2163) * update transaction table * notes * adjust parser --- .../src/components/accounts/Account.jsx | 2 + .../components/modals/ImportTransactions.jsx | 60 +++++++++++++++++-- upcoming-release-notes/2163.md | 6 ++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 upcoming-release-notes/2163.md diff --git a/packages/desktop-client/src/components/accounts/Account.jsx b/packages/desktop-client/src/components/accounts/Account.jsx index a91ad99ee55..e61c46c68ad 100644 --- a/packages/desktop-client/src/components/accounts/Account.jsx +++ b/packages/desktop-client/src/components/accounts/Account.jsx @@ -463,6 +463,7 @@ class AccountInternal extends PureComponent { onImport = async () => { const accountId = this.props.accountId; const account = this.props.accounts.find(acct => acct.id === accountId); + const categories = await this.props.getCategories(); if (account) { const res = await window.Actual.openFileDialog({ @@ -477,6 +478,7 @@ class AccountInternal extends PureComponent { if (res) { this.props.pushModal('import-transactions', { accountId, + categories, filename: res[0], onImported: didChange => { if (didChange) { diff --git a/packages/desktop-client/src/components/modals/ImportTransactions.jsx b/packages/desktop-client/src/components/modals/ImportTransactions.jsx index 8c55e3b322e..79f23a37a84 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactions.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactions.jsx @@ -194,14 +194,24 @@ function getInitialMappings(transactions) { fields.find(([name, value]) => value.match(/^-?[.,\d]+$/)), ); + const categoryField = key( + fields.find(([name, value]) => name.toLowerCase().includes('category')), + ); + const payeeField = key( - fields.find(([name, value]) => name !== dateField && name !== amountField), + fields.find( + ([name, value]) => + name !== dateField && name !== amountField && name !== categoryField, + ), ); const notesField = key( fields.find( ([name, value]) => - name !== dateField && name !== amountField && name !== payeeField, + name !== dateField && + name !== amountField && + name !== categoryField && + name !== payeeField, ), ); @@ -221,6 +231,7 @@ function getInitialMappings(transactions) { payee: payeeField, notes: notesField, inOut: inOutField, + category: categoryField, }; } @@ -290,6 +301,19 @@ function parseAmountFields( }; } +function parseCategoryFields(trans, categories) { + let match = null; + categories.forEach(category => { + if (category.id === trans.category) { + return null; + } + if (category.name === trans.category) { + match = category.id; + } + }); + return match; +} + function Transaction({ transaction: rawTransaction, fieldMappings, @@ -301,7 +325,9 @@ function Transaction({ outValue, flipAmount, multiplierAmount, + categories, }) { + const categoryList = categories.map(category => category.name); const transaction = useMemo( () => fieldMappings @@ -348,6 +374,14 @@ function Transaction({ {transaction.notes} + + {categoryList.includes(transaction.category) && transaction.category} + {splitMode ? ( <> + + + onChange('category', name)} + hasHeaderRow={hasHeaderRow} + firstTransaction={transactions[0]} + /> + {splitMode ? ( <> @@ -676,7 +721,7 @@ export function ImportTransactions({ modalProps, options }) { const [outValue, setOutValue] = useState(''); const [flipAmount, setFlipAmount] = useState(false); const [multiplierEnabled, setMultiplierEnabled] = useState(false); - const { accountId, onImported } = options; + const { accountId, categories, onImported } = options; // This cannot be set after parsing the file, because changing it // requires re-parsing the file. This is different from the other @@ -866,6 +911,11 @@ export function ImportTransactions({ modalProps, options }) { break; } + const category_id = parseCategoryFields(trans, categories.list); + if (category_id != null) { + trans.category = category_id; + } + const { inflow, outflow, inOut, ...finalTransaction } = trans; finalTransactions.push({ ...finalTransaction, @@ -919,6 +969,7 @@ export function ImportTransactions({ modalProps, options }) { { name: 'Date', width: 200 }, { name: 'Payee', width: 'flex' }, { name: 'Notes', width: 'flex' }, + { name: 'Category', width: 'flex' }, ]; if (inOutMode) { @@ -959,7 +1010,7 @@ export function ImportTransactions({ modalProps, options }) { index} renderEmpty={() => { @@ -989,6 +1040,7 @@ export function ImportTransactions({ modalProps, options }) { outValue={outValue} flipAmount={flipAmount} multiplierAmount={multiplierAmount} + categories={categories.list} /> )} diff --git a/upcoming-release-notes/2163.md b/upcoming-release-notes/2163.md new file mode 100644 index 00000000000..2d7313306ba --- /dev/null +++ b/upcoming-release-notes/2163.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [ScottFries, blakegearin, carkom] +--- + +Add ability to import categories from CSV \ No newline at end of file