From 096d4d0530ee50602d0a605114dac004d1827218 Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Sat, 2 Aug 2025 10:07:36 +0200 Subject: [PATCH] Refactor BankingKreditorSelector and DocumentRenderer components to enhance transaction assignment handling. Remove unused 'assigned_by' field from API requests and update onSave callbacks to pass assigned Kreditor details. Improve SQL queries in bankingTransactions.js to handle both numeric and string transaction IDs, and streamline transaction insertion logic. Update transaction retrieval in transactions.js to include additional assigned Kreditor information. --- .../src/components/BankingKreditorSelector.js | 8 +- .../cellRenderers/DocumentRenderer.js | 32 +++-- src/routes/data/bankingTransactions.js | 110 ++++++++++++------ src/routes/data/transactions.js | 11 +- 4 files changed, 111 insertions(+), 50 deletions(-) diff --git a/client/src/components/BankingKreditorSelector.js b/client/src/components/BankingKreditorSelector.js index c36258c..b35ec5a 100644 --- a/client/src/components/BankingKreditorSelector.js +++ b/client/src/components/BankingKreditorSelector.js @@ -225,7 +225,6 @@ class BankingKreditorSelector extends Component { method: 'PUT', body: JSON.stringify({ assigned_kreditor_id: parseInt(selectedKreditorId), - assigned_by: user?.username || 'Unknown', }) } ); @@ -240,7 +239,6 @@ class BankingKreditorSelector extends Component { csv_transaction_id: transaction.isFromCSV ? transaction.id : null, banking_iban: transaction['Kontonummer/IBAN'] || transaction.kontonummer_iban, assigned_kreditor_id: parseInt(selectedKreditorId), - assigned_by: user?.username || 'Unknown', }) } ); @@ -257,7 +255,11 @@ class BankingKreditorSelector extends Component { if (response && response.ok) { this.setState({ saving: false }); if (onSave) { - onSave(); + // Find the assigned kreditor from the list to pass to callback + const assignedKreditor = this.state.assignableKreditors.find( + k => k.id === parseInt(selectedKreditorId) + ); + onSave(assignedKreditor); } } else { const errorData = await response.json(); diff --git a/client/src/components/cellRenderers/DocumentRenderer.js b/client/src/components/cellRenderers/DocumentRenderer.js index 4a2885c..25cb542 100644 --- a/client/src/components/cellRenderers/DocumentRenderer.js +++ b/client/src/components/cellRenderers/DocumentRenderer.js @@ -386,13 +386,13 @@ const DocumentRenderer = (params) => { sx={{ color: !params.data['Kontonummer/IBAN'] ? 'text.secondary' - : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) + : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) || params.data.assignedKreditor ? 'success.main' : 'warning.main', '&.Mui-selected': { color: !params.data['Kontonummer/IBAN'] ? 'text.secondary' - : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) + : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) || params.data.assignedKreditor ? 'success.main' : 'warning.main', } @@ -543,14 +543,22 @@ const DocumentRenderer = (params) => { { - // Refresh the grid to show updated assignment + onSave={async (assignedKreditor) => { + // Update the transaction data with the new assignment + if (assignedKreditor) { + params.data.assignedKreditor = assignedKreditor; + } + + // Refresh the entire row to update colors and content if (params.api) { params.api.refreshCells({ - columns: ['Kontonummer/IBAN'], + rowNodes: [params.node], force: true }); } + + // Force dialog re-render by updating state + this.forceUpdate(); }} /> @@ -620,14 +628,22 @@ const DocumentRenderer = (params) => { { - // Refresh the grid to show updated assignment + onSave={async (assignedKreditor) => { + // Update the transaction data with the new assignment + if (assignedKreditor) { + params.data.assignedKreditor = assignedKreditor; + } + + // Refresh the entire row to update colors and content if (params.api) { params.api.refreshCells({ - columns: ['Kontonummer/IBAN'], + rowNodes: [params.node], force: true }); } + + // Force dialog re-render by updating state + this.forceUpdate(); }} /> diff --git a/src/routes/data/bankingTransactions.js b/src/routes/data/bankingTransactions.js index 6344277..491d631 100644 --- a/src/routes/data/bankingTransactions.js +++ b/src/routes/data/bankingTransactions.js @@ -9,17 +9,37 @@ router.get('/banking-transactions/:transactionId', authenticateToken, async (req const { executeQuery } = require('../../config/database'); const { transactionId } = req.params; - const query = ` - SELECT - bat.*, - k.name as assigned_kreditor_name, - k.kreditorId as assigned_kreditor_id_code - FROM fibdash.BankingAccountTransactions bat - LEFT JOIN fibdash.Kreditor k ON bat.assigned_kreditor_id = k.id - WHERE bat.transaction_id = @transactionId OR bat.csv_transaction_id = @transactionId - `; + // Try both numeric and string format + let query, params; + const numericId = parseInt(transactionId, 10); - const result = await executeQuery(query, { transactionId: parseInt(transactionId, 10) }); + if (!isNaN(numericId) && numericId.toString() === transactionId) { + // It's a numeric ID - check transaction_id column + query = ` + SELECT + bat.*, + k.name as assigned_kreditor_name, + k.kreditorId as assigned_kreditor_id_code + FROM fibdash.BankingAccountTransactions bat + LEFT JOIN fibdash.Kreditor k ON bat.assigned_kreditor_id = k.id + WHERE bat.transaction_id = @transactionId + `; + params = { transactionId: numericId }; + } else { + // It's a string ID - check csv_transaction_id column + query = ` + SELECT + bat.*, + k.name as assigned_kreditor_name, + k.kreditorId as assigned_kreditor_id_code + FROM fibdash.BankingAccountTransactions bat + LEFT JOIN fibdash.Kreditor k ON bat.assigned_kreditor_id = k.id + WHERE bat.csv_transaction_id = @transactionId + `; + params = { transactionId }; + } + + const result = await executeQuery(query, params); res.json(result.recordset); } catch (error) { @@ -32,7 +52,7 @@ router.get('/banking-transactions/:transactionId', authenticateToken, async (req router.post('/banking-transactions', authenticateToken, async (req, res) => { try { const { executeQuery } = require('../../config/database'); - const { transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes, assigned_by } = req.body; + const { transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes } = req.body; if ((!transaction_id && !csv_transaction_id) || !banking_iban || !assigned_kreditor_id) { return res.status(400).json({ @@ -40,35 +60,55 @@ router.post('/banking-transactions', authenticateToken, async (req, res) => { }); } - const checkQuery = ` - SELECT id FROM fibdash.BankingAccountTransactions - WHERE transaction_id = @transaction_id OR csv_transaction_id = @csv_transaction_id - `; + let checkQuery, checkParams; - const checkResult = await executeQuery(checkQuery, { - transaction_id: transaction_id || null, - csv_transaction_id: csv_transaction_id || null - }); + if (csv_transaction_id) { + checkQuery = `SELECT id FROM fibdash.BankingAccountTransactions WHERE csv_transaction_id = @csv_transaction_id`; + checkParams = { csv_transaction_id }; + } else { + checkQuery = `SELECT id FROM fibdash.BankingAccountTransactions WHERE transaction_id = @transaction_id`; + checkParams = { transaction_id }; + } + + const checkResult = await executeQuery(checkQuery, checkParams); if (checkResult.recordset.length > 0) { return res.status(409).json({ error: 'Banking transaction assignment already exists' }); } - const insertQuery = ` - INSERT INTO fibdash.BankingAccountTransactions - (transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes, assigned_by) - OUTPUT INSERTED.* - VALUES (@transaction_id, @csv_transaction_id, @banking_iban, @assigned_kreditor_id, @notes, @assigned_by) - `; + let insertQuery, queryParams; - const result = await executeQuery(insertQuery, { - transaction_id: transaction_id || null, - csv_transaction_id: csv_transaction_id || null, - banking_iban, - assigned_kreditor_id, - notes: notes || null, - assigned_by: assigned_by || null - }); + if (csv_transaction_id) { + // For CSV transactions, use a placeholder transaction_id since it's NOT NULL + insertQuery = ` + INSERT INTO fibdash.BankingAccountTransactions + (transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes) + OUTPUT INSERTED.* + VALUES (-1, @csv_transaction_id, @banking_iban, @assigned_kreditor_id, @notes) + `; + queryParams = { + csv_transaction_id, + banking_iban, + assigned_kreditor_id, + notes: notes || null + }; + } else { + // For regular transactions + insertQuery = ` + INSERT INTO fibdash.BankingAccountTransactions + (transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes) + OUTPUT INSERTED.* + VALUES (@transaction_id, NULL, @banking_iban, @assigned_kreditor_id, @notes) + `; + queryParams = { + transaction_id, + banking_iban, + assigned_kreditor_id, + notes: notes || null + }; + } + + const result = await executeQuery(insertQuery, queryParams); res.status(201).json(result.recordset[0]); } catch (error) { @@ -82,7 +122,7 @@ router.put('/banking-transactions/:id', authenticateToken, async (req, res) => { try { const { executeQuery } = require('../../config/database'); const { id } = req.params; - const { assigned_kreditor_id, notes, assigned_by } = req.body; + const { assigned_kreditor_id, notes } = req.body; if (!assigned_kreditor_id) { return res.status(400).json({ error: 'Assigned kreditor ID is required' }); @@ -92,7 +132,6 @@ router.put('/banking-transactions/:id', authenticateToken, async (req, res) => { UPDATE fibdash.BankingAccountTransactions SET assigned_kreditor_id = @assigned_kreditor_id, notes = @notes, - assigned_by = @assigned_by, assigned_date = GETDATE() OUTPUT INSERTED.* WHERE id = @id @@ -101,7 +140,6 @@ router.put('/banking-transactions/:id', authenticateToken, async (req, res) => { const result = await executeQuery(updateQuery, { assigned_kreditor_id, notes: notes || null, - assigned_by: assigned_by || null, id: parseInt(id, 10) }); diff --git a/src/routes/data/transactions.js b/src/routes/data/transactions.js index 914bc32..e267c14 100644 --- a/src/routes/data/transactions.js +++ b/src/routes/data/transactions.js @@ -12,6 +12,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { const { executeQuery } = require('../../config/database'); const query = ` SELECT + csv.id as id, csv.buchungstag as 'Buchungstag', csv.wertstellung as 'Valutadatum', csv.umsatzart as 'Buchungstext', @@ -31,6 +32,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { k.is_banking as kreditor_is_banking, bat.assigned_kreditor_id, ak.name as assigned_kreditor_name, + ak.kreditorId as assigned_kreditor_kreditorId, 0 as isJTLOnly, 1 as isFromCSV, ub.textContent as jtl_document_data, @@ -49,6 +51,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { UNION ALL SELECT + jtl.kZahlungsabgleichUmsatz as id, FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Buchungstag', FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Valutadatum', 'JTL Transaction' as 'Buchungstext', @@ -68,6 +71,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { NULL as kreditor_is_banking, NULL as assigned_kreditor_id, NULL as assigned_kreditor_name, + NULL as assigned_kreditor_kreditorId, 1 as isJTLOnly, 0 as isFromCSV, ub.textContent as jtl_document_data, @@ -108,7 +112,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { ...transaction, parsedDate: new Date(transaction.parsed_date), hasJTL: Boolean(transaction.hasJTL), - isFromCSV: true, + isFromCSV: Boolean(transaction.isFromCSV), jtlDatabaseAvailable: true, hasKreditor: !!transaction.kreditor_name, kreditor: transaction.kreditor_name ? { @@ -116,9 +120,10 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => { kreditorId: transaction.kreditor_id, is_banking: Boolean(transaction.kreditor_is_banking) } : null, - assigned_kreditor: transaction.assigned_kreditor_name ? { + assignedKreditor: transaction.assigned_kreditor_name ? { name: transaction.assigned_kreditor_name, - id: transaction.assigned_kreditor_id + id: transaction.assigned_kreditor_id, + kreditorId: transaction.assigned_kreditor_kreditorId } : null, pdfs: transaction.jtl_document_data ? [{ content: transaction.jtl_document_data,