summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/AcceptedCountries.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TaxId.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/accepted-countries.json2
9 files changed, 38 insertions, 18 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/AcceptedCountries.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/AcceptedCountries.java
index c665b4fb7c2..082eaac7315 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/AcceptedCountries.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/AcceptedCountries.java
@@ -13,7 +13,7 @@ public record AcceptedCountries(List<Country> countries) {
countries = List.copyOf(countries);
}
- public record Country(String code, String displayName, List<TaxType> taxTypes) {
+ public record Country(String code, String displayName, boolean taxIdMandatory, List<TaxType> taxTypes) {
public Country {
taxTypes = List.copyOf(taxTypes);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
index 18dd339b4a1..9012b45748c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
@@ -210,10 +210,10 @@ public class MockBillingController implements BillingController {
public AcceptedCountries getAcceptedCountries() {
return new AcceptedCountries(List.of(
new AcceptedCountries.Country(
- "NO", "Norway",
+ "NO", "Norway", true,
List.of(new AcceptedCountries.TaxType("no_vat", "Norwegian VAT number", "[0-9]{9}MVA", "123456789MVA"))),
new AcceptedCountries.Country(
- "CA", "Canada",
+ "CA", "Canada", true,
List.of(new AcceptedCountries.TaxType("ca_gst_hst", "Canadian GST/HST number", "([0-9]{9}) ?RT ?([0-9]{4})", "123456789RT0002"),
new AcceptedCountries.TaxType("ca_pst_bc", "Canadian PST number (British Columbia)", "PST-?([0-9]{4})-?([0-9]{4})", "PST-1234-5678")))
));
@@ -221,7 +221,7 @@ public class MockBillingController implements BillingController {
@Override
public void validateTaxId(TaxId id) throws IllegalArgumentException {
- if (id.isEmpty() || id.isLegacy()) return;
+ if (id.isEmpty() || id.isLegacy() || id.country().isEmpty()) return;
if (!List.of("eu_vat", "no_vat").contains(id.type().value()))
throw new IllegalArgumentException("Unknown tax id type '%s'".formatted(id.type().value()));
if (!id.code().value().matches("\\w+"))
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TaxId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TaxId.java
index 99c2400c58c..172ad257f6a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TaxId.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TaxId.java
@@ -8,17 +8,27 @@ import static ai.vespa.validation.Validation.requireLength;
/**
* @author olaa
*/
-public record TaxId(Type type, Code code) {
+public record TaxId(Country country, Type type, Code code) {
- public TaxId(String type, String code) { this(new Type(type), new Code(code)); }
+ public TaxId(String country, String type, String code) { this(new Country(country), new Type(type), new Code(code)); }
- public static TaxId empty() { return new TaxId(Type.empty(), Code.empty()); }
- public boolean isEmpty() { return type.isEmpty() && code.isEmpty(); }
+ public static TaxId empty() { return new TaxId(Country.empty(), Type.empty(), Code.empty()); }
+ public boolean isEmpty() { return country.isEmpty() && type.isEmpty() && code.isEmpty(); }
// TODO(bjorncs) Remove legacy once no longer present in ZK
- public static TaxId legacy(String code) { return new TaxId(Type.empty(), new Code(code)); }
+ public static TaxId legacy(String code) { return new TaxId(Country.empty(), Type.empty(), new Code(code)); }
public boolean isLegacy() { return type.isEmpty() && !code.isEmpty(); }
+ public static class Country extends StringWrapper<Country> {
+ public Country(String value) {
+ super(value);
+ requireLength(value, "tax code country length", 0, 2);
+ }
+
+ public static Country empty() { return new Country(""); }
+ public boolean isEmpty() { return value().isEmpty(); }
+ }
+
public static class Type extends StringWrapper<Type> {
public Type(String value) {
super(value);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
index 85b7acd603a..7801efe504b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
@@ -96,6 +96,7 @@ public class TenantSerializer {
private static final String accountField = "account";
private static final String templateVersionField = "templateVersion";
private static final String taxIdField = "taxId";
+ private static final String taxIdCountryField = "country";
private static final String taxIdTypeField = "type";
private static final String taxIdCodeField = "code";
private static final String purchaseOrderField = "purchaseOrder";
@@ -293,9 +294,10 @@ public class TenantSerializer {
var taxId = switch (taxIdInspector.type()) {
case STRING -> TaxId.legacy(taxIdInspector.asString());
case OBJECT -> {
+ var taxIdCountry = taxIdInspector.field(taxIdCountryField).asString();
var taxIdType = taxIdInspector.field(taxIdTypeField).asString();
var taxIdCode = taxIdInspector.field(taxIdCodeField).asString();
- yield new TaxId(new TaxId.Type(taxIdType), new TaxId.Code(taxIdCode));
+ yield new TaxId(new TaxId.Country(taxIdCountry), new TaxId.Type(taxIdType), new TaxId.Code(taxIdCode));
}
case NIX -> TaxId.empty();
default -> throw new IllegalStateException(taxIdInspector.type().name());
@@ -374,6 +376,7 @@ public class TenantSerializer {
billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
billingCursor.setString("phone", billingContact.contact().phone());
var taxIdCursor = billingCursor.setObject(taxIdField);
+ taxIdCursor.setString(taxIdCountryField, billingContact.getTaxId().country().value());
taxIdCursor.setString(taxIdTypeField, billingContact.getTaxId().type().value());
taxIdCursor.setString(taxIdCodeField, billingContact.getTaxId().code().value());
billingCursor.setString(purchaseOrderField, billingContact.getPurchaseOrder().value());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 078c27f5d00..1fd8e7c8f3b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -697,6 +697,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
contact.setBool("emailVerified", billingContact.contact().email().isVerified());
contact.setString("phone", billingContact.contact().phone());
var taxIdCursor = root.setObject("taxId");
+ taxIdCursor.setString("country", billingContact.getTaxId().country().value());
taxIdCursor.setString("type", billingContact.getTaxId().type().value());
taxIdCursor.setString("code", billingContact.getTaxId().code().value());
root.setString("purchaseOrder", billingContact.getPurchaseOrder().value());
@@ -720,10 +721,6 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
var mergedPurchaseOrder = optional("purchaseOrder", inspector).map(PurchaseOrder::new).orElse(billing.getPurchaseOrder());
var mergedInvoiceEmail = optional("invoiceEmail", inspector).map(mail -> new Email(mail, false)).orElse(billing.getInvoiceEmail());
- if (!inspector.field("taxId").valid() && inspector.field("address").valid()) {
- throw new IllegalArgumentException("Tax ID information is mandatory for setting up billing");
- }
-
var mergedBilling = info.billingContact()
.withContact(mergedContact)
.withAddress(mergedAddress)
@@ -809,6 +806,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
billingCursor.setString("phone", billingContact.contact().phone());
var taxIdCursor = billingCursor.setObject("taxId");
+ taxIdCursor.setString("country", billingContact.getTaxId().country().value());
taxIdCursor.setString("type", billingContact.getTaxId().type().value());
taxIdCursor.setString("code", billingContact.getTaxId().code().value());
billingCursor.setString("purchaseOrder", billingContact.getPurchaseOrder().value());
@@ -924,8 +922,10 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private TaxId updateAndValidateTaxId(Inspector insp, TaxId old) {
if (!insp.valid()) return old;
- var taxId = new TaxId(getString(insp.field("type"), old.type().value()),
- getString(insp.field("code"), old.code().value()));
+ var taxId = new TaxId(
+ getString(insp.field("country"), old.country().value()),
+ getString(insp.field("type"), old.type().value()),
+ getString(insp.field("code"), old.code().value()));
controller.serviceRegistry().billingController().validateTaxId(taxId);
return taxId;
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
index b4237a46c5a..93c13e6ed4c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
@@ -131,6 +131,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
var countryCursor = countries.addObject();
countryCursor.setString("code", country.code());
countryCursor.setString("name", country.displayName());
+ countryCursor.setBool("taxIdMandatory", country.taxIdMandatory());
var taxTypesCursors = countryCursor.setArray("taxTypes");
country.taxTypes().forEach(taxType -> {
var taxTypeCursor = taxTypesCursors.addObject();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
index cfc3320b083..f2fc43933df 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
@@ -238,7 +238,7 @@ public class TenantSerializerTest {
.withAddress("Central Station")
.withRegion("Irish Sea"))
.withPurchaseOrder(new PurchaseOrder("PO42"))
- .withTaxId(new TaxId("no_vat", "123456789MVA"))
+ .withTaxId(new TaxId("NO", "no_vat", "123456789MVA"))
.withInvoiceEmail(new Email("billing@mycomp.any", false))
);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 1ad88675169..f8ae7c8ea50 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -107,6 +107,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
"phone":""
},
"taxId": {
+ "country": "",
"type": "",
"code": ""
},
@@ -125,7 +126,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
"email":"foo@example",
"phone":"phone"
},
- "taxId":{"type": "no_vat", "code": "123456789MVA"},
+ "taxId":{"country": "NO", "type": "no_vat", "code": "123456789MVA"},
"purchaseOrder":"PO9001",
"invoiceEmail":"billing@mycomp.any",
"address": {
@@ -151,6 +152,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
"phone":"phone"
},
"taxId": {
+ "country": "NO",
"type": "no_vat",
"code": "123456789MVA"
},
@@ -238,6 +240,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
"email":"","emailVerified":false,
"phone":"",
"taxId": {
+ "country": "",
"type": "",
"code": ""
},
@@ -272,6 +275,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
"emailVerified":false,
"phone":"phone",
"taxId": {
+ "country": "",
"type": "",
"code": ""
},
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/accepted-countries.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/accepted-countries.json
index 2714de1e64d..5247b84a900 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/accepted-countries.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/accepted-countries.json
@@ -3,6 +3,7 @@
{
"code": "NO",
"name": "Norway",
+ "taxIdMandatory": true,
"taxTypes": [
{
"id": "no_vat",
@@ -15,6 +16,7 @@
{
"code": "CA",
"name": "Canada",
+ "taxIdMandatory": true,
"taxTypes": [
{
"id": "ca_gst_hst",