added new view in contracts customer and contracts

This commit is contained in:
2026-02-04 00:52:04 +01:00
parent 97b4670643
commit 2b23ed64c4
12 changed files with 1022 additions and 758 deletions
+10 -1
View File
@@ -5,7 +5,16 @@ import { ApiResponse, AuthRequest } from '../types/index.js';
export async function getContracts(req: AuthRequest, res: Response): Promise<void> {
try {
const { customerId, type, status, search, page, limit } = req.query;
const { customerId, type, status, search, page, limit, tree } = req.query;
// Baumstruktur für Kundenansicht
if (tree === 'true' && customerId) {
const treeData = await contractService.getContractTreeForCustomer(
parseInt(customerId as string)
);
res.json({ success: true, data: treeData } as ApiResponse);
return;
}
// Für Kundenportal-Benutzer: nur eigene + vertretene Kunden-Verträge anzeigen
let customerIds: number[] | undefined;
+94 -1
View File
@@ -77,7 +77,7 @@ export async function getAllContracts(filters: ContractFilters) {
where,
skip,
take,
orderBy: [{ startDate: 'desc' }, { createdAt: 'desc' }],
orderBy: [{ createdAt: 'desc' }],
include: {
customer: {
select: {
@@ -778,3 +778,96 @@ export async function getSipCredentials(phoneNumberId: number): Promise<{ passwo
return { password: null };
}
}
// ==================== VERTRAGSBAUM FÜR KUNDENANSICHT ====================
export interface ContractTreeNode {
contract: {
id: number;
contractNumber: string;
type: ContractType;
status: ContractStatus;
startDate: Date | null;
endDate: Date | null;
providerName: string | null;
tariffName: string | null;
previousContractId: number | null;
provider?: { id: number; name: string } | null;
tariff?: { id: number; name: string } | null;
contractCategory?: { id: number; name: string } | null;
};
predecessors: ContractTreeNode[];
hasHistory: boolean;
}
/**
* Verträge eines Kunden als Baumstruktur abrufen.
* Wurzelknoten = Verträge ohne Nachfolger (aktuellste Verträge)
* Vorgänger werden rekursiv eingebettet.
*/
export async function getContractTreeForCustomer(customerId: number): Promise<ContractTreeNode[]> {
// Alle Verträge des Kunden laden (außer DEACTIVATED)
const allContracts = await prisma.contract.findMany({
where: {
customerId,
status: { not: ContractStatus.DEACTIVATED },
},
select: {
id: true,
contractNumber: true,
type: true,
status: true,
startDate: true,
endDate: true,
providerName: true,
tariffName: true,
previousContractId: true,
provider: { select: { id: true, name: true } },
tariff: { select: { id: true, name: true } },
contractCategory: { select: { id: true, name: true } },
},
orderBy: [{ startDate: 'desc' }, { createdAt: 'desc' }],
});
// Map für schnellen Zugriff: contractId -> contract
const contractMap = new Map(allContracts.map(c => [c.id, c]));
// Set der IDs die als Vorgänger referenziert werden
const predecessorIds = new Set(
allContracts
.filter(c => c.previousContractId !== null)
.map(c => c.previousContractId!)
);
// Wurzelverträge = Verträge die keinen Nachfolger haben
// (werden von keinem anderen Vertrag als previousContractId referenziert)
const rootContracts = allContracts.filter(c => !predecessorIds.has(c.id));
// Rekursive Funktion um Vorgängerkette aufzubauen
function buildPredecessorChain(contractId: number | null): ContractTreeNode[] {
if (contractId === null) return [];
const contract = contractMap.get(contractId);
if (!contract) return [];
const predecessors = buildPredecessorChain(contract.previousContractId);
return [{
contract,
predecessors,
hasHistory: predecessors.length > 0,
}];
}
// Baumstruktur für jeden Wurzelvertrag aufbauen
const tree: ContractTreeNode[] = rootContracts.map(contract => {
const predecessors = buildPredecessorChain(contract.previousContractId);
return {
contract,
predecessors,
hasHistory: predecessors.length > 0,
};
});
return tree;
}