Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ follow semantic versioning; release dates are ISO 8601.

### Public API

- **A billed party carries a printed registration.** A supplier could already state a
tax registration through `InvoiceContactBlock.taxRegistrationLabel` /
`taxRegistrationNumber`, and the party being billed could not — but most B2B invoices
print the customer's VAT or tax number too, under its address. `subline` was the only
spare field and it is contractually the attention line that sits *above* the address,
so a registration put there renders in the wrong place. `InvoiceRecipient` now carries
`registrationLabel` and `registrationNumber`, mirroring the pair the supplier block
already has, with `hasRegistration()` for the presets that draw the row only when
there is a number to draw. Both are plain strings, blank when absent, and the
six-argument constructor is kept explicitly, so existing calls compile and link
unchanged and every recipient built through them still prints no registration.

- **An invoice line carries a mark.** A design that opens each service line with a
glyph — a card for a billing line, a shield for fraud screening, a globe for a hosted
service — had nowhere to say which one, and deriving it from the description would
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,33 @@ void aNullMarkNormalizesToBlankLikeTheFieldsBesideIt() {
assertThat(line.icon()).isEmpty();
}

@Test
void theRecipientConstructorThatPredatesTheRegistrationLeavesItBlank() {
InvoiceRecipient recipient = new InvoiceRecipient("BILL TO", "Northwind Ltd.", "",
List.of("42 Bridgewater Street"), "Email", "ap@example.test");
assertThat(recipient.registrationLabel()).isEmpty();
assertThat(recipient.registrationNumber()).isEmpty();
assertThat(recipient.hasRegistration()).isFalse();
}

@Test
void aRecipientCarriesTheRegistrationItIsGiven() {
InvoiceRecipient recipient = new InvoiceRecipient("BILL TO", "Northwind Ltd.", "",
List.of("42 Bridgewater Street"), "", "", "VAT Number", "GB 987 6543 21");
assertThat(recipient.registrationLabel()).isEqualTo("VAT Number");
assertThat(recipient.registrationNumber()).isEqualTo("GB 987 6543 21");
assertThat(recipient.hasRegistration()).isTrue();
}

@Test
void aRecipientWithALabelAndNoNumberPrintsNothing() {
// The number is what makes the row worth drawing; a label on its own is
// a heading over an absence.
InvoiceRecipient recipient = new InvoiceRecipient("BILL TO", "Northwind Ltd.", "",
List.of(), "", "", "VAT Number", null);
assertThat(recipient.hasRegistration()).isFalse();
}

@Test
void theDataConstructorThatPredatesShipToLeavesItEmptyRatherThanNull() {
// The record normalizes an absent block to its empty form, so preset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@
* @param addressLines the address lines, in order
* @param emailLabel the label printed before the email address
* @param email the recipient's email address
* @param registrationLabel the label a printed registration is named by
* (e.g. {@code "VAT Number"}); blank when absent
* @param registrationNumber the registration itself, printed under the
* address rather than under the name; blank when
* absent
*/
public record InvoiceRecipient(
String heading,
String name,
String subline,
List<String> addressLines,
String emailLabel,
String email) {
String email,
String registrationLabel,
String registrationNumber) {

/**
* Normalizes optional fields and freezes the address lines.
Expand All @@ -38,5 +45,32 @@ public record InvoiceRecipient(
addressLines = List.copyOf(Objects.requireNonNullElse(addressLines, List.of()));
emailLabel = Objects.requireNonNullElse(emailLabel, "");
email = Objects.requireNonNullElse(email, "");
registrationLabel = Objects.requireNonNullElse(registrationLabel, "");
registrationNumber = Objects.requireNonNullElse(registrationNumber, "");
}

/**
* Backward-compatible constructor for callers that predate the printed
* registration. The recipient simply carries none.
*
* @param heading the block heading
* @param name the recipient's name
* @param subline the attention line under the name
* @param addressLines the address lines, in order
* @param emailLabel the label printed before the email address
* @param email the recipient's email address
*/
public InvoiceRecipient(String heading, String name, String subline,
List<String> addressLines, String emailLabel, String email) {
this(heading, name, subline, addressLines, emailLabel, email, "", "");
}

/**
* Whether this recipient prints a registration under its address.
*
* @return {@code true} when the number is set
*/
public boolean hasRegistration() {
return !registrationNumber.isBlank();
}
}
Loading