Basic Invoice PDF for JasperReports
Secured billing invoice package using a Jasper .jrxml template and an annotated DTO. RBAC, watermark and 30-minute expiry are wired in.
Package metadata
Template file
classpath:/reports/templates/invoice-basic.jrxml
File-name pattern
invoice-${invoiceNumber}.pdf
Provider module
report-boot-jasper
Template type
.jrxml
What's inside the package
- InvoiceReportDto.java
- InvoiceItemDto.java
- invoice-basic.jrxml
Annotations used
Every annotation declared on the DTO, with a paste-ready example.
| Annotation | Description |
|---|---|
| @ReportTemplate | Template identity and output contract. @ReportTemplate(code = "invoice-basic", title = "Basic Invoice", templateFile = "classpath:/reports/templates/invoice-basic.jrxml", output = ReportOutput.PDF) |
| @ReportEngineType | Selects the Jasper renderer. @ReportEngineType(ReportEngine.JASPER) |
| @ReportSecured | Watermark + 30 minute download expiry. @ReportSecured(watermark = true, expiryMinutes = 30) |
| @ReportAccess | Required role for generate / download. @ReportAccess(role = "ROLE_REPORT_ADMIN") |
| @ReportField | Maps a DTO field to a template parameter. @ReportField("invoiceNumber") |
| @ReportTable | Tabular data source for invoice line items. @ReportTable("items") |
Field contract
Template keys ↔ DTO fields. Use this when designing or editing the underlying template file.
| Template key | Source | Type | Required |
|---|---|---|---|
| invoiceNumber | @ReportField | String | Yes |
| customerName | @ReportField | String | Yes |
| invoiceDate | @ReportField | LocalDate | No |
| totalAmount | @ReportField | BigDecimal | No |
| items | @ReportTable | List<InvoiceItemDto> | No |
| items.itemName | row @ReportField | String | No |
| items.quantity | row @ReportField | Integer | No |
| items.unitPrice | row @ReportField | BigDecimal | No |
| items.total | row @ReportField | BigDecimal | No |
Ready-to-use DTO
@ReportTemplate(
code = "invoice-basic",
title = "Basic Invoice",
templateFile = "classpath:/reports/templates/invoice-basic.jrxml",
output = ReportOutput.PDF
)
@ReportEngineType(ReportEngine.JASPER)
@ReportSecured(watermark = true, expiryMinutes = 30)
@ReportFileName("invoice-${invoiceNumber}.pdf")
@ReportAccess(role = "ROLE_REPORT_ADMIN")
@ReportTitle("Advanced Invoice Report")
@ReportDescription("Demo invoice report with role-based access control and extended metadata.")
@ReportVersion("1.0.0")
@ReportCategory("Billing")
public class InvoiceReportDto {
@ReportField("invoiceNumber")
@ReportRequired
@ReportSensitive
private String invoiceNumber;
@ReportField("customerName")
@ReportRequired
private String customerName;
@ReportField("invoiceDate")
@ReportDateFormat("yyyy-MM-dd")
private LocalDate invoiceDate;
@ReportField("totalAmount")
@ReportFormat(pattern = "#,##0.00")
@ReportCurrency("USD")
@ReportLocale("en_US")
private BigDecimal totalAmount;
@ReportTable("items")
private List<InvoiceItemDto> items;
}
// --- Shared row DTO ---
public class InvoiceItemDto {
@ReportField("itemName") private String itemName;
@ReportField("quantity") private Integer quantity;
@ReportField("unitPrice") private BigDecimal unitPrice;
@ReportField("total") private BigDecimal total;
// getters / setters
}Controller example
Standard generate + download flow. Returns a JSON body with the report-order UUID and a download URL.
@RestController
@RequestMapping("/api/reports/jasper/invoice")
public class JasperInvoiceReportController {
private final ReportService reportService;
public JasperInvoiceReportController(ReportService reportService) {
this.reportService = reportService;
}
@PostMapping
public ResponseEntity<Map<String, Object>> generate(@RequestBody InvoiceReportDto dto) {
GeneratedReport report = reportService.generate(dto);
String url = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/api/reports/jasper/invoice/")
.path(report.getReportOrderUuid().toString())
.path("/download")
.toUriString();
return ResponseEntity.status(HttpStatus.CREATED)
.body(Map.of(
"reportOrderUuid", report.getReportOrderUuid(),
"fileName", report.getFileName(),
"contentType", report.getContentType(),
"generatedAt", report.getGeneratedAt(),
"downloadUrl", url
));
}
@GetMapping("/{uuid}/download")
public ResponseEntity<byte[]> download(@PathVariable UUID uuid) {
GeneratedReport report = reportService.download(uuid);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + report.getFileName() + "\"")
.contentType(MediaType.parseMediaType(report.getContentType()))
.body(report.getContent());
}
}
// File extension for this provider: .jrxmlSample request
{
"invoiceNumber": "INV-1001",
"customerName": "ACME Corp",
"invoiceDate": "2026-05-31",
"totalAmount": 1200.50,
"items": [
{ "itemName": "Implementation package", "quantity": 1, "unitPrice": 950.00, "total": 950.00 },
{ "itemName": "Support hours", "quantity": 5, "unitPrice": 50.10, "total": 250.50 }
]
}Security notes
- Downloads are served by report-order UUID, never by file path.
- Expiry is enforced after 30 minutes (@ReportSecured).
- Access requires role
ROLE_REPORT_ADMIN(@ReportAccess). - Watermark metadata is applied at render time (@ReportSecured(watermark = true)).
AI installer prompt
Paste this prompt into Cursor, Copilot Chat or any IDE assistant. It will add dependencies, copy the DTO + template, register the controller and respect the security policy declared on the DTO. Each marketplace package ships its own curated prompt.
You are a Report-Boot assistant embedded in the developer's IDE.
Goal: integrate the "InvoiceReportDto" template into the user's Spring Boot project.
Steps:
1. Confirm Java 17+ and Spring Boot 3.x are present.
2. Add Maven dependencies:
- com.reportboot:report-boot-spring-boot-starter:0.1.0-MVP
- com.reportboot:report-boot-jasper:0.1.0-MVP
- the matching third-party engine dependency (the user is responsible for its license).
3. Copy invoice-basic.jrxml into src/main/resources/reports/templates/.
4. Copy InvoiceReportDto.java and InvoiceItemDto.java into the user's DTO package and adjust the package declaration.
5. Inject ReportService and call reportService.generate(dto) then expose a download endpoint by UUID.
6. Surface the secured download URL and respect the expiry / role declared by the @ReportSecured and @ReportAccess annotations.
7. Verify with a smoke test POST using the provided sample JSON.
Output a concise change-list and the exact files to create or modify. Do not invent annotations; only use the ones declared on the DTO.