Add giftcard fields, improve errors

This commit is contained in:
2024-10-27 14:21:23 +01:00
parent 59b5c2f323
commit 19177eefdf
21 changed files with 1146 additions and 1239 deletions
+2 -2
View File
@@ -14,13 +14,13 @@
<maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target> <maven.compiler.target>${java.version}</maven.compiler.target>
<querydsl.version>5.1.0</querydsl.version> <querydsl.version>5.1.0</querydsl.version>
<revision>0.4.3</revision> <revision>0.5.0</revision>
</properties> </properties>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version> <version>3.3.5</version>
<relativePath /> <relativePath />
</parent> </parent>
@@ -172,7 +172,8 @@ public class TurnoverManager {
JPAQuery<Tuple> query = jpaQueryFactory.from(qTurnover).where(builder.getValue()).groupBy(qTurnover.username) JPAQuery<Tuple> query = jpaQueryFactory.from(qTurnover).where(builder.getValue()).groupBy(qTurnover.username)
.select(qTurnover.username.as("username"), qTurnover.price.sum().as("price"), .select(qTurnover.username.as("username"), qTurnover.price.sum().as("price"),
qTurnover.timeInvestment.sum().as("timeInvestment")); qTurnover.timeInvestment.sum().as("timeInvestment"),
qTurnover.giftcardPrice.sum().as("giftcardPrice"));
Long total = query.clone().select(qTurnover.username.countDistinct()).fetchOne(); Long total = query.clone().select(qTurnover.username.countDistinct()).fetchOne();
if (StringUtils.hasText(sortBy)) { if (StringUtils.hasText(sortBy)) {
@@ -34,5 +34,11 @@ public class TurnoverValidator implements Validator {
if (turnover.getPrice() < 0) { if (turnover.getPrice() < 0) {
errors.rejectValue("price", "POSITIVE_VALUE"); errors.rejectValue("price", "POSITIVE_VALUE");
} }
if (turnover.getGiftcardPrice() != null && turnover.getGiftcardPrice() < 0) {
errors.rejectValue("giftcardPrice", "POSITIVE_VALUE");
} else if (turnover.getGiftcardPrice() != null && turnover.getGiftcardPrice() > turnover.getPrice()) {
errors.rejectValue("giftcardPrice", "GREATER_THAN_PRICE");
}
} }
} }
@@ -51,6 +51,12 @@ public class Turnover {
@Column(name = "material_consumption", nullable = true, length = 5000) @Column(name = "material_consumption", nullable = true, length = 5000)
private String materialConsumption; private String materialConsumption;
@Column(name = "giftcard_number", nullable = true)
private String giftcardNumber;
@Column(name = "giftcard_price", nullable = true)
private Float giftcardPrice;
public Long getId() { public Long getId() {
return id; return id;
} }
@@ -139,6 +145,22 @@ public class Turnover {
this.materialConsumption = materialConsumption; this.materialConsumption = materialConsumption;
} }
public String getGiftcardNumber() {
return giftcardNumber;
}
public void setGiftcardNumber(String giftcardNumber) {
this.giftcardNumber = giftcardNumber;
}
public Float getGiftcardPrice() {
return giftcardPrice;
}
public void setGiftcardPrice(Float giftcardPrice) {
this.giftcardPrice = giftcardPrice;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof Turnover)) { if (!(obj instanceof Turnover)) {
@@ -170,6 +192,12 @@ public class Turnover {
equals &= materialConsumption == null && turnover.getMaterialConsumption() == null equals &= materialConsumption == null && turnover.getMaterialConsumption() == null
|| materialConsumption != null && materialConsumption.equals(turnover.getMaterialConsumption()); || materialConsumption != null && materialConsumption.equals(turnover.getMaterialConsumption());
equals &= giftcardNumber == null && turnover.getGiftcardNumber() == null
|| giftcardNumber != null && giftcardNumber.equals(turnover.getGiftcardNumber());
equals &= giftcardPrice == null && turnover.getGiftcardPrice() == null
|| giftcardPrice != null && giftcardPrice.equals(turnover.getGiftcardPrice());
return equals; return equals;
} }
} }
+875 -1128
View File
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -1,6 +1,6 @@
{ {
"name": "buntspecht-web", "name": "buntspecht-web",
"version": "0.4.3", "version": "0.5.0",
"license": "AGPL3", "license": "AGPL3",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
@@ -12,30 +12,30 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^18.2.7", "@angular/animations": "^18.2.9",
"@angular/cdk": "^18.2.7", "@angular/cdk": "^18.2.10",
"@angular/common": "^18.2.7", "@angular/common": "^18.2.9",
"@angular/compiler": "^18.2.7", "@angular/compiler": "^18.2.9",
"@angular/core": "^18.2.7", "@angular/core": "^18.2.9",
"@angular/forms": "^18.2.7", "@angular/forms": "^18.2.9",
"@angular/material": "^18.2.7", "@angular/material": "^18.2.10",
"@angular/material-moment-adapter": "^18.2.7", "@angular/material-moment-adapter": "^18.2.10",
"@angular/platform-browser": "^18.2.7", "@angular/platform-browser": "^18.2.9",
"@angular/platform-browser-dynamic": "^18.2.7", "@angular/platform-browser-dynamic": "^18.2.9",
"@angular/router": "^18.2.7", "@angular/router": "^18.2.9",
"@angular/service-worker": "^18.2.7", "@angular/service-worker": "^18.2.9",
"moment": "^2.30.1", "moment": "^2.30.1",
"rxjs": "~7.8.1", "rxjs": "~7.8.1",
"tslib": "^2.7.0", "tslib": "^2.8.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^18.2.7", "@angular-devkit/build-angular": "^18.2.10",
"@angular/cli": "^18.2.7", "@angular/cli": "^18.2.10",
"@angular/compiler-cli": "^18.2.7", "@angular/compiler-cli": "^18.2.9",
"@angular/localize": "^18.2.7", "@angular/localize": "^18.2.9",
"@types/jasmine": "^5.1.4", "@types/jasmine": "^5.1.4",
"jasmine-core": "~5.3.0", "jasmine-core": "~5.4.0",
"karma": "^6.4.4", "karma": "^6.4.4",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.1", "karma-coverage": "~2.2.1",
@@ -1,20 +1,23 @@
<div class="flex column fill"> <div class="flex column fill">
@if (entries && entries.error) { @if (entries && entries.error) {<div class="container">
<div class="flex column fill"> <div class="flex column fill center middle">
<mat-card class="accent box"> <mat-card class="accent error box">
<mat-card-header> <mat-card-header>
<mat-card-title>{{ 'management.error.' + entries.error.status | i18n}}</mat-card-title> <mat-card-title>{{ 'management.error.' + entries.error.status | i18n}}</mat-card-title>
<mat-card-subtitle>{{'management.error' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'management.error' | i18n}}</mat-card-subtitle>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<p> <p>
{{ 'management.error.' + entries.error.status + '.text' | i18n}} {{ 'management.error.' + entries.error.status + '.text' | i18n}}
</p> </p>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div>
</div> </div>
} }
@if (entries && !entries.error) {
<div class="flex wrap filter-container"> <div class="flex wrap filter-container">
<a mat-icon-button (click)="filterOpen=!filterOpen" title="{{'turnovers.filter' | i18n}}" <a mat-icon-button (click)="filterOpen=!filterOpen" title="{{'turnovers.filter' | i18n}}"
[color]="filterOpen ? 'primary': 'accent'"> [color]="filterOpen ? 'primary': 'accent'">
@@ -51,10 +54,12 @@
</form> </form>
} }
<span class="spacer"></span> <span class="spacer"></span>
<a class="margin" mat-icon-button (click)="export()" title="{{'turnovers.export' | i18n}}" color="primary" [disabled]="!entries.total"> <a class="margin" mat-icon-button (click)="export()" title="{{'turnovers.export' | i18n}}" color="primary"
[disabled]="!entries.total">
<mat-icon>file_download</mat-icon> <mat-icon>file_download</mat-icon>
</a> </a>
</div> </div>
}
@if (entries && entries.total == 0) { @if (entries && entries.total == 0) {
<mat-list> <mat-list>
@@ -109,6 +114,22 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="giftcardPrice">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
<span class="spacer"></span>
<span>{{'turnover.giftcard.priceUsage' | i18n}}</span>
</th>
<td mat-cell *matCellDef="let entry">
<div class="flex">
<span class="spacer"></span>
<span>{{entry[3] | number: '1.2-2'}}</span>
@if (entry[3]) {
<span>&nbsp;{{'turnover.price.suffix' | i18n}}</span>
}
</div>
</td>
</ng-container>
<ng-container matColumnDef="menu"> <ng-container matColumnDef="menu">
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let entry"> <td mat-cell *matCellDef="let entry">
@@ -127,8 +148,8 @@
<ng-container matColumnDef="expanded"> <ng-container matColumnDef="expanded">
<td mat-cell *matCellDef="let entry" [attr.colspan]="columns.length"> <td mat-cell *matCellDef="let entry" [attr.colspan]="columns.length">
@if (expanded && entries.total && entries.filter && entries.filter.username == entry[0]) { @if (expanded && entries.total && entries.filter && entries.filter.username == entry[0]) {
<ui-turnovers #uiTurnovers class="flex column fill" [turnovers]="turnovers" (page)="applyTurnoverPage($event)" <ui-turnovers #uiTurnovers class="flex column fill" [turnovers]="turnovers"
[enableSort]="false"></ui-turnovers> (page)="applyTurnoverPage($event)" [enableSort]="false"></ui-turnovers>
} }
</td> </td>
</ng-container> </ng-container>
@@ -22,7 +22,7 @@ export class PageManagement implements OnInit {
descending: boolean = false; descending: boolean = false;
filterOpen: boolean = true; filterOpen: boolean = true;
columns: string[] = ['username', 'price', 'timeInvestment', 'menu']; columns: string[] = ['username', 'price', 'timeInvestment', 'giftcardPrice', 'menu'];
expanded: boolean = false; expanded: boolean = false;
users: Observable<any>; users: Observable<any>;
@@ -198,10 +198,10 @@ export class PageManagement implements OnInit {
export() { export() {
if (this.entries.total) { if (this.entries.total) {
let rows = [[this.i18n.get('user.username'), this.i18n.get('turnover.price'), this.i18n.get('turnover.timeInvestment')]]; let rows = [[this.i18n.get('user.username'), this.i18n.get('turnover.price'), this.i18n.get('turnover.timeInvestment'), this.i18n.get('turnover.giftcard.priceUsage')]];
this.entries.results.forEach(result => { this.entries.results.forEach(result => {
rows[rows.length] = [result[0], result[1], [result[2]]] rows[rows.length] = [result[0], result[1], result[2], result[3]]
}); });
if (this.uiTurnovers) { if (this.uiTurnovers) {
@@ -1,6 +1,6 @@
<div class="container"> <div class="container">
<div class="flex column fill center middle"> <div class="flex column fill center middle">
<mat-card class="accent box"> <mat-card class="accent error box">
<mat-card-header> <mat-card-header>
<mat-card-title>404</mat-card-title> <mat-card-title>404</mat-card-title>
<mat-card-subtitle>{{'not-found' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'not-found' | i18n}}</mat-card-subtitle>
@@ -8,21 +8,21 @@
<input matInput formControlName="old" type="password"> <input matInput formControlName="old" type="password">
<mat-error *ngFor="let error of passwordForm.get('old').errors | keyvalue"> <mat-error *ngFor="let error of passwordForm.get('old').errors | keyvalue">
{{'password.error.' + error.key | i18n}} {{'password.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password.new' | i18n}}</mat-label> <mat-label>{{'password.new' | i18n}}</mat-label>
<input matInput formControlName="password" type="password"> <input matInput formControlName="password" type="password">
<mat-error *ngFor="let error of passwordForm.get('password').errors | keyvalue"> <mat-error *ngFor="let error of passwordForm.get('password').errors | keyvalue">
{{'password.error.' + error.key | i18n}} {{'password.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password.repeat' | i18n}}</mat-label> <mat-label>{{'password.repeat' | i18n}}</mat-label>
<input matInput formControlName="password2" type="password"> <input matInput formControlName="password2" type="password">
<mat-error *ngFor="let error of passwordForm.get('password2').errors | keyvalue"> <mat-error *ngFor="let error of passwordForm.get('password2').errors | keyvalue">
{{'password.error.' + error.key | i18n}} {{'password.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
@@ -24,16 +24,16 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'turnover.customer' | i18n}}</mat-label> <mat-label>{{'turnover.customer' | i18n}}</mat-label>
<input matInput formControlName="customer" type="text" [required]="true"> <input matInput formControlName="customer" type="text" [required]="true">
<mat-error *ngIf="hasError('customer')"> <mat-error *ngFor="let error of form.get('customer').errors | keyvalue">
{{'turnover.customer.error' | i18n}} {{'turnover.customer.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'turnover.motif' | i18n}}</mat-label> <mat-label>{{'turnover.motif' | i18n}}</mat-label>
<input matInput formControlName="motif" type="text" [required]="true"> <input matInput formControlName="motif" type="text" [required]="true">
<mat-error *ngIf="hasError('motif')"> <mat-error *ngFor="let error of form.get('motif').errors | keyvalue">
{{'turnover.motif.error' | i18n}} {{'turnover.motif.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@@ -41,8 +41,29 @@
<mat-label>{{'turnover.price' | i18n}}</mat-label> <mat-label>{{'turnover.price' | i18n}}</mat-label>
<input matInput formControlName="price" type="number" min="0" step="0.01" [required]="true"> <input matInput formControlName="price" type="number" min="0" step="0.01" [required]="true">
<span matTextSuffix>{{'turnover.price.suffix' | i18n}}</span> <span matTextSuffix>{{'turnover.price.suffix' | i18n}}</span>
<mat-error *ngIf="hasError('price')"> <mat-error *ngFor="let error of form.get('price').errors | keyvalue">
{{'turnover.price.error' | i18n}} {{'turnover.price.error.' + error.key | i18n}}
</mat-error>
</mat-form-field>
<mat-slide-toggle class="margin" [checked]="hasGiftcard" (change)="hasGiftcard=$event.checked">
{{'turnover.giftcard' | i18n}}
</mat-slide-toggle>
<mat-form-field [ngClass]="{'hidden' : !hasGiftcard}">
<mat-label>{{'turnover.giftcard.number' | i18n}}</mat-label>
<input matInput formControlName="giftcardNumber" type="text">
<mat-error *ngFor="let error of form.get('giftcardNumber').errors | keyvalue">
{{'turnover.giftcard.number.error.' + error.key | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field [ngClass]="{'hidden' : !hasGiftcard}">
<mat-label>{{'turnover.giftcard.price' | i18n}}</mat-label>
<input matInput formControlName="giftcardPrice" type="number" min="0" step="0.01">
<span matTextSuffix>{{'turnover.price.suffix' | i18n}}</span>
<mat-error *ngFor="let error of form.get('giftcardPrice').errors | keyvalue">
{{'turnover.giftcard.price.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@@ -50,24 +71,24 @@
<mat-label>{{'turnover.timeInvestment' | i18n}}</mat-label> <mat-label>{{'turnover.timeInvestment' | i18n}}</mat-label>
<input matInput formControlName="timeInvestment" type="number" min="0" step="0.1"> <input matInput formControlName="timeInvestment" type="number" min="0" step="0.1">
<span matTextSuffix>{{'turnover.timeInvestment.suffix' | i18n}}</span> <span matTextSuffix>{{'turnover.timeInvestment.suffix' | i18n}}</span>
<mat-error *ngIf="hasError('timeInvestment')"> <mat-error *ngFor="let error of form.get('timeInvestment').errors | keyvalue">
{{'turnover.timeInvestment.error' | i18n}} {{'turnover.timeInvestment.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'turnover.remark' | i18n}}</mat-label> <mat-label>{{'turnover.remark' | i18n}}</mat-label>
<textarea matAutosize matAutosizeMinRows="3" matInput formControlName="remark"></textarea> <textarea matAutosize matAutosizeMinRows="3" matInput formControlName="remark"></textarea>
<mat-error *ngIf="hasError('remark')"> <mat-error *ngFor="let error of form.get('remark').errors | keyvalue">
{{'turnover.remark.error' | i18n}} {{'turnover.remark.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'turnover.materialConsumption' | i18n}}</mat-label> <mat-label>{{'turnover.materialConsumption' | i18n}}</mat-label>
<textarea matAutosize matAutosizeMinRows="3" matInput formControlName="materialConsumption"></textarea> <textarea matAutosize matAutosizeMinRows="3" matInput formControlName="materialConsumption"></textarea>
<mat-error *ngIf="hasError('materialConsumption')"> <mat-error *ngFor="let error of form.get('materialConsumption').errors | keyvalue">
{{'turnover.materialConsumption.error' | i18n}} {{'turnover.materialConsumption.error.' + error.key | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@@ -2,6 +2,11 @@ mat-form-field {
display: block; display: block;
margin: 20px 0 !important; margin: 20px 0 !important;
} }
.hidden {
display: none;
}
form { form {
margin: 5px; margin: 5px;
min-width: 390px; min-width: 390px;
@@ -26,6 +26,7 @@ export class PageTurnover implements OnInit {
username: string = ""; username: string = "";
admin: boolean = false; admin: boolean = false;
today: Moment = moment(); today: Moment = moment();
hasGiftcard: boolean = false;
constructor( constructor(
private turnoverService: TurnoverService, private turnoverService: TurnoverService,
@@ -43,6 +44,8 @@ export class PageTurnover implements OnInit {
customer: ['', Validators.required], customer: ['', Validators.required],
motif: ['', Validators.required], motif: ['', Validators.required],
price: ['', Validators.required], price: ['', Validators.required],
giftcardNumber: ['', Validators.nullValidator],
giftcardPrice: ['', Validators.nullValidator],
timeInvestment: ['', Validators.nullValidator], timeInvestment: ['', Validators.nullValidator],
remark: ['', Validators.nullValidator], remark: ['', Validators.nullValidator],
materialConsumption: ['', Validators.nullValidator], materialConsumption: ['', Validators.nullValidator],
@@ -74,10 +77,12 @@ export class PageTurnover implements OnInit {
this.form.get("customer").setValue(this.turnover.customer); this.form.get("customer").setValue(this.turnover.customer);
this.form.get("motif").setValue(this.turnover.motif); this.form.get("motif").setValue(this.turnover.motif);
this.form.get("price").setValue(this.turnover.price); this.form.get("price").setValue(this.turnover.price);
this.form.get("giftcardNumber").setValue(this.turnover.giftcardNumber);
this.form.get("giftcardPrice").setValue(this.turnover.giftcardPrice);
this.form.get("timeInvestment").setValue(this.turnover.timeInvestment); this.form.get("timeInvestment").setValue(this.turnover.timeInvestment);
this.form.get("remark").setValue(this.turnover.remark); this.form.get("remark").setValue(this.turnover.remark);
this.form.get("materialConsumption").setValue(this.turnover.materialConsumption); this.form.get("materialConsumption").setValue(this.turnover.materialConsumption);
this.hasGiftcard = this.turnover.giftcardNumber || this.turnover.giftcardPrice || false;
}, },
error: (error) => { error: (error) => {
if (error.status == 404) { if (error.status == 404) {
@@ -116,6 +121,13 @@ export class PageTurnover implements OnInit {
this.turnover.timeInvestment = this.form.get("timeInvestment").value; this.turnover.timeInvestment = this.form.get("timeInvestment").value;
this.turnover.remark = this.form.get("remark").value; this.turnover.remark = this.form.get("remark").value;
this.turnover.materialConsumption = this.form.get("materialConsumption").value; this.turnover.materialConsumption = this.form.get("materialConsumption").value;
if (this.hasGiftcard) {
this.turnover.giftcardNumber = this.form.get("giftcardNumber").value;
this.turnover.giftcardPrice = this.form.get("giftcardPrice").value;
} else {
this.turnover.giftcardNumber = undefined;
this.turnover.giftcardPrice = undefined;
}
this.turnoverService.create(this.turnover).subscribe({ this.turnoverService.create(this.turnover).subscribe({
next: (data) => { next: (data) => {
@@ -152,6 +164,13 @@ export class PageTurnover implements OnInit {
this.turnover.timeInvestment = this.form.get("timeInvestment").value; this.turnover.timeInvestment = this.form.get("timeInvestment").value;
this.turnover.remark = this.form.get("remark").value; this.turnover.remark = this.form.get("remark").value;
this.turnover.materialConsumption = this.form.get("materialConsumption").value; this.turnover.materialConsumption = this.form.get("materialConsumption").value;
if (this.hasGiftcard) {
this.turnover.giftcardNumber = this.form.get("giftcardNumber").value;
this.turnover.giftcardPrice = this.form.get("giftcardPrice").value;
} else {
this.turnover.giftcardNumber = undefined;
this.turnover.giftcardPrice = undefined;
}
const request = this.admin ? this.turnoverManagementService.update(this.turnover) : this.turnoverService.update(this.turnover); const request = this.admin ? this.turnoverManagementService.update(this.turnover) : this.turnoverService.update(this.turnover);
@@ -163,10 +182,11 @@ export class PageTurnover implements OnInit {
}, },
error: (error) => { error: (error) => {
this.working = false; this.working = false;
this.success = false;
if (error.status == 403) { if (error.status == 403) {
this.snackBar.open("Error"); this.snackBar.open("Error");
} }
if (error.status == 422) { if (error.status == 409) {
let errors = {}; let errors = {};
for (let code of error.error) { for (let code of error.error) {
errors[code.field] = errors[code.field] || {}; errors[code.field] = errors[code.field] || {};
@@ -1,4 +1,5 @@
<div class="flex column fill"> <div class="flex column fill">
@if (turnovers && !turnovers.error) {
<div class="flex wrap filter-container"> <div class="flex wrap filter-container">
<a mat-icon-button (click)="filterOpen=!filterOpen" title="{{'turnovers.filter' | i18n}}" <a mat-icon-button (click)="filterOpen=!filterOpen" title="{{'turnovers.filter' | i18n}}"
[color]="filterOpen ? 'primary': 'accent'"> [color]="filterOpen ? 'primary': 'accent'">
@@ -35,11 +36,13 @@
</form> </form>
} }
<span class="spacer"></span> <span class="spacer"></span>
<a class="margin" mat-icon-button (click)="export()" title="{{'turnovers.export' | i18n}}" color="primary" [disabled]="!turnovers.total"> <a class="margin" mat-icon-button (click)="export()" title="{{'turnovers.export' | i18n}}" color="primary"
[disabled]="!turnovers.total">
<mat-icon>file_download</mat-icon> <mat-icon>file_download</mat-icon>
</a> </a>
</div> </div>
}
<ui-turnovers #uiTurnovers class="flex column grow" [turnovers]="turnovers" [overview]="overview" (page)="applyPage($event)" <ui-turnovers #uiTurnovers class="flex column grow" [turnovers]="turnovers" [overview]="overview"
(sort)="applySort($event)"></ui-turnovers> (page)="applyPage($event)" (sort)="applySort($event)"></ui-turnovers>
</div> </div>
@@ -1,6 +1,6 @@
<div class="container"> <div class="container">
<div class="flex column fill center middle"> <div class="flex column fill center middle">
<mat-card class="warn box"> <mat-card class="warn error box">
<mat-card-header> <mat-card-header>
<mat-card-title>503</mat-card-title> <mat-card-title>503</mat-card-title>
<mat-card-subtitle>{{'service-unavailable' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'service-unavailable' | i18n}}</mat-card-subtitle>
@@ -1,21 +1,22 @@
mat-form-field { mat-form-field {
display: block; display: block;
margin: 20px 0 !important; margin: 20px 0 !important;
}
form {
margin: 5px;
min-width: 390px;
@media screen and (min-width: 576px) {
max-width: 100%;
} }
form {
margin: 5px; @media screen and (min-width: 768px) {
min-width: 390px; max-width: 80%;
margin: 15px;
@media screen and (min-width: 576px) { }
max-width: 100%;
} @media screen and (min-width: 992px) {
max-width: 50%;
@media screen and (min-width: 768px) { }
max-width: 80%; }
margin: 15px;
}
@media screen and (min-width: 992px) {
max-width: 50%;
}
}
+14 -12
View File
@@ -1,17 +1,19 @@
<div class="flex column fill"> <div class="flex column fill">
@if (users && users.error) { @if (users && users.error) {
<div class="flex column fill"> <div class="container">
<mat-card class="accent box"> <div class="flex column fill center middle">
<mat-card-header> <mat-card class="accent error box">
<mat-card-title>{{ 'users.error.' + users.error.status | i18n}}</mat-card-title> <mat-card-header>
<mat-card-subtitle>{{'users.error' | i18n}}</mat-card-subtitle> <mat-card-title>{{ 'users.error.' + users.error.status | i18n}}</mat-card-title>
</mat-card-header> <mat-card-subtitle>{{'users.error' | i18n}}</mat-card-subtitle>
<mat-card-content> </mat-card-header>
<p> <mat-card-content>
{{ 'users.error.' + users.error.status + '.text' | i18n}} <p>
</p> {{ 'users.error.' + users.error.status + '.text' | i18n}}
</mat-card-content> </p>
</mat-card> </mat-card-content>
</mat-card>
</div>
</div> </div>
} }
+31 -13
View File
@@ -1,20 +1,22 @@
@if (turnovers && turnovers.error) { @if (turnovers && turnovers.error) {
<div class="flex column fill"> <div class="container">
<mat-card class="accent box"> <div class="flex column fill center middle">
<mat-card-header> <mat-card class="accent error box">
<mat-card-title>{{ 'turnovers.error.' + turnovers.error.status | i18n}}</mat-card-title> <mat-card-header>
<mat-card-subtitle>{{'turnovers.error' | i18n}}</mat-card-subtitle> <mat-card-title>{{ 'turnovers.error.' + turnovers.error.status | i18n}}</mat-card-title>
</mat-card-header> <mat-card-subtitle>{{'turnovers.error' | i18n}}</mat-card-subtitle>
<mat-card-content> </mat-card-header>
<p> <mat-card-content>
{{ 'turnovers.error.' + turnovers.error.status + '.text' | i18n}} <p>
</p> {{ 'turnovers.error.' + turnovers.error.status + '.text' | i18n}}
</mat-card-content> </p>
</mat-card> </mat-card-content>
</mat-card>
</div>
</div> </div>
} }
@if (turnovers) { @if (turnovers && !turnovers.error) {
@if (turnovers.total == 0) { @if (turnovers.total == 0) {
<mat-list> <mat-list>
@@ -69,6 +71,22 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="giftcardPrice">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
<span class="spacer"></span>
<span>{{'turnover.giftcard.price' | i18n}}</span>
</th>
<td mat-cell *matCellDef="let turnover">
<div class="flex">
<span class="spacer"></span>
<span>{{turnover.giftcardPrice | number: '1.2-2'}}</span>
@if (turnover.giftcardPrice) {
<span>&nbsp;{{'turnover.price.suffix' | i18n}}</span>
}
</div>
</td>
</ng-container>
<ng-container matColumnDef="timeInvestment"> <ng-container matColumnDef="timeInvestment">
<th mat-header-cell *matHeaderCellDef mat-sort-header> <th mat-header-cell *matHeaderCellDef mat-sort-header>
<span class="spacer"></span> <span class="spacer"></span>
@@ -43,7 +43,7 @@ export class UiTurnovers implements OnInit {
if (width < 992) { if (width < 992) {
this.columns = ['customer', 'price', 'motif']; this.columns = ['customer', 'price', 'motif'];
} else { } else {
this.columns = ['customer', 'motif', 'price', 'timeInvestment', 'remark', 'materialConsumption', 'created', 'updated']; this.columns = ['customer', 'motif', 'price', 'giftcardPrice', 'timeInvestment', 'remark', 'materialConsumption', 'created', 'updated'];
} }
if (this.username) { if (this.username) {
this.columns.unshift('username'); this.columns.unshift('username');
@@ -62,6 +62,8 @@ export class UiTurnovers implements OnInit {
this.i18n.get('turnover.customer'), this.i18n.get('turnover.customer'),
this.i18n.get('turnover.motif'), this.i18n.get('turnover.motif'),
this.i18n.get('turnover.price'), this.i18n.get('turnover.price'),
this.i18n.get('turnover.giftcard.number'),
this.i18n.get('turnover.giftcard.price'),
this.i18n.get('turnover.timeInvestment'), this.i18n.get('turnover.timeInvestment'),
this.i18n.get('turnover.remark'), this.i18n.get('turnover.remark'),
this.i18n.get('turnover.materialConsumption'), this.i18n.get('turnover.materialConsumption'),
@@ -79,6 +81,8 @@ export class UiTurnovers implements OnInit {
turnover.customer, turnover.customer,
turnover.motif, turnover.motif,
turnover.price.toFixed(2), turnover.price.toFixed(2),
turnover.giftcardNumber,
turnover.giftcardPrice && turnover.giftcardPrice.toFixed(2) || '',
turnover.timeInvestment.toFixed(1), turnover.timeInvestment.toFixed(1),
turnover.remark, turnover.remark,
turnover.materialConsumption, turnover.materialConsumption,
+28 -3
View File
@@ -107,7 +107,9 @@
}, },
"customer": { "customer": {
".": "Kunde", ".": "Kunde",
"error": "Angabe von Kunde erforderlich" "error": {
"required": "Angabe von Kunde erforderlich"
}
}, },
"dueDate": { "dueDate": {
".": "Fälligkeitsdatum", ".": "Fälligkeitsdatum",
@@ -117,15 +119,35 @@
}, },
"delete": "Löschen", "delete": "Löschen",
"edit": "Buchung bearbeiten", "edit": "Buchung bearbeiten",
"giftcard": {
".": "Gutschein",
"number": {
".": "Gutscheinnummer",
"error": ""
},
"price": {
".": "Gutscheinwert",
"error": {
"GREATER_THAN_PRICE": "Gutscheinwert übersteigt Preis",
"min": "Üngültiger Wert"
}
},
"priceUsage": "Gutscheinanteil"
},
"info": "Neue Buchung erstellen", "info": "Neue Buchung erstellen",
"materialConsumption": "Materialverbrauch", "materialConsumption": "Materialverbrauch",
"motif": { "motif": {
".": "Motiv", ".": "Motiv",
"error": "Angabe von Motiv erforderlich" "error": {
"required": "Angabe von Motiv erforderlich"
}
}, },
"price": { "price": {
".": "Preis", ".": "Preis",
"error": "Angabe des Preises erforderlich", "error": {
"min": "Üngültiger Wert",
"required": "Angabe des Preises erforderlich"
},
"suffix": "€", "suffix": "€",
"total": "Umsatz: {0} €" "total": "Umsatz: {0} €"
}, },
@@ -133,6 +155,9 @@
"success": "Erfolgreich gespeichert", "success": "Erfolgreich gespeichert",
"timeInvestment": { "timeInvestment": {
".": "Zeiteinsatz", ".": "Zeiteinsatz",
"error": {
"min": "Üngültiger Wert"
},
"suffix": "Std.", "suffix": "Std.",
"total": "Zeiteinsatz: {0} Std." "total": "Zeiteinsatz: {0} Std."
}, },
+5
View File
@@ -78,6 +78,11 @@ app-root {
background-color: #fafafa; background-color: #fafafa;
} }
mat-card.error {
width: 400px;
max-width: 100%;
}
ui-main { ui-main {
position: fixed; position: fixed;
top: 0; top: 0;