feat(back-end - front end): improvements in email and invoice rendering
All checks were successful
Build, Test and Deploy / test-backend (push) Successful in 37s
Build, Test and Deploy / build-and-push (push) Successful in 45s
Build, Test and Deploy / deploy (push) Successful in 10s

This commit is contained in:
2026-02-25 16:35:58 +01:00
parent fecb394272
commit c58d674a70
15 changed files with 317 additions and 102 deletions

View File

@@ -170,6 +170,16 @@ export class QuoteEstimatorService {
});
}
getOrderConfirmation(orderId: string): Observable<Blob> {
const headers: any = {};
// @ts-ignore
if (environment.basicAuth) headers['Authorization'] = 'Basic ' + btoa(environment.basicAuth);
return this.http.get(`${environment.apiUrl}/api/orders/${orderId}/confirmation`, {
headers,
responseType: 'blob'
});
}
getTwintPayment(orderId: string): Observable<any> {
const headers: any = {};
// @ts-ignore

View File

@@ -11,26 +11,26 @@
<div class="container">
<ng-container *ngIf="order() as o">
<div class="status-timeline mb-6">
<div class="timeline-step"
[class.active]="o.status === 'PENDING_PAYMENT' && o.paymentStatus !== 'REPORTED'"
<div class="timeline-step"
[class.active]="o.status === 'PENDING_PAYMENT' && o.paymentStatus !== 'REPORTED'"
[class.completed]="o.paymentStatus === 'REPORTED' || o.status !== 'PENDING_PAYMENT'">
<div class="circle">1</div>
<div class="label">{{ 'TRACKING.STEP_PENDING' | translate }}</div>
</div>
<div class="timeline-step"
[class.active]="o.paymentStatus === 'REPORTED' && o.status === 'PENDING_PAYMENT'"
<div class="timeline-step"
[class.active]="o.paymentStatus === 'REPORTED' && o.status === 'PENDING_PAYMENT'"
[class.completed]="o.status === 'PAID' || o.status === 'IN_PRODUCTION' || o.status === 'SHIPPED' || o.status === 'COMPLETED'">
<div class="circle">2</div>
<div class="label">{{ 'TRACKING.STEP_REPORTED' | translate }}</div>
</div>
<div class="timeline-step"
[class.active]="o.status === 'PAID' || o.status === 'IN_PRODUCTION'"
<div class="timeline-step"
[class.active]="o.status === 'PAID' || o.status === 'IN_PRODUCTION'"
[class.completed]="o.status === 'SHIPPED' || o.status === 'COMPLETED'">
<div class="circle">3</div>
<div class="label">{{ 'TRACKING.STEP_PRODUCTION' | translate }}</div>
</div>
<div class="timeline-step"
[class.active]="o.status === 'SHIPPED'"
<div class="timeline-step"
[class.active]="o.status === 'SHIPPED'"
[class.completed]="o.status === 'COMPLETED'">
<div class="circle">4</div>
<div class="label">{{ 'TRACKING.STEP_SHIPPED' | translate }}</div>
@@ -87,18 +87,15 @@ align-items: center;" (click)="openTwintPayment()">
</div>
</div>
<div class="payment-details fade-in" *ngIf="selectedPaymentMethod === 'bill'">
<div class="payment-details fade-in text-center" *ngIf="selectedPaymentMethod === 'bill'">
<div class="details-header">
<h4>{{ 'PAYMENT.BANK_TITLE' | translate }}</h4>
</div>
<div class="bank-details">
<p><strong>{{ 'PAYMENT.BANK_OWNER' | translate }}:</strong> Küng, Joe</p>
<p><strong>{{ 'PAYMENT.BANK_IBAN' | translate }}:</strong> CH74 0900 0000 1548 2158 1</p>
<p><strong>{{ 'PAYMENT.BANK_REF' | translate }}:</strong> {{ getDisplayOrderNumber(o) }}</p>
<p class="billing-hint">{{ 'PAYMENT.BILLING_INFO_HINT' | translate }}</p>
<br>
<div class="qr-bill-actions">
<app-button (click)="downloadInvoice()">
<app-button (click)="downloadQrInvoice()">
{{ 'PAYMENT.DOWNLOAD_QR' | translate }}
</app-button>
</div>
@@ -138,6 +135,7 @@ align-items: center;" (click)="openTwintPayment()">
<span>{{ o.totalChf | currency:'CHF' }}</span>
</div>
</div>
</app-card>
</div>
</div>

View File

@@ -58,21 +58,21 @@ export class OrderComponent implements OnInit {
this.selectedPaymentMethod = method;
}
downloadInvoice() {
downloadQrInvoice() {
const orderId = this.orderId;
if (!orderId) return;
this.quoteService.getOrderInvoice(orderId).subscribe({
this.quoteService.getOrderConfirmation(orderId).subscribe({
next: (blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
const fallbackOrderNumber = this.extractOrderNumber(orderId);
const orderNumber = this.order()?.orderNumber ?? fallbackOrderNumber;
a.download = `invoice-${orderNumber}.pdf`;
a.download = `qr-invoice-${orderNumber}.pdf`;
a.click();
window.URL.revokeObjectURL(url);
},
error: (err) => console.error('Failed to download invoice', err)
error: (err) => console.error('Failed to download QR invoice', err)
});
}

View File

@@ -199,6 +199,7 @@
"BANK_REF": "Reference",
"BILLING_INFO_HINT": "Add the same information used in billing.",
"DOWNLOAD_QR": "Download QR-Invoice (PDF)",
"DOWNLOAD_CONFIRMATION": "Download Confirmation (PDF)",
"CONFIRM": "I have completed the payment",
"SUMMARY_TITLE": "Order Summary",
"SUBTOTAL": "Subtotal",

View File

@@ -271,6 +271,7 @@
"BANK_REF": "Riferimento",
"BILLING_INFO_HINT": "Abbiamo compilato i campi per te, per favore non modificare il motivo del pagamento",
"DOWNLOAD_QR": "Scarica QR-Fattura (PDF)",
"DOWNLOAD_CONFIRMATION": "Scarica Conferma (PDF)",
"CONFIRM": "Ho completato il pagamento",
"SUMMARY_TITLE": "Riepilogo Ordine",
"SUBTOTAL": "Subtotale",