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