From 9c6d9ecf0afe204805ec7c1b35cf62528e380fb2 Mon Sep 17 00:00:00 2001 From: Shazib Hussain Date: Mon, 20 Nov 2023 16:41:13 +0000 Subject: [PATCH] Don't allow duplicate category names (per group) (#1833) * Remove author from electron package.json * Don't allow dupes in cat names (per group) * Release Notes * Handle reorders and moves * Fix linter warnings * Fix typecheck * Show the name of the duplicate category * missed func call * Upper case before compare * lint fixes --------- Co-authored-by: Shazib Hussain --- .../src/components/budget/index.tsx | 37 +++++++++++++++++++ packages/loot-core/src/server/db/index.ts | 11 ++++++ upcoming-release-notes/1833.md | 6 +++ 3 files changed, 54 insertions(+) create mode 100644 upcoming-release-notes/1833.md diff --git a/packages/desktop-client/src/components/budget/index.tsx b/packages/desktop-client/src/components/budget/index.tsx index 12c70c57065..81ab8b88b0d 100644 --- a/packages/desktop-client/src/components/budget/index.tsx +++ b/packages/desktop-client/src/components/budget/index.tsx @@ -92,6 +92,7 @@ type BudgetProps = { moveCategory: BoundActions['moveCategory']; moveCategoryGroup: BoundActions['moveCategoryGroup']; loadPrefs: BoundActions['loadPrefs']; + addNotification: BoundActions['addNotification']; }; function Budget(props: BudgetProps) { @@ -241,7 +242,28 @@ function Budget(props: BudgetProps) { setIsAddingGroup(false); }; + const categoryNameAlreadyExistsNotification = name => { + props.addNotification({ + type: 'error', + message: `Category ‘${name}’ already exists in group (May be Hidden)`, + }); + }; + const onSaveCategory = async category => { + let exists = + (await props.getCategories()).grouped + .filter(g => g.id === category.cat_group)[0] + .categories.filter( + c => c.name.toUpperCase() === category.name.toUpperCase(), + ) + .filter(c => (category.id === 'new' ? true : c.id !== category.id)) + .length > 0; + + if (exists) { + categoryNameAlreadyExistsNotification(category.name); + return; + } + if (category.id === 'new') { let id = await props.createCategory( category.name, @@ -350,6 +372,21 @@ function Budget(props: BudgetProps) { }; const onReorderCategory = async sortInfo => { + let cats = await props.getCategories(); + let moveCandidate = cats.list.filter(c => c.id === sortInfo.id)[0]; + let exists = + cats.grouped + .filter(g => g.id === sortInfo.groupId)[0] + .categories.filter( + c => c.name.toUpperCase() === moveCandidate.name.toUpperCase(), + ) + .filter(c => c.id !== moveCandidate.id).length > 0; + + if (exists) { + categoryNameAlreadyExistsNotification(moveCandidate.name); + return; + } + props.moveCategory(sortInfo.id, sortInfo.groupId, sortInfo.targetId); setCategoryGroups(state => moveCategory(state, sortInfo.id, sortInfo.groupId, sortInfo.targetId), diff --git a/packages/loot-core/src/server/db/index.ts b/packages/loot-core/src/server/db/index.ts index a06e08dc2d9..caeb038397b 100644 --- a/packages/loot-core/src/server/db/index.ts +++ b/packages/loot-core/src/server/db/index.ts @@ -346,6 +346,17 @@ export async function insertCategory( let id_; await batchMessages(async () => { + // Dont allow duplicated names in groups + const existingCatInGroup = await first( + `SELECT id FROM categories WHERE cat_group = ? and UPPER(name) = ? LIMIT 1`, + [category.cat_group, category.name.toUpperCase()], + ); + if (existingCatInGroup) { + throw new Error( + `Category ‘${category.name}’ already exists in group ‘${category.cat_group}’`, + ); + } + if (atEnd) { const lastCat = await first(` SELECT sort_order FROM categories WHERE tombstone = 0 ORDER BY sort_order DESC, id DESC LIMIT 1 diff --git a/upcoming-release-notes/1833.md b/upcoming-release-notes/1833.md new file mode 100644 index 00000000000..acc02c1e41d --- /dev/null +++ b/upcoming-release-notes/1833.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [Shazib] +--- + +Not allowed duplicated Category names in Category Groups \ No newline at end of file