Finanzen: Offen zeigt auch Ueberfaellige, Rechnungsliste laedt nach Zuordnen neu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1feeb18c08
commit
24a3cc41ef
6 files changed
+149
-29
No files matched your search
+54
-9
@@ -406,16 +406,53 @@ export function aboView(abos) {
|
||||
|
||||
// --- Rechnungen (Block 6) ----------------------------------------------------------------------------------
|
||||
|
||||
/** Filter über der Rechnungsliste (Schlüssel = `status` der API); Standard ist „Offen“. */
|
||||
/**
|
||||
* Filter über der Rechnungsliste. `statuses` sind die API-Status, die der Filter abruft: „Offen“ zeigt offene UND
|
||||
* überfällige Rechnungen (zwei Abrufe, zusammengeführt), alle anderen genau ihren Status. Standard ist „Offen“.
|
||||
*/
|
||||
export const INVOICE_FILTERS = [
|
||||
{ key: 'offen', label: 'Offen' },
|
||||
{ key: 'ueberfaellig', label: 'Überfällig' },
|
||||
{ key: 'bezahlt', label: 'Bezahlt' },
|
||||
{ key: 'entwurf', label: 'Entwürfe' },
|
||||
{ key: 'alle', label: 'Alle' },
|
||||
{ key: 'offen', label: 'Offen', statuses: ['offen', 'ueberfaellig'] },
|
||||
{ key: 'ueberfaellig', label: 'Überfällig', statuses: ['ueberfaellig'] },
|
||||
{ key: 'bezahlt', label: 'Bezahlt', statuses: ['bezahlt'] },
|
||||
{ key: 'entwurf', label: 'Entwürfe', statuses: ['entwurf'] },
|
||||
{ key: 'alle', label: 'Alle', statuses: ['alle'] },
|
||||
]
|
||||
export const DEFAULT_INVOICE_FILTER = 'offen'
|
||||
|
||||
/** API-Status, die ein Filter abruft (unbekannter Filter = alle). */
|
||||
export function invoiceFilterStatuses(filterKey) {
|
||||
const filter = INVOICE_FILTERS.find((f) => f.key === filterKey)
|
||||
return filter ? [...filter.statuses] : ['alle']
|
||||
}
|
||||
|
||||
/**
|
||||
* Mehrere Antwortlisten (je Status eine) zu einer Liste: neueste zuerst nach Rechnungsdatum, gleiche Tage behalten
|
||||
* die Reihenfolge der Listen; eine Rechnung, die in mehreren vorkommt, nur einmal.
|
||||
*/
|
||||
export function mergeInvoiceLists(lists) {
|
||||
const seen = new Set()
|
||||
const all = []
|
||||
for (const list of Array.isArray(lists) ? lists : []) {
|
||||
for (const inv of Array.isArray(list) ? list : []) {
|
||||
if (!inv || typeof inv !== 'object') continue
|
||||
if (inv.id) {
|
||||
if (seen.has(inv.id)) continue
|
||||
seen.add(inv.id)
|
||||
}
|
||||
all.push(inv)
|
||||
}
|
||||
}
|
||||
return all
|
||||
.map((inv, index) => ({ inv, index }))
|
||||
.sort((a, b) => {
|
||||
const da = text(a.inv.createdDate)
|
||||
const db = text(b.inv.createdDate)
|
||||
if (da === db) return a.index - b.index
|
||||
return da < db ? 1 : -1
|
||||
})
|
||||
.map((x) => x.inv)
|
||||
}
|
||||
|
||||
const obj = (v) => (v && typeof v === 'object' ? v : {})
|
||||
const objects = (list) => (Array.isArray(list) ? list.filter((x) => x && typeof x === 'object') : [])
|
||||
const OPEN_INVOICE = new Set(['offen', 'ueberfaellig'])
|
||||
@@ -790,6 +827,7 @@ export function transactionRows(buchungen) {
|
||||
|
||||
// --- Zuordnen (Dialog) -------------------------------------------------------------------------------------
|
||||
|
||||
const NOT_OPEN_ANYMORE = 'Diese Rechnung ist nicht mehr offen.'
|
||||
export const PENDING_ASSIGN_HINT = 'Vorgemerkte Buchungen können erst nach der Buchung einer Rechnung zugeordnet werden.'
|
||||
|
||||
/** Bestätigungstext vor dem Bezahlt-Markieren (nicht umkehrbar). */
|
||||
@@ -809,8 +847,10 @@ const fixedCostLabel = (f) => `${text(f.name) || 'Fixkosten'} · ${euro(f.amount
|
||||
* - Eingang: Vorschläge des Servers, andere offene Rechnungen, Kategorien (auch „Einnahme ohne Stripe-Rechnung“)
|
||||
* - Ausgang: Fixkosten (aktive), Privat/Umbuchung, Sonstiges
|
||||
* Rechnungen, deren offener Betrag vom Betrag der Buchung abweicht, sind nicht wählbar (Teilzahlungen in Stripe).
|
||||
* invoicesLoaded = die offenen Rechnungen wurden frisch und ohne Fehler geladen: ein Vorschlag, dessen Rechnung nicht
|
||||
* (mehr) darin steht, ist dann nicht wählbar („Diese Rechnung ist nicht mehr offen.“).
|
||||
*/
|
||||
export function assignModel(tx, { invoices = [], fixedCosts = [] } = {}) {
|
||||
export function assignModel(tx, { invoices = [], fixedCosts = [], invoicesLoaded = false } = {}) {
|
||||
const t = obj(tx)
|
||||
const amount = t.amountCents
|
||||
const incoming = isNum(amount) && amount > 0
|
||||
@@ -868,6 +908,7 @@ export function assignModel(tx, { invoices = [], fixedCosts = [] } = {}) {
|
||||
} else if (s.kind === 'invoice' && text(s.id) && !seen.has(s.id)) {
|
||||
seen.add(s.id)
|
||||
const known = open.find((inv) => inv.id === s.id)
|
||||
const gone = invoicesLoaded && !known
|
||||
const differs = s.amountDiffers === true || (known ? known.remainingCents !== amount : false)
|
||||
const number = text(s.number) || text(known?.number)
|
||||
model.suggestions.push({
|
||||
@@ -876,8 +917,12 @@ export function assignModel(tx, { invoices = [], fixedCosts = [] } = {}) {
|
||||
label: `Rechnung ${number || s.id}${known ? ` · ${text(known.customerName) || text(known.customerEmail)}` : ''}`,
|
||||
detail: [text(s.reason), known ? `${euro(known.remainingCents)} offen` : ''].filter(Boolean).join(' · '),
|
||||
confidence: s.confidence,
|
||||
disabled: differs,
|
||||
disabledReason: differs ? 'Betrag weicht ab, Teilzahlungen bitte direkt in Stripe erfassen' : '',
|
||||
disabled: gone || differs,
|
||||
disabledReason: gone
|
||||
? NOT_OPEN_ANYMORE
|
||||
: differs
|
||||
? 'Betrag weicht ab, Teilzahlungen bitte direkt in Stripe erfassen'
|
||||
: '',
|
||||
number,
|
||||
body: { invoiceId: s.id },
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user