introduce hs_booking_debitor_rv for HsBookingDebitorEntity including full debitorNumber

This commit is contained in:
Michael Hoennig 2024-06-05 19:09:06 +02:00
parent 31cedf1261
commit 8343152382
22 changed files with 64 additions and 35 deletions

View File

@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity // a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
@Entity @Entity
@Table(name = "hs_office_debitor_rv") // TODO.impl: create a readonly view for this, which also joins the partner-number? @Table(name = "hs_booking_debitor_rv")
@Getter @Getter
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@ -37,8 +37,8 @@ public class HsBookingDebitorEntity implements Stringifyable {
@Id @Id
private UUID uuid; private UUID uuid;
@Column(name = "debitornumbersuffix", length = 2) @Column(name = "debitornumber")
private String debitorNumberSuffix; private Integer debitorNumber;
@Column(name = "defaultprefix", columnDefinition = "char(3) not null") @Column(name = "defaultprefix", columnDefinition = "char(3) not null")
private String defaultPrefix; private String defaultPrefix;
@ -50,6 +50,6 @@ public class HsBookingDebitorEntity implements Stringifyable {
@Override @Override
public String toShortString() { public String toShortString() {
return DEBITOR_NUMBER_TAG + defaultPrefix; return DEBITOR_NUMBER_TAG + debitorNumber;
} }
} }

View File

@ -10,7 +10,5 @@ public interface HsBookingDebitorRepository extends Repository<HsBookingDebitorE
Optional<HsBookingDebitorEntity> findByUuid(UUID id); Optional<HsBookingDebitorEntity> findByUuid(UUID id);
List<HsBookingDebitorEntity> findByDefaultPrefix(String defaultPrefix); List<HsBookingDebitorEntity> findByDefaultPrefix(String defaultPrefix); // FIXME: change to findByDebitorNumber
long count();
} }

View File

@ -202,6 +202,6 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatab
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("6-hs-booking/620-booking-item/6203-hs-booking-item-rbac"); rbac().generateWithBaseFileName("6-hs-booking/630-booking-item/6303-hs-booking-item-rbac");
} }
} }

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.hs.booking.project; package net.hostsharing.hsadminng.hs.booking.project;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjectsApi; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjectsApi;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource;
@ -12,8 +13,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityNotFoundException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer;
@RestController @RestController
public class HsBookingProjectController implements HsBookingProjectsApi { public class HsBookingProjectController implements HsBookingProjectsApi {
@ -27,6 +30,9 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Autowired @Autowired
private HsBookingProjectRepository bookingProjectRepo; private HsBookingProjectRepository bookingProjectRepo;
@Autowired
private HsBookingDebitorRepository debitorRepo;
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsBookingProjectResource>> listBookingProjectsByDebitorUuid( public ResponseEntity<List<HsBookingProjectResource>> listBookingProjectsByDebitorUuid(
@ -50,7 +56,7 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsBookingProjectEntity.class); final var entityToSave = mapper.map(body, HsBookingProjectEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
final var saved = bookingProjectRepo.save(entityToSave); final var saved = bookingProjectRepo.save(entityToSave);
@ -111,4 +117,12 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
final var mapped = mapper.map(saved, HsBookingProjectResource.class); final var mapped = mapper.map(saved, HsBookingProjectResource.class);
return ResponseEntity.ok(mapped); return ResponseEntity.ok(mapped);
} }
final BiConsumer<HsBookingProjectInsertResource, HsBookingProjectEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
if (resource.getDebitorUuid() != null) {
entity.setDebitor(debitorRepo.findByUuid(resource.getDebitorUuid())
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] debitorUuid %s not found".formatted(
resource.getDebitorUuid()))));
}
};
} }

View File

@ -109,6 +109,6 @@ public class HsBookingProjectEntity implements Stringifyable, RbacObject {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("6-hs-booking/610-booking-project/6103-hs-booking-project-rbac"); rbac().generateWithBaseFileName("6-hs-booking/620-booking-project/6203-hs-booking-project-rbac");
} }
} }

View File

