This commit is contained in:
Bastian Wagner
2026-03-14 09:31:47 +01:00
parent 0d30e01a5f
commit ef4485115d
15 changed files with 157 additions and 48 deletions

View File

@@ -1,17 +1,17 @@
export class KeyHandoverPDFDataDto { export class KeyHandoutPDFDataDto {
handoverId!: string; handoverId: string;
handoverDate!: Date; handoverDate: Date;
place!: string; place: string;
giverName!: string; giverName: string;
giverAddress?: string; giverAddress: string;
receiverName!: string; receiverName: string;
receiverAddress?: string; receiverAddress: string;
keyType!: string; keyType: string;
keyNumber?: string; keyNumber: string;
quantity!: number; quantity: number;
objectDescription?: string; objectDescription: string;
notes?: string; notes: string;
} }

View File

@@ -5,3 +5,4 @@ export * from './customer.entity';
export * from './key-handout.entity'; export * from './key-handout.entity';
export * from './activity.entity'; export * from './activity.entity';
export * from './user'; export * from './user';
export * from './key-handout-pdf-data.entity';

View File

@@ -0,0 +1,52 @@
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
import { KeyHandout } from "./key-handout.entity";
@Entity()
export class KeyHandoutPdfDataEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@ManyToOne(() => KeyHandout, handout => handout.pdfs)
handout: KeyHandout
@Column({ type: 'date' })
handoverDate: Date;
@Column({ name: 'giver_name' })
giverName: string;
@Column({ name: 'giver_address', nullable: true })
giverAddress: string;
@Column({ name: 'receiver_name' })
receiverName: string;
@Column({ name: 'receiver_address', nullable: true })
receiverAddress: string;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@Column({ name: 'file_name', nullable: true })
fileName: string;
@Column({ name: 'key_nr' })
keyNumber: string;
@Column({ type: 'int', default: 1 })
quantity: number;
@Column({ name: 'object_description', nullable: true })
objectDescription: string;
@Column({ nullable: true })
notes: string;
@UpdateDateColumn({ name: 'updatet_at' })
updatedAt: Date;
}

View File

@@ -4,11 +4,13 @@ import {
CreateDateColumn, CreateDateColumn,
Entity, Entity,
ManyToOne, ManyToOne,
OneToMany,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
} from 'typeorm'; } from 'typeorm';
import { Key } from './key.entity'; import { Key } from './key.entity';
import { Customer } from './customer.entity'; import { Customer } from './customer.entity';
import { User } from './user'; import { User } from './user';
import { KeyHandoutPdfDataEntity } from './key-handout-pdf-data.entity';
@Entity() @Entity()
export class KeyHandout { export class KeyHandout {
@@ -34,8 +36,8 @@ export class KeyHandout {
user: User; user: User;
@Column({ nullable: true }) @OneToMany(() => KeyHandoutPdfDataEntity, pdf => pdf.handout)
pdfFormKey: string; pdfs: KeyHandoutPdfDataEntity[];
@BeforeInsert() @BeforeInsert()
insertTimestamp() { insertTimestamp() {

View File

@@ -7,3 +7,4 @@ export * from './key.repository';
export * from './customer.repository'; export * from './customer.repository';
export * from './activity.repository'; export * from './activity.repository';
export * from './user.settings.repository'; export * from './user.settings.repository';
export * from './key-handout-pdf-data.repository';

View File

@@ -0,0 +1,10 @@
import { Injectable } from '@nestjs/common';
import { Repository, DataSource } from 'typeorm';
import { KeyHandoutPdfDataEntity } from '../entitites';
@Injectable()
export class KeyHandoutPdfDataEntityRepository extends Repository<KeyHandoutPdfDataEntity> {
constructor(dataSource: DataSource) {
super(KeyHandoutPdfDataEntity, dataSource.createEntityManager());
}
}

View File

@@ -8,7 +8,6 @@ import {
Put, Put,
Req, Req,
Res, Res,
Sse,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { Response } from 'express'; import { Response } from 'express';
@@ -16,7 +15,7 @@ import { KeyService } from './key.service';
import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface'; import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface';
import { AuthGuard } from 'src/core/guards/auth.guard'; import { AuthGuard } from 'src/core/guards/auth.guard';
import { Key } from 'src/model/entitites'; import { Key } from 'src/model/entitites';
import { KeyHandoverPDFDataDto } from 'src/model/dto/key-handover.dto'; import { KeyHandoutPDFDataDto } from 'src/model/dto/key-handover.dto';
@UseGuards(AuthGuard) @UseGuards(AuthGuard)
@@ -91,7 +90,7 @@ export class KeyController {
} }
@Post('pdf') @Post('pdf')
async generatePdf(@Body() dto: KeyHandoverPDFDataDto, @Res() res: Response) { async generatePdf(@Body() dto: KeyHandoutPDFDataDto, @Res() res: Response) {
const { pdf, response } = await this.service.createPdf(dto); const { pdf, response } = await this.service.createPdf(dto);
res.setHeader( res.setHeader(

View File

@@ -2,8 +2,8 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { Customer, Cylinder, Key, User } from 'src/model/entitites'; import { Customer, Cylinder, Key, User } from 'src/model/entitites';
import { import {
CylinderRepository, CylinderRepository,
KeyHandoutPdfDataEntityRepository,
KeyRepository, KeyRepository,
KeySystemRepository,
} from 'src/model/repositories'; } from 'src/model/repositories';
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository'; import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
import { ActivityHelperService } from 'src/shared/service/activity.logger.service'; import { ActivityHelperService } from 'src/shared/service/activity.logger.service';
@@ -12,9 +12,7 @@ import { FindOperator, IsNull, Not } from 'typeorm';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { MailService } from '../mail/mail.service'; import { MailService } from '../mail/mail.service';
import { SseService } from '../realtime/sse/sse.service'; import { SseService } from '../realtime/sse/sse.service';
import { KeyHandoverPDFDataDto } from 'src/model/dto/key-handover.dto'; import { KeyHandoutPDFDataDto } from 'src/model/dto/key-handover.dto';
import { firstValueFrom } from 'rxjs';
import { HttpService } from '@nestjs/axios';
import { PdfService } from 'src/shared/storage/services/pdf/pdf.service'; import { PdfService } from 'src/shared/storage/services/pdf/pdf.service';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
@@ -29,8 +27,11 @@ export class KeyService {
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly mailService: MailService, private readonly mailService: MailService,
private readonly sseService: SseService, private readonly sseService: SseService,
private readonly pdfService: PdfService private readonly pdfService: PdfService,
) { } private readonly handoutPDFRepo: KeyHandoutPdfDataEntityRepository
) {
// this.getLatestHandoverPDF('a4f4f32b-96de-4f20-b5bd-ac3a128da121')
}
get isDevelopMode(): boolean { get isDevelopMode(): boolean {
return (this.configService.get('DEVELOP_MODE') || '').toLowerCase() == 'true'; return (this.configService.get('DEVELOP_MODE') || '').toLowerCase() == 'true';
@@ -168,7 +169,7 @@ export class KeyService {
timestamp: { direction: 'DESC' }, timestamp: { direction: 'DESC' },
created: { direction: 'DESC' }, created: { direction: 'DESC' },
}, },
relations: ['customer'], relations: ['customer', 'pdfs'],
}); });
} }
@@ -234,12 +235,34 @@ export class KeyService {
return k; return k;
} }
async createPdf(dto: KeyHandoverPDFDataDto): Promise<{ pdf: Buffer, response: AxiosResponse}> { async createPdf(dto: KeyHandoutPDFDataDto): Promise<{ pdf: Buffer, response: AxiosResponse}> {
const x = await this.pdfService.generatePDF(dto) const handout = await this.handoverRepo.findOneByOrFail({ id: dto.handoverId })
return x; const data = {
...dto,
handout
}
data.handoverDate = new Date(data.handoverDate);
const entity = await this.handoutPDFRepo.save(this.handoutPDFRepo.create(data));
const fileName = await this.pdfService.generatePDF(dto);
entity.createdAt = new Date();
entity.fileName = fileName;
await this.handoutPDFRepo.save(entity);
const file = await this.pdfService.getPDFByHandoverKey(fileName);
return file;
} }
async getPdf(handoverId: string): Promise<{ pdf: Buffer, response: AxiosResponse}> { async getPdf(handoverId: string): Promise<{ pdf: Buffer, response: AxiosResponse}> {
return this.pdfService.getPDFByHandoverKey(handoverId); return this.pdfService.getPDFByHandoverKey(handoverId);
} }
async getLatestHandoverPDF(handoverId: string) {
const pdf = await this.handoutPDFRepo.findOne({
where: { handout: { id: handoverId}},
order: {createdAt: 'DESC' },
});
console.log(pdf)
}
} }

View File

@@ -6,6 +6,7 @@ import {
Cylinder, Cylinder,
Key, Key,
KeyHandout, KeyHandout,
KeyHandoutPdfDataEntity,
Role, Role,
SSOUser, SSOUser,
User, User,
@@ -18,6 +19,7 @@ import {
ActivityRepository, ActivityRepository,
CustomerRepository, CustomerRepository,
CylinderRepository, CylinderRepository,
KeyHandoutPdfDataEntityRepository,
KeyRepository, KeyRepository,
KeySystemRepository, KeySystemRepository,
RoleRepository, RoleRepository,
@@ -41,7 +43,8 @@ const ENTITIES = [
Activity, Activity,
EmailLog, EmailLog,
UserSettings, UserSettings,
Impersonation Impersonation,
KeyHandoutPdfDataEntity
]; ];
const REPOSITORIES = [ const REPOSITORIES = [
UserRepository, UserRepository,
@@ -55,7 +58,8 @@ const REPOSITORIES = [
ActivityRepository, ActivityRepository,
EmailLogRepository, EmailLogRepository,
UserSettingsRepository, UserSettingsRepository,
ImpersonationRepository ImpersonationRepository,
KeyHandoutPdfDataEntityRepository
]; ];
@Module({ @Module({

View File

@@ -1,9 +1,9 @@
import { HttpService } from '@nestjs/axios'; import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { KeyHandoverPDFDataDto } from 'src/model/dto/key-handover.dto'; import { KeyHandoutPDFDataDto } from 'src/model/dto/key-handover.dto';
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository'; import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
@Injectable() @Injectable()
@@ -13,17 +13,13 @@ export class PdfService {
constructor( constructor(
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly httpService: HttpService, private readonly httpService: HttpService
private readonly handoverRepo: KeyHandoutRepository
) { } ) { }
public async generatePDF(dto: KeyHandoverPDFDataDto): Promise<{ pdf: Buffer, response: AxiosResponse}> { public async generatePDF(dto: KeyHandoutPDFDataDto): Promise<string> {
const h = await this.handoverRepo.findOneByOrFail({ id: dto.handoverId });
const response = await this.post(`${this.STORAGESERVER}/pdf/keyhandover`, dto); const response = await this.post(`${this.STORAGESERVER}/pdf/keyhandover`, dto);
if (response?.data == null) { return; } if (response?.data == null) { throw new HttpException('Konnte nicht erstellt werden', HttpStatus.INTERNAL_SERVER_ERROR) }
h.pdfFormKey = response.data; return response.data;
await this.handoverRepo.save(h);
return this.getPDFByHandoverKey(response.data)
} }

View File

@@ -3,10 +3,9 @@ import { StorageService } from './storage.service';
import { HttpModule } from '@nestjs/axios'; import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { PdfService } from './services/pdf/pdf.service'; import { PdfService } from './services/pdf/pdf.service';
import { DatabaseModule } from '../database/database.module';
@Module({ @Module({
imports: [HttpModule, ConfigModule, DatabaseModule], imports: [HttpModule, ConfigModule],
providers: [StorageService, PdfService ], providers: [StorageService, PdfService ],
exports: [ StorageService, PdfService ] exports: [ StorageService, PdfService ]
}) })

View File

@@ -1,3 +1,8 @@
@if(isLoading) {
<div class="loading-overlay">
<mat-spinner></mat-spinner>
</div>
}
<h2 mat-dialog-title>Übergabeprotokoll</h2> <h2 mat-dialog-title>Übergabeprotokoll</h2>
<mat-dialog-content> <mat-dialog-content>
<div class="text" style="margin: 0 0 24px 0;"> <div class="text" style="margin: 0 0 24px 0;">

View File

@@ -8,10 +8,11 @@ import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { IKeyHandoverPDF } from '../../../../model/interface/handover-pdf.interface'; import { IKeyHandoverPDF } from '../../../../model/interface/handover-pdf.interface';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
@Component({ @Component({
selector: 'app-create-handover-pdf-dialog', selector: 'app-create-handover-pdf-dialog',
imports: [MatDialogModule, FormsModule, ReactiveFormsModule, CommonModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatIconModule], imports: [MatDialogModule, FormsModule, ReactiveFormsModule, CommonModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule],
templateUrl: './create-handover-pdf-dialog.component.html', templateUrl: './create-handover-pdf-dialog.component.html',
styleUrl: './create-handover-pdf-dialog.component.scss', styleUrl: './create-handover-pdf-dialog.component.scss',
}) })

View File

@@ -83,11 +83,11 @@ export class HandoverDialogComponent extends AgGridContainerComponent {
{ {
colId: 'download', colId: 'download',
headerName: 'PDF', headerName: 'PDF',
field: 'pdfFormKey', field: 'pdfs',
width: 60, width: 60,
tooltipValueGetter: () => 'Übergabeprotokoll herunterladen', tooltipValueGetter: () => 'Übergabeprotokoll herunterladen',
cellRenderer: () => '', cellRenderer: () => '',
cellClass: (data: any) => data.value ? 'download-pdf' : '', cellClass: (data: any) => data.value.length ? 'download-pdf' : '',
onCellClicked: (event) => { onCellClicked: (event) => {
this.downloadHandoverPDF(event.value) this.downloadHandoverPDF(event.value)
}, },
@@ -232,9 +232,10 @@ export class HandoverDialogComponent extends AgGridContainerComponent {
//this.downloadHandoverPDF('5b1f92d9-c66c-49ad-b654-b9a9a6a59752') //this.downloadHandoverPDF('5b1f92d9-c66c-49ad-b654-b9a9a6a59752')
} }
downloadHandoverPDF(key: string) { downloadHandoverPDF(pdfs: any[]) {
if (!key) { return; } if (!pdfs || pdfs.length < 1) { return; }
this.api.getHandoverPdf(key)
this.api.getHandoverPdf(pdfs[0]['fileName'])
} }
} }

View File

@@ -246,4 +246,19 @@ div.ag-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 12px; gap: 12px;
}
.loading-overlay {
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
padding: 0;
margin: 0;
box-sizing: border-box;
z-index: 2;
background: rgba(255, 255, 255, 0.5);
display: flex;
align-items: center;
justify-content: center;
} }