feat(back-end front-end): uuid truncated for better UX
This commit is contained in:
@@ -29,8 +29,11 @@ public class DevEmailTestController {
|
||||
public ResponseEntity<String> testTemplate() {
|
||||
Context context = new Context();
|
||||
Map<String, Object> templateData = new HashMap<>();
|
||||
UUID orderId = UUID.randomUUID();
|
||||
templateData.put("customerName", "Mario Rossi");
|
||||
templateData.put("orderId", UUID.randomUUID());
|
||||
templateData.put("orderId", orderId);
|
||||
templateData.put("orderNumber", orderId.toString().split("-")[0]);
|
||||
templateData.put("orderDetailsUrl", "https://tuosito.it/ordine/" + orderId);
|
||||
templateData.put("orderDate", OffsetDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")));
|
||||
templateData.put("totalCost", "45.50");
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public class OrderController {
|
||||
vars.put("sellerAddressLine2", "Sede Bienne, Svizzera");
|
||||
vars.put("sellerEmail", "info@3dfab.ch");
|
||||
|
||||
vars.put("invoiceNumber", "INV-" + order.getId().toString().substring(0, 8).toUpperCase());
|
||||
vars.put("invoiceNumber", "INV-" + getDisplayOrderNumber(order).toUpperCase());
|
||||
vars.put("invoiceDate", order.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
vars.put("dueDate", order.getCreatedAt().plusDays(7).format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
|
||||
@@ -187,7 +187,7 @@ public class OrderController {
|
||||
byte[] pdf = invoiceService.generateInvoicePdfBytesFromTemplate(vars, qrBillSvg);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Disposition", "attachment; filename=\"invoice-" + orderId + ".pdf\"")
|
||||
.header("Content-Disposition", "attachment; filename=\"invoice-" + getDisplayOrderNumber(order) + ".pdf\"")
|
||||
.contentType(MediaType.APPLICATION_PDF)
|
||||
.body(pdf);
|
||||
}
|
||||
@@ -249,6 +249,7 @@ public class OrderController {
|
||||
private OrderDto convertToDto(Order order, List<OrderItem> items) {
|
||||
OrderDto dto = new OrderDto();
|
||||
dto.setId(order.getId());
|
||||
dto.setOrderNumber(getDisplayOrderNumber(order));
|
||||
dto.setStatus(order.getStatus());
|
||||
dto.setCustomerEmail(order.getCustomerEmail());
|
||||
dto.setCustomerPhone(order.getCustomerPhone());
|
||||
@@ -306,4 +307,12 @@ public class OrderController {
|
||||
return dto;
|
||||
}
|
||||
|
||||
private String getDisplayOrderNumber(Order order) {
|
||||
String orderNumber = order.getOrderNumber();
|
||||
if (orderNumber != null && !orderNumber.isBlank()) {
|
||||
return orderNumber;
|
||||
}
|
||||
return order.getId() != null ? order.getId().toString() : "unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.UUID;
|
||||
|
||||
public class OrderDto {
|
||||
private UUID id;
|
||||
private String orderNumber;
|
||||
private String status;
|
||||
private String customerEmail;
|
||||
private String customerPhone;
|
||||
@@ -27,6 +28,9 @@ public class OrderDto {
|
||||
public UUID getId() { return id; }
|
||||
public void setId(UUID id) { this.id = id; }
|
||||
|
||||
public String getOrderNumber() { return orderNumber; }
|
||||
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
|
||||
@@ -138,6 +138,16 @@ public class Order {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Transient
|
||||
public String getOrderNumber() {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
String rawId = id.toString();
|
||||
int dashIndex = rawId.indexOf('-');
|
||||
return dashIndex > 0 ? rawId.substring(0, dashIndex) : rawId;
|
||||
}
|
||||
|
||||
public QuoteSession getSourceQuoteSession() {
|
||||
return sourceQuoteSession;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ public class OrderEmailListener {
|
||||
@Value("${app.mail.admin.address:}")
|
||||
private String adminMailAddress;
|
||||
|
||||
@Value("${app.frontend.base-url:http://localhost:4200}")
|
||||
private String frontendBaseUrl;
|
||||
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
|
||||
@@ -48,12 +51,14 @@ public class OrderEmailListener {
|
||||
Map<String, Object> templateData = new HashMap<>();
|
||||
templateData.put("customerName", order.getCustomer().getFirstName());
|
||||
templateData.put("orderId", order.getId());
|
||||
templateData.put("orderNumber", getDisplayOrderNumber(order));
|
||||
templateData.put("orderDetailsUrl", buildOrderDetailsUrl(order));
|
||||
templateData.put("orderDate", order.getCreatedAt().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")));
|
||||
templateData.put("totalCost", String.format("%.2f", order.getTotalChf()));
|
||||
|
||||
emailNotificationService.sendEmail(
|
||||
order.getCustomer().getEmail(),
|
||||
"Conferma Ordine #" + order.getId() + " - 3D-Fab",
|
||||
"Conferma Ordine #" + getDisplayOrderNumber(order) + " - 3D-Fab",
|
||||
"order-confirmation",
|
||||
templateData
|
||||
);
|
||||
@@ -63,15 +68,30 @@ public class OrderEmailListener {
|
||||
Map<String, Object> templateData = new HashMap<>();
|
||||
templateData.put("customerName", order.getCustomer().getFirstName() + " " + order.getCustomer().getLastName());
|
||||
templateData.put("orderId", order.getId());
|
||||
templateData.put("orderNumber", getDisplayOrderNumber(order));
|
||||
templateData.put("orderDetailsUrl", buildOrderDetailsUrl(order));
|
||||
templateData.put("orderDate", order.getCreatedAt().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")));
|
||||
templateData.put("totalCost", String.format("%.2f", order.getTotalChf()));
|
||||
|
||||
// Possiamo riutilizzare lo stesso template per ora o crearne uno ad-hoc in futuro
|
||||
emailNotificationService.sendEmail(
|
||||
adminMailAddress,
|
||||
"Nuovo Ordine Ricevuto #" + order.getId() + " - " + order.getCustomer().getLastName(),
|
||||
"Nuovo Ordine Ricevuto #" + getDisplayOrderNumber(order) + " - " + order.getCustomer().getLastName(),
|
||||
"order-confirmation",
|
||||
templateData
|
||||
);
|
||||
}
|
||||
|
||||
private String getDisplayOrderNumber(Order order) {
|
||||
String orderNumber = order.getOrderNumber();
|
||||
if (orderNumber != null && !orderNumber.isBlank()) {
|
||||
return orderNumber;
|
||||
}
|
||||
return order.getId() != null ? order.getId().toString() : "unknown";
|
||||
}
|
||||
|
||||
private String buildOrderDetailsUrl(Order order) {
|
||||
String baseUrl = frontendBaseUrl == null ? "" : frontendBaseUrl.replaceAll("/+$", "");
|
||||
return baseUrl + "/ordine/" + order.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public class OrderService {
|
||||
vars.put("sellerAddressLine2", "Sede Bienne, Svizzera");
|
||||
vars.put("sellerEmail", "info@3dfab.ch");
|
||||
|
||||
vars.put("invoiceNumber", "INV-" + order.getId().toString().substring(0, 8).toUpperCase());
|
||||
vars.put("invoiceNumber", "INV-" + getDisplayOrderNumber(order).toUpperCase());
|
||||
vars.put("invoiceDate", order.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
vars.put("dueDate", order.getCreatedAt().plusDays(7).format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
|
||||
@@ -305,4 +305,12 @@ public class OrderService {
|
||||
}
|
||||
return "stl";
|
||||
}
|
||||
|
||||
private String getDisplayOrderNumber(Order order) {
|
||||
String orderNumber = order.getOrderNumber();
|
||||
if (orderNumber != null && !orderNumber.isBlank()) {
|
||||
return orderNumber;
|
||||
}
|
||||
return order.getId() != null ? order.getId().toString() : "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ public class QrBillService {
|
||||
|
||||
// Reference
|
||||
// bill.setReference(QRBill.createCreditorReference("...")); // If using QRR
|
||||
bill.setUnstructuredMessage("Order " + order.getId());
|
||||
String orderRef = order.getOrderNumber() != null ? order.getOrderNumber() : order.getId().toString();
|
||||
bill.setUnstructuredMessage("Order " + orderRef);
|
||||
|
||||
return bill;
|
||||
}
|
||||
|
||||
@@ -39,3 +39,4 @@ spring.mail.properties.mail.smtp.starttls.enable=${MAIL_SMTP_STARTTLS:false}
|
||||
app.mail.from=${APP_MAIL_FROM:${MAIL_USERNAME:noreply@printcalculator.local}}
|
||||
app.mail.admin.enabled=${APP_MAIL_ADMIN_ENABLED:true}
|
||||
app.mail.admin.address=${APP_MAIL_ADMIN_ADDRESS:admin@printcalculator.local}
|
||||
app.frontend.base-url=${APP_FRONTEND_BASE_URL:http://localhost:4200}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Grazie per il tuo ordine!</h1>
|
||||
<h1>Grazie per il tuo ordine #<span th:text="${orderNumber}">00000000</span></h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Ciao <span th:text="${customerName}">Cliente</span>,</p>
|
||||
@@ -65,7 +65,7 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th>Numero Ordine:</th>
|
||||
<td th:text="${orderId}">#0000</td>
|
||||
<td th:text="${orderNumber}">00000000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Data:</th>
|
||||
@@ -78,6 +78,11 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Clicca qui per i dettagli:
|
||||
<a th:href="${orderDetailsUrl}" th:text="${orderDetailsUrl}">https://tuosito.it/ordine/00000000-0000-0000-0000-000000000000</a>
|
||||
</p>
|
||||
|
||||
<p>Se hai domande o dubbi, non esitare a contattarci.</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
|
||||
@@ -33,6 +33,10 @@ export const routes: Routes = [
|
||||
path: 'payment/:orderId',
|
||||
loadComponent: () => import('./features/payment/payment.component').then(m => m.PaymentComponent)
|
||||
},
|
||||
{
|
||||
path: 'ordine/:orderId',
|
||||
loadComponent: () => import('./features/payment/payment.component').then(m => m.PaymentComponent)
|
||||
},
|
||||
{
|
||||
path: 'order-confirmed/:orderId',
|
||||
loadComponent: () => import('./features/order-confirmed/order-confirmed.component').then(m => m.OrderConfirmedComponent)
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<app-card class="status-card">
|
||||
<div class="status-badge">{{ 'ORDER_CONFIRMED.STATUS' | translate }}</div>
|
||||
<h2>{{ 'ORDER_CONFIRMED.HEADING' | translate }}</h2>
|
||||
<p class="order-ref" *ngIf="orderId">
|
||||
{{ 'ORDER_CONFIRMED.ORDER_REF' | translate }}: <strong>#{{ orderId.substring(0, 8) }}</strong>
|
||||
<p class="order-ref" *ngIf="orderNumber">
|
||||
{{ 'ORDER_CONFIRMED.ORDER_REF' | translate }}: <strong>#{{ orderNumber }}</strong>
|
||||
</p>
|
||||
|
||||
<div class="message-block">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
|
||||
import { AppCardComponent } from '../../shared/components/app-card/app-card.component';
|
||||
import { QuoteEstimatorService } from '../calculator/services/quote-estimator.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-order-confirmed',
|
||||
@@ -15,14 +16,33 @@ import { AppCardComponent } from '../../shared/components/app-card/app-card.comp
|
||||
export class OrderConfirmedComponent implements OnInit {
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private quoteService = inject(QuoteEstimatorService);
|
||||
|
||||
orderId: string | null = null;
|
||||
orderNumber: string | null = null;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.orderId = this.route.snapshot.paramMap.get('orderId');
|
||||
if (!this.orderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.orderNumber = this.extractOrderNumber(this.orderId);
|
||||
this.quoteService.getOrder(this.orderId).subscribe({
|
||||
next: (order) => {
|
||||
this.orderNumber = order?.orderNumber ?? this.orderNumber;
|
||||
},
|
||||
error: () => {
|
||||
// Keep fallback derived from UUID when API is unavailable.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
goHome(): void {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
private extractOrderNumber(orderId: string): string {
|
||||
return orderId.split('-')[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<div class="bank-details">
|
||||
<p><strong>{{ 'PAYMENT.BANK_OWNER' | translate }}:</strong> 3D Fab Switzerland</p>
|
||||
<p><strong>{{ 'PAYMENT.BANK_IBAN' | translate }}:</strong> CH98 0000 0000 0000 0000 0</p>
|
||||
<p><strong>{{ 'PAYMENT.BANK_REF' | translate }}:</strong> {{ o.id }}</p>
|
||||
<p><strong>{{ 'PAYMENT.BANK_REF' | translate }}:</strong> {{ getDisplayOrderNumber(o) }}</p>
|
||||
<p class="billing-hint">{{ 'PAYMENT.BILLING_INFO_HINT' | translate }}</p>
|
||||
|
||||
<div class="qr-bill-actions">
|
||||
@@ -80,7 +80,7 @@
|
||||
<app-card class="sticky-card">
|
||||
<div class="card-header-simple">
|
||||
<h3>{{ 'PAYMENT.SUMMARY_TITLE' | translate }}</h3>
|
||||
<p class="order-id">#{{ o.id.substring(0, 8) }}</p>
|
||||
<p class="order-id">#{{ getDisplayOrderNumber(o) }}</p>
|
||||
</div>
|
||||
|
||||
<div class="summary-totals">
|
||||
|
||||
@@ -58,13 +58,16 @@ export class PaymentComponent implements OnInit {
|
||||
}
|
||||
|
||||
downloadInvoice() {
|
||||
if (!this.orderId) return;
|
||||
this.quoteService.getOrderInvoice(this.orderId).subscribe({
|
||||
const orderId = this.orderId;
|
||||
if (!orderId) return;
|
||||
this.quoteService.getOrderInvoice(orderId).subscribe({
|
||||
next: (blob) => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `invoice-${this.orderId}.pdf`;
|
||||
const fallbackOrderNumber = this.extractOrderNumber(orderId);
|
||||
const orderNumber = this.order()?.orderNumber ?? fallbackOrderNumber;
|
||||
a.download = `invoice-${orderNumber}.pdf`;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
},
|
||||
@@ -119,4 +122,18 @@ export class PaymentComponent implements OnInit {
|
||||
}
|
||||
this.router.navigate(['/order-confirmed', this.orderId]);
|
||||
}
|
||||
|
||||
getDisplayOrderNumber(order: any): string {
|
||||
if (order?.orderNumber) {
|
||||
return order.orderNumber;
|
||||
}
|
||||
if (order?.id) {
|
||||
return this.extractOrderNumber(order.id);
|
||||
}
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
private extractOrderNumber(orderId: string): string {
|
||||
return orderId.split('-')[0];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user