@ -0,0 +1,17 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-booking-debitor-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create view hs_booking_debitor_rv as
select debitor.uuid,
debitor.version,
(partner.partnerNumber::varchar || debitor.debitorNumberSuffix)::numeric as debitorNumber,
debitor.defaultPrefix
from hs_office_debitor_rv debitor
-- RBAC for debitor is sufficient, for faster access we are bypassing RBAC for the join tables
join hs_office_relation debitorRel on debitor.debitorReluUid=debitorRel.uuid
join hs_office_relation partnerRel on partnerRel.holderUuid=debitorRel.anchorUuid
join hs_office_partner partner on partner.partnerReluUid=partnerRel.uuid;
--//

View File

@ -130,17 +130,19 @@ databaseChangeLog:
- include: - include:
file: db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql file: db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql
- include: - include:
file: db/changelog/6-hs-booking/610-booking-project/6100-hs-booking-project.sql file: db/changelog/6-hs-booking/610-booking-debitor/6100-hs-booking-debitor.sql
- include: - include:
file: db/changelog/6-hs-booking/610-booking-project/6103-hs-booking-project-rbac.sql file: db/changelog/6-hs-booking/620-booking-project/6200-hs-booking-project.sql
- include: - include:
file: db/changelog/6-hs-booking/610-booking-project/6108-hs-booking-project-test-data.sql file: db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql
- include: - include:
file: db/changelog/6-hs-booking/620-booking-item/6200-hs-booking-item.sql file: db/changelog/6-hs-booking/620-booking-project/6208-hs-booking-project-test-data.sql
- include: - include:
file: db/changelog/6-hs-booking/620-booking-item/6203-hs-booking-item-rbac.sql file: db/changelog/6-hs-booking/630-booking-item/6200-hs-booking-item.sql
- include: - include:
file: db/changelog/6-hs-booking/620-booking-item/6208-hs-booking-item-test-data.sql file: db/changelog/6-hs-booking/630-booking-item/6203-hs-booking-item-rbac.sql
- include:
file: db/changelog/6-hs-booking/630-booking-item/6208-hs-booking-item-test-data.sql
- include: - include:
file: db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql file: db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql
- include: - include:

View File

@ -9,25 +9,25 @@ class HsBookingDebitorEntityTest {
@Test @Test
void toStringContainsDebitorNumberAndDefaultPrefix() { void toStringContainsDebitorNumberAndDefaultPrefix() {
final var given = HsBookingDebitorEntity.builder() final var given = HsBookingDebitorEntity.builder()
.debitorNumberSuffix("67") .debitorNumber(1234567)
.defaultPrefix("som") .defaultPrefix("som")
.build(); .build();
final var result = given.toString(); final var result = given.toString();
assertThat(result).isEqualTo("booking-debitor(D-som: som)"); // FIXME: I want "booking-debitor(D-1000167: som)" assertThat(result).isEqualTo("booking-debitor(D-1234567: som)");
} }
@Test @Test
void toShortStringContainsDefaultPrefix() { void toShortStringContainsDefaultPrefix() {
final var given = HsBookingDebitorEntity.builder() final var given = HsBookingDebitorEntity.builder()
.debitorNumberSuffix("67") .debitorNumber(1234567)
.defaultPrefix("som") .defaultPrefix("som")
.build(); .build();
final var result = given.toShortString(); final var result = given.toShortString();
assertThat(result).isEqualTo("D-som"); // FIXME: I want "booking-debitor(D-1000167: som)" assertThat(result).isEqualTo("D-1234567");
} }
} }

View File

@ -6,10 +6,8 @@ import lombok.experimental.UtilityClass;
@UtilityClass @UtilityClass
public class TestHsBookingDebitor { public class TestHsBookingDebitor {
public String DEFAULT_DEBITOR_SUFFIX = "00";
public static final HsBookingDebitorEntity TEST_BOOKING_DEBITOR = HsBookingDebitorEntity.builder() public static final HsBookingDebitorEntity TEST_BOOKING_DEBITOR = HsBookingDebitorEntity.builder()
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX) .debitorNumber(1234500)
.defaultPrefix("abc") .defaultPrefix("abc")
.build(); .build();
} }

View File

@ -311,7 +311,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(bookingItemRepo.findByUuid(givenBookingItem.getUuid())).isPresent().get() assertThat(bookingItemRepo.findByUuid(givenBookingItem.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getProject().getDebitor().toString()).isEqualTo("booking-debitor(D-fir: fir)"); assertThat(mandate.getProject().getDebitor().toString()).isEqualTo("booking-debitor(D-1000111: fir)");
assertThat(mandate.getValidFrom()).isEqualTo("2022-11-01"); assertThat(mandate.getValidFrom()).isEqualTo("2022-11-01");
assertThat(mandate.getValidTo()).isEqualTo("2022-12-31"); assertThat(mandate.getValidTo()).isEqualTo("2022-12-31");
return true; return true;

View File

@ -29,14 +29,14 @@ class HsBookingItemEntityUnitTest {
void toStringContainsAllPropertiesAndResourcesSortedByKey() { void toStringContainsAllPropertiesAndResourcesSortedByKey() {
final var result = givenBookingItem.toString(); final var result = givenBookingItem.toString();
assertThat(result).isEqualTo("HsBookingItemEntity(D-abc:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })"); assertThat(result).isEqualTo("HsBookingItemEntity(D-1234500:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })");
} }
@Test @Test
void toShortStringContainsOnlyMemberNumberAndCaption() { void toShortStringContainsOnlyMemberNumberAndCaption() {
final var result = givenBookingItem.toShortString(); final var result = givenBookingItem.toShortString();
assertThat(result).isEqualTo("D-abc:test project:some caption"); assertThat(result).isEqualTo("D-1234500:test project:some caption");
} }
@Test @Test

View File

@ -174,9 +174,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
// then // then
allTheseBookingItemsAreReturned( allTheseBookingItemsAreReturned(
result, result,
"HsBookingItemEntity(D-sec:D-1000212 default project, MANAGED_WEBSPACE, [2022-10-01,), some ManagedWebspace, { Daemons: 2, Multi: 4, SDD: 512, Traffic: 12 })", "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_WEBSPACE, [2022-10-01,), some ManagedWebspace, { Daemons: 2, Multi: 4, SDD: 512, Traffic: 12 })",
"HsBookingItemEntity(D-sec:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })", "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })",
"HsBookingItemEntity(D-sec:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })"); "HsBookingItemEntity(D-1000212:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })");
} }
@Test @Test
@ -194,9 +194,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
// then: // then:
exactlyTheseBookingItemsAreReturned( exactlyTheseBookingItemsAreReturned(
result, result,
"HsBookingItemEntity(D-fir:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })", "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })",
"HsBookingItemEntity(D-fir:D-1000111 default project, MANAGED_WEBSPACE, [2022-10-01,), some ManagedWebspace, { Daemons: 2, Multi: 4, SDD: 512, Traffic: 12 })", "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_WEBSPACE, [2022-10-01,), some ManagedWebspace, { Daemons: 2, Multi: 4, SDD: 512, Traffic: 12 })",
"HsBookingItemEntity(D-fir:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })"); "HsBookingItemEntity(D-1000111:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })");
} }
} }

View File

@ -220,7 +220,7 @@ class HsBookingProjectControllerAcceptanceTest extends ContextBasedTestWithClean
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(bookingProjectRepo.findByUuid(givenBookingProject.getUuid())).isPresent().get() assertThat(bookingProjectRepo.findByUuid(givenBookingProject.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: rel(anchor='LP First GmbH', type='DEBITOR', holder='LP First GmbH'), fir)"); assertThat(mandate.getDebitor().toString()).isEqualTo("booking-debitor(D-1000111: fir)");
return true; return true;
}); });
} }

View File

@ -15,13 +15,13 @@ class HsBookingProjectEntityUnitTest {
void toStringContainsAllPropertiesAndResourcesSortedByKey() { void toStringContainsAllPropertiesAndResourcesSortedByKey() {
final var result = givenBookingProject.toString(); final var result = givenBookingProject.toString();
assertThat(result).isEqualTo("HsBookingProjectEntity(D-1000100, some caption)"); assertThat(result).isEqualTo("HsBookingProjectEntity(D-1234500, some caption)");
} }
@Test @Test
void toShortStringContainsOnlyMemberNumberAndCaption() { void toShortStringContainsOnlyMemberNumberAndCaption() {
final var result = givenBookingProject.toShortString(); final var result = givenBookingProject.toShortString();
assertThat(result).isEqualTo("D-1000100:some caption"); assertThat(result).isEqualTo("D-1234500:some caption");
} }
} }