PDF-Export: kompakte Tabelle, Filter nach letztem Status-Datum

Verbose Blöcke (Notizen + kompletter Verlauf) durch eine autoTable ersetzt:
eine Zeile pro Bewerbung mit Datum, Firma, Stelle, Art und letztem Status
(farbiges Badge). Export filtert/datiert nun nach dem effektiven Datum, also
der letzten Statusaenderung – eine im Juni gesendete, im Juli zum Gespraech
gewordene Bewerbung erscheint dadurch im Juli-Export. Modal-Auswahl nutzt
dieselben effektiven Monate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 14:12:24 +02:00
co-authored by Claude Opus 4.8
parent b269dcaecc
commit 78b1ea78d0
3 changed files with 115 additions and 121 deletions
+37 -7
View File
@@ -917,12 +917,29 @@ initializeDatabase().then(() => {
// Get available months/years for filter
const availableMonths = await dbAll(`
SELECT DISTINCT strftime("%Y-%m", datum) as yearmonth,
strftime("%m", datum) as month,
SELECT DISTINCT strftime("%Y-%m", datum) as yearmonth,
strftime("%m", datum) as month,
strftime("%Y", datum) as year
FROM bewerbungen ORDER BY datum DESC
`);
// Months/years for the PDF export, keyed by the effective date (last status
// change) so a period like Juli 2026 is selectable even when the underlying
// application was created in an earlier month.
const exportMonths = await dbAll(`
SELECT DISTINCT strftime("%Y-%m", eff) as yearmonth,
strftime("%m", eff) as month,
strftime("%Y", eff) as year
FROM (
SELECT COALESCE(
(SELECT MAX(date(sv.datum)) FROM status_verlauf sv WHERE sv.bewerbung_id = b.id),
date(b.datum)
) AS eff
FROM bewerbungen b
)
ORDER BY yearmonth DESC
`);
res.render('index', {
applications,
settings,
@@ -932,6 +949,7 @@ initializeDatabase().then(() => {
byStatus
},
availableMonths,
exportMonths,
currentFilter: { month, year },
kommendeTermine,
caldavTz: caldav.TZ,
@@ -1168,19 +1186,31 @@ initializeDatabase().then(() => {
try {
const { month, year } = req.query;
let query = 'SELECT * FROM bewerbungen ORDER BY datum DESC';
// Effektives Datum einer Bewerbung = Datum ihrer letzten Statusänderung
// (fällt auf das Bewerbungsdatum zurück, wenn es keinen Verlauf gibt). Der
// Monatsexport listet eine Bewerbung im Monat ihres LETZTEN Status: Eine im
// Juni gesendete Bewerbung, die im Juli zum Vorstellungsgespräch wird,
// erscheint dadurch im Export für Juli.
const base = `
SELECT b.*, COALESCE(
(SELECT MAX(date(sv.datum)) FROM status_verlauf sv WHERE sv.bewerbung_id = b.id),
date(b.datum)
) AS eff_datum
FROM bewerbungen b
`;
let query = `SELECT * FROM (${base}) ORDER BY eff_datum DESC`;
const params = [];
if (month && year) {
query = 'SELECT * FROM bewerbungen WHERE strftime("%m", datum) = ? AND strftime("%Y", datum) = ? ORDER BY datum DESC';
query = `SELECT * FROM (${base}) WHERE strftime("%m", eff_datum) = ? AND strftime("%Y", eff_datum) = ? ORDER BY eff_datum DESC`;
params.push(month.padStart(2, '0'), year);
} else if (month) {
// A month without a year must still restrict the export to that month —
// never fall through to exporting every application.
query = 'SELECT * FROM bewerbungen WHERE strftime("%m", datum) = ? ORDER BY datum DESC';
query = `SELECT * FROM (${base}) WHERE strftime("%m", eff_datum) = ? ORDER BY eff_datum DESC`;
params.push(month.padStart(2, '0'));
} else if (year) {
query = 'SELECT * FROM bewerbungen WHERE strftime("%Y", datum) = ? ORDER BY datum DESC';
query = `SELECT * FROM (${base}) WHERE strftime("%Y", eff_datum) = ? ORDER BY eff_datum DESC`;
params.push(year);
}