diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java index 6f0ee297..332ea8dd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java @@ -53,8 +53,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { @JoinColumn(name = "billingcontactuuid") private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson - @Column(name = "billable") - private boolean billable; + @Column(name = "billable", nullable = false) + private Boolean billable; // not a primitive because otherwise the default would be false @Column(name = "vatid") private String vatId; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java index 2565ae30..092a0a9b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.OptionalFromJson; import jakarta.persistence.EntityManager; +import java.util.Optional; class HsOfficeDebitorEntityPatcher implements EntityPatcher { @@ -25,12 +26,11 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher { - verifyNotNull(newValue, "vatBusiness"); - entity.setVatBusiness(newValue); - }); + Optional.ofNullable(resource.getVatBusiness()).ifPresent(entity::setVatBusiness); + Optional.ofNullable(resource.getVatReverseCharge()).ifPresent(entity::setVatReverseCharge); OptionalFromJson.of(resource.getDefaultPrefix()).ifPresent(newValue -> { verifyNotNull(newValue, "defaultPrefix"); entity.setDefaultPrefix(newValue); diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml index 372b57bf..26736fac 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml @@ -23,6 +23,8 @@ components: $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' billingContact: $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' + billable: + type: boolean vatId: type: string vatCountryCode: @@ -30,6 +32,8 @@ components: pattern: '^[A-Z][A-Z]$' vatBusiness: type: boolean + vatReverseCharge: + type: boolean refundBankAccount: $ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount' defaultPrefix: @@ -43,6 +47,9 @@ components: type: string format: uuid nullable: true + billable: + type: boolean + nullable: false vatId: type: string nullable: true @@ -52,7 +59,10 @@ components: nullable: true vatBusiness: type: boolean - nullable: true + nullable: false + vatReverseCharge: + type: boolean + nullable: false refundBankAccountUuid: type: string format: uuid @@ -78,6 +88,8 @@ components: format: int8 minimum: 00 maximum: 99 + billable: + type: boolean vatId: type: string vatCountryCode: @@ -85,6 +97,8 @@ components: pattern: '^[A-Z][A-Z]$' vatBusiness: type: boolean + vatReverseCharge: + type: boolean refundBankAccountUuid: type: string format: uuid @@ -96,3 +110,4 @@ components: - partnerUuid - billingContactUuid - defaultPrefix + - billable diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index cba8e203..1de3bb06 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -164,9 +164,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "refundBankAccountUuid": "%s", "defaultPrefix": "for" } @@ -208,7 +210,9 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", - "defaultPrefix": "for" + "defaultPrefix": "for", + "billable": "true", + "vatReverseCharge": "false" } """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix)) .port(port) @@ -250,9 +254,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "defaultPrefix": "thi" } """ @@ -282,9 +288,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "defaultPrefix": "for" } """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix)) @@ -535,9 +543,11 @@ class HsOfficeDebitorControllerAcceptanceTest { final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newDebitor = HsOfficeDebitorEntity.builder() .debitorNumberSuffix(++nextDebitorSuffix) + .billable(true) .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .vatReverseCharge(false) .build(); return debitorRepo.save(newDebitor); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java index 8703ccdb..d94e9323 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java @@ -36,6 +36,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< private static final boolean PATCHED_VAT_BUSINESS = false; + private static final boolean INITIAL_BILLABLE = false; + private static final boolean PATCHED_BILLABLE = true; + + private static final boolean INITIAL_VAT_REVERSE_CHARGE = true; + private static final boolean PATCHED_VAT_REVERSE_CHARGE = false; + private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder() .uuid(INITIAL_PARTNER_UUID) .build(); @@ -60,9 +66,11 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< entity.setUuid(INITIAL_DEBITOR_UUID); entity.setPartner(givenInitialPartner); entity.setBillingContact(givenInitialContact); + entity.setBillable(INITIAL_BILLABLE); entity.setVatId("initial VAT-ID"); entity.setVatCountryCode("AA"); entity.setVatBusiness(true); + entity.setVatReverseCharge(INITIAL_VAT_REVERSE_CHARGE); entity.setDefaultPrefix("abc"); return entity; } @@ -87,6 +95,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorEntity::setBillingContact, newBillingContact(PATCHED_CONTACT_UUID)) .notNullable(), + new SimpleProperty<>( + "personType", + HsOfficeDebitorPatchResource::setBillable, + PATCHED_BILLABLE, + HsOfficeDebitorEntity::setBillable) + .notNullable(), new JsonNullableProperty<>( "vatId", HsOfficeDebitorPatchResource::setVatId, @@ -97,12 +111,24 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorPatchResource::setVatCountryCode, PATCHED_VAT_COUNTRY_CODE, HsOfficeDebitorEntity::setVatCountryCode), - new JsonNullableProperty<>( - "vatBusiness", + new SimpleProperty<>( + "vatReverseCharge", + HsOfficeDebitorPatchResource::setVatReverseCharge, + PATCHED_VAT_REVERSE_CHARGE, + HsOfficeDebitorEntity::setVatReverseCharge) + .notNullable(), + new SimpleProperty<>( + "personType", HsOfficeDebitorPatchResource::setVatBusiness, - PATCHED_VAT_BUSINESS, + PATCHED_BILLABLE, HsOfficeDebitorEntity::setVatBusiness) .notNullable(), + new SimpleProperty<>( + "personType", + HsOfficeDebitorPatchResource::setVatReverseCharge, + PATCHED_BILLABLE, + HsOfficeDebitorEntity::setVatReverseCharge) + .notNullable(), new JsonNullableProperty<>( "defaultPrefix", HsOfficeDebitorPatchResource::setDefaultPrefix, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index b6fb9332..2b12cffe 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -85,6 +85,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .billable(false) .build(); return debitorRepo.save(newDebitor); }); @@ -96,33 +97,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { assertThat(debitorRepo.count()).isEqualTo(count + 1); } -// @ParameterizedTest -// @ValueSource(strings = {"", "a", "ab", "a12", "123", "12a"}) -// public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) { -// // given -// context("superuser-alex@hostsharing.net"); -// final var count = debitorRepo.count(); -// final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); -// final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); -// -// // when -// final var result = attempt(em, () -> { -// final var newDebitor = HsOfficeDebitorEntity.builder() -// .debitorNumberSuffix((byte)21) -// .partner(givenPartner) -// .billingContact(givenContact) -// .defaultPrefix(givenPrefix) -// .build(); -// return debitorRepo.save(newDebitor); -// }); -// -// // then -// result.assertSuccessful(); -// assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeDebitorEntity::getUuid).isNotNull(); -// assertThatDebitorIsPersisted(result.returnedValue()); -// assertThat(debitorRepo.count()).isEqualTo(count + 1); -// } - @Test public void createsAndGrantsRoles() { // given @@ -131,7 +105,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream() // some search+replace to make the output fit into the screen width .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) -// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) @@ -147,6 +120,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .billable(false) .build(); return debitorRepo.save(newDebitor); }).assertSuccessful(); @@ -161,7 +135,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { "hs_office_debitor#1000422:Fourthe.G.-forthcontact.guest")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) -// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) @@ -584,6 +557,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .billingContact(givenContact) .refundBankAccount(givenBankAccount) .defaultPrefix(defaultPrefix) + .billable(true) .build(); return debitorRepo.save(newDebitor);