Merge pull request #4244 from MaximilianKrambach/callListPdf
call list pdf export
This commit is contained in:
commit
f26f7f9c08
@ -119,23 +119,36 @@ export class PdfDocumentService {
|
||||
* @param documentContent the content of the pdf as object
|
||||
* @param metadata
|
||||
* @param imageUrls Array of optional images (url, placeholder) to be inserted
|
||||
* @param customMargins optionally overrides the margins
|
||||
* @param landscape optional landscape page orientation instead of default portrait
|
||||
* @returns the pdf document definition ready to export
|
||||
*/
|
||||
private async getStandardPaper(documentContent: object, metadata?: object, imageUrls?: string[]): Promise<object> {
|
||||
private async getStandardPaper(
|
||||
documentContent: object,
|
||||
metadata?: object,
|
||||
imageUrls?: string[],
|
||||
customMargins?: [number, number, number, number],
|
||||
landscape?: boolean
|
||||
): Promise<object> {
|
||||
this.initFonts();
|
||||
this.imageUrls = imageUrls ? imageUrls : [];
|
||||
pdfMake.vfs = await this.initVfs();
|
||||
const result = {
|
||||
pageSize: 'A4',
|
||||
pageMargins: [75, 90, 75, 75],
|
||||
pageOrientation: landscape ? landscape : 'portrait',
|
||||
pageMargins: customMargins || [75, 90, 75, 75],
|
||||
defaultStyle: {
|
||||
font: 'PdfFont',
|
||||
fontSize: this.configService.instant('general_export_pdf_fontsize')
|
||||
},
|
||||
header: this.getHeader(),
|
||||
header: this.getHeader(customMargins ? [customMargins[0], customMargins[2]] : null),
|
||||
// TODO: option for no footer, wherever this can be defined
|
||||
footer: (currentPage, pageCount) => {
|
||||
return this.getFooter(currentPage, pageCount);
|
||||
return this.getFooter(
|
||||
currentPage,
|
||||
pageCount,
|
||||
customMargins ? [customMargins[0], customMargins[2]] : null
|
||||
);
|
||||
},
|
||||
info: metadata,
|
||||
content: documentContent,
|
||||
@ -188,9 +201,10 @@ export class PdfDocumentService {
|
||||
/**
|
||||
* Creates the header doc definition for normal PDF documents
|
||||
*
|
||||
* @param lrMargin optional margin overrides
|
||||
* @returns an object that contains the necessary header definition
|
||||
*/
|
||||
private getHeader(): object {
|
||||
private getHeader(lrMargin?: [number, number]): object {
|
||||
// check for the required logos
|
||||
let logoHeaderLeftUrl = this.configService.instant<any>('logo_pdf_header_L').path;
|
||||
let logoHeaderRightUrl = this.configService.instant<any>('logo_pdf_header_R').path;
|
||||
@ -247,11 +261,13 @@ export class PdfDocumentService {
|
||||
});
|
||||
this.imageUrls.push(logoHeaderRightUrl);
|
||||
}
|
||||
const margin = [lrMargin ? lrMargin[0] : 75, 30, lrMargin ? lrMargin[0] : 75, 10];
|
||||
// pdfmake order: [left, top, right, bottom]
|
||||
|
||||
return {
|
||||
color: '#555',
|
||||
fontSize: 9,
|
||||
margin: [75, 30, 75, 10], // [left, top, right, bottom]
|
||||
margin: margin,
|
||||
columns: columns,
|
||||
columnGap: 10
|
||||
};
|
||||
@ -265,9 +281,10 @@ export class PdfDocumentService {
|
||||
*
|
||||
* @param currentPage holds the number of the current page
|
||||
* @param pageCount holds the page count
|
||||
* @param lrMargin optionally overriding the margins
|
||||
* @returns the footer doc definition
|
||||
*/
|
||||
private getFooter(currentPage: number, pageCount: number): object {
|
||||
private getFooter(currentPage: number, pageCount: number, lrMargin?: [number, number]): object {
|
||||
const columns = [];
|
||||
let logoContainerWidth: string;
|
||||
let pageNumberPosition: string;
|
||||
@ -330,8 +347,9 @@ export class PdfDocumentService {
|
||||
this.imageUrls.push(logoFooterRightUrl);
|
||||
}
|
||||
|
||||
const margin = [lrMargin ? lrMargin[0] : 75, 0, lrMargin ? lrMargin[0] : 75, 10];
|
||||
return {
|
||||
margin: [75, 0, 75, 10],
|
||||
margin: margin,
|
||||
columns: columns,
|
||||
columnGap: 10
|
||||
};
|
||||
@ -359,6 +377,18 @@ export class PdfDocumentService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a pdf in landscape orientation
|
||||
*
|
||||
* @param docDefinition the structure of the PDF document
|
||||
* @param filename the name of the file to use
|
||||
* @param metadata
|
||||
*/
|
||||
public downloadLandscape(docDefinition: object, filename: string, metadata?: object): void {
|
||||
this.getStandardPaper(docDefinition, metadata, null, [50, 80, 50, 75], true).then(doc => {
|
||||
this.createPdf(doc, filename);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Downloads a pdf with the ballot papet page definitions.
|
||||
*
|
||||
|
@ -4,10 +4,10 @@
|
||||
<h2 translate>Call list</h2>
|
||||
</div>
|
||||
|
||||
<!-- Export -->
|
||||
<!-- Export menu -->
|
||||
<div class="menu-slot">
|
||||
<button mat-icon-button (click)="csvExportCallList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<button type="button" mat-icon-button [matMenuTriggerFor]="downloadMenu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
@ -15,7 +15,25 @@
|
||||
<mat-card>
|
||||
<button mat-button (click)="expandCollapseAll(true)">{{ 'Expand all' | translate }}</button>
|
||||
<button mat-button (click)="expandCollapseAll(false)">{{ 'Collapse all' | translate }}</button>
|
||||
<os-sorting-tree #sorter (sort)="sort($event)" parentIdKey="sort_parent_id"
|
||||
weightKey="weight" [modelsObservable]="motionsObservable" [expandCollapseAll]="expandCollapse">
|
||||
<os-sorting-tree
|
||||
#sorter
|
||||
(sort)="sort($event)"
|
||||
parentIdKey="sort_parent_id"
|
||||
weightKey="weight"
|
||||
[modelsObservable]="motionsObservable"
|
||||
[expandCollapseAll]="expandCollapse"
|
||||
>
|
||||
</os-sorting-tree>
|
||||
</mat-card>
|
||||
|
||||
<mat-menu #downloadMenu="matMenu">
|
||||
<button mat-menu-item (click)="pdfExportCallList()">
|
||||
<mat-icon>picture_as_pdf</mat-icon>
|
||||
<span translate>Export (PDF)</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="csvExportCallList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export (CSV)</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
@ -7,9 +7,11 @@ import { Observable } from 'rxjs';
|
||||
|
||||
import { BaseViewComponent } from '../../../base/base-view';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { MotionCsvExportService } from '../../services/motion-csv-export.service';
|
||||
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
|
||||
|
||||
import { ViewMotion } from '../../models/view-motion';
|
||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||
import { MotionCsvExportService } from '../../services/motion-csv-export.service';
|
||||
|
||||
/**
|
||||
* Sort view for the call list.
|
||||
@ -46,7 +48,8 @@ export class CallListComponent extends BaseViewComponent {
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private motionRepo: MotionRepositoryService,
|
||||
private motionCsvExport: MotionCsvExportService
|
||||
private motionCsvExport: MotionCsvExportService,
|
||||
private motionPdfExport: MotionPdfExportService
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
|
||||
@ -83,4 +86,11 @@ export class CallListComponent extends BaseViewComponent {
|
||||
public csvExportCallList(): void {
|
||||
this.motionCsvExport.exportCallList(this.motions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a pdf export of the call list
|
||||
*/
|
||||
public pdfExportCallList(): void {
|
||||
this.motionPdfExport.exportPdfCallList(this.motions);
|
||||
}
|
||||
}
|
||||
|
@ -70,4 +70,18 @@ export class MotionPdfExportService {
|
||||
};
|
||||
this.pdfDocumentService.download(doc, filename, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a table of the motions in order of their call list
|
||||
*
|
||||
* @param motions the motions to export
|
||||
*/
|
||||
public exportPdfCallList(motions: ViewMotion[]): void {
|
||||
const doc = this.motionPdfService.callListToDoc(motions);
|
||||
const filename = this.translate.instant('Call list');
|
||||
const metadata = {
|
||||
title: filename
|
||||
};
|
||||
this.pdfDocumentService.downloadLandscape(doc, filename, metadata);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,12 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
|
||||
import { MotionRepositoryService } from '../../../core/repositories/motions/motion-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { ChangeRecommendationRepositoryService } from '../../../core/repositories/motions/change-recommendation-repository.service';
|
||||
import { ViewUnifiedChange } from '../models/view-unified-change';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { MotionRepositoryService } from '../../../core/repositories/motions/motion-repository.service';
|
||||
import { HtmlToPdfService } from 'app/core/ui-services/html-to-pdf.service';
|
||||
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
|
||||
import { ViewUnifiedChange } from '../models/view-unified-change';
|
||||
|
||||
/**
|
||||
* Converts a motion to pdf. Can be used from the motion detail view or executed on a list of motions
|
||||
@ -413,4 +413,88 @@ export class MotionPdfService {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates pdfMake definitions for the call list of given motions
|
||||
*
|
||||
* @param motions A list of motions
|
||||
* @returns definitions ready to be opened or exported via {@link PdfDocumentService}
|
||||
*/
|
||||
public callListToDoc(motions: ViewMotion[]): object {
|
||||
motions.sort((a, b) => a.callListWeight - b.callListWeight);
|
||||
const title = {
|
||||
text: this.translate.instant('Call list'),
|
||||
style: 'title'
|
||||
};
|
||||
const callListTableBody: object[] = [
|
||||
[
|
||||
{
|
||||
text: this.translate.instant('Called'),
|
||||
style: 'tableHeader'
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('Called with'),
|
||||
style: 'tableHeader'
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('Submitters'),
|
||||
style: 'tableHeader'
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('Title'),
|
||||
style: 'tableHeader'
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('Recommendation'),
|
||||
style: 'tableHeader'
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('Motion block'),
|
||||
style: 'tableHeader'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const callListRows = motions.map(motion => this.createCallListRow(motion));
|
||||
const table: object = {
|
||||
table: {
|
||||
widths: ['auto', 'auto', 'auto', '*', 'auto', 'auto'],
|
||||
headerRows: 1,
|
||||
body: callListTableBody.concat(callListRows)
|
||||
},
|
||||
layout: {
|
||||
hLineWidth: rowIndex => {
|
||||
return rowIndex === 1;
|
||||
},
|
||||
vLineWidth: () => {
|
||||
return 0;
|
||||
},
|
||||
fillColor: rowIndex => {
|
||||
return rowIndex % 2 === 0 ? '#EEEEEE' : null;
|
||||
}
|
||||
}
|
||||
};
|
||||
return [title, table];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the pdfMake definitions for a row of the call List table
|
||||
*
|
||||
* @param motion
|
||||
* @returns pdfmakre definitions
|
||||
*/
|
||||
private createCallListRow(motion: ViewMotion): object {
|
||||
return [
|
||||
{
|
||||
text: motion.sort_parent_id ? '' : motion.identifierOrTitle
|
||||
},
|
||||
{ text: motion.sort_parent_id ? motion.identifierOrTitle : '' },
|
||||
{ text: motion.submitters.length ? motion.submitters.map(s => s.short_name).join(', ') : '' },
|
||||
{ text: motion.title },
|
||||
{
|
||||
text: motion.recommendation ? this.translate.instant(motion.recommendation.recommendation_label) : ''
|
||||
},
|
||||
{ text: motion.motion_block ? motion.motion_block.title : '' }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user