feat(07-01): install DKV deps, add Prisma models, wire ScheduleModule + crypto export
- pnpm add imapflow@^1.4.3, pdf-parse@^2.4.5, xlsx@^0.18.5 to @tessera/api - Append DkvModuleConfig, DkvVehicleMaster, DkvInvoiceHistory, SmtpConfig models to schema.prisma (15 models total) - Add ScheduleModule.forRoot() to AppModule imports (prerequisite for DkvSchedulerService) - Export CalendarCryptoService from CalendarModule (needed by DkvModule + SettingsModule) - Create apps/api/src/dkv/dkv.types.ts with DkvVehicleBlock, DkvTransaction, InboxConfig, InboxEmail, InboxAttachment, ExportRow interfaces
This commit is contained in:
@@ -27,15 +27,18 @@
|
||||
"class-validator": "^0.15.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"ews-javascript-api": "0.15.3",
|
||||
"imapflow": "^1.4.3",
|
||||
"ldapts": "^8.1.8",
|
||||
"node-ical": "0.26.1",
|
||||
"nodemailer": "^9.0.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-parse": "^2.4.5",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.0.0",
|
||||
"tsdav": "2.2.2"
|
||||
"tsdav": "2.2.2",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
|
||||
@@ -168,3 +168,66 @@ model CalendarSource {
|
||||
@@index([userId])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
model DkvModuleConfig {
|
||||
id String @id @default(uuid())
|
||||
tenantId String @unique
|
||||
protocol String @default("imap") // 'imap' | 'exchange'
|
||||
host String?
|
||||
port Int?
|
||||
encryption String @default("ssl-tls") // 'none' | 'starttls' | 'ssl-tls'
|
||||
folder String @default("INBOX")
|
||||
senderFilter String?
|
||||
pollIntervalMin Int @default(60)
|
||||
isActive Boolean @default(false)
|
||||
exportRecipient String?
|
||||
vehicleFormatString String @default("{Marke}/{Modell}/{Kennzeichen}")
|
||||
encryptedInboxCreds String? // AES-256-GCM: JSON { username, password } encrypted
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
model DkvVehicleMaster {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
kennzeichen String
|
||||
marke String
|
||||
modell String
|
||||
fahrer String // "Vorname Nachname"
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([tenantId, kennzeichen])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
model DkvInvoiceHistory {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
datumZeit DateTime @default(now())
|
||||
rechnungsnummer String
|
||||
anzahlFahrzeuge Int @default(0)
|
||||
anzahlTransaktionen Int @default(0)
|
||||
status String // 'Verarbeitet' | 'Fehler' | 'Versand fehlgeschlagen'
|
||||
errorMessage String?
|
||||
exportFilename String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([tenantId])
|
||||
@@index([datumZeit])
|
||||
}
|
||||
|
||||
model SmtpConfig {
|
||||
id String @id @default(uuid())
|
||||
tenantId String @unique
|
||||
host String
|
||||
port Int @default(587)
|
||||
encryption String @default("starttls") // 'none' | 'starttls' | 'ssl-tls'
|
||||
username String?
|
||||
encryptedPassword String? // AES-256-GCM via CalendarCryptoService
|
||||
fromAddress String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from './auth/guards/roles.guard';
|
||||
@@ -20,6 +21,7 @@ import { UserModule } from './user/user.module';
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
ScheduleModule.forRoot(),
|
||||
PrismaModule,
|
||||
AuthModule,
|
||||
UserModule,
|
||||
|
||||
@@ -28,6 +28,6 @@ import { ExchangeProvider } from './providers/exchange.provider';
|
||||
ICSProvider,
|
||||
ExchangeProvider,
|
||||
],
|
||||
exports: [CalendarService],
|
||||
exports: [CalendarService, CalendarCryptoService],
|
||||
})
|
||||
export class CalendarModule {}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Shared types for the DKV Fleet Module.
|
||||
*
|
||||
* These interfaces are the single source of truth for data shapes exchanged
|
||||
* between the parser, scheduler, export, and mail services.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A single fuel transaction row extracted from a DKV E-Rechnung PDF.
|
||||
*/
|
||||
export interface DkvTransaction {
|
||||
/** Date string in German format "DD.MM.YYYY" */
|
||||
lieferdatum: string;
|
||||
/** Service station city / location */
|
||||
ort: string;
|
||||
/** Odometer reading at time of fuelling (numeric, German thousands-dots stripped) */
|
||||
kilometerstand: number;
|
||||
/** Product name (e.g., "Diesel", "AdBlue") */
|
||||
produkt: string;
|
||||
/** Quantity dispensed (German decimal notation converted to JS float) */
|
||||
menge: number;
|
||||
/** Unit of measure (e.g., "L") */
|
||||
einheit: string;
|
||||
/** Net amount in EUR */
|
||||
netto: number;
|
||||
/** Gross amount in EUR */
|
||||
brutto: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A vehicle section extracted from a DKV E-Rechnung PDF.
|
||||
* Each block corresponds to one VEHICLE: ... CARD NO.: ... section.
|
||||
*/
|
||||
export interface DkvVehicleBlock {
|
||||
/** License plate (Kennzeichen), e.g. "GP-JL 728E" */
|
||||
kennzeichen: string;
|
||||
/** DKV card number */
|
||||
cardNumber: string;
|
||||
/** All fuel transactions for this vehicle in the invoice */
|
||||
transactions: DkvTransaction[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inbox connection configuration (supports IMAP and Exchange).
|
||||
* Mirrors DkvModuleConfig from Prisma but with decrypted credentials.
|
||||
*/
|
||||
export interface InboxConfig {
|
||||
/** 'imap' | 'exchange' */
|
||||
protocol: string;
|
||||
host: string;
|
||||
port: number;
|
||||
/** Optional username (may be empty for anonymous or Exchange-with-integrated-auth) */
|
||||
username?: string;
|
||||
/** Decrypted password (never persisted in this form) */
|
||||
password?: string;
|
||||
/** 'none' | 'starttls' | 'ssl-tls' */
|
||||
encryption: string;
|
||||
/** IMAP folder to monitor (e.g., "INBOX") */
|
||||
folder: string;
|
||||
/** Sender filter: only process emails from this address */
|
||||
senderFilter?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single email attachment, already downloaded as a Buffer.
|
||||
*/
|
||||
export interface InboxAttachment {
|
||||
filename: string;
|
||||
contentType: string;
|
||||
buffer: Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fetched email message with its PDF attachments.
|
||||
*/
|
||||
export interface InboxEmail {
|
||||
/** IMAP UID or Exchange item ID */
|
||||
uid: number | string;
|
||||
/** RFC 2822 Message-ID header */
|
||||
messageId: string;
|
||||
subject: string;
|
||||
from: string;
|
||||
date: Date;
|
||||
attachments: InboxAttachment[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A single row in the Excel export (5 columns per D-13).
|
||||
*/
|
||||
export interface ExportRow {
|
||||
/** "DD.MM.YYYY" */
|
||||
lieferdatum: string;
|
||||
/** Resolved from format string, e.g. "{Marke}/{Modell}/{Kennzeichen}" */
|
||||
fahrzeug: string;
|
||||
/** "Vorname Nachname" from vehicle master */
|
||||
fahrer: string;
|
||||
/** Service station city from PDF */
|
||||
ort: string;
|
||||
/** Numeric odometer reading */
|
||||
kilometerstand: number;
|
||||
}
|
||||
Reference in New Issue
Block a user