added invoices and status in cockpit, created info button for contract status types
This commit is contained in:
+40
@@ -242,6 +242,16 @@ export declare function getContractById(id: number, decryptPassword?: boolean):
|
||||
meterNumber: string;
|
||||
location: string | null;
|
||||
}) | null;
|
||||
invoices: {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
notes: string | null;
|
||||
documentPath: string | null;
|
||||
energyContractDetailsId: number;
|
||||
invoiceDate: Date;
|
||||
invoiceType: import(".prisma/client").$Enums.InvoiceType;
|
||||
}[];
|
||||
} & {
|
||||
id: number;
|
||||
meterId: number | null;
|
||||
@@ -421,6 +431,16 @@ export declare function getContractById(id: number, decryptPassword?: boolean):
|
||||
meterNumber: string;
|
||||
location: string | null;
|
||||
}) | null;
|
||||
invoices: {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
notes: string | null;
|
||||
documentPath: string | null;
|
||||
energyContractDetailsId: number;
|
||||
invoiceDate: Date;
|
||||
invoiceType: import(".prisma/client").$Enums.InvoiceType;
|
||||
}[];
|
||||
} & {
|
||||
id: number;
|
||||
meterId: number | null;
|
||||
@@ -974,6 +994,16 @@ export declare function updateContract(id: number, data: Partial<ContractCreateD
|
||||
meterNumber: string;
|
||||
location: string | null;
|
||||
}) | null;
|
||||
invoices: {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
notes: string | null;
|
||||
documentPath: string | null;
|
||||
energyContractDetailsId: number;
|
||||
invoiceDate: Date;
|
||||
invoiceType: import(".prisma/client").$Enums.InvoiceType;
|
||||
}[];
|
||||
} & {
|
||||
id: number;
|
||||
meterId: number | null;
|
||||
@@ -1153,6 +1183,16 @@ export declare function updateContract(id: number, data: Partial<ContractCreateD
|
||||
meterNumber: string;
|
||||
location: string | null;
|
||||
}) | null;
|
||||
invoices: {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
notes: string | null;
|
||||
documentPath: string | null;
|
||||
energyContractDetailsId: number;
|
||||
invoiceDate: Date;
|
||||
invoiceType: import(".prisma/client").$Enums.InvoiceType;
|
||||
}[];
|
||||
} & {
|
||||
id: number;
|
||||
meterId: number | null;
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -121,14 +121,14 @@ async function getContractById(id, decryptPassword = false) {
|
||||
contractCategory: true,
|
||||
previousContract: {
|
||||
include: {
|
||||
energyDetails: { include: { meter: { include: { readings: true } } } },
|
||||
energyDetails: { include: { meter: { include: { readings: true } }, invoices: true } },
|
||||
internetDetails: { include: { phoneNumbers: true } },
|
||||
mobileDetails: { include: { simCards: true } },
|
||||
tvDetails: true,
|
||||
carInsuranceDetails: true,
|
||||
},
|
||||
},
|
||||
energyDetails: { include: { meter: { include: { readings: true } } } },
|
||||
energyDetails: { include: { meter: { include: { readings: true } }, invoices: true } },
|
||||
internetDetails: { include: { phoneNumbers: true } },
|
||||
mobileDetails: { include: { simCards: true } },
|
||||
tvDetails: true,
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -40,6 +40,7 @@ export interface CockpitSummary {
|
||||
contractEnding: number;
|
||||
missingCredentials: number;
|
||||
missingData: number;
|
||||
missingInvoices: number;
|
||||
openTasks: number;
|
||||
pendingContracts: number;
|
||||
};
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"contractCockpit.service.d.ts","sourceRoot":"","sources":["../../src/services/contractCockpit.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EACpC,MAAM,gBAAgB,CAAC;AAMxB,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,MAAM,CAAC,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,cAAc,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,qBAAqB,EAAE,MAAM,CAAC;QAC9B,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAyED,wBAAsB,cAAc,IAAI,OAAO,CAAC,aAAa,CAAC,CA8V7D"}
|
||||
{"version":3,"file":"contractCockpit.service.d.ts","sourceRoot":"","sources":["../../src/services/contractCockpit.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EACpC,MAAM,gBAAgB,CAAC;AAMxB,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,MAAM,CAAC,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,cAAc,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,qBAAqB,EAAE,MAAM,CAAC;QAC9B,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAyED,wBAAsB,cAAc,IAAI,OAAO,CAAC,aAAa,CAAC,CAqa7D"}
|
||||
+66
-2
@@ -105,11 +105,11 @@ async function getCockpitData() {
|
||||
const criticalDays = parseInt(settings.deadlineCriticalDays) || 14;
|
||||
const warningDays = parseInt(settings.deadlineWarningDays) || 42;
|
||||
const okDays = parseInt(settings.deadlineOkDays) || 90;
|
||||
// Lade alle aktiven/pending Verträge mit allen relevanten Daten
|
||||
// Lade alle relevanten Verträge (inkl. CANCELLED/DEACTIVATED für Schlussrechnung-Check)
|
||||
const contracts = await prisma.contract.findMany({
|
||||
where: {
|
||||
status: {
|
||||
in: ['ACTIVE', 'PENDING', 'DRAFT'],
|
||||
in: ['ACTIVE', 'PENDING', 'DRAFT', 'CANCELLED', 'DEACTIVATED', 'EXPIRED'],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
@@ -145,6 +145,7 @@ async function getCockpitData() {
|
||||
energyDetails: {
|
||||
include: {
|
||||
meter: true,
|
||||
invoices: true,
|
||||
},
|
||||
},
|
||||
internetDetails: {
|
||||
@@ -186,6 +187,7 @@ async function getCockpitData() {
|
||||
contractEnding: 0,
|
||||
missingCredentials: 0,
|
||||
missingData: 0,
|
||||
missingInvoices: 0,
|
||||
openTasks: 0,
|
||||
pendingContracts: 0,
|
||||
},
|
||||
@@ -367,6 +369,68 @@ async function getCockpitData() {
|
||||
});
|
||||
summary.byCategory.pendingContracts++;
|
||||
}
|
||||
// 13. ENERGIE-RECHNUNGEN (nur für ELECTRICITY und GAS)
|
||||
if (['ELECTRICITY', 'GAS'].includes(contract.type) && contract.energyDetails) {
|
||||
const invoices = contract.energyDetails.invoices || [];
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
// 13a. SCHLUSSRECHNUNG FEHLT (nur wenn Vertrag gekündigt/deaktiviert ist)
|
||||
// "Beendet" = CANCELLED oder DEACTIVATED (nicht nur Laufzeit abgelaufen!)
|
||||
const isContractTerminated = contract.status === 'CANCELLED' || contract.status === 'DEACTIVATED';
|
||||
if (isContractTerminated) {
|
||||
const hasFinalInvoice = invoices.some(inv => inv.invoiceType === 'FINAL');
|
||||
const hasNotAvailable = invoices.some(inv => inv.invoiceType === 'NOT_AVAILABLE');
|
||||
if (!hasFinalInvoice && !hasNotAvailable) {
|
||||
issues.push({
|
||||
type: 'missing_final_invoice',
|
||||
label: 'Schlussrechnung fehlt',
|
||||
urgency: 'warning',
|
||||
details: 'Vertrag gekündigt/deaktiviert, aber keine Schlussrechnung vorhanden',
|
||||
});
|
||||
summary.byCategory.missingInvoices++;
|
||||
}
|
||||
}
|
||||
// 13b. ZWISCHENRECHNUNG FEHLT/ÜBERFÄLLIG (wenn Vertrag > 12 Monate läuft)
|
||||
// Für alle Status außer DRAFT und nicht gekündigt/deaktiviert
|
||||
// Auch EXPIRED zählt hier, da der Vertrag ohne Kündigung weiterläuft!
|
||||
if (contract.startDate && contract.status !== 'DRAFT' && !isContractTerminated) {
|
||||
const startDate = new Date(contract.startDate);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
const daysSinceStart = Math.floor((now.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||
if (daysSinceStart > 365) {
|
||||
// Vertrag läuft > 12 Monate
|
||||
if (invoices.length === 0) {
|
||||
// Keine Rechnungen vorhanden
|
||||
issues.push({
|
||||
type: 'missing_interim_invoice',
|
||||
label: 'Zwischenrechnung fehlt',
|
||||
urgency: 'warning',
|
||||
details: 'Vertrag läuft über 12 Monate ohne Rechnung',
|
||||
});
|
||||
summary.byCategory.missingInvoices++;
|
||||
}
|
||||
else {
|
||||
// Prüfen ob letzte Rechnung > 12 Monate alt
|
||||
const latestInvoice = invoices
|
||||
.filter(inv => inv.invoiceType !== 'NOT_AVAILABLE')
|
||||
.sort((a, b) => new Date(b.invoiceDate).getTime() - new Date(a.invoiceDate).getTime())[0];
|
||||
if (latestInvoice) {
|
||||
const invoiceDate = new Date(latestInvoice.invoiceDate);
|
||||
const daysSinceInvoice = Math.floor((now.getTime() - invoiceDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||
if (daysSinceInvoice > 365) {
|
||||
issues.push({
|
||||
type: 'overdue_interim_invoice',
|
||||
label: 'Zwischenrechnung überfällig',
|
||||
urgency: 'warning',
|
||||
details: `Letzte Rechnung vor ${Math.floor(daysSinceInvoice / 30)} Monaten`,
|
||||
});
|
||||
summary.byCategory.missingInvoices++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nur Verträge mit Issues hinzufügen
|
||||
if (issues.length > 0) {
|
||||
const highestUrgency = getHighestUrgency(issues);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user