produzione 1 #9

Merged
JoeKung merged 135 commits from dev into main 2026-03-03 09:58:04 +01:00
11 changed files with 80 additions and 29 deletions
Showing only changes of commit 57f6301e03 - Show all commits

View File

@@ -212,15 +212,16 @@ public class OrderController {
@GetMapping("/{orderId}/twint") @GetMapping("/{orderId}/twint")
public ResponseEntity<Map<String, String>> getTwintPayment(@PathVariable UUID orderId) { public ResponseEntity<Map<String, String>> getTwintPayment(@PathVariable UUID orderId) {
if (!orderRepo.existsById(orderId)) { Order order = orderRepo.findById(orderId).orElse(null);
if (order == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
byte[] qrPng = twintPaymentService.generateQrPng(360); byte[] qrPng = twintPaymentService.generateQrPng(order, 360);
String qrDataUri = "data:image/png;base64," + Base64.getEncoder().encodeToString(qrPng); String qrDataUri = "data:image/png;base64," + Base64.getEncoder().encodeToString(qrPng);
Map<String, String> data = new HashMap<>(); Map<String, String> data = new HashMap<>();
data.put("paymentUrl", twintPaymentService.getTwintPaymentUrl()); data.put("paymentUrl", twintPaymentService.getTwintPaymentUrl(order));
data.put("openUrl", "/api/orders/" + orderId + "/twint/open"); data.put("openUrl", "/api/orders/" + orderId + "/twint/open");
data.put("qrImageUrl", "/api/orders/" + orderId + "/twint/qr"); data.put("qrImageUrl", "/api/orders/" + orderId + "/twint/qr");
data.put("qrImageDataUri", qrDataUri); data.put("qrImageDataUri", qrDataUri);
@@ -229,12 +230,13 @@ public class OrderController {
@GetMapping("/{orderId}/twint/open") @GetMapping("/{orderId}/twint/open")
public ResponseEntity<Void> openTwintPayment(@PathVariable UUID orderId) { public ResponseEntity<Void> openTwintPayment(@PathVariable UUID orderId) {
if (!orderRepo.existsById(orderId)) { Order order = orderRepo.findById(orderId).orElse(null);
if (order == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.status(302) return ResponseEntity.status(302)
.location(URI.create(twintPaymentService.getTwintPaymentUrl())) .location(URI.create(twintPaymentService.getTwintPaymentUrl(order)))
.build(); .build();
} }
@@ -243,12 +245,13 @@ public class OrderController {
@PathVariable UUID orderId, @PathVariable UUID orderId,
@RequestParam(defaultValue = "320") int size @RequestParam(defaultValue = "320") int size
) { ) {
if (!orderRepo.existsById(orderId)) { Order order = orderRepo.findById(orderId).orElse(null);
if (order == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
int normalizedSize = Math.max(200, Math.min(size, 600)); int normalizedSize = Math.max(200, Math.min(size, 600));
byte[] png = twintPaymentService.generateQrPng(normalizedSize); byte[] png = twintPaymentService.generateQrPng(order, normalizedSize);
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG) .contentType(MediaType.IMAGE_PNG)

View File

@@ -71,7 +71,7 @@ public class QuoteSessionController {
session.setPricingVersion("v1"); session.setPricingVersion("v1");
// Default material/settings will be set when items are added or updated? // Default material/settings will be set when items are added or updated?
// For now set safe defaults // For now set safe defaults
session.setMaterialCode("pla_basic"); session.setMaterialCode("PLA");
session.setSupportsEnabled(false); session.setSupportsEnabled(false);
session.setCreatedAt(OffsetDateTime.now()); session.setCreatedAt(OffsetDateTime.now());
session.setExpiresAt(OffsetDateTime.now().plusDays(30)); session.setExpiresAt(OffsetDateTime.now().plusDays(30));
@@ -125,6 +125,15 @@ public class QuoteSessionController {
// Apply Basic/Advanced Logic // Apply Basic/Advanced Logic
applyPrintSettings(settings); applyPrintSettings(settings);
// Update session global settings from the most recent item added
session.setMaterialCode(settings.getMaterial());
session.setNozzleDiameterMm(BigDecimal.valueOf(settings.getNozzleDiameter() != null ? settings.getNozzleDiameter() : 0.4));
session.setLayerHeightMm(BigDecimal.valueOf(settings.getLayerHeight() != null ? settings.getLayerHeight() : 0.2));
session.setInfillPattern(settings.getInfillPattern());
session.setInfillPercent(settings.getInfillDensity() != null ? settings.getInfillDensity().intValue() : 20);
session.setSupportsEnabled(settings.getSupportsEnabled() != null ? settings.getSupportsEnabled() : false);
sessionRepo.save(session);
// REAL SLICING // REAL SLICING
// 1. Pick Machine (default to first active or specific) // 1. Pick Machine (default to first active or specific)
PrinterMachine machine = machineRepo.findFirstByIsActiveTrue() PrinterMachine machine = machineRepo.findFirstByIsActiveTrue()

View File

@@ -15,6 +15,7 @@ public class PrintSettingsDto {
private String quality; // "draft", "standard", "high" private String quality; // "draft", "standard", "high"
// Advanced Mode (Optional in Basic) // Advanced Mode (Optional in Basic)
private Double nozzleDiameter;
private Double layerHeight; private Double layerHeight;
private Double infillDensity; private Double infillDensity;
private String infillPattern; private String infillPattern;

View File

@@ -21,14 +21,37 @@ public class TwintPaymentService {
this.twintPaymentUrl = twintPaymentUrl; this.twintPaymentUrl = twintPaymentUrl;
} }
public String getTwintPaymentUrl() { public String getTwintPaymentUrl(com.printcalculator.entity.Order order) {
return twintPaymentUrl; StringBuilder urlBuilder = new StringBuilder(twintPaymentUrl);
if (order != null) {
if (order.getTotalChf() != null) {
urlBuilder.append("&amount=").append(order.getTotalChf().toPlainString());
} }
public byte[] generateQrPng(int sizePx) { String orderNumber = order.getOrderNumber();
if (orderNumber == null && order.getId() != null) {
orderNumber = order.getId().toString();
}
if (orderNumber != null) {
try { try {
urlBuilder.append("&trxInfo=").append(order.getId());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
return urlBuilder.toString();
}
public byte[] generateQrPng(com.printcalculator.entity.Order order, int sizePx) {
try {
String url = getTwintPaymentUrl(order);
// Use High Error Correction for financial QR codes // Use High Error Correction for financial QR codes
QrCode qrCode = QrCode.encodeText(twintPaymentUrl, QrCode.Ecc.HIGH); QrCode qrCode = QrCode.encodeText(url, QrCode.Ecc.HIGH);
// Standard QR quiet zone is 4 modules // Standard QR quiet zone is 4 modules
int borderModules = 4; int borderModules = 4;

View File

@@ -31,7 +31,7 @@ payment.twint.url=${TWINT_PAYMENT_URL:https://go.twint.ch/1/e/tw?tw=acq.gERQQytO
spring.mail.host=${MAIL_HOST:mail.infomaniak.com} spring.mail.host=${MAIL_HOST:mail.infomaniak.com}
spring.mail.port=${MAIL_PORT:587} spring.mail.port=${MAIL_PORT:587}
spring.mail.username=${MAIL_USERNAME:info@3d-fab.ch} spring.mail.username=${MAIL_USERNAME:info@3d-fab.ch}
spring.mail.password=${MAIL_PASSWORD:ht*44k+Tq39R+R-O} spring.mail.password=${MAIL_PASSWORD:}
spring.mail.properties.mail.smtp.auth=${MAIL_SMTP_AUTH:false} spring.mail.properties.mail.smtp.auth=${MAIL_SMTP_AUTH:false}
spring.mail.properties.mail.smtp.starttls.enable=${MAIL_SMTP_STARTTLS:false} spring.mail.properties.mail.smtp.starttls.enable=${MAIL_SMTP_STARTTLS:false}

View File

@@ -49,8 +49,12 @@ export class CalculatorPageComponent implements OnInit {
this.route.queryParams.subscribe(params => { this.route.queryParams.subscribe(params => {
const sessionId = params['session']; const sessionId = params['session'];
if (sessionId) { if (sessionId) {
// Avoid reloading if we just calculated this session
const currentRes = this.result();
if (!currentRes || currentRes.sessionId !== sessionId) {
this.loadSession(sessionId); this.loadSession(sessionId);
} }
}
}); });
} }

View File

@@ -215,7 +215,7 @@ export class QuoteEstimatorService {
const settings = { const settings = {
complexityMode: request.mode.toUpperCase(), complexityMode: request.mode.toUpperCase(),
material: this.mapMaterial(request.material), material: request.material,
quality: request.quality, quality: request.quality,
supportsEnabled: request.supportEnabled, supportsEnabled: request.supportEnabled,
color: item.color || '#FFFFFF', color: item.color || '#FFFFFF',
@@ -317,14 +317,6 @@ export class QuoteEstimatorService {
}); });
} }
private mapMaterial(mat: string): string {
const m = mat.toUpperCase();
if (m.includes('PLA')) return 'pla_basic';
if (m.includes('PETG')) return 'petg_basic';
if (m.includes('TPU')) return 'tpu_95a';
return 'pla_basic';
}
// Consultation Data Transfer // Consultation Data Transfer
private pendingConsultation = signal<{files: File[], message: string} | null>(null); private pendingConsultation = signal<{files: File[], message: string} | null>(null);

View File

@@ -71,10 +71,17 @@
<img *ngIf="twintQrUrl()" class="twint-qr" [src]="getTwintQrUrl()" (error)="onTwintQrError()" alt="TWINT payment QR" /> <img *ngIf="twintQrUrl()" class="twint-qr" [src]="getTwintQrUrl()" (error)="onTwintQrError()" alt="TWINT payment QR" />
<p>{{ 'PAYMENT.TWINT_DESC' | translate }}</p> <p>{{ 'PAYMENT.TWINT_DESC' | translate }}</p>
<p class="billing-hint">{{ 'PAYMENT.BILLING_INFO_HINT' | translate }}</p> <p class="billing-hint">{{ 'PAYMENT.BILLING_INFO_HINT' | translate }}</p>
<div class="twint-mobile-action"> <div class="twint-mobile-action twint-button-container">
<app-button (click)="openTwintPayment()" [fullWidth]="true"> <button style="width: auto; height: 58px;
{{ 'PAYMENT.TWINT_OPEN' | translate }} border-radius: 6px;
</app-button> display: flex;
justify-content: center;
cursor: pointer;
background-color: transparent;
border: none;
align-items: center;" (click)="openTwintPayment()">
<img style="width: auto; height: 58px" alt="Embedded TWINT button" [src]="getTwintButtonImageUrl()"/>
</button>
</div> </div>
<p class="amount">{{ 'PAYMENT.TOTAL' | translate }}: {{ o.totalChf | currency:'CHF' }}</p> <p class="amount">{{ 'PAYMENT.TOTAL' | translate }}: {{ o.totalChf | currency:'CHF' }}</p>
</div> </div>

View File

@@ -150,6 +150,8 @@
width: 100%; width: 100%;
max-width: 320px; max-width: 320px;
margin-top: var(--space-3); margin-top: var(--space-3);
display: flex;
justify-content: center;
} }
.amount { .amount {

View File

@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { AppButtonComponent } from '../../shared/components/app-button/app-button.component'; import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
import { AppCardComponent } from '../../shared/components/app-card/app-card.component'; import { AppCardComponent } from '../../shared/components/app-card/app-card.component';
import { QuoteEstimatorService } from '../calculator/services/quote-estimator.service'; import { QuoteEstimatorService } from '../calculator/services/quote-estimator.service';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
@Component({ @Component({
@@ -18,6 +18,7 @@ export class OrderComponent implements OnInit {
private route = inject(ActivatedRoute); private route = inject(ActivatedRoute);
private router = inject(Router); private router = inject(Router);
private quoteService = inject(QuoteEstimatorService); private quoteService = inject(QuoteEstimatorService);
private translate = inject(TranslateService);
orderId: string | null = null; orderId: string | null = null;
selectedPaymentMethod: 'twint' | 'bill' | null = 'twint'; selectedPaymentMethod: 'twint' | 'bill' | null = 'twint';
@@ -101,6 +102,15 @@ export class OrderComponent implements OnInit {
return this.twintQrUrl() ?? ''; return this.twintQrUrl() ?? '';
} }
getTwintButtonImageUrl(): string {
const lang = this.translate.currentLang;
if (lang === 'de') {
return 'https://go.twint.ch/static/img/button_dark_de.svg';
}
// Default to EN for everything else (it, fr, en) as instructed or if not DE
return 'https://go.twint.ch/static/img/button_dark_en.svg';
}
onTwintQrError(): void { onTwintQrError(): void {
this.twintQrUrl.set(null); this.twintQrUrl.set(null);
} }

View File

@@ -262,7 +262,7 @@
"BANK_OWNER": "Titolare", "BANK_OWNER": "Titolare",
"BANK_IBAN": "IBAN", "BANK_IBAN": "IBAN",
"BANK_REF": "Riferimento", "BANK_REF": "Riferimento",
"BILLING_INFO_HINT": "Aggiungi le informazioni uguali a quelle della fatturazione.", "BILLING_INFO_HINT": "Abbiamo compilato i campi per te, per favore non modificare il motivo del pagamento",
"DOWNLOAD_QR": "Scarica QR-Fattura (PDF)", "DOWNLOAD_QR": "Scarica QR-Fattura (PDF)",
"CONFIRM": "Ho completato il pagamento", "CONFIRM": "Ho completato il pagamento",
"SUMMARY_TITLE": "Riepilogo Ordine", "SUMMARY_TITLE": "Riepilogo Ordine",