fix(accounting): tax report 500 on Postgres — drop SQL date() from cost queries
The #4 cost side used 'date(COALESCE(invoice_date, created_at)) BETWEEN ...' and 'date(created_at) BETWEEN ...'. The mocked unit tests never execute the SQL, so the Postgres failure (date()/COALESCE(date,timestamp)) slipped through and surfaced as a 500 on the live tax report. Replaced with plain range comparisons (col >= from AND col <= '<to> 23:59:59.999') — valid on both PG and SQLite, inclusive of the whole end day. Same fix applied to ledgerService buildPostings (the Treuhänder export would have 500'd identically).
This commit is contained in:
@@ -244,6 +244,9 @@ async function buildPostings({ from, to, currency } = {}) {
|
||||
if (!from || !to) throw httpError(400, '`from` and `to` are required (YYYY-MM-DD)', 'VALIDATION');
|
||||
if (!currency) throw httpError(400, '`currency` is required', 'VALIDATION');
|
||||
const cur = String(currency).toUpperCase();
|
||||
// Inclusive end-of-day bound; plain range comparison (no SQL date()) so it's
|
||||
// valid on both Postgres and SQLite.
|
||||
const toEnd = `${to} 23:59:59.999`;
|
||||
|
||||
return withRetry(async () => {
|
||||
const cfg = await getConfig();
|
||||
@@ -294,7 +297,7 @@ async function buildPostings({ from, to, currency } = {}) {
|
||||
.modify((q) => {
|
||||
if (hasCatCol) q.leftJoin('expense_categories', 'inbound_documents.category_id', 'expense_categories.id');
|
||||
})
|
||||
.whereRaw('date(COALESCE(inbound_documents.invoice_date, inbound_documents.created_at)) BETWEEN ? AND ?', [from, to])
|
||||
.whereRaw('COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) >= ? AND COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) <= ?', [from, toEnd])
|
||||
.where('inbound_documents.currency', cur)
|
||||
.whereNotIn('inbound_documents.status', ['declined', 'duplicate'])
|
||||
.orderByRaw('COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) asc')
|
||||
@@ -333,7 +336,7 @@ async function buildPostings({ from, to, currency } = {}) {
|
||||
.modify((q) => {
|
||||
if (hasCatCol) q.leftJoin('expense_categories', 'expenses.category_id', 'expense_categories.id');
|
||||
})
|
||||
.whereRaw('date(expenses.created_at) BETWEEN ? AND ?', [from, to])
|
||||
.whereRaw('expenses.created_at >= ? AND expenses.created_at <= ?', [from, toEnd])
|
||||
.whereNot('expenses.status', 'declined')
|
||||
.whereNotIn('expenses.disposition', ['duplikat', 'abgelehnt'])
|
||||
.modify((q) => { if (cur !== 'CHF') q.where('expenses.original_currency', cur); })
|
||||
|
||||
@@ -195,6 +195,11 @@ async function loadCosts({ from, to, cur }) {
|
||||
let totalNet = 0;
|
||||
let totalVat = 0;
|
||||
let totalGross = 0;
|
||||
// Inclusive upper bound covering the whole `to` day. Plain range comparison
|
||||
// (no SQL date() function) so it's valid on both Postgres and SQLite — the
|
||||
// mocked unit tests can't catch a PG-only function error. invoice_date is a
|
||||
// DATE, created_at a TIMESTAMP; both compare correctly against ISO literals.
|
||||
const toEnd = `${to} 23:59:59.999`;
|
||||
|
||||
const push = (r) => {
|
||||
rows.push(r);
|
||||
@@ -207,7 +212,7 @@ async function loadCosts({ from, to, cur }) {
|
||||
if (await db.schema.hasTable('inbound_documents')) {
|
||||
const inbound = await db('inbound_documents')
|
||||
.leftJoin('events', 'inbound_documents.event_id', 'events.id')
|
||||
.whereRaw('date(COALESCE(inbound_documents.invoice_date, inbound_documents.created_at)) BETWEEN ? AND ?', [from, to])
|
||||
.whereRaw('COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) >= ? AND COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) <= ?', [from, toEnd])
|
||||
.where('inbound_documents.currency', cur)
|
||||
.whereNotIn('inbound_documents.status', ['declined', 'duplicate'])
|
||||
.orderByRaw('COALESCE(inbound_documents.invoice_date, inbound_documents.created_at) asc')
|
||||
@@ -254,7 +259,7 @@ async function loadCosts({ from, to, cur }) {
|
||||
const isChf = cur === 'CHF';
|
||||
const q = db('expenses')
|
||||
.leftJoin('events', 'expenses.event_id', 'events.id')
|
||||
.whereRaw('date(expenses.created_at) BETWEEN ? AND ?', [from, to])
|
||||
.whereRaw('expenses.created_at >= ? AND expenses.created_at <= ?', [from, toEnd])
|
||||
.whereNot('expenses.status', 'declined')
|
||||
.whereNotIn('expenses.disposition', ['duplikat', 'abgelehnt']);
|
||||
// CHF report includes every expense (all carry a CHF base). A
|
||||
|
||||
Reference in New Issue
Block a user