This commit is contained in:
2026-05-06 14:20:32 -05:00
parent e69c423b0c
commit 55d9d803e0

View File

@@ -135,21 +135,43 @@ async function getRegister({ accountId, startDate, endDate }) {
}
/**
* Normalisiert die QBO TransactionList Report Antwort in eine flache
* Liste mit { date, type, docNum, payee, account, memo, amount, qboId }.
*
* Der Report liefert Columns dynamisch — wir bauen eine Index-Map und
* lesen die Zellen darüber aus.
* Normalisiert die QBO TransactionList Report Antwort in eine flache Liste.
* Wir mappen über ColTitle (immer vorhanden), nicht über ColType (manchmal leer).
*/
function normalizeTransactionListReport(report) {
const columns = (report.Columns && report.Columns.Column) || [];
// Map: ColTitle (lowercase) → Index
const colIndex = {};
columns.forEach((c, i) => {
// ColType ist z.B. "tx_date", "txn_type", "doc_num", "name", "account_name",
// "memo", "subt_nat_amount", "split_acc"
if (c.ColType) colIndex[c.ColType] = i;
if (c.ColTitle) colIndex[c.ColTitle.toLowerCase()] = i;
if (c.ColType) colIndex[c.ColType.toLowerCase()] = i; // Fallback, falls vorhanden
});
// Resolver: probiert mehrere mögliche Titel/Typen
const resolve = (...candidates) => {
for (const k of candidates) {
const idx = colIndex[k.toLowerCase()];
if (idx != null) return idx;
}
return null;
};
const idxDate = resolve('Date', 'tx_date');
const idxType = resolve('Transaction Type', 'Type', 'txn_type');
const idxDocNum = resolve('Num', 'No.', 'doc_num');
const idxPayee = resolve('Name', 'Payee', 'name');
const idxAccount = resolve('Account', 'account_name');
const idxMemo = resolve('Memo/Description', 'Memo', 'memo');
const idxSplit = resolve('Split', 'split_acc');
const idxAmount = resolve('Amount', 'subt_nat_amount', 'subt_nat_home_amount');
const cellAt = (colData, idx) => {
if (idx == null) return null;
const c = colData[idx];
return c || null;
};
const rows = [];
function walk(rowGroup) {
@@ -160,25 +182,17 @@ function normalizeTransactionListReport(report) {
walk(r.Rows && r.Rows.Row);
continue;
}
// Data row
if (!r.ColData) continue;
const cell = (key) => {
const idx = colIndex[key];
if (idx == null) return null;
const c = r.ColData[idx];
return c ? c : null;
};
const dateCell = cell('tx_date');
const typeCell = cell('txn_type');
const docCell = cell('doc_num');
const payeeCell = cell('name');
const acctCell = cell('account_name');
const memoCell = cell('memo');
const amtCell = cell('subt_nat_amount');
const splitCell = cell('split_acc');
// QBO setzt die qbo Txn Id meistens als value im Date-Cell oder DocNum-Cell.
// Wir greifen sicherheitshalber an mehreren Stellen.
const dateCell = cellAt(r.ColData, idxDate);
const typeCell = cellAt(r.ColData, idxType);
const docCell = cellAt(r.ColData, idxDocNum);
const payeeCell = cellAt(r.ColData, idxPayee);
const acctCell = cellAt(r.ColData, idxAccount);
const memoCell = cellAt(r.ColData, idxMemo);
const splitCell = cellAt(r.ColData, idxSplit);
const amtCell = cellAt(r.ColData, idxAmount);
const qboId =
(dateCell && dateCell.id) ||
(docCell && docCell.id) ||
@@ -192,7 +206,8 @@ function normalizeTransactionListReport(report) {
payee: payeeCell ? payeeCell.value : null,
account: acctCell ? acctCell.value : null,
memo: memoCell ? memoCell.value : null,
amount: amtCell && amtCell.value !== '' ? Number(amtCell.value) : null,
amount: amtCell && amtCell.value !== '' && amtCell.value != null
? Number(amtCell.value) : null,
splitAccount: splitCell ? splitCell.value : null,
qboId
});
@@ -209,7 +224,7 @@ function normalizeTransactionListReport(report) {
currency: report.Header && report.Header.Currency,
time: report.Header && report.Header.Time
},
columns: columns.map(c => ({ title: c.ColTitle, type: c.ColType })),
columns: columns.map(c => ({ title: c.ColTitle, type: c.ColType || c.type })),
rows
};
}