Skip to content

Commit 83eaec1

Browse files
committed
chore(release): v1.2.3 — accept category_id on transaction create
Create endpoint resolved category only by name and ignored numeric category_id; native clients send category_id, so app-created transactions were saved uncategorised. Create now honours category_id as a fallback (validated against existing categories) and duplicate detection resolves the name from the id. Web (name-based) unchanged.
1 parent daa1717 commit 83eaec1

3 files changed

Lines changed: 24 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to this project are documented in this file.
44

5+
## [v1.2.3] - 2026-06-06
6+
7+
### Fixed
8+
- **Native-app transaction categories now persist on create**: the create endpoint previously resolved a category only from a `category` *name* and silently ignored a numeric `category_id`. Native clients send the category as a `category_id`, so every transaction created from the app was saved uncategorised — while edits were unaffected because the update endpoint already honoured `category_id`. Create now accepts `category_id` as a fallback when no name is supplied, validating that it refers to an existing category, and duplicate detection resolves the category name from that id so id-based creates are matched correctly. The web app (which sends the category name) is unchanged.
9+
510
## [v1.2.2] - 2026-06-01
611

712
### Fixed

controllers/transactionController.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const createTransaction = async (req, res) => {
1313
try {
1414
const {
1515
category,
16+
category_id,
1617
source,
1718
target,
1819
amount,
@@ -36,8 +37,9 @@ const createTransaction = async (req, res) => {
3637
const inSource = norm(source);
3738
const inTarget = norm(target);
3839

39-
// Handle category (global by name)
40+
// Handle category (global by name, or by numeric id from native clients)
4041
let categoryId = null;
42+
let categoryNameForDup = inCategory;
4143
if (inCategory) {
4244
try {
4345
// Try to find any category with the same name regardless of owner
@@ -59,6 +61,20 @@ const createTransaction = async (req, res) => {
5961
console.error("Category handling error:", err);
6062
return res.status(500).json({ message: "Failed to process category" });
6163
}
64+
} else if (category_id != null) {
65+
// Native clients send the category as a numeric id (names are display-only).
66+
// Accept the id directly when it refers to an existing category.
67+
const idNum = Number(category_id);
68+
if (Number.isInteger(idNum) && idNum > 0) {
69+
const byId = await db.query(
70+
"SELECT id, name FROM categories WHERE id = $1 LIMIT 1",
71+
[idNum],
72+
);
73+
if (byId.rows.length > 0) {
74+
categoryId = byId.rows[0].id;
75+
categoryNameForDup = byId.rows[0].name;
76+
}
77+
}
6278
}
6379

6480
// Handle source and target normally - let frontend manage the semantic mapping
@@ -129,7 +145,7 @@ const createTransaction = async (req, res) => {
129145
amount,
130146
transactionType,
131147
description || "",
132-
inCategory || null,
148+
categoryNameForDup || null,
133149
inSource || null,
134150
inTarget || null,
135151
]);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "finx",
3-
"version": "1.2.2",
3+
"version": "1.2.3",
44
"description": "A modern personal finance management application",
55
"main": "server.js",
66
"scripts": {

0 commit comments

Comments
 (0)