Skip to content

feat: implement item descriptions and VAT handling for invoices#65

Merged
fmazmz merged 15 commits intomainfrom
feature/Invoice-&-InvoiceItems
Jan 12, 2026
Merged

feat: implement item descriptions and VAT handling for invoices#65
fmazmz merged 15 commits intomainfrom
feature/Invoice-&-InvoiceItems

Conversation

@annikaholmqvist94
Copy link
Copy Markdown

@annikaholmqvist94 annikaholmqvist94 commented Jan 9, 2026

This PR introduces two major enhancements to the Invoice Management System: descriptive invoice items and manual VAT handling. It also refactors the update logic to support a partial update pattern.

Key Changes:
Descriptive Invoice Items:
Added name (String) to the InvoiceItem entity and corresponding DTOs.
Updated CLI to allow users to input a description (e.g., "Consulting Services") instead of only numeric values.

VAT Support:
Added vatAmount (BigDecimal) to Invoice to allow manual entry of VAT on a per-invoice basis.
Updated CreateInvoiceDTO and UpdateInvoiceDTO to carry VAT data.

Business Logic:
Enhanced recalcTotals() in the Invoice entity to automatically calculate the final amount as Subtotal + vatAmount.

Implemented a Partial Update pattern in InvoiceService. By allowing null values in the UpdateInvoiceDTO, we can now update specific parts of an invoice (like adding an item) without accidentally overwriting existing data like the due date or VAT.

CLI Improvements:
Synchronized all invoice management menus (add, update, remove items) with the new DTO structures.

Technical Notes:
The system now ensures that even when items are cleared and refreshed during an update, the name property is persisted.
The recalcTotals() method acts as the "Single Source of Truth" for the financial integrity of the invoice.

Closes #52

Summary by CodeRabbit

  • New Features
    • Added optional VAT rate on invoices with automatic VAT amount and total calculation.
    • Added item name field for clearer item display and input across UI/CLI flows.
  • Validation
    • Enforced VAT rate range (greater than 0% up to 100%) with input validation and error feedback.
  • Tests
    • Updated tests to cover new VAT and item-name behaviors.

✏️ Tip: You can customize this high-level summary in your review settings.

