9f8511114a
The cost side is supplementary — it must never 500 the core revenue report. getTaxReport now wraps loadCosts in try/catch: on failure it returns empty costs + a costsError string and logs the real error. The tax page shows the revenue report plus a non-fatal amber banner with the cost-side error message, so the actual cause is visible in the UI instead of an opaque 500.