first commit

This commit is contained in:
Stefan Hacker
2026-01-29 01:16:54 +01:00
commit 32a7dc74e1
12111 changed files with 2480685 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Adding contract categories...');
// Create contract categories (matching existing enum values)
const contractCategories = [
{ code: 'ELECTRICITY', name: 'Strom', icon: 'Zap', color: '#FFC107', sortOrder: 1 },
{ code: 'GAS', name: 'Gas', icon: 'Flame', color: '#FF5722', sortOrder: 2 },
{ code: 'DSL', name: 'DSL', icon: 'Wifi', color: '#2196F3', sortOrder: 3 },
{ code: 'FIBER', name: 'Glasfaser', icon: 'Cable', color: '#9C27B0', sortOrder: 4 },
{ code: 'MOBILE', name: 'Mobilfunk', icon: 'Smartphone', color: '#4CAF50', sortOrder: 5 },
{ code: 'TV', name: 'TV', icon: 'Tv', color: '#E91E63', sortOrder: 6 },
{ code: 'CAR_INSURANCE', name: 'KFZ-Versicherung', icon: 'Car', color: '#607D8B', sortOrder: 7 },
];
for (const category of contractCategories) {
await prisma.contractCategory.upsert({
where: { code: category.code },
update: { name: category.name, icon: category.icon, color: category.color, sortOrder: category.sortOrder },
create: category,
});
console.log(`Created/updated category: ${category.name}`);
}
// Update existing contracts to link to their categories
console.log('Linking existing contracts to categories...');
for (const category of contractCategories) {
const cat = await prisma.contractCategory.findUnique({ where: { code: category.code } });
if (cat) {
const result = await prisma.contract.updateMany({
where: {
type: category.code as any,
contractCategoryId: null,
},
data: {
contractCategoryId: cat.id,
},
});
if (result.count > 0) {
console.log(`Linked ${result.count} ${category.name} contracts`);
}
}
}
console.log('Contract categories setup completed!');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
@@ -0,0 +1,55 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Adding developer:access permission...');
// Create or get the developer:access permission
const developerPerm = await prisma.permission.upsert({
where: { resource_action: { resource: 'developer', action: 'access' } },
update: {},
create: { resource: 'developer', action: 'access' },
});
console.log('Permission created/found:', developerPerm);
// Get the Admin role
const adminRole = await prisma.role.findUnique({
where: { name: 'Admin' },
include: { permissions: true },
});
if (!adminRole) {
console.log('Admin role not found!');
return;
}
// Check if Admin already has this permission
const hasPermission = adminRole.permissions.some(
(rp) => rp.permissionId === developerPerm.id
);
if (!hasPermission) {
await prisma.rolePermission.create({
data: {
roleId: adminRole.id,
permissionId: developerPerm.id,
},
});
console.log('Added developer:access permission to Admin role');
} else {
console.log('Admin role already has developer:access permission');
}
console.log('Done!');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
+117
View File
@@ -0,0 +1,117 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Adding provider permissions...');
// Create provider permissions
const actions = ['create', 'read', 'update', 'delete'];
for (const action of actions) {
await prisma.permission.upsert({
where: { resource_action: { resource: 'providers', action } },
update: {},
create: { resource: 'providers', action },
});
}
console.log('Provider permissions created');
// Get all provider permissions
const providerPermissions = await prisma.permission.findMany({
where: { resource: 'providers' },
});
// Get admin role
const adminRole = await prisma.role.findUnique({
where: { name: 'Admin' },
include: { permissions: true },
});
if (adminRole) {
// Add provider permissions to admin role if not already assigned
for (const perm of providerPermissions) {
const exists = adminRole.permissions.some(rp => rp.permissionId === perm.id);
if (!exists) {
await prisma.rolePermission.create({
data: {
roleId: adminRole.id,
permissionId: perm.id,
},
});
console.log(`Added providers:${perm.action} to Admin role`);
}
}
}
// Get employee role and add read permission
const employeeRole = await prisma.role.findUnique({
where: { name: 'Mitarbeiter' },
include: { permissions: true },
});
const providerReadPerm = providerPermissions.find(p => p.action === 'read');
if (employeeRole && providerReadPerm) {
const exists = employeeRole.permissions.some(rp => rp.permissionId === providerReadPerm.id);
if (!exists) {
await prisma.rolePermission.create({
data: {
roleId: employeeRole.id,
permissionId: providerReadPerm.id,
},
});
console.log('Added providers:read to Mitarbeiter role');
}
}
// Get read-only role and add read permission
const readOnlyRole = await prisma.role.findUnique({
where: { name: 'Mitarbeiter (Nur-Lesen)' },
include: { permissions: true },
});
if (readOnlyRole && providerReadPerm) {
const exists = readOnlyRole.permissions.some(rp => rp.permissionId === providerReadPerm.id);
if (!exists) {
await prisma.rolePermission.create({
data: {
roleId: readOnlyRole.id,
permissionId: providerReadPerm.id,
},
});
console.log('Added providers:read to Mitarbeiter (Nur-Lesen) role');
}
}
// Get customer role and add read permission
const customerRole = await prisma.role.findUnique({
where: { name: 'Kunde' },
include: { permissions: true },
});
if (customerRole && providerReadPerm) {
const exists = customerRole.permissions.some(rp => rp.permissionId === providerReadPerm.id);
if (!exists) {
await prisma.rolePermission.create({
data: {
roleId: customerRole.id,
permissionId: providerReadPerm.id,
},
});
console.log('Added providers:read to Kunde role');
}
}
console.log('Provider permissions setup completed!');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
@@ -0,0 +1,64 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// Create settings permissions
const settingsPermissions = [
{ resource: 'settings', action: 'read' },
{ resource: 'settings', action: 'update' },
];
for (const perm of settingsPermissions) {
await prisma.permission.upsert({
where: {
resource_action: {
resource: perm.resource,
action: perm.action,
},
},
update: {},
create: perm,
});
}
console.log('Settings permissions created');
// Add to Admin role
const adminRole = await prisma.role.findUnique({
where: { name: 'Admin' },
});
if (adminRole) {
const newPermissions = await prisma.permission.findMany({
where: { resource: 'settings' },
});
for (const perm of newPermissions) {
await prisma.rolePermission.upsert({
where: {
roleId_permissionId: {
roleId: adminRole.id,
permissionId: perm.id,
},
},
update: {},
create: {
roleId: adminRole.id,
permissionId: perm.id,
},
});
}
console.log('Settings permissions added to Admin role');
}
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
@@ -0,0 +1,354 @@
-- CreateTable
CREATE TABLE `User` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`email` VARCHAR(191) NOT NULL,
`password` VARCHAR(191) NOT NULL,
`firstName` VARCHAR(191) NOT NULL,
`lastName` VARCHAR(191) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`customerId` INTEGER NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `User_email_key`(`email`),
UNIQUE INDEX `User_customerId_key`(`customerId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Role` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Role_name_key`(`name`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Permission` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`resource` VARCHAR(191) NOT NULL,
`action` VARCHAR(191) NOT NULL,
UNIQUE INDEX `Permission_resource_action_key`(`resource`, `action`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `RolePermission` (
`roleId` INTEGER NOT NULL,
`permissionId` INTEGER NOT NULL,
PRIMARY KEY (`roleId`, `permissionId`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `UserRole` (
`userId` INTEGER NOT NULL,
`roleId` INTEGER NOT NULL,
PRIMARY KEY (`userId`, `roleId`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Customer` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`customerNumber` VARCHAR(191) NOT NULL,
`type` ENUM('PRIVATE', 'BUSINESS') NOT NULL DEFAULT 'PRIVATE',
`salutation` VARCHAR(191) NULL,
`firstName` VARCHAR(191) NOT NULL,
`lastName` VARCHAR(191) NOT NULL,
`companyName` VARCHAR(191) NULL,
`email` VARCHAR(191) NULL,
`phone` VARCHAR(191) NULL,
`mobile` VARCHAR(191) NULL,
`taxNumber` VARCHAR(191) NULL,
`businessRegistration` TEXT NULL,
`commercialRegister` VARCHAR(191) NULL,
`notes` TEXT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Customer_customerNumber_key`(`customerNumber`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Address` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`customerId` INTEGER NOT NULL,
`type` ENUM('DELIVERY_RESIDENCE', 'BILLING') NOT NULL DEFAULT 'DELIVERY_RESIDENCE',
`street` VARCHAR(191) NOT NULL,
`houseNumber` VARCHAR(191) NOT NULL,
`postalCode` VARCHAR(191) NOT NULL,
`city` VARCHAR(191) NOT NULL,
`country` VARCHAR(191) NOT NULL DEFAULT 'Deutschland',
`isDefault` BOOLEAN NOT NULL DEFAULT false,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `BankCard` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`customerId` INTEGER NOT NULL,
`accountHolder` VARCHAR(191) NOT NULL,
`iban` VARCHAR(191) NOT NULL,
`bic` VARCHAR(191) NULL,
`bankName` VARCHAR(191) NULL,
`expiryDate` DATETIME(3) NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `IdentityDocument` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`customerId` INTEGER NOT NULL,
`type` ENUM('ID_CARD', 'PASSPORT', 'DRIVERS_LICENSE', 'OTHER') NOT NULL DEFAULT 'ID_CARD',
`documentNumber` VARCHAR(191) NOT NULL,
`issuingAuthority` VARCHAR(191) NULL,
`issueDate` DATETIME(3) NULL,
`expiryDate` DATETIME(3) NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Meter` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`customerId` INTEGER NOT NULL,
`meterNumber` VARCHAR(191) NOT NULL,
`type` ENUM('ELECTRICITY', 'GAS') NOT NULL,
`location` VARCHAR(191) NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `MeterReading` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`meterId` INTEGER NOT NULL,
`readingDate` DATETIME(3) NOT NULL,
`value` DOUBLE NOT NULL,
`unit` VARCHAR(191) NOT NULL DEFAULT 'kWh',
`notes` VARCHAR(191) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `SalesPlatform` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`contactInfo` TEXT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `SalesPlatform_name_key`(`name`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Contract` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractNumber` VARCHAR(191) NOT NULL,
`customerId` INTEGER NOT NULL,
`type` ENUM('ELECTRICITY', 'GAS', 'DSL', 'FIBER', 'MOBILE', 'TV', 'CAR_INSURANCE') NOT NULL,
`status` ENUM('DRAFT', 'PENDING', 'ACTIVE', 'CANCELLED', 'EXPIRED') NOT NULL DEFAULT 'DRAFT',
`addressId` INTEGER NULL,
`bankCardId` INTEGER NULL,
`identityDocumentId` INTEGER NULL,
`salesPlatformId` INTEGER NULL,
`previousContractId` INTEGER NULL,
`providerName` VARCHAR(191) NULL,
`tariffName` VARCHAR(191) NULL,
`customerNumberAtProvider` VARCHAR(191) NULL,
`startDate` DATETIME(3) NULL,
`endDate` DATETIME(3) NULL,
`cancellationPeriod` INTEGER NULL,
`commission` DOUBLE NULL,
`portalUsername` VARCHAR(191) NULL,
`portalPasswordEncrypted` VARCHAR(191) NULL,
`notes` TEXT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Contract_contractNumber_key`(`contractNumber`),
UNIQUE INDEX `Contract_previousContractId_key`(`previousContractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `EnergyContractDetails` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractId` INTEGER NOT NULL,
`meterId` INTEGER NULL,
`annualConsumption` DOUBLE NULL,
`basePrice` DOUBLE NULL,
`unitPrice` DOUBLE NULL,
`bonus` DOUBLE NULL,
`previousProviderName` VARCHAR(191) NULL,
`previousCustomerNumber` VARCHAR(191) NULL,
UNIQUE INDEX `EnergyContractDetails_contractId_key`(`contractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `InternetContractDetails` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractId` INTEGER NOT NULL,
`downloadSpeed` INTEGER NULL,
`uploadSpeed` INTEGER NULL,
`routerModel` VARCHAR(191) NULL,
`routerSerialNumber` VARCHAR(191) NULL,
`installationDate` DATETIME(3) NULL,
UNIQUE INDEX `InternetContractDetails_contractId_key`(`contractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `PhoneNumber` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`internetContractDetailsId` INTEGER NOT NULL,
`phoneNumber` VARCHAR(191) NOT NULL,
`isMain` BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `MobileContractDetails` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractId` INTEGER NOT NULL,
`phoneNumber` VARCHAR(191) NULL,
`simCardNumber` VARCHAR(191) NULL,
`dataVolume` DOUBLE NULL,
`includedMinutes` INTEGER NULL,
`includedSMS` INTEGER NULL,
`deviceModel` VARCHAR(191) NULL,
`deviceImei` VARCHAR(191) NULL,
UNIQUE INDEX `MobileContractDetails_contractId_key`(`contractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `TvContractDetails` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractId` INTEGER NOT NULL,
`receiverModel` VARCHAR(191) NULL,
`smartcardNumber` VARCHAR(191) NULL,
`package` VARCHAR(191) NULL,
UNIQUE INDEX `TvContractDetails_contractId_key`(`contractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `CarInsuranceDetails` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`contractId` INTEGER NOT NULL,
`licensePlate` VARCHAR(191) NULL,
`hsn` VARCHAR(191) NULL,
`tsn` VARCHAR(191) NULL,
`vin` VARCHAR(191) NULL,
`vehicleType` VARCHAR(191) NULL,
`firstRegistration` DATETIME(3) NULL,
`noClaimsClass` VARCHAR(191) NULL,
`insuranceType` ENUM('LIABILITY', 'PARTIAL', 'FULL') NOT NULL DEFAULT 'LIABILITY',
`deductiblePartial` DOUBLE NULL,
`deductibleFull` DOUBLE NULL,
`policyNumber` VARCHAR(191) NULL,
`previousInsurer` VARCHAR(191) NULL,
UNIQUE INDEX `CarInsuranceDetails_contractId_key`(`contractId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `User` ADD CONSTRAINT `User_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `RolePermission` ADD CONSTRAINT `RolePermission_roleId_fkey` FOREIGN KEY (`roleId`) REFERENCES `Role`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `RolePermission` ADD CONSTRAINT `RolePermission_permissionId_fkey` FOREIGN KEY (`permissionId`) REFERENCES `Permission`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `UserRole` ADD CONSTRAINT `UserRole_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `UserRole` ADD CONSTRAINT `UserRole_roleId_fkey` FOREIGN KEY (`roleId`) REFERENCES `Role`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Address` ADD CONSTRAINT `Address_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `BankCard` ADD CONSTRAINT `BankCard_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `IdentityDocument` ADD CONSTRAINT `IdentityDocument_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Meter` ADD CONSTRAINT `Meter_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `MeterReading` ADD CONSTRAINT `MeterReading_meterId_fkey` FOREIGN KEY (`meterId`) REFERENCES `Meter`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_customerId_fkey` FOREIGN KEY (`customerId`) REFERENCES `Customer`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_addressId_fkey` FOREIGN KEY (`addressId`) REFERENCES `Address`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_bankCardId_fkey` FOREIGN KEY (`bankCardId`) REFERENCES `BankCard`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_identityDocumentId_fkey` FOREIGN KEY (`identityDocumentId`) REFERENCES `IdentityDocument`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_salesPlatformId_fkey` FOREIGN KEY (`salesPlatformId`) REFERENCES `SalesPlatform`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_previousContractId_fkey` FOREIGN KEY (`previousContractId`) REFERENCES `Contract`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `EnergyContractDetails` ADD CONSTRAINT `EnergyContractDetails_contractId_fkey` FOREIGN KEY (`contractId`) REFERENCES `Contract`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `EnergyContractDetails` ADD CONSTRAINT `EnergyContractDetails_meterId_fkey` FOREIGN KEY (`meterId`) REFERENCES `Meter`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `InternetContractDetails` ADD CONSTRAINT `InternetContractDetails_contractId_fkey` FOREIGN KEY (`contractId`) REFERENCES `Contract`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `PhoneNumber` ADD CONSTRAINT `PhoneNumber_internetContractDetailsId_fkey` FOREIGN KEY (`internetContractDetailsId`) REFERENCES `InternetContractDetails`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `MobileContractDetails` ADD CONSTRAINT `MobileContractDetails_contractId_fkey` FOREIGN KEY (`contractId`) REFERENCES `Contract`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `TvContractDetails` ADD CONSTRAINT `TvContractDetails_contractId_fkey` FOREIGN KEY (`contractId`) REFERENCES `Contract`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `CarInsuranceDetails` ADD CONSTRAINT `CarInsuranceDetails_contractId_fkey` FOREIGN KEY (`contractId`) REFERENCES `Contract`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE `BankCard` ADD COLUMN `documentPath` VARCHAR(191) NULL;
-- AlterTable
ALTER TABLE `IdentityDocument` ADD COLUMN `documentPath` VARCHAR(191) NULL;
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE `Customer` ADD COLUMN `birthDate` DATETIME(3) NULL,
ADD COLUMN `birthPlace` VARCHAR(191) NULL;
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE `IdentityDocument` ADD COLUMN `licenseClasses` VARCHAR(191) NULL,
ADD COLUMN `licenseIssueDate` DATETIME(3) NULL;
@@ -0,0 +1,14 @@
/*
Warnings:
- You are about to drop the column `businessRegistration` on the `Customer` table. All the data in the column will be lost.
- You are about to drop the column `commercialRegister` on the `Customer` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE `Customer` DROP COLUMN `businessRegistration`,
DROP COLUMN `commercialRegister`,
ADD COLUMN `businessRegistrationPath` VARCHAR(191) NULL,
ADD COLUMN `commercialRegisterNumber` VARCHAR(191) NULL,
ADD COLUMN `commercialRegisterPath` VARCHAR(191) NULL,
ADD COLUMN `foundingDate` DATETIME(3) NULL;
@@ -0,0 +1,31 @@
/*
Warnings:
- You are about to drop the column `cancellationPeriod` on the `Contract` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE `Contract` DROP COLUMN `cancellationPeriod`,
ADD COLUMN `cancellationPeriodId` INTEGER NULL,
ADD COLUMN `priceAfter24Months` VARCHAR(191) NULL,
ADD COLUMN `priceFirst12Months` VARCHAR(191) NULL,
ADD COLUMN `priceFrom13Months` VARCHAR(191) NULL;
-- AlterTable
ALTER TABLE `Customer` ADD COLUMN `privacyPolicyPath` VARCHAR(191) NULL;
-- CreateTable
CREATE TABLE `CancellationPeriod` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`code` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `CancellationPeriod_code_key`(`code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_cancellationPeriodId_fkey` FOREIGN KEY (`cancellationPeriodId`) REFERENCES `CancellationPeriod`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,18 @@
-- AlterTable
ALTER TABLE `Contract` ADD COLUMN `contractDurationId` INTEGER NULL;
-- CreateTable
CREATE TABLE `ContractDuration` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`code` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `ContractDuration_code_key`(`code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_contractDurationId_fkey` FOREIGN KEY (`contractDurationId`) REFERENCES `ContractDuration`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,8 @@
-- AlterTable
ALTER TABLE `Contract` ADD COLUMN `cancellationConfirmationDate` DATETIME(3) NULL,
ADD COLUMN `cancellationConfirmationOptionsDate` DATETIME(3) NULL,
ADD COLUMN `cancellationConfirmationOptionsPath` VARCHAR(191) NULL,
ADD COLUMN `cancellationConfirmationPath` VARCHAR(191) NULL,
ADD COLUMN `cancellationLetterOptionsPath` VARCHAR(191) NULL,
ADD COLUMN `cancellationLetterPath` VARCHAR(191) NULL,
ADD COLUMN `wasSpecialCancellation` BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,40 @@
-- AlterTable
ALTER TABLE `Contract` ADD COLUMN `providerId` INTEGER NULL,
ADD COLUMN `tariffId` INTEGER NULL;
-- CreateTable
CREATE TABLE `Provider` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`portalUrl` VARCHAR(191) NULL,
`usernameFieldName` VARCHAR(191) NULL,
`passwordFieldName` VARCHAR(191) NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Provider_name_key`(`name`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Tariff` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`providerId` INTEGER NOT NULL,
`name` VARCHAR(191) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Tariff_providerId_name_key`(`providerId`, `name`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Tariff` ADD CONSTRAINT `Tariff_providerId_fkey` FOREIGN KEY (`providerId`) REFERENCES `Provider`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_providerId_fkey` FOREIGN KEY (`providerId`) REFERENCES `Provider`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_tariffId_fkey` FOREIGN KEY (`tariffId`) REFERENCES `Tariff`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE `MobileContractDetails` ADD COLUMN `requiresMultisim` BOOLEAN NOT NULL DEFAULT false;
-- CreateTable
CREATE TABLE `SimCard` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`mobileDetailsId` INTEGER NOT NULL,
`phoneNumber` VARCHAR(191) NULL,
`simCardNumber` VARCHAR(191) NULL,
`pin` VARCHAR(191) NULL,
`puk` VARCHAR(191) NULL,
`isMultisim` BOOLEAN NOT NULL DEFAULT false,
`isMain` BOOLEAN NOT NULL DEFAULT false,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `SimCard` ADD CONSTRAINT `SimCard_mobileDetailsId_fkey` FOREIGN KEY (`mobileDetailsId`) REFERENCES `MobileContractDetails`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,21 @@
-- AlterTable
ALTER TABLE `Contract` ADD COLUMN `contractCategoryId` INTEGER NULL;
-- CreateTable
CREATE TABLE `ContractCategory` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`code` VARCHAR(191) NOT NULL,
`name` VARCHAR(191) NOT NULL,
`icon` VARCHAR(191) NULL,
`color` VARCHAR(191) NULL,
`sortOrder` INTEGER NOT NULL DEFAULT 0,
`isActive` BOOLEAN NOT NULL DEFAULT true,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `ContractCategory_code_key`(`code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Contract` ADD CONSTRAINT `Contract_contractCategoryId_fkey` FOREIGN KEY (`contractCategoryId`) REFERENCES `ContractCategory`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,13 @@
-- AlterTable
ALTER TABLE `Contract` MODIFY `type` ENUM('ELECTRICITY', 'GAS', 'DSL', 'CABLE', 'FIBER', 'MOBILE', 'TV', 'CAR_INSURANCE') NOT NULL;
-- AlterTable
ALTER TABLE `InternetContractDetails` ADD COLUMN `activationCode` VARCHAR(191) NULL,
ADD COLUMN `homeId` VARCHAR(191) NULL,
ADD COLUMN `internetPasswordEncrypted` VARCHAR(191) NULL,
ADD COLUMN `internetUsername` VARCHAR(191) NULL;
-- AlterTable
ALTER TABLE `PhoneNumber` ADD COLUMN `sipPasswordEncrypted` VARCHAR(191) NULL,
ADD COLUMN `sipServer` VARCHAR(191) NULL,
ADD COLUMN `sipUsername` VARCHAR(191) NULL;
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "mysql"
+623
View File
@@ -0,0 +1,623 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
// ==================== APP SETTINGS ====================
model AppSetting {
id Int @id @default(autoincrement())
key String @unique
value String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== USERS & AUTH ====================
model User {
id Int @id @default(autoincrement())
email String @unique
password String
firstName String
lastName String
isActive Boolean @default(true)
customerId Int? @unique
customer Customer? @relation(fields: [customerId], references: [id])
roles UserRole[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Role {
id Int @id @default(autoincrement())
name String @unique
description String?
permissions RolePermission[]
users UserRole[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Permission {
id Int @id @default(autoincrement())
resource String
action String
roles RolePermission[]
@@unique([resource, action])
}
model RolePermission {
roleId Int
permissionId Int
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
@@id([roleId, permissionId])
}
model UserRole {
userId Int
roleId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
@@id([userId, roleId])
}
// ==================== CUSTOMERS ====================
enum CustomerType {
PRIVATE
BUSINESS
}
model Customer {
id Int @id @default(autoincrement())
customerNumber String @unique
type CustomerType @default(PRIVATE)
salutation String?
firstName String
lastName String
companyName String?
foundingDate DateTime? // Gründungsdatum (für Firmen)
birthDate DateTime?
birthPlace String?
email String?
phone String?
mobile String?
taxNumber String?
businessRegistrationPath String? // PDF-Pfad zur Gewerbeanmeldung
commercialRegisterPath String? // PDF-Pfad zum Handelsregisterauszug
commercialRegisterNumber String? // Handelsregisternummer (Text)
privacyPolicyPath String? // PDF-Pfad zur Datenschutzerklärung (für alle Kunden)
notes String? @db.Text
// ===== Portal-Zugangsdaten =====
portalEnabled Boolean @default(false) // Portal aktiviert?
portalEmail String? @unique // Portal-Login E-Mail
portalPasswordHash String? // Gehashtes Passwort (für Login)
portalPasswordEncrypted String? // Verschlüsseltes Passwort (für Anzeige)
portalLastLogin DateTime? // Letzte Anmeldung
user User?
addresses Address[]
bankCards BankCard[]
identityDocuments IdentityDocument[]
meters Meter[]
stressfreiEmails StressfreiEmail[]
contracts Contract[]
// Vertreter-Beziehungen (Kunde kann für andere Kunden handeln)
representingFor CustomerRepresentative[] @relation("RepresentativeCustomer")
representedBy CustomerRepresentative[] @relation("RepresentedCustomer")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== CUSTOMER REPRESENTATIVES ====================
// Vertretungsbeziehung: Ein Kunde kann die Verträge eines anderen Kunden einsehen
// z.B. Sohn (representativeId) kann Verträge der Mutter (customerId) sehen
model CustomerRepresentative {
id Int @id @default(autoincrement())
customerId Int // Der Kunde, dessen Verträge eingesehen werden (z.B. Mutter)
customer Customer @relation("RepresentedCustomer", fields: [customerId], references: [id], onDelete: Cascade)
representativeId Int // Der Kunde, der einsehen darf (z.B. Sohn)
representative Customer @relation("RepresentativeCustomer", fields: [representativeId], references: [id], onDelete: Cascade)
notes String? // Notizen zur Vertretung
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([customerId, representativeId]) // Keine doppelten Einträge
}
// ==================== ADDRESSES ====================
enum AddressType {
DELIVERY_RESIDENCE
BILLING
}
model Address {
id Int @id @default(autoincrement())
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
type AddressType @default(DELIVERY_RESIDENCE)
street String
houseNumber String
postalCode String
city String
country String @default("Deutschland")
isDefault Boolean @default(false)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== BANK CARDS ====================
model BankCard {
id Int @id @default(autoincrement())
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
accountHolder String
iban String
bic String?
bankName String?
expiryDate DateTime?
documentPath String? // Pfad zur hochgeladenen PDF
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== IDENTITY DOCUMENTS ====================
enum DocumentType {
ID_CARD
PASSPORT
DRIVERS_LICENSE
OTHER
}
model IdentityDocument {
id Int @id @default(autoincrement())
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
type DocumentType @default(ID_CARD)
documentNumber String
issuingAuthority String?
issueDate DateTime?
expiryDate DateTime?
documentPath String? // Pfad zur hochgeladenen PDF
isActive Boolean @default(true)
// Führerschein-spezifische Felder
licenseClasses String? // z.B. "B, BE, AM, L" - kommasepariert
licenseIssueDate DateTime? // Datum des Führerscheinerwerbs (Klasse B)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== EMAIL PROVIDER CONFIG (Plesk, cPanel etc.) ====================
enum EmailProviderType {
PLESK
CPANEL
DIRECTADMIN
}
model EmailProviderConfig {
id Int @id @default(autoincrement())
name String @unique // z.B. "Plesk Hauptserver"
type EmailProviderType
apiUrl String // API-URL (z.B. https://server.de:8443)
apiKey String? // API-Key (verschlüsselt)
username String? // Benutzername für API
passwordEncrypted String? // Passwort (verschlüsselt)
domain String // Domain für E-Mails (z.B. stressfrei-wechseln.de)
defaultForwardEmail String? // Standard-Weiterleitungsadresse (unsere eigene)
isActive Boolean @default(true)
isDefault Boolean @default(false) // Standard-Provider
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== STRESSFREI-WECHSELN EMAIL ADDRESSES ====================
model StressfreiEmail {
id Int @id @default(autoincrement())
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
email String // Die Weiterleitungs-E-Mail-Adresse
platform String? // Für welche Plattform (z.B. "Freenet", "Klarmobil")
notes String? @db.Text // Optionale Notizen
isActive Boolean @default(true)
isProvisioned Boolean @default(false) // Wurde bei Provider angelegt?
provisionedAt DateTime? // Wann wurde provisioniert?
provisionError String? @db.Text // Fehlermeldung falls Provisionierung fehlschlug
contracts Contract[] // Verträge die diese E-Mail als Benutzername verwenden
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== METERS (Energy) ====================
enum MeterType {
ELECTRICITY
GAS
}
model Meter {
id Int @id @default(autoincrement())
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
meterNumber String
type MeterType
location String?
isActive Boolean @default(true)
readings MeterReading[]
energyDetails EnergyContractDetails[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model MeterReading {
id Int @id @default(autoincrement())
meterId Int
meter Meter @relation(fields: [meterId], references: [id], onDelete: Cascade)
readingDate DateTime
value Float
unit String @default("kWh")
notes String?
createdAt DateTime @default(now())
}
// ==================== SALES PLATFORMS ====================
model SalesPlatform {
id Int @id @default(autoincrement())
name String @unique
contactInfo String? @db.Text
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== CANCELLATION PERIODS ====================
model CancellationPeriod {
id Int @id @default(autoincrement())
code String @unique // z.B. "14T", "1M", "3M", "12M", "1J"
description String // z.B. "14 Tage", "1 Monat", "3 Monate"
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== CONTRACT DURATIONS ====================
model ContractDuration {
id Int @id @default(autoincrement())
code String @unique // z.B. "12M", "24M", "1J", "2J"
description String // z.B. "12 Monate", "24 Monate", "1 Jahr"
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== PROVIDERS (Anbieter) ====================
model Provider {
id Int @id @default(autoincrement())
name String @unique // Anbietername
portalUrl String? // Kundenkontourl (Login-Seite)
usernameFieldName String? // Benutzernamefeld (z.B. "email", "username")
passwordFieldName String? // Kennwortfeld (z.B. "password", "pwd")
isActive Boolean @default(true)
tariffs Tariff[]
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== TARIFFS (Tarife) ====================
model Tariff {
id Int @id @default(autoincrement())
providerId Int
provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
name String // Tarifname
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([providerId, name]) // Eindeutiger Tarif pro Anbieter
}
// ==================== CONTRACT CATEGORIES ====================
model ContractCategory {
id Int @id @default(autoincrement())
code String @unique // Technischer Code (z.B. ELECTRICITY, GAS)
name String // Anzeigename (z.B. Strom, Gas)
icon String? // Icon-Name für UI (z.B. "Zap", "Flame")
color String? // Farbe für UI (z.B. "#FFC107")
sortOrder Int @default(0)
isActive Boolean @default(true)
contracts Contract[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== CONTRACTS ====================
// Legacy Enum - wird durch ContractCategory ersetzt
enum ContractType {
ELECTRICITY
GAS
DSL
CABLE
FIBER
MOBILE
TV
CAR_INSURANCE
}
enum ContractStatus {
DRAFT
PENDING
ACTIVE
CANCELLED
EXPIRED
DEACTIVATED
}
model Contract {
id Int @id @default(autoincrement())
contractNumber String @unique
customerId Int
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
type ContractType
status ContractStatus @default(DRAFT)
// Neue konfigurierbare Kategorie (ersetzt langfristig das type-Enum)
contractCategoryId Int?
contractCategory ContractCategory? @relation(fields: [contractCategoryId], references: [id])
addressId Int?
address Address? @relation(fields: [addressId], references: [id])
bankCardId Int?
bankCard BankCard? @relation(fields: [bankCardId], references: [id])
identityDocumentId Int?
identityDocument IdentityDocument? @relation(fields: [identityDocumentId], references: [id])
salesPlatformId Int?
salesPlatform SalesPlatform? @relation(fields: [salesPlatformId], references: [id])
cancellationPeriodId Int?
cancellationPeriod CancellationPeriod? @relation(fields: [cancellationPeriodId], references: [id])
contractDurationId Int?
contractDuration ContractDuration? @relation(fields: [contractDurationId], references: [id])
previousContractId Int? @unique
previousContract Contract? @relation("ContractHistory", fields: [previousContractId], references: [id])
followUpContract Contract? @relation("ContractHistory")
// Anbieter & Tarif (neue Verknüpfung)
providerId Int?
provider Provider? @relation(fields: [providerId], references: [id])
tariffId Int?
tariff Tariff? @relation(fields: [tariffId], references: [id])
// Legacy-Felder (für Abwärtskompatibilität)
providerName String?
tariffName String?
customerNumberAtProvider String?
priceFirst12Months String? // Preis erste 12 Monate
priceFrom13Months String? // Preis ab 13. Monat
priceAfter24Months String? // Preis nach 24 Monaten
startDate DateTime?
endDate DateTime? // Wird aus startDate + contractDuration berechnet
commission Float?
// Kündigungsdokumente
cancellationLetterPath String? // Kündigungsschreiben PDF
cancellationConfirmationPath String? // Kündigungsbestätigung PDF
cancellationLetterOptionsPath String? // Kündigungsschreiben Optionen PDF
cancellationConfirmationOptionsPath String? // Kündigungsbestätigung Optionen PDF
// Kündigungsdaten
cancellationConfirmationDate DateTime? // Kündigungsbestätigungsdatum
cancellationConfirmationOptionsDate DateTime? // Kündigungsbestätigungsoptionendatum
wasSpecialCancellation Boolean @default(false) // Wurde sondergekündigt?
portalUsername String?
portalPasswordEncrypted String?
// Stressfrei-Wechseln E-Mail als Benutzername (Alternative zu portalUsername)
stressfreiEmailId Int?
stressfreiEmail StressfreiEmail? @relation(fields: [stressfreiEmailId], references: [id])
notes String? @db.Text
energyDetails EnergyContractDetails?
internetDetails InternetContractDetails?
mobileDetails MobileContractDetails?
tvDetails TvContractDetails?
carInsuranceDetails CarInsuranceDetails?
tasks ContractTask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== CONTRACT TASKS ====================
enum ContractTaskStatus {
OPEN
COMPLETED
}
model ContractTask {
id Int @id @default(autoincrement())
contractId Int
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
title String
description String? @db.Text
status ContractTaskStatus @default(OPEN)
visibleInPortal Boolean @default(false)
createdBy String? // Name des Erstellers
completedAt DateTime?
subtasks ContractTaskSubtask[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ContractTaskSubtask {
id Int @id @default(autoincrement())
taskId Int
task ContractTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
title String
status ContractTaskStatus @default(OPEN)
createdBy String?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== ENERGY CONTRACT DETAILS ====================
model EnergyContractDetails {
id Int @id @default(autoincrement())
contractId Int @unique
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
meterId Int?
meter Meter? @relation(fields: [meterId], references: [id])
annualConsumption Float?
basePrice Float?
unitPrice Float?
bonus Float?
previousProviderName String?
previousCustomerNumber String?
}
// ==================== INTERNET CONTRACT DETAILS ====================
model InternetContractDetails {
id Int @id @default(autoincrement())
contractId Int @unique
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
downloadSpeed Int?
uploadSpeed Int?
routerModel String?
routerSerialNumber String?
installationDate DateTime?
// Internet-Zugangsdaten
internetUsername String?
internetPasswordEncrypted String? // Verschlüsselt gespeichert
// Glasfaser-spezifisch
homeId String?
// Vodafone DSL/Kabel spezifisch
activationCode String?
phoneNumbers PhoneNumber[]
}
model PhoneNumber {
id Int @id @default(autoincrement())
internetContractDetailsId Int
internetDetails InternetContractDetails @relation(fields: [internetContractDetailsId], references: [id], onDelete: Cascade)
phoneNumber String
isMain Boolean @default(false)
// SIP-Zugangsdaten
sipUsername String?
sipPasswordEncrypted String? // Verschlüsselt gespeichert
sipServer String?
}
// ==================== MOBILE CONTRACT DETAILS ====================
model MobileContractDetails {
id Int @id @default(autoincrement())
contractId Int @unique
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
requiresMultisim Boolean @default(false) // Multisim erforderlich?
dataVolume Float?
includedMinutes Int?
includedSMS Int?
deviceModel String?
deviceImei String?
simCards SimCard[]
// Legacy-Felder (für Abwärtskompatibilität, werden durch simCards ersetzt)
phoneNumber String?
simCardNumber String?
}
model SimCard {
id Int @id @default(autoincrement())
mobileDetailsId Int
mobileDetails MobileContractDetails @relation(fields: [mobileDetailsId], references: [id], onDelete: Cascade)
phoneNumber String? // Rufnummer
simCardNumber String? // SIM-Kartennummer
pin String? // PIN (verschlüsselt gespeichert)
puk String? // PUK (verschlüsselt gespeichert)
isMultisim Boolean @default(false) // Ist dies eine Multisim-Karte?
isMain Boolean @default(false) // Ist dies die Hauptkarte?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ==================== TV CONTRACT DETAILS ====================
model TvContractDetails {
id Int @id @default(autoincrement())
contractId Int @unique
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
receiverModel String?
smartcardNumber String?
package String?
}
// ==================== CAR INSURANCE DETAILS ====================
enum InsuranceType {
LIABILITY
PARTIAL
FULL
}
model CarInsuranceDetails {
id Int @id @default(autoincrement())
contractId Int @unique
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
licensePlate String?
hsn String?
tsn String?
vin String?
vehicleType String?
firstRegistration DateTime?
noClaimsClass String?
insuranceType InsuranceType @default(LIABILITY)
deductiblePartial Float?
deductibleFull Float?
policyNumber String?
previousInsurer String?
}
+183
View File
@@ -0,0 +1,183 @@
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
const prisma = new PrismaClient();
async function main() {
console.log('Seeding database...');
// Create permissions
const resources = ['customers', 'contracts', 'users', 'platforms', 'providers', 'developer'];
const actions = ['create', 'read', 'update', 'delete', 'access'];
const permissions: { resource: string; action: string }[] = [];
for (const resource of resources) {
for (const action of actions) {
// developer nur mit 'access' action
if (resource === 'developer' && action !== 'access') continue;
// andere resources ohne 'access' action
if (resource !== 'developer' && action === 'access') continue;
permissions.push({ resource, action });
}
}
for (const perm of permissions) {
await prisma.permission.upsert({
where: { resource_action: perm },
update: {},
create: perm,
});
}
console.log('Permissions created');
// Get all permissions
const allPermissions = await prisma.permission.findMany();
const customerReadPerm = allPermissions.find(
(p) => p.resource === 'customers' && p.action === 'read'
);
const contractReadPerm = allPermissions.find(
(p) => p.resource === 'contracts' && p.action === 'read'
);
const platformReadPerm = allPermissions.find(
(p) => p.resource === 'platforms' && p.action === 'read'
);
const providerReadPerm = allPermissions.find(
(p) => p.resource === 'providers' && p.action === 'read'
);
// Create roles
// Admin - all permissions EXCEPT developer:access (that's controlled separately)
const adminPermissions = allPermissions.filter(
(p) => !(p.resource === 'developer' && p.action === 'access')
);
const adminRole = await prisma.role.upsert({
where: { name: 'Admin' },
update: {},
create: {
name: 'Admin',
description: 'Voller Zugriff auf alle Funktionen',
permissions: {
create: adminPermissions.map((p) => ({ permissionId: p.id })),
},
},
});
// Employee - full access to customers, contracts, read platforms and providers
const employeePermIds = allPermissions
.filter(
(p) =>
p.resource === 'customers' ||
p.resource === 'contracts' ||
(p.resource === 'platforms' && p.action === 'read') ||
(p.resource === 'providers' && p.action === 'read')
)
.map((p) => p.id);
const employeeRole = await prisma.role.upsert({
where: { name: 'Mitarbeiter' },
update: {},
create: {
name: 'Mitarbeiter',
description: 'Kann Kunden und Verträge verwalten',
permissions: {
create: employeePermIds.map((id) => ({ permissionId: id })),
},
},
});
// Read-only employee
const readOnlyPermIds = [customerReadPerm?.id, contractReadPerm?.id, platformReadPerm?.id, providerReadPerm?.id].filter(
(id): id is number => id !== undefined
);
const readOnlyRole = await prisma.role.upsert({
where: { name: 'Mitarbeiter (Nur-Lesen)' },
update: {},
create: {
name: 'Mitarbeiter (Nur-Lesen)',
description: 'Kann nur lesen, keine Änderungen',
permissions: {
create: readOnlyPermIds.map((id) => ({ permissionId: id })),
},
},
});
// Customer role - read own data only (handled in middleware)
const customerRole = await prisma.role.upsert({
where: { name: 'Kunde' },
update: {},
create: {
name: 'Kunde',
description: 'Kann nur eigene Daten lesen',
permissions: {
create: readOnlyPermIds.map((id) => ({ permissionId: id })),
},
},
});
console.log('Roles created');
// Create admin user
const hashedPassword = await bcrypt.hash('admin', 10);
const adminUser = await prisma.user.upsert({
where: { email: 'admin@admin.com' },
update: {},
create: {
email: 'admin@admin.com',
password: hashedPassword,
firstName: 'Admin',
lastName: 'User',
roles: {
create: [{ roleId: adminRole.id }],
},
},
});
console.log('Admin user created: admin@admin.com / admin');
// Create some sales platforms
const platforms = ['Moon Fachhandel', 'Verivox', 'Check24', 'Eigenvermittlung'];
for (const name of platforms) {
await prisma.salesPlatform.upsert({
where: { name },
update: {},
create: { name, isActive: true },
});
}
console.log('Sales platforms created');
// Create contract categories (matching existing enum values)
const contractCategories = [
{ code: 'ELECTRICITY', name: 'Strom', icon: 'Zap', color: '#FFC107', sortOrder: 1 },
{ code: 'GAS', name: 'Gas', icon: 'Flame', color: '#FF5722', sortOrder: 2 },
{ code: 'DSL', name: 'DSL', icon: 'Wifi', color: '#2196F3', sortOrder: 3 },
{ code: 'FIBER', name: 'Glasfaser', icon: 'Cable', color: '#9C27B0', sortOrder: 4 },
{ code: 'MOBILE', name: 'Mobilfunk', icon: 'Smartphone', color: '#4CAF50', sortOrder: 5 },
{ code: 'TV', name: 'TV', icon: 'Tv', color: '#E91E63', sortOrder: 6 },
{ code: 'CAR_INSURANCE', name: 'KFZ-Versicherung', icon: 'Car', color: '#607D8B', sortOrder: 7 },
];
for (const category of contractCategories) {
await prisma.contractCategory.upsert({
where: { code: category.code },
update: { name: category.name, icon: category.icon, color: category.color, sortOrder: category.sortOrder },
create: category,
});
}
console.log('Contract categories created');
console.log('Seeding completed!');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});