…als calculation, and adjust related DTOs and CLI inputs.
…ount` in `InvoiceDTO`, and adjust related mappings. Closes #52
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 9, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds VAT rate and VAT amount tracking to invoices and a required item name on invoice items; updates DTOs, entity fields, service logic, CLI menus, tests, and the application entry point accordingly.

Changes

Cohort / File(s) Summary
Entities / Domain
src/main/java/org/example/entity/invoice/Invoice.java, src/main/java/org/example/entity/invoice/InvoiceItem.java
Added vatRate: BigDecimal and vatAmount: BigDecimal usage in Invoice; added name: String column to InvoiceItem; updated invoice totals calculation to include VAT; changed @ManyToOne to fetch = FetchType.LAZY.
Input DTOs
src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java, src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
Inserted vatRate: BigDecimal into public record signatures (positioned before items); added @DecimalMin/@DecimalMax validation constraints and imports.
Output DTOs / Item DTO
src/main/java/org/example/entity/invoice/InvoiceDTO.java, src/main/java/org/example/entity/invoice/InvoiceItemDTO.java
Added vatRate and vatAmount to InvoiceDTO; added name: String (with @NotBlank) to InvoiceItemDTO and updated mapping methods to include these fields.
Service Layer
src/main/java/org/example/service/InvoiceService.java
In create/update flows, now consume vatRate from DTOs, validate its range, set invoice VAT rate, recalc totals, and set item names when mapping item DTOs to entities; added a client-id log line.
CLI / App Entry
src/main/java/org/example/App.java, src/main/java/org/example/cli/menu/InvoiceMenu.java, src/main/java/org/example/cli/menu/InvoiceItemMenu.java
Added App.main entry point; CLI prompts now accept optional VAT rate on invoice creation and item name on item add/update; DTO constructor usage updated to include new vatRate and item name parameters.
Tests
src/test/java/org/example/InvoiceServiceTest.java
Updated test fixtures/constructors to include vatRate and item name in DTOs; adjusted assertions where totals and signatures changed.

Sequence Diagram(s)

sequenceDiagram
  participant User as CLI User
  participant CLI as InvoiceMenu / InvoiceItemMenu
  participant Service as InvoiceService
  participant Repo as InvoiceRepository
  participant DB as Database

  User->>CLI: provide invoice data (number, dueDate, optional vatRate, items with name/qty/price)
  CLI->>Service: CreateInvoiceDTO(vatRate, items...)
  Service->>Service: validate vatRate (0.0 < vatRate <= 1.0)
  Service->>Repo: save Invoice entity (with vatRate, items)
  Repo->>DB: persist invoice + items (name stored)
  Repo-->>Service: persisted Invoice (id, stored fields)
  Service->>Service: recalcTotals (subTotal, vatAmount = vatRate * subTotal, total)
  Service-->>CLI: return InvoiceDTO (vatRate, vatAmount, items)
  CLI-->>User: display created invoice (including VAT and item names)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped in with a vatRate, tidy and keen,
Item names now sparkle, not just numbers on screen.
From CLI to DB, totals snugly align,
I nibble carrots while invoices balance just fine. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes include additional updates beyond issue requirements: vatRate validation in both CreateInvoiceDTO and UpdateInvoiceDTO, recalcTotals logic refactoring, InvoiceService logging, and FetchType.LAZY optimization on InvoiceItem. Review whether vatRate validation constraints and FetchType.LAZY optimization are part of issue #52 scope, or clarify if these enhancements were intentional extensions of the stated requirements.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: adding item descriptions (name field) and VAT handling (vatRate/vatAmount) to invoice entities.
Linked Issues check ✅ Passed All objectives from #52 are met: vatRate field added to DTOs with validation, vatAmount added to Invoice, name field added to InvoiceItem, and all are persisted and integrated into service logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db5dd6c and c88c15e.

📒 Files selected for processing (3)
  • src/main/java/org/example/App.java
  • src/main/java/org/example/cli/menu/InvoiceItemMenu.java
  • src/main/java/org/example/cli/menu/InvoiceMenu.java

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/main/java/org/example/service/InvoiceService.java (1)

65-71: Add null validation for item name before persisting.

Line 67 sets item.setName(itemDTO.name()) without validation, but InvoiceItem.name is marked @Column(nullable = false). If itemDTO.name() returns null, this will cause a database constraint violation at persist time.

🐛 Proposed fix to validate item name
         dto.items().forEach(itemDTO -> {
+            if (itemDTO.name() == null || itemDTO.name().isBlank()) {
+                throw new IllegalArgumentException("Item name is required and cannot be blank");
+            }
             InvoiceItem item = new InvoiceItem();
             item.setName(itemDTO.name());
             item.setQuantity(itemDTO.quantity());
src/main/java/org/example/entity/invoice/Invoice.java (2)

54-54: Fix the Lombok builder initialization.

The pipeline warning indicates that @Builder ignores the = new ArrayList<>() initializing expression. This could result in invoiceItems being null when using the builder.

🔧 Proposed fix
-    @OneToMany(mappedBy = "invoice", cascade = CascadeType.ALL, orphanRemoval = true)
-    private List<InvoiceItem> invoiceItems = new ArrayList<>();
+    @OneToMany(mappedBy = "invoice", cascade = CascadeType.ALL, orphanRemoval = true)
+    @Builder.Default
+    private List<InvoiceItem> invoiceItems = new ArrayList<>();

92-99: Add null/empty validation for item name.

Line 95 sets the item name from itemDTO.name() without validation. Since InvoiceItem.name has a nullable = false constraint, passing a null or empty name will cause a persistence exception.

🛡️ Proposed fix with validation
         if (dto.items() != null) {
             dto.items().forEach(itemDTO -> {
+                if (itemDTO.name() == null || itemDTO.name().isBlank()) {
+                    throw new ValidationException("Item name cannot be null or empty");
+                }
                 InvoiceItem item = new InvoiceItem();
                 item.setName(itemDTO.name());
                 item.setQuantity(itemDTO.quantity());
                 item.setUnitPrice(itemDTO.unitPrice());
                 invoice.addItem(item);
             });
         }
src/main/java/org/example/CliApp.java (1)

822-826: Critical: Item name lost during update.

Line 824 creates an updated InvoiceItemDTO but omits the .name() field in the builder. This causes item names to be lost when updating quantity or price, resulting in null name values that violate the InvoiceItem entity's nullable = false constraint.

🐛 Proposed fix to preserve item name
             List<InvoiceItemDTO> updated = items.stream()
                 .map(i -> i.id().equals(item.id())
-                    ? InvoiceItemDTO.builder().id(i.id()).quantity(quantity).unitPrice(unitPrice).build()
+                    ? InvoiceItemDTO.builder()
+                        .id(i.id())
+                        .name(i.name())
+                        .quantity(quantity)
+                        .unitPrice(unitPrice)
+                        .build()
                     : i)
                 .toList();
🤖 Fix all issues with AI agents
In @src/main/java/org/example/CliApp.java:
- Around line 687-688: The prompt for the item name currently assigns String
name = scanner.nextLine().trim() without validation, which allows empty/blank
values that will violate the InvoiceItem nullable=false constraint; update the
code around the scanner input (the variable name and the scanner instance) to
validate that name is not empty after trimming (e.g., loop prompting until a
non-blank value is entered), provide a user-facing error message when input is
invalid, and only proceed to construct or persist the InvoiceItem once the
validated non-empty name is obtained.

In @src/main/java/org/example/entity/invoice/InvoiceItemDTO.java:
- Around line 9-14: The DTO's name component lacks a validation annotation
causing mismatch with the InvoiceItem entity; update the InvoiceItemDTO record
declaration to annotate the name parameter with a validation constraint (e.g.,
add @NotBlank or @NotNull to the name component in public record
InvoiceItemDTO(...)) and add the corresponding import
(jakarta.validation.constraints.NotBlank or NotNull) so the DTO enforces
non-empty names consistent with the entity.
🧹 Nitpick comments (2)
src/main/java/org/example/entity/invoice/InvoiceItem.java (1)

27-28: Consider adding bean validation for defense in depth.

The nullable = false constraint enforces the requirement at the database level, but adding @NotBlank would provide earlier validation and clearer error messages.

♻️ Proposed enhancement with bean validation
+    import jakarta.validation.constraints.NotBlank;
+
+    @NotBlank(message = "Item name is required")
     @Column(name = "name", nullable = false)
     private String name;
src/main/java/org/example/CliApp.java (1)

646-647: Add validation for VAT amount input.

The VAT amount input is parsed directly without validation. While the outer try-catch handles NumberFormatException, there's no validation for negative values or other invalid inputs.

Consider adding input validation to ensure the VAT amount is a valid non-negative decimal.

📝 Suggested improvement
             System.out.print("Enter VAT amount (e.g. 250.00): ");
-            BigDecimal vatAmount = new BigDecimal(scanner.nextLine().trim());
+            String vatInput = scanner.nextLine().trim();
+            BigDecimal vatAmount = new BigDecimal(vatInput);
+            if (vatAmount.compareTo(BigDecimal.ZERO) < 0) {
+                System.out.println("✗ VAT amount cannot be negative.");
+                return;
+            }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27634a9 and 405ca63.

📒 Files selected for processing (8)
  • src/main/java/org/example/CliApp.java
  • src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java
  • src/main/java/org/example/entity/invoice/Invoice.java
  • src/main/java/org/example/entity/invoice/InvoiceDTO.java
  • src/main/java/org/example/entity/invoice/InvoiceItem.java
  • src/main/java/org/example/entity/invoice/InvoiceItemDTO.java
  • src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
  • src/main/java/org/example/service/InvoiceService.java
🧰 Additional context used
🪛 GitHub Actions: Java Maven Build
src/main/java/org/example/entity/invoice/Invoice.java

[warning] 54-54: @builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default.

🔇 Additional comments (10)
src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java (1)

8-14: LGTM! VAT field added correctly for partial updates.

The vatAmount field is properly positioned and typed as BigDecimal, allowing null values to support the partial update pattern described in the PR objectives.

src/main/java/org/example/service/InvoiceService.java (1)

60-60: LGTM! VAT amount update follows partial update pattern.

The null check ensures vatAmount is only updated when explicitly provided in the DTO.

src/main/java/org/example/entity/invoice/InvoiceDTO.java (1)

11-22: LGTM! VAT amount properly integrated into DTO.

The vatAmount field is correctly positioned and mapped from the entity in the fromEntity method.

src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java (1)

8-15: LGTM! VAT amount added to invoice creation flow.

The vatAmount field is properly typed and positioned. Consider adding validation in the service layer to reject negative VAT amounts if applicable to your business rules.

src/main/java/org/example/entity/invoice/InvoiceItemDTO.java (1)

15-22: LGTM!

The fromEntity method correctly populates all fields including the new name field.

src/main/java/org/example/entity/invoice/Invoice.java (2)

70-78: LGTM!

The recalcTotals() method correctly handles null vatAmount and properly computes the final amount as subtotal plus VAT.


80-90: LGTM!

The fromDTO method correctly initializes vatAmount and calls recalcTotals() after populating items to ensure accurate totals.

Also applies to: 102-102

src/main/java/org/example/CliApp.java (3)

696-700: LGTM!

The InvoiceItemDTO builder correctly includes the name field along with quantity and unit price.


766-780: LGTM!

The method correctly prompts for the item name and includes it in the InvoiceItemDTO builder.


783-783: No action needed. The UpdateInvoiceDTO constructor signature matches the call pattern: (invoiceId: UUID, dueDate: LocalDateTime, vatAmount: BigDecimal, items: List<InvoiceItemDTO>, status: InvoiceStatus). Passing null values for partial updates is acceptable and follows the intended design.

Comment thread src/main/java/org/example/CliApp.java Outdated
Comment thread src/main/java/org/example/entity/invoice/InvoiceItemDTO.java
annikaholmqvist94 and others added 3 commits January 9, 2026 18:17
…oice` and `InvoiceItem` entities, and refine totals calculation logic.
- Add a new field of type float vatRate that will hold the vatRate of the invoice
- Keep vatAmount as BigDecimal that will hold the actual currency amount of the totatl vat
- Remove DTO display from CLI and prettyprint Invoices and InvoiceItems listings
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/org/example/entity/invoice/Invoice.java (1)

56-56: Address pipeline warning: Add @Builder.Default annotation.

The pipeline warns that @Builder ignores the new ArrayList<>() initialization. Without @Builder.Default, the builder will use null for invoiceItems instead of an empty list, potentially causing NPEs.

🔧 Proposed fix
     @OneToMany(mappedBy = "invoice", cascade = CascadeType.ALL, orphanRemoval = true)
+    @Builder.Default
     private List<InvoiceItem> invoiceItems = new ArrayList<>();

Based on learnings and pipeline failure logs.

src/main/java/org/example/CliApp.java (1)

853-859: Critical: Preserve item name during update.

Line 855 rebuilds the InvoiceItemDTO with only id, quantity, and unitPrice, losing the item's name field. When this updated list is sent to the service, all item names will be lost or set to null.

🐛 Proposed fix to preserve item name
             List<InvoiceItemDTO> updated = items.stream()
                 .map(i -> i.id().equals(item.id())
-                    ? InvoiceItemDTO.builder().id(i.id()).quantity(quantity).unitPrice(unitPrice).build()
+                    ? InvoiceItemDTO.builder()
+                        .id(i.id())
+                        .name(i.name())
+                        .quantity(quantity)
+                        .unitPrice(unitPrice)
+                        .build()
                     : i)
                 .toList();
🤖 Fix all issues with AI agents
In @src/main/java/org/example/CliApp.java:
- Around line 775-789: The loop that prints InvoiceItemDTO items may NPE when
calling item.name().isBlank(); update the printing logic inside the for-loop
(the block iterating invoice.items()) to null-safe-check the name before calling
isBlank — e.g., compute a safeName by checking item.name() == null ||
item.name().isBlank() and use "(no name)" when true, otherwise use item.name();
also apply the same null-safe pattern to the analogous logic in listInvoices()
to avoid duplicate NPEs.
- Around line 598-620: The code prints invoice items using item.name().isBlank()
which can throw NPE if name() is null; update the printing logic in the invoice
items loop (in the block iterating InvoiceItemDTO items) to treat null names as
blank before calling isBlank(), e.g., compute a safeName via a null check (or
use Objects.toString(item.name(), "") ) and then test safeName.isBlank(), and
use "(no name)" when safeName is null/blank.
- Around line 667-669: Replace the Float parsing for VAT with BigDecimal: change
the vatRate variable to BigDecimal, keep the empty-input -> null behavior, and
wrap the parse in a try-catch to handle invalid input (catch
NumberFormatException); if parsing fails, inform the user or re-prompt (or set
vatRate to null) and avoid letting the exception propagate. Use new
BigDecimal(vatInput) (after trimming) for conversion and include any necessary
scale/rounding adjustments where downstream code expects them.

In @src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java:
- Line 12: The vatRate field in CreateInvoiceDTO is currently a Float which can
cause rounding errors; change its type to java.math.BigDecimal (replace Float
vatRate with BigDecimal vatRate) and update all related DTOs and domain classes:
adjust Invoice.java (field declaration and any arithmetic to use BigDecimal
operations), InvoiceDTO.java and UpdateInvoiceDTO.java (field types), and update
parsing logic in CliApp.java to construct BigDecimal from String (e.g., new
BigDecimal(input) or using parse with proper scale/MathContext) ensuring all
arithmetic uses BigDecimal methods (add, multiply, setScale) to preserve
financial precision.

In @src/main/java/org/example/entity/invoice/Invoice.java:
- Line 42: The vatRate field in class Invoice is declared as Float which causes
precision loss before any BigDecimal conversion; change the field declaration
vatRate from Float to BigDecimal and update all usages (getters/setters,
constructors, equals/hashCode, toString) as well as any DTOs, mappers, and
service methods that set or read Invoice.vatRate to accept/return BigDecimal;
ensure any parsing or JSON (de)serialization uses BigDecimal (e.g., adjust
Jackson annotations or DTO types) and update calculations that previously called
new BigDecimal(vatRate) to use the BigDecimal directly.
- Around line 73-80: The VAT calculation currently converts a Float vatRate to
BigDecimal (causing precision loss) and uses a ternary that still creates a
BigDecimal for nulls; change the logic to use a BigDecimal vatRate (or, if you
cannot change the field type, convert from Float via
BigDecimal.valueOf(vatRate.doubleValue()) only when non-null) and compute vat as
Optional.ofNullable(vatRate).orElse(BigDecimal.ZERO).multiply(subTotal) (apply
an explicit scale/rounding mode if needed), then set this.vatAmount = vat and
this.amount = subTotal.add(vat); update references to vatRate, vatAmount, amount
in class Invoice accordingly.

In @src/main/java/org/example/service/InvoiceService.java:
- Line 67: The call to item.setName(itemDTO.name()) can pass null and violate
the non-null DB constraint on InvoiceItem.name; update the code around
item.setName to guard against null (e.g., supply a default empty string when
itemDTO.name() is null) or enforce non-null at the DTO level by adding @NotNull
to InvoiceItemDTO.name so the setter always receives a non-null value.
🧹 Nitpick comments (1)
src/main/java/org/example/CliApp.java (1)

707-722: LGTM! Item name collection implemented correctly.

The method now properly collects item names and includes them in the InvoiceItemDTO. The flow is clear and user-friendly.

Consider adding validation to prevent blank item names:

String name = scanner.nextLine().trim();
if (name.isEmpty()) {
    System.out.println("Item name cannot be empty.");
    continue;
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 405ca63 and de4c63c.

📒 Files selected for processing (6)
  • src/main/java/org/example/CliApp.java
  • src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java
  • src/main/java/org/example/entity/invoice/Invoice.java
  • src/main/java/org/example/entity/invoice/InvoiceDTO.java
  • src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
  • src/main/java/org/example/service/InvoiceService.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/example/entity/invoice/InvoiceDTO.java
  • src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
🧰 Additional context used
🪛 GitHub Actions: Java Maven Build
src/main/java/org/example/entity/invoice/Invoice.java

[warning] 56-56: @builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.

🔇 Additional comments (5)
src/main/java/org/example/service/InvoiceService.java (1)

60-60: LGTM with caveat on Float type.

The VAT rate update logic correctly checks for null before applying. However, note that using Float for VAT rate (as flagged in CreateInvoiceDTO) will propagate precision issues throughout the calculation chain.

src/main/java/org/example/entity/invoice/Invoice.java (1)

97-97: Verify null handling for item.setName().

Similar to InvoiceService, this sets the item name without checking if itemDTO.name() is null. Ensure this aligns with the InvoiceItem entity's constraints.

This concern can be verified using the same script provided in the InvoiceService.java review.

src/main/java/org/example/CliApp.java (3)

895-899: LGTM!

The removal logic correctly filters out the selected item while preserving the rest of the items unchanged.


941-946: LGTM! Improved invoice selection UI.

The enhanced formatting with separators and invoice details makes the selection interface clearer and more user-friendly.


797-814: LGTM! The UpdateInvoiceDTO parameter order is correct, and the item name collection is properly implemented. All code changes look good.

Comment thread src/main/java/org/example/CliApp.java Outdated
Comment thread src/main/java/org/example/CliApp.java Outdated
Comment thread src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java Outdated
Comment thread src/main/java/org/example/entity/invoice/Invoice.java Outdated
Comment thread src/main/java/org/example/entity/invoice/Invoice.java
invoice.clearItems();
dto.items().forEach(itemDTO -> {
InvoiceItem item = new InvoiceItem();
item.setName(itemDTO.name());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find InvoiceItem.java file first
fd -name "InvoiceItem.java" --type f

# Once found, examine the entity definition and name field
if [ -f "src/main/java/org/example/model/InvoiceItem.java" ]; then
  cat -n "src/main/java/org/example/model/InvoiceItem.java"
fi

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 306


🏁 Script executed:

#!/bin/bash
# Find InvoiceItem.java file
fd -name "InvoiceItem.java" --type f

# Read the file with line numbers
find . -name "InvoiceItem.java" -type f -exec cat -n {} \;

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 1283


🏁 Script executed:

#!/bin/bash
# Find ItemDTO file
find . -name "*ItemDTO*" -type f -exec cat -n {} \;

# Also check InvoiceService.java around line 67
find . -name "InvoiceService.java" -type f -exec sed -n '50,85p' {} +

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 2180


🏁 Script executed:

#!/bin/bash
# Find UpdateInvoiceDTO definition
find . -name "*UpdateInvoiceDTO*" -type f -exec cat -n {} \;

# Also search for validation annotations on the items field
rg -B5 -A5 "items()" --type=java

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 20715


Add null check before setting item name.

The code calls item.setName(itemDTO.name()) without null validation. Since InvoiceItem.name has @Column(nullable = false), passing a null value will violate the database constraint and cause a runtime error. Add a null check or provide a default value before setting the name:

item.setName(itemDTO.name() != null ? itemDTO.name() : "");

Alternatively, add @NotNull validation to InvoiceItemDTO.name to enforce constraints at the DTO level.

🤖 Prompt for AI Agents
In @src/main/java/org/example/service/InvoiceService.java at line 67, The call
to item.setName(itemDTO.name()) can pass null and violate the non-null DB
constraint on InvoiceItem.name; update the code around item.setName to guard
against null (e.g., supply a default empty string when itemDTO.name() is null)
or enforce non-null at the DTO level by adding @NotNull to InvoiceItemDTO.name
so the setter always receives a non-null value.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/java/org/example/InvoiceServiceTest.java (1)

119-129: Add assertions to verify VAT calculations are applied correctly.

This test updates an invoice with a vatRate of 0.25f and items totaling 1000.00 (2 × 500.00), but doesn't verify the VAT amount or final total. The Invoice.recalcTotals() method calculates vatAmount as vatRate × subtotal and sets amount as subtotal + vatAmount, and InvoiceService.updateInvoice() calls this method before returning the result.

Add assertions to verify:

  • VAT amount is correctly calculated (250.00 for 25% of 1000.00)
  • Final amount includes subtotal + VAT (1250.00)
  • The returned InvoiceDTO contains the expected vatAmount value
🧹 Nitpick comments (3)
src/test/java/org/example/InvoiceServiceTest.java (3)

51-51: Consider adding test coverage for edge cases of vatRate.

All test cases use the same hardcoded vatRate value (0.25f). While this is sufficient for basic functionality testing, consider adding tests that cover edge cases such as:

  • Zero VAT rate (0.0f)
  • Maximum reasonable VAT rate (e.g., 1.0f for 100%)
  • Boundary values

This would improve confidence in the VAT handling logic across different scenarios.

Also applies to: 155-155, 167-167, 207-207


119-119: Use more descriptive test data for item name.

The InvoiceItemDTO uses the literal string "name" as the name parameter. Consider using a more descriptive value like "Test Item", "Consulting Service", or "Product A" to make the test data clearer and more realistic.

♻️ Proposed refactor
-        UpdateInvoiceDTO updateDto = new UpdateInvoiceDTO(invoiceId, LocalDateTime.now().plusDays(30), 0.25f, List.of(new InvoiceItemDTO(null, "name", 2, new BigDecimal("500.00"))), InvoiceStatus.SENT);
+        UpdateInvoiceDTO updateDto = new UpdateInvoiceDTO(invoiceId, LocalDateTime.now().plusDays(30), 0.25f, List.of(new InvoiceItemDTO(null, "Consulting Service", 2, new BigDecimal("500.00"))), InvoiceStatus.SENT);

212-227: Consider initializing VAT fields in the test helper for consistency.

The createFullInvoice helper doesn't initialize vatRate or vatAmount. While the Invoice.recalcTotals() method safely handles null values (treating vatRate as zero when null), initializing these fields—similar to how Invoice.fromDTO() explicitly sets vatRate—would improve clarity and consistency across test setup methods.

Suggested approach: Initialize vatRate to 0f and vatAmount to BigDecimal.ZERO in the helper.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de4c63c and e35cf1d.

📒 Files selected for processing (1)
  • src/test/java/org/example/InvoiceServiceTest.java

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/main/java/org/example/CliApp.java (5)

594-625: Fix printf("%.2f", BigDecimal) (runtime IllegalFormatConversionException) and guard item.name() nulls.
If amount() / vatAmount() / unitPrice() are BigDecimal (as implied by the PR), %.2f will crash at runtime; also item.name().isBlank() will NPE if name is null (see update flow).

Proposed fix
@@
 import java.math.BigDecimal;
+import java.math.RoundingMode;
@@
     private void listInvoices() {
@@
             for (InvoiceDTO invoice : invoices) {
                 System.out.println("Invoice: " + invoice.number());
                 System.out.println("Status : " + invoice.status());
-                System.out.printf("Amount : %.2f%n", invoice.amount());
-                System.out.printf("VAT    : %.2f%n", invoice.vatAmount());
+                System.out.println("Amount : " + formatMoney(invoice.amount()));
+                System.out.println("VAT    : " + formatMoney(invoice.vatAmount()));
                 System.out.println("Due    : " + invoice.dueDate().toLocalDate());
@@
                     for (InvoiceItemDTO item : invoice.items()) {
-                        System.out.printf(" - %s: %d x %.2f%n",
-                            item.name().isBlank() ? "(no name)" : item.name(),
-                            item.quantity(),
-                            item.unitPrice());
+                        System.out.printf(" - %s: %d x %s%n",
+                            displayName(item.name()),
+                            item.quantity(),
+                            formatMoney(item.unitPrice()));
                     }
                 }
                 System.out.println("--------------------------------------------------");
             }
@@
     }
+
+    private static String displayName(String name) {
+        return (name == null || name.isBlank()) ? "(no name)" : name;
+    }
+
+    private static String formatMoney(BigDecimal value) {
+        if (value == null) return "0.00";
+        return value.setScale(2, RoundingMode.HALF_UP).toPlainString();
+    }

710-733: Handle invalid unit price input here (otherwise createInvoice() blames VAT).
new BigDecimal(scanner.nextLine().trim()) can throw NumberFormatException; right now that gets caught in createInvoice() and prints a VAT error.

Proposed fix
@@
             System.out.print("Unit price: ");
-            BigDecimal unitPrice = new BigDecimal(scanner.nextLine().trim());
+            BigDecimal unitPrice;
+            try {
+                unitPrice = new BigDecimal(scanner.nextLine().trim());
+            } catch (NumberFormatException e) {
+                System.out.println("✗ Invalid unit price format.");
+                continue;
+            }

779-798: Same formatting + null-name issues as listInvoices(); reuse the helper methods.


831-877: Updating an item drops name (and may introduce name == null, causing NPE in listings).
You rebuild the DTO with only id/quantity/unitPrice; if name is required/persisted, this will either wipe it or set it null.

Proposed fix (preserve name)
@@
             List<InvoiceItemDTO> updated = items.stream()
                 .map(i -> i.id().equals(item.id())
-                    ? InvoiceItemDTO.builder().id(i.id()).quantity(quantity).unitPrice(unitPrice).build()
+                    ? InvoiceItemDTO.builder()
+                        .id(i.id())
+                        .name(i.name())
+                        .quantity(quantity)
+                        .unitPrice(unitPrice)
+                        .build()
                     : i)
                 .toList();

627-708: Broaden NumberFormatException catch message—not VAT-specific.

The VAT field is correctly implemented: CreateInvoiceDTO expects vatRate (not vatAmount), and the service calculates vatAmount internally from vatRate * subTotal in recalcTotals(). The CLI prompt for "VAT rate" aligns with this design.

However, the NumberFormatException catch is overly specific. It prints a VAT-focused message, but the exception can be triggered by either invalid VAT input or invalid unit prices from readInvoiceItems(). Use a generic message instead:

         catch (NumberFormatException e) {
-            System.out.println("✗ Invalid VAT rate format. Please use decimal format (e.g., 0.25).");
+            System.out.println("✗ Invalid decimal format (VAT or unit price).");
         }
🤖 Fix all issues with AI agents
In @src/main/java/org/example/entity/invoice/Invoice.java:
- Around line 72-81: The recalcTotals() method assumes vatRate is a decimal
(e.g., 0.25) but there is no documentation or validation; add JavaDoc to the
vatRate field clarifying "VAT rate as a decimal (e.g., 0.25 for 25%) and
expected range 0.0–1.0", and enforce validation either in the
Invoice.fromDTO(...) factory or a setVatRate(...) setter (validate vatRate !=
null then ensure 0.0 <= vatRate <= 1.0, otherwise throw
IllegalArgumentException) so callers cannot pass negative values or whole
percentages like 25.
- Around line 42-44: Add explicit @Column annotations to the Invoice entity's
vatRate and vatAmount fields to match the financial precision used elsewhere:
annotate vatRate (field name vatRate) with precision=5, scale=4 and annotate
vatAmount (field name vatAmount) with precision=19, scale=2 so VAT rate storage
supports four decimal places and VAT amounts use the same precision/scale as
amount; update the import if necessary and ensure the annotations are placed
directly above the corresponding fields in the Invoice class.
🧹 Nitpick comments (2)
src/main/java/org/example/CliApp.java (2)

800-829: Item add flow looks consistent with the new name field.
Only suggestion: validate name/quantity/unitPrice before sending the DTO (to avoid service-layer roundtrips).


879-915: Remove-item flow looks fine; just ensure the update DTO semantics are “replace items” (not “patch lists”).
Given the PR mentions partial-update patterns, it’s worth confirming this call won’t unintentionally overwrite other invoice fields.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e35cf1d and 71dcbf8.

📒 Files selected for processing (6)
  • src/main/java/org/example/CliApp.java
  • src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java
  • src/main/java/org/example/entity/invoice/Invoice.java
  • src/main/java/org/example/entity/invoice/InvoiceDTO.java
  • src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
  • src/test/java/org/example/InvoiceServiceTest.java
🔇 Additional comments (8)
src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java (1)

3-14: LGTM! Clean DTO update.

The addition of vatRate to the DTO is straightforward and aligns with the PR's objective to support manual VAT handling.

src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java (1)

3-15: LGTM! Consistent DTO extension.

The addition of vatRate follows the same pattern as UpdateInvoiceDTO and supports the VAT handling feature.

src/main/java/org/example/entity/invoice/InvoiceDTO.java (1)

17-18: LGTM! Proper DTO-to-entity mapping.

The DTO correctly includes both vatRate and the computed vatAmount, and fromEntity properly populates them from the Invoice entity.

Also applies to: 35-36

src/main/java/org/example/entity/invoice/Invoice.java (2)

83-93: LGTM! Proper builder usage for vatRate.

The vatRate is correctly wired from the DTO through the builder pattern. Consider adding validation for vatRate format as noted in the separate comment on the recalcTotals method.


95-103: LGTM! Item name properly populated.

The addition of item.setName(itemDTO.name()) correctly implements the item description feature from the PR objectives.

src/test/java/org/example/InvoiceServiceTest.java (2)

51-51: Tests properly updated for new DTO signatures.

All test cases correctly include the new vatRate parameter using new BigDecimal("0.25"), confirming the expected decimal format (0.25 = 25% VAT). The test at line 119 also properly includes the new name field in InvoiceItemDTO.

Also applies to: 119-119, 155-155, 167-167, 179-179, 207-207


51-51: AI summary contains incorrect field naming.

The AI-generated summary for this file incorrectly refers to the new BigDecimal parameter as "extraCharge" when it's actually vatRate. This inconsistency might confuse reviewers.

Actual field: vatRate (as seen in CreateInvoiceDTO.java and UpdateInvoiceDTO.java)
Summary claims: "extraCharge (BigDecimal)"

Also applies to: 119-119, 155-155, 167-167, 179-179, 207-207

src/main/java/org/example/CliApp.java (1)

939-965: Invoice selection output improvements look good.

Comment on lines +42 to 44
private BigDecimal vatRate;

private BigDecimal vatAmount;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add @column annotations with precision and scale for financial fields.

The vatRate and vatAmount fields lack @Column annotations with precision and scale attributes. Unlike the amount field (line 40), which properly specifies precision = 19, scale = 2, these VAT-related fields rely on database defaults, which can lead to:

  • Inconsistent precision across different database vendors
  • Potential rounding errors in VAT calculations
  • Loss of precision during persistence
🔧 Proposed fix with proper column definitions
-    private BigDecimal vatRate;
-
-    private BigDecimal vatAmount;
+    @Column(name = "vat_rate", precision = 5, scale = 4)
+    private BigDecimal vatRate;
+
+    @Column(name = "vat_amount", precision = 19, scale = 2)
+    private BigDecimal vatAmount;

Rationale:

  • vatRate: precision=5, scale=4 allows rates like 0.2500 (25%) up to 9.9999 (999.99%)
  • vatAmount: precision=19, scale=2 matches the amount field for consistency
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private BigDecimal vatRate;
private BigDecimal vatAmount;
@Column(name = "vat_rate", precision = 5, scale = 4)
private BigDecimal vatRate;
@Column(name = "vat_amount", precision = 19, scale = 2)
private BigDecimal vatAmount;
🤖 Prompt for AI Agents
In @src/main/java/org/example/entity/invoice/Invoice.java around lines 42 - 44,
Add explicit @Column annotations to the Invoice entity's vatRate and vatAmount
fields to match the financial precision used elsewhere: annotate vatRate (field
name vatRate) with precision=5, scale=4 and annotate vatAmount (field name
vatAmount) with precision=19, scale=2 so VAT rate storage supports four decimal
places and VAT amounts use the same precision/scale as amount; update the import
if necessary and ensure the annotations are placed directly above the
corresponding fields in the Invoice class.

Comment on lines 72 to 81
public void recalcTotals() {
amount = invoiceItems.stream()
.map(i -> i.getUnitPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
BigDecimal subTotal = invoiceItems.stream()
.map(item -> item.getUnitPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);

BigDecimal vat = (vatRate != null ? vatRate : BigDecimal.ZERO)
.multiply(subTotal);
this.vatAmount = vat;
this.amount = subTotal.add(vat);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Document and validate vatRate format expectations.

The VAT calculation assumes vatRate is in decimal form (e.g., 0.25 for 25% VAT), as evidenced by the direct multiplication at line 78. However:

  1. No documentation: The expected format for vatRate isn't documented in the entity or DTOs
  2. No validation: Nothing prevents invalid values (e.g., negative rates, rates > 1.0 for typical VAT scenarios, or someone mistakenly entering 25 instead of 0.25)
  3. Potential user confusion: Without clear guidance, API consumers might enter 25 instead of 0.25

Recommendations:

  1. Add JavaDoc to clarify the format:
/**
 * VAT rate as a decimal (e.g., 0.25 for 25% VAT).
 * Should be between 0.0 and 1.0 for typical use cases.
 */
@Column(name = "vat_rate", precision = 5, scale = 4)
private BigDecimal vatRate;
  1. Consider adding validation in fromDTO or a setter:
if (vatRate != null && (vatRate.compareTo(BigDecimal.ZERO) < 0 || vatRate.compareTo(BigDecimal.ONE) > 1)) {
    throw new IllegalArgumentException("VAT rate must be between 0.0 and 1.0");
}
🤖 Prompt for AI Agents
In @src/main/java/org/example/entity/invoice/Invoice.java around lines 72 - 81,
The recalcTotals() method assumes vatRate is a decimal (e.g., 0.25) but there is
no documentation or validation; add JavaDoc to the vatRate field clarifying "VAT
rate as a decimal (e.g., 0.25 for 25%) and expected range 0.0–1.0", and enforce
validation either in the Invoice.fromDTO(...) factory or a setVatRate(...)
setter (validate vatRate != null then ensure 0.0 <= vatRate <= 1.0, otherwise
throw IllegalArgumentException) so callers cannot pass negative values or whole
percentages like 25.

…into feature/Invoice-&-InvoiceItems

# Conflicts:
#	src/main/java/org/example/entity/invoice/Invoice.java
…Jakarta Validation dependencies in `pom.xml`.
…efine `name` checks in CLI, and improve error handling for invalid inputs.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/example/CliApp.java (1)

858-868: Critical: Item name is lost during update.

Line 866 creates an InvoiceItemDTO with only id, quantity, and unitPrice, omitting the name field. This will cause the item name to be set to null during the update, resulting in data loss. The original item's name should be preserved.

🐛 Preserve item name during update
             List<InvoiceItemDTO> updated = items.stream()
                 .map(i -> i.id().equals(item.id())
-                    ? InvoiceItemDTO.builder().id(i.id()).quantity(quantity).unitPrice(unitPrice).build()
+                    ? InvoiceItemDTO.builder().id(i.id()).name(i.name()).quantity(quantity).unitPrice(unitPrice).build()
                     : i)
                 .toList();
🤖 Fix all issues with AI agents
In @pom.xml:
- Around line 91-100: Update the hibernate-validator dependency to a 9.x release
to move off limited-support 8.x; locate the <dependency> block with groupId
"org.hibernate.validator" and artifactId "hibernate-validator" and change its
<version> from "8.0.1.Final" to a 9.x release such as "9.1.0.Final" (or
"9.0.1.Final") while leaving the jakarta.validation-api dependency unchanged.

In @src/main/java/org/example/CliApp.java:
- Around line 786-800: The loop printing InvoiceItemDTO items calls
item.name().isBlank() without checking for null, risking a NullPointerException;
update the printing logic in the loop that iterates invoice.items() to first
check if item.name() == null (or use Objects.toString/item.name() safely) and
treat null the same as blank by printing "(no name)"; modify the loop around the
InvoiceItemDTO variable `item` to use a null-safe name check before calling
isBlank().
- Around line 668-670: The code parses VAT input with vatInput and constructs
vatRate using new BigDecimal(vatInput) without catching NumberFormatException;
update the input handling around scanner.nextLine()/vatInput so non-numeric
entries are validated or wrapped in a try/catch, e.g., attempt to parse vatInput
into a BigDecimal inside a try block and on NumberFormatException prompt the
user again or set vatRate to null, ensuring any thrown NumberFormatException
from new BigDecimal(...) is handled alongside the existing
IllegalArgumentException handling.

In @src/main/java/org/example/entity/invoice/InvoiceItemDTO.java:
- Around line 12-13: The InvoiceItemDTO field 'name' is annotated with @NotBlank
but the project never invokes Jakarta Validation, so either remove @NotBlank
from InvoiceItemDTO or add enforcement where DTOs are created (e.g., CLI input
handling) by obtaining a Validator via ValidatorFactory.getValidator() and
validating the InvoiceItemDTO instance and rejecting/inputting again on
violations; locate the InvoiceItemDTO class and the CLI input code that
constructs it, then choose to (A) delete the @NotBlank annotation from
InvoiceItemDTO if you don't want validation, or (B) call
ValidatorFactory.getValidator().validate(invoiceItemDto) after building
InvoiceItemDTO and handle ConstraintViolations to enforce the "Name cannot be
empty" rule.
🧹 Nitpick comments (1)
src/main/java/org/example/entity/invoice/Invoice.java (1)

101-109: Consider adding defensive validation for item name.

Line 104 sets the item name from itemDTO.name() without checking for null or blank values. While InvoiceItemDTO has a @NotBlank annotation, Bean Validation constraints are not automatically enforced in plain JPA applications without explicit validator invocation.

🛡️ Add defensive check
         if (dto.items() != null) {
             dto.items().forEach(itemDTO -> {
+                if (itemDTO.name() == null || itemDTO.name().isBlank()) {
+                    throw new IllegalArgumentException("Item name cannot be empty");
+                }
                 InvoiceItem item = new InvoiceItem();
                 item.setName(itemDTO.name());
                 item.setQuantity(itemDTO.quantity());
                 item.setUnitPrice(itemDTO.unitPrice());
                 invoice.addItem(item);
             });
         }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71dcbf8 and 9873eab.

📒 Files selected for processing (6)
  • pom.xml
  • src/main/java/org/example/CliApp.java
  • src/main/java/org/example/entity/invoice/Invoice.java
  • src/main/java/org/example/entity/invoice/InvoiceItem.java
  • src/main/java/org/example/entity/invoice/InvoiceItemDTO.java
  • src/main/java/org/example/service/InvoiceService.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/example/service/InvoiceService.java
  • src/main/java/org/example/entity/invoice/InvoiceItem.java
🔇 Additional comments (6)
src/main/java/org/example/entity/invoice/Invoice.java (3)

42-43: LGTM!

The vatRate column definition with precision = 5, scale = 4 appropriately supports rates between 0.0 and 1.0 with four decimal places of precision (e.g., 0.2525 for 25.25%).


73-82: LGTM!

The recalcTotals() implementation correctly computes the invoice totals:

  1. Subtotal from item quantities and prices
  2. VAT amount from the rate (with appropriate null handling)
  3. Final amount as subtotal plus VAT

85-89: LGTM!

The VAT rate validation correctly enforces the range constraint (0.0 to 1.0) with a clear error message, and appropriately allows null values for invoices without VAT.

src/main/java/org/example/CliApp.java (3)

716-721: LGTM!

The item name input validation correctly ensures names are not empty and provides clear feedback, allowing users to retry when validation fails.


602-621: LGTM!

The enhanced invoice listing correctly handles null names with the null-safe check on line 615, providing a clear fallback display for missing item names.


825-825: UpdateInvoiceDTO constructor signature is correct and properly handles partial updates.

The constructor signature matches the three calls at lines 825, 870, and 910. The UpdateInvoiceDTO record defines fields: invoiceId, dueDate, vatRate, items, and status. The calls correctly pass null for dueDate, vatRate, and status to update only the items list. The InvoiceService.updateInvoice() method properly implements the partial update pattern by checking each field with if (dto.field() != null) before applying updates, treating null as "no change."

Comment thread pom.xml
Comment on lines +91 to +100
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Jakarta Validation API Versions ==="
curl -s https://repo1.maven.org/maven2/jakarta/validation/jakarta.validation-api/maven-metadata.xml | grep -oP '(?<=<version>)[^<]+' | tail -10

echo -e "\n=== Hibernate Validator Versions ==="
curl -s https://repo1.maven.org/maven2/org/hibernate/validator/hibernate-validator/maven-metadata.xml | grep -oP '(?<=<version>)[^<]+' | tail -10

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 343


🌐 Web query:

Jakarta Validation API 3.0.2 security vulnerability CVE

💡 Result:

I couldn't find any CVE that names jakarta.validation-api version 3.0.2 itself as vulnerable. Relevant public CVEs affect related projects/implementations (not the API artifact):

  • CVE-2020-10693 — message interpolation flaw in Hibernate Validator (affects some Hibernate Validator versions). [1]
  • CVE-2019-10219 — SafeHtml sanitization/XSS issue in Hibernate Validator. [2]
  • CVE-2025-35036 — expression-language interpolation / arbitrary code risk in Hibernate Validator (reported 2025). [3]
  • CVE-2021-28170 — bug in Jakarta Expression Language (jakarta.el) allowing invalid EL expressions to be evaluated. [4]

If you want, I can: (a) check whether a specific Maven/GAV that pulls jakarta.validation-api:3.0.2 is transitively affected by any of the above, or (b) scan a project dependency tree for vulnerabilities. Which would you prefer?


🌐 Web query:

Hibernate Validator 8.0.1.Final security vulnerability CVE 2024 2025

💡 Result:

Short answer: There is no widely‑published upstream CVE that specifically names org.hibernate.validator:hibernate-validator 8.0.1.Final as vulnerable in 2024–2025. However:

  • Red Hat/EAP packaged variants had a related SAML/signature issue (CVE‑2024‑8698) affecting eap8-hibernate-validator-cdi; Red Hat published fixes. [1]
  • Separate, higher‑impact CVEs from 2025 (e.g. CVE‑2025‑35036) affect older Hibernate Validator series (pre‑6.2.0 / pre‑7.0.0) due to Expression Language interpolation, not 8.0.1.Final upstream. [2][3]
  • Hibernate Validator 8.0 is in limited support; newer 8.x releases (e.g. 8.0.3.Final) and 9.x are available — upgrading to a supported/updated release or using vendor patches (for Red Hat/EAP builds) is recommended. [4]

Sources:

  1. Snyk / Red Hat advisory for CVE‑2024‑8698 (eap8‑hibernate‑validator‑cdi). [https://security.snyk.io/vuln/SNYK-RHEL8-EAP8HIBERNATEVALIDATORCDI-8342924] [1]
  2. NVD / CVE‑2025‑35036 (Expression Language interpolation; affects pre‑6.2.0 / pre‑7.0.0). [https://nvd.nist.gov/vuln/detail/CVE-2025-35036] [2]
  3. GitHub Advisory entry for the same 2025 advisory. [https://github.com/advisories/GHSA-7v6m-28jr-rg84] [3]
  4. Hibernate Validator 8.0 release/maintenance page (shows limited‑support status and newer 8.x releases). [https://hibernate.org/validator/releases/8.0/] [4]

If you want, I can:

  • check your specific distribution/vendor build (Red Hat, WildFly, Payara) for vendor advisories for 8.0.1.Final, or
  • scan a pom.gradle to see if you use a packaged variant that is affected.

Upgrade Hibernate Validator to 9.x series; Jakarta Validation API version is acceptable.

Jakarta Validation API 3.0.2 is stable and reasonably current (3.1.1 is the latest stable release). However, Hibernate Validator 8.0.1.Final is in limited-support status with version 9.1.0.Final available. Upgrade Hibernate Validator to 9.1.0.Final or 9.0.1.Final to ensure continued support and compatibility with current Jakarta Validation standards.

🤖 Prompt for AI Agents
In @pom.xml around lines 91 - 100, Update the hibernate-validator dependency to
a 9.x release to move off limited-support 8.x; locate the <dependency> block
with groupId "org.hibernate.validator" and artifactId "hibernate-validator" and
change its <version> from "8.0.1.Final" to a 9.x release such as "9.1.0.Final"
(or "9.0.1.Final") while leaving the jakarta.validation-api dependency
unchanged.

Comment thread src/main/java/org/example/CliApp.java Outdated
Comment on lines +668 to +670
System.out.print("Enter VAT rate (e.g. 0.25, 0.12): ");
String vatInput = scanner.nextLine().trim();
BigDecimal vatRate = vatInput.isEmpty() ? null : new BigDecimal(vatInput);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

VAT rate parsing lacks error handling.

If the user enters non-numeric input for the VAT rate, new BigDecimal(vatInput) will throw a NumberFormatException that isn't caught by the existing exception handlers (lines 692-706). While IllegalArgumentException is caught at line 692, it doesn't cover parsing errors.

🔧 Improve error handling
             System.out.print("Enter VAT rate (e.g. 0.25, 0.12): ");
             String vatInput = scanner.nextLine().trim();
-            BigDecimal vatRate = vatInput.isEmpty() ? null : new BigDecimal(vatInput);
+            BigDecimal vatRate = null;
+            if (!vatInput.isEmpty()) {
+                try {
+                    vatRate = new BigDecimal(vatInput);
+                } catch (NumberFormatException e) {
+                    System.out.println("✗ Invalid VAT rate format.");
+                    return;
+                }
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
System.out.print("Enter VAT rate (e.g. 0.25, 0.12): ");
String vatInput = scanner.nextLine().trim();
BigDecimal vatRate = vatInput.isEmpty() ? null : new BigDecimal(vatInput);
System.out.print("Enter VAT rate (e.g. 0.25, 0.12): ");
String vatInput = scanner.nextLine().trim();
BigDecimal vatRate = null;
if (!vatInput.isEmpty()) {
try {
vatRate = new BigDecimal(vatInput);
} catch (NumberFormatException e) {
System.out.println("✗ Invalid VAT rate format.");
return;
}
}
🤖 Prompt for AI Agents
In @src/main/java/org/example/CliApp.java around lines 668 - 670, The code
parses VAT input with vatInput and constructs vatRate using new
BigDecimal(vatInput) without catching NumberFormatException; update the input
handling around scanner.nextLine()/vatInput so non-numeric entries are validated
or wrapped in a try/catch, e.g., attempt to parse vatInput into a BigDecimal
inside a try block and on NumberFormatException prompt the user again or set
vatRate to null, ensuring any thrown NumberFormatException from new
BigDecimal(...) is handled alongside the existing IllegalArgumentException
handling.

Comment thread src/main/java/org/example/CliApp.java Outdated
Comment on lines +12 to +13
@NotBlank(message = "Name cannot be empty")
String name,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for Jakarta Validator usage in service layer and entity lifecycle

echo "=== Checking for explicit Validator usage ==="
rg -n --type=java 'Validator\.validate|ValidatorFactory|@Valid' -C 2

echo -e "\n=== Checking for JPA validation callbacks ==="
rg -n --type=java '@PrePersist|@PreUpdate' -C 3

echo -e "\n=== Checking for Spring @Validated or validation configuration ==="
rg -n --type=java '@Validated|ValidationConfiguration|ValidatorBean' -C 2

echo -e "\n=== Checking for JAX-RS @Valid ==="
rg -n --type=java 'javax.validation|jakarta.validation' -C 2

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 697


🏁 Script executed:

#!/bin/bash
# Find where InvoiceItemDTO is used
echo "=== Searching for InvoiceItemDTO usage ==="
rg -n 'InvoiceItemDTO' --type=java

echo -e "\n=== Looking for REST controllers with @PostMapping or @Valid ==="
rg -n --type=java '@RestController|@PostMapping|@Valid' -C 1

echo -e "\n=== Checking if Spring Boot is used ==="
git ls-files | grep -E '(pom.xml|build.gradle|application.properties|application.yml)' | head -5

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 2883


🏁 Script executed:

#!/bin/bash
echo "=== Checking pom.xml for validation dependencies ==="
cat pom.xml | grep -A 20 -B 5 'validation\|jakarta'

echo -e "\n=== Full dependency section ==="
cat pom.xml | grep -A 100 '<dependencies>'

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 4681


Remove @NotBlank or implement validation enforcement.

The @NotBlank annotation is present but not enforced—no validation code calls ValidatorFactory.getValidator() or uses JPA callbacks. Although Jakarta Validation and Hibernate Validator libraries are available as dependencies, they are unused throughout the codebase. Either remove the annotation if validation is not needed, or implement explicit validation in the CLI input handling to enforce the constraint.

🤖 Prompt for AI Agents
In @src/main/java/org/example/entity/invoice/InvoiceItemDTO.java around lines 12
- 13, The InvoiceItemDTO field 'name' is annotated with @NotBlank but the
project never invokes Jakarta Validation, so either remove @NotBlank from
InvoiceItemDTO or add enforcement where DTOs are created (e.g., CLI input
handling) by obtaining a Validator via ValidatorFactory.getValidator() and
validating the InvoiceItemDTO instance and rejecting/inputting again on
violations; locate the InvoiceItemDTO class and the CLI input code that
constructs it, then choose to (A) delete the @NotBlank annotation from
InvoiceItemDTO if you don't want validation, or (B) call
ValidatorFactory.getValidator().validate(invoiceItemDto) after building
InvoiceItemDTO and handle ConstraintViolations to enforce the "Name cannot be
empty" rule.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/org/example/CliApp.java (2)

826-841: Missing validation for empty item name.

Unlike readInvoiceItems() (lines 736-739) which validates that item names cannot be empty, addInvoiceItem() allows empty names. This inconsistency could lead to items being created with blank names.

🐛 Proposed fix
             System.out.print("Item name: ");
             String name = scanner.nextLine().trim();
+            if (name.isEmpty()) {
+                System.out.println("✗ Item name cannot be empty.");
+                return;
+            }

             System.out.print("Quantity: ");

882-888: Item name is lost when updating invoice items.

The updated item is built without setting the name field (line 884). This causes data loss - the item's name will be nullified after any update to quantity or unit price.

🐛 Proposed fix to preserve item name
             List<InvoiceItemDTO> updated = items.stream()
                 .map(i -> i.id().equals(item.id())
-                    ? InvoiceItemDTO.builder().id(i.id()).quantity(quantity).unitPrice(unitPrice).build()
+                    ? InvoiceItemDTO.builder().id(i.id()).name(i.name()).quantity(quantity).unitPrice(unitPrice).build()
                     : i)
                 .toList();
🤖 Fix all issues with AI agents
In @src/main/java/org/example/CliApp.java:
- Around line 811-816: The loop printing invoice items can throw a
NullPointerException because it calls item.name().isBlank() without null
checking; update the loop that iterates invoice.items() (and any formatting call
using InvoiceItemDTO.name()) to guard against null by checking item.name() ==
null || item.name().isBlank() (or use Objects.toString(item.name(), "") and then
isBlank()), and use "(no name)" when the name is null or blank so behavior
matches the earlier listInvoices handling.
🧹 Nitpick comments (2)
src/main/java/org/example/CliApp.java (2)

686-688: VAT input error handling could be more specific.

Invalid VAT input (e.g., "abc") throws NumberFormatException, which is caught by IllegalArgumentException. The generic error message may confuse users about which input was malformed.

💡 Suggested improvement
             System.out.print("Enter VAT rate (e.g. 0.25, 0.12): ");
             String vatInput = scanner.nextLine().trim();
-            BigDecimal vatRate = vatInput.isEmpty() ? null : new BigDecimal(vatInput);
+            BigDecimal vatRate = null;
+            if (!vatInput.isEmpty()) {
+                try {
+                    vatRate = new BigDecimal(vatInput);
+                } catch (NumberFormatException e) {
+                    System.out.println("✗ Invalid VAT rate format. Please enter a decimal number.");
+                    return;
+                }
+            }

709-724: Inconsistent formatting in catch blocks.

The catch block formatting is inconsistent - some use }catch (line 710) while others use } catch with a space (implied standard). This is a minor style issue.

✨ Consistent formatting
-        }catch (IllegalArgumentException e) {
+        } catch (IllegalArgumentException e) {
             System.out.println("✗ Input error: " + e.getMessage());
-        }
-        catch (DateTimeParseException e) {
+        } catch (DateTimeParseException e) {
             System.out.println("✗ Invalid date format. Please use yyyy-MM-dd.");
-        }
-        catch (BusinessRuleException e) {
+        } catch (BusinessRuleException e) {
             System.out.println("✗ Business Rule Violation: " + e.getMessage());
-        }
-        catch (EntityNotFoundException e) {
+        } catch (EntityNotFoundException e) {
             System.out.println("✗ Creation failed: " + e.getMessage());
-        }
-        catch (ValidationException e) {
+        } catch (ValidationException e) {
             System.out.println("✗ Validation error: " + e.getMessage());
         }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9873eab and 40b668f.

📒 Files selected for processing (2)
  • src/main/java/org/example/CliApp.java
  • src/main/java/org/example/service/InvoiceService.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/example/service/InvoiceService.java
🔇 Additional comments (3)
src/main/java/org/example/CliApp.java (3)

727-754: LGTM!

Good validation for empty item names with user feedback, and proper use of the builder pattern.


923-929: LGTM!

The item removal logic correctly filters out the selected item and uses the partial update pattern.


968-975: LGTM!

Enhanced invoice selection display with clearer formatting and visual separators.

Comment thread src/main/java/org/example/CliApp.java Outdated
Comment on lines +811 to +816
for (InvoiceItemDTO item : invoice.items()) {
System.out.printf(" - %s: %d x %.2f%n",
item.name().isBlank() ? "(no name)" : item.name(),
item.quantity(),
item.unitPrice());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential NullPointerException on item.name().isBlank().

Line 813 calls item.name().isBlank() without a null check, unlike line 633 in listInvoices which properly handles null: item.name() == null || item.name().isBlank(). This will throw NPE if an item has a null name.

🐛 Proposed fix
             for (InvoiceItemDTO item : invoice.items()) {
                 System.out.printf(" - %s: %d x %.2f%n",
-                    item.name().isBlank() ? "(no name)" : item.name(),
+                    item.name() == null || item.name().isBlank() ? "(no name)" : item.name(),
                     item.quantity(),
                     item.unitPrice());
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (InvoiceItemDTO item : invoice.items()) {
System.out.printf(" - %s: %d x %.2f%n",
item.name().isBlank() ? "(no name)" : item.name(),
item.quantity(),
item.unitPrice());
}
for (InvoiceItemDTO item : invoice.items()) {
System.out.printf(" - %s: %d x %.2f%n",
item.name() == null || item.name().isBlank() ? "(no name)" : item.name(),
item.quantity(),
item.unitPrice());
}
🤖 Prompt for AI Agents
In @src/main/java/org/example/CliApp.java around lines 811 - 816, The loop
printing invoice items can throw a NullPointerException because it calls
item.name().isBlank() without null checking; update the loop that iterates
invoice.items() (and any formatting call using InvoiceItemDTO.name()) to guard
against null by checking item.name() == null || item.name().isBlank() (or use
Objects.toString(item.name(), "") and then isBlank()), and use "(no name)" when
the name is null or blank so behavior matches the earlier listInvoices handling.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java:
- Around line 19-22: The @DecimalMin on the vatRate field in UpdateInvoiceDTO is
currently set to inclusive = false which incorrectly disallows a 0.0 VAT that
the business logic (Invoice class VAT check) permits; change the @DecimalMin on
the BigDecimal vatRate in UpdateInvoiceDTO to inclusive = true and update the
message to indicate the range allows 0.0 through 1.0 (e.g., "VAT rate must be
between 0.0 and 1.0"), and make the same change to the vatRate field in
CreateInvoiceDTO so DTO validation matches Invoice validation.
🧹 Nitpick comments (2)
src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java (1)

28-31: Validation range assumes fraction (0–1); ensure callers don’t pass “25” for 25%

If user input is likely “25%”, you’ll either need mapping (25 → 0.25) or change constraints to 0–100. Right now tests use 0.25, but the CLI/user-facing contract needs to be explicit.

src/test/java/org/example/InvoiceServiceTest.java (1)

51-53: Tests: make VAT semantics + timestamps less brittle

  • You’re now passing new BigDecimal("0.25"); please ensure this matches the intended contract (fraction vs percent) and add at least one assertion around totals/VAT behavior if this PR changes invoice math.
  • LocalDateTime.now() can cause flaky tests around time boundaries; consider a fixed timestamp in tests.

Also applies to: 120-121, 156-158, 169-171, 182-183, 210-212

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40b668f and db5dd6c.

📒 Files selected for processing (4)
  • src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java
  • src/main/java/org/example/entity/invoice/InvoiceItemDTO.java
  • src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java
  • src/test/java/org/example/InvoiceServiceTest.java
🔇 Additional comments (4)
src/main/java/org/example/entity/invoice/CreateInvoiceDTO.java (1)

28-31: vatRate is intentionally optional—no action needed

The current implementation correctly allows null for vatRate. Both the Invoice entity column (no nullable = false) and the recalcTotals() method (which handles null by defaulting to BigDecimal.ZERO) confirm this is by design. The absence of @NotNull matches the pattern used elsewhere for optional fields like dueDate in UpdateInvoiceDTO.

src/main/java/org/example/entity/invoice/InvoiceItemDTO.java (1)

13-33: The entity-level constraint already prevents NULL names; no legacy data concern exists here

The InvoiceItem entity already has @Column(name = "name", nullable = false), which enforces the constraint at the database level. Since Hibernate auto-generates the schema based on entity annotations (via schema-generation.database.action = "update"), NULL names cannot be inserted. The @NotBlank validation on InvoiceItemDTO is aligned with this constraint, and fromEntity() is safe—it will never receive NULL values from the database. No migration or backfill is required.

src/test/java/org/example/InvoiceServiceTest.java (1)

13-23: No action needed on dependency version. JUnit Jupiter 6.0.1 is a real artifact available on Maven Central with proper distributions (jar, sources, javadoc). The version is supported and correctly specified.

Likely an incorrect or invalid review comment.

src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java (1)

19-22: No change needed—vatRate field design is correct and intentional

The DTO properly uses vatRate as a fraction (0–1, e.g., 0.25 for 25%), which is consistent with the Invoice entity, all related DTOs, service mappings, and validation messages. The entity also includes a separate vatAmount field that is automatically calculated as vatRate × subTotal. The naming and units are clear and correctly implemented across the codebase.

Comment on lines +19 to +22
@DecimalMin(value = "0.0", inclusive = false, message = "VAT rate must be greater than 0")
@DecimalMax(value = "1.0", inclusive = true, message = "VAT rate must not exceed 1.0")
BigDecimal vatRate,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Locate and read the UpdateInvoiceDTO file
find . -name "UpdateInvoiceDTO.java" -type f

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 140


🏁 Script executed:

# Search for usage of UpdateInvoiceDTO to understand the context
rg -t java "UpdateInvoiceDTO" --max-count 20

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 1219


🏁 Script executed:

# Check for any tests related to VAT validation
rg -t java "vatRate|VAT" --max-count 30 | head -50

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 1902


🏁 Script executed:

cat -n ./src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 1024


🏁 Script executed:

# Read Invoice.java to see the server-side validation logic
cat -n ./src/main/java/org/example/entity/invoice/Invoice.java | grep -A 5 -B 5 "vatRate"

Repository: ithsjava25/project-jpa-grupp-3-d

Length of output: 1919


@DecimalMin constraint incorrectly blocks 0% VAT, contradicting server-side validation

The @DecimalMin(inclusive = false) constraint rejects 0.0, but Invoice.java line 86 explicitly allows VAT rates >= 0.0. This creates an inconsistency where the DTO validation prevents a scenario the business logic supports. The null-handling semantics are correct (null = "leave unchanged"), but 0% VAT should be a valid value. Update the constraint to inclusive = true and adjust the message to match the intended range.

Required fix
-    @DecimalMin(value = "0.0", inclusive = false, message = "VAT rate must be greater than 0")
+    @DecimalMin(value = "0.0", inclusive = true, message = "VAT rate must be at least 0")
     @DecimalMax(value = "1.0", inclusive = true, message = "VAT rate must not exceed 1.0")
     BigDecimal vatRate,

Note: CreateInvoiceDTO.java has the same constraint issue and should also be corrected.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@DecimalMin(value = "0.0", inclusive = false, message = "VAT rate must be greater than 0")
@DecimalMax(value = "1.0", inclusive = true, message = "VAT rate must not exceed 1.0")
BigDecimal vatRate,
@DecimalMin(value = "0.0", inclusive = true, message = "VAT rate must be at least 0")
@DecimalMax(value = "1.0", inclusive = true, message = "VAT rate must not exceed 1.0")
BigDecimal vatRate,
🤖 Prompt for AI Agents
In @src/main/java/org/example/entity/invoice/UpdateInvoiceDTO.java around lines
19 - 22, The @DecimalMin on the vatRate field in UpdateInvoiceDTO is currently
set to inclusive = false which incorrectly disallows a 0.0 VAT that the business
logic (Invoice class VAT check) permits; change the @DecimalMin on the
BigDecimal vatRate in UpdateInvoiceDTO to inclusive = true and update the
message to indicate the range allows 0.0 through 1.0 (e.g., "VAT rate must be
between 0.0 and 1.0"), and make the same change to the vatRate field in
CreateInvoiceDTO so DTO validation matches Invoice validation.

@fmazmz fmazmz merged commit 8bd35f3 into main Jan 12, 2026
1 of 2 checks passed
@fmazmz fmazmz deleted the feature/Invoice-&-InvoiceItems branch January 12, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance Invoice & InvoiceItems

2 participants