Add daily expense tracking with sort/split and CSV export

Standalone expenses (date, description, category, amount) with the same
equal/own/payer split machinery as entry costs, merged into the costs
panel and settle-up as a single expense bucket. Self-fetching card
with per-day grouping, client-side sorting, and quick-add. CSV export
interleaves expenses with priced entries, one share column per member
(UTF-8 BOM, RFC 4180, formula-injection guard on text cells).

Also fixes trip deletion, which hit a foreign-key violation and rolled
back for any trip with checklist items.
This commit is contained in:
2026-08-06 17:55:04 +07:00
parent e342cd9a91
commit f272e74b84
19 changed files with 2123 additions and 6 deletions
+10 -1
View File
@@ -5,6 +5,15 @@
import { el } from '../dom.js';
import { typeInfo, formatMoney } from '../format.js';
// byType's "expense" key isn't an entry type (typeInfo only knows
// activity/stay/transport/flight/rental/note), so it'd otherwise fall back to
// a bare bullet + the raw key. Give it its own icon/label to match the
// Expenses card.
function byTypeInfo(type) {
if (type === 'expense') return { icon: '💸', label: 'Expenses', color: '#64748b' };
return typeInfo(type);
}
export function renderCosts(tctx) {
const costs = tctx.costs || {};
const currency = costs.currency || (tctx.trip.trip && tctx.trip.trip.currency) || 'USD';
@@ -72,7 +81,7 @@ export function renderCosts(tctx) {
if (typeKeys.length) {
const bt = el('div', { class: 'cost-block' }, el('h3', {}, 'By type'));
for (const type of typeKeys) {
const info = typeInfo(type);
const info = byTypeInfo(type);
bt.appendChild(
el(
'div',