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.

This commit is contained in:
sebseb7
2025-08-02 10:07:36 +02:00
parent 20cd0b34bc
commit 096d4d0530
4 changed files with 111 additions and 50 deletions

View File

@@ -225,7 +225,6 @@ class BankingKreditorSelector extends Component {
method: 'PUT', method: 'PUT',
body: JSON.stringify({ body: JSON.stringify({
assigned_kreditor_id: parseInt(selectedKreditorId), 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, csv_transaction_id: transaction.isFromCSV ? transaction.id : null,
banking_iban: transaction['Kontonummer/IBAN'] || transaction.kontonummer_iban, banking_iban: transaction['Kontonummer/IBAN'] || transaction.kontonummer_iban,
assigned_kreditor_id: parseInt(selectedKreditorId), assigned_kreditor_id: parseInt(selectedKreditorId),
assigned_by: user?.username || 'Unknown',
}) })
} }
); );
@@ -257,7 +255,11 @@ class BankingKreditorSelector extends Component {
if (response && response.ok) { if (response && response.ok) {
this.setState({ saving: false }); this.setState({ saving: false });
if (onSave) { 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 { } else {
const errorData = await response.json(); const errorData = await response.json();

View File

@@ -386,13 +386,13 @@ const DocumentRenderer = (params) => {
sx={{ sx={{
color: !params.data['Kontonummer/IBAN'] color: !params.data['Kontonummer/IBAN']
? 'text.secondary' ? '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' ? 'success.main'
: 'warning.main', : 'warning.main',
'&.Mui-selected': { '&.Mui-selected': {
color: !params.data['Kontonummer/IBAN'] color: !params.data['Kontonummer/IBAN']
? 'text.secondary' ? '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' ? 'success.main'
: 'warning.main', : 'warning.main',
} }
@@ -543,14 +543,22 @@ const DocumentRenderer = (params) => {
<BankingKreditorSelector <BankingKreditorSelector
transaction={params.data} transaction={params.data}
user={params.context?.user} user={params.context?.user}
onSave={() => { onSave={async (assignedKreditor) => {
// Refresh the grid to show updated assignment // 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) { if (params.api) {
params.api.refreshCells({ params.api.refreshCells({
columns: ['Kontonummer/IBAN'], rowNodes: [params.node],
force: true force: true
}); });
} }
// Force dialog re-render by updating state
this.forceUpdate();
}} }}
/> />
</Box> </Box>
@@ -620,14 +628,22 @@ const DocumentRenderer = (params) => {
<BankingKreditorSelector <BankingKreditorSelector
transaction={params.data} transaction={params.data}
user={params.context?.user} user={params.context?.user}
onSave={() => { onSave={async (assignedKreditor) => {
// Refresh the grid to show updated assignment // 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) { if (params.api) {
params.api.refreshCells({ params.api.refreshCells({
columns: ['Kontonummer/IBAN'], rowNodes: [params.node],
force: true force: true
}); });
} }
// Force dialog re-render by updating state
this.forceUpdate();
}} }}
/> />
</Box> </Box>

View File

@@ -9,17 +9,37 @@ router.get('/banking-transactions/:transactionId', authenticateToken, async (req
const { executeQuery } = require('../../config/database'); const { executeQuery } = require('../../config/database');
const { transactionId } = req.params; const { transactionId } = req.params;
const query = ` // Try both numeric and string format
SELECT let query, params;
bat.*, const numericId = parseInt(transactionId, 10);
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
`;
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); res.json(result.recordset);
} catch (error) { } catch (error) {
@@ -32,7 +52,7 @@ router.get('/banking-transactions/:transactionId', authenticateToken, async (req
router.post('/banking-transactions', authenticateToken, async (req, res) => { router.post('/banking-transactions', authenticateToken, async (req, res) => {
try { try {
const { executeQuery } = require('../../config/database'); 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) { if ((!transaction_id && !csv_transaction_id) || !banking_iban || !assigned_kreditor_id) {
return res.status(400).json({ return res.status(400).json({
@@ -40,35 +60,55 @@ router.post('/banking-transactions', authenticateToken, async (req, res) => {
}); });
} }
const checkQuery = ` let checkQuery, checkParams;
SELECT id FROM fibdash.BankingAccountTransactions
WHERE transaction_id = @transaction_id OR csv_transaction_id = @csv_transaction_id
`;
const checkResult = await executeQuery(checkQuery, { if (csv_transaction_id) {
transaction_id: transaction_id || null, checkQuery = `SELECT id FROM fibdash.BankingAccountTransactions WHERE csv_transaction_id = @csv_transaction_id`;
csv_transaction_id: csv_transaction_id || null 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) { if (checkResult.recordset.length > 0) {
return res.status(409).json({ error: 'Banking transaction assignment already exists' }); return res.status(409).json({ error: 'Banking transaction assignment already exists' });
} }
const insertQuery = ` let insertQuery, queryParams;
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)
`;
const result = await executeQuery(insertQuery, { if (csv_transaction_id) {
transaction_id: transaction_id || null, // For CSV transactions, use a placeholder transaction_id since it's NOT NULL
csv_transaction_id: csv_transaction_id || null, insertQuery = `
banking_iban, INSERT INTO fibdash.BankingAccountTransactions
assigned_kreditor_id, (transaction_id, csv_transaction_id, banking_iban, assigned_kreditor_id, notes)
notes: notes || null, OUTPUT INSERTED.*
assigned_by: assigned_by || null 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]); res.status(201).json(result.recordset[0]);
} catch (error) { } catch (error) {
@@ -82,7 +122,7 @@ router.put('/banking-transactions/:id', authenticateToken, async (req, res) => {
try { try {
const { executeQuery } = require('../../config/database'); const { executeQuery } = require('../../config/database');
const { id } = req.params; const { id } = req.params;
const { assigned_kreditor_id, notes, assigned_by } = req.body; const { assigned_kreditor_id, notes } = req.body;
if (!assigned_kreditor_id) { if (!assigned_kreditor_id) {
return res.status(400).json({ error: 'Assigned kreditor ID is required' }); 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 UPDATE fibdash.BankingAccountTransactions
SET assigned_kreditor_id = @assigned_kreditor_id, SET assigned_kreditor_id = @assigned_kreditor_id,
notes = @notes, notes = @notes,
assigned_by = @assigned_by,
assigned_date = GETDATE() assigned_date = GETDATE()
OUTPUT INSERTED.* OUTPUT INSERTED.*
WHERE id = @id WHERE id = @id
@@ -101,7 +140,6 @@ router.put('/banking-transactions/:id', authenticateToken, async (req, res) => {
const result = await executeQuery(updateQuery, { const result = await executeQuery(updateQuery, {
assigned_kreditor_id, assigned_kreditor_id,
notes: notes || null, notes: notes || null,
assigned_by: assigned_by || null,
id: parseInt(id, 10) id: parseInt(id, 10)
}); });

View File

@@ -12,6 +12,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
const { executeQuery } = require('../../config/database'); const { executeQuery } = require('../../config/database');
const query = ` const query = `
SELECT SELECT
csv.id as id,
csv.buchungstag as 'Buchungstag', csv.buchungstag as 'Buchungstag',
csv.wertstellung as 'Valutadatum', csv.wertstellung as 'Valutadatum',
csv.umsatzart as 'Buchungstext', csv.umsatzart as 'Buchungstext',
@@ -31,6 +32,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
k.is_banking as kreditor_is_banking, k.is_banking as kreditor_is_banking,
bat.assigned_kreditor_id, bat.assigned_kreditor_id,
ak.name as assigned_kreditor_name, ak.name as assigned_kreditor_name,
ak.kreditorId as assigned_kreditor_kreditorId,
0 as isJTLOnly, 0 as isJTLOnly,
1 as isFromCSV, 1 as isFromCSV,
ub.textContent as jtl_document_data, ub.textContent as jtl_document_data,
@@ -49,6 +51,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
UNION ALL UNION ALL
SELECT SELECT
jtl.kZahlungsabgleichUmsatz as id,
FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Buchungstag', FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Buchungstag',
FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Valutadatum', FORMAT(jtl.dBuchungsdatum, 'dd.MM.yy') as 'Valutadatum',
'JTL Transaction' as 'Buchungstext', 'JTL Transaction' as 'Buchungstext',
@@ -68,6 +71,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
NULL as kreditor_is_banking, NULL as kreditor_is_banking,
NULL as assigned_kreditor_id, NULL as assigned_kreditor_id,
NULL as assigned_kreditor_name, NULL as assigned_kreditor_name,
NULL as assigned_kreditor_kreditorId,
1 as isJTLOnly, 1 as isJTLOnly,
0 as isFromCSV, 0 as isFromCSV,
ub.textContent as jtl_document_data, ub.textContent as jtl_document_data,
@@ -108,7 +112,7 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
...transaction, ...transaction,
parsedDate: new Date(transaction.parsed_date), parsedDate: new Date(transaction.parsed_date),
hasJTL: Boolean(transaction.hasJTL), hasJTL: Boolean(transaction.hasJTL),
isFromCSV: true, isFromCSV: Boolean(transaction.isFromCSV),
jtlDatabaseAvailable: true, jtlDatabaseAvailable: true,
hasKreditor: !!transaction.kreditor_name, hasKreditor: !!transaction.kreditor_name,
kreditor: transaction.kreditor_name ? { kreditor: transaction.kreditor_name ? {
@@ -116,9 +120,10 @@ router.get('/transactions/:timeRange', authenticateToken, async (req, res) => {
kreditorId: transaction.kreditor_id, kreditorId: transaction.kreditor_id,
is_banking: Boolean(transaction.kreditor_is_banking) is_banking: Boolean(transaction.kreditor_is_banking)
} : null, } : null,
assigned_kreditor: transaction.assigned_kreditor_name ? { assignedKreditor: transaction.assigned_kreditor_name ? {
name: 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, } : null,
pdfs: transaction.jtl_document_data ? [{ pdfs: transaction.jtl_document_data ? [{
content: transaction.jtl_document_data, content: transaction.jtl_document_data,