diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Beneficiary.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Beneficiary.java index f6b36345f1a..9450a3cab2f 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Beneficiary.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Beneficiary.java @@ -1,5 +1,9 @@ package de.metas.contracts.commission.businesslogic; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.NonNull; + import de.metas.bpartner.BPartnerId; import lombok.Value; @@ -29,5 +33,22 @@ @Value public class Beneficiary { + @JsonCreator + public static Beneficiary of(@JsonProperty("bPartnerId") @NonNull final BPartnerId bPartnerId) + { + return new Beneficiary(bPartnerId); + } + BPartnerId bPartnerId; + + private Beneficiary(BPartnerId bPartnerId) + { + this.bPartnerId = bPartnerId; + } + + @JsonProperty("bPartnerId") + public BPartnerId getBPartnerId() + { + return bPartnerId; + } } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionConfig.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionConfig.java index fbe02051a17..b72ba052950 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionConfig.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionConfig.java @@ -1,6 +1,13 @@ package de.metas.contracts.commission.businesslogic; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import de.metas.contracts.commission.businesslogic.algorithms.HierarchyConfig; import de.metas.contracts.commission.businesslogic.hierarchy.Hierarchy; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; + +import com.fasterxml.jackson.annotation.JsonSubTypes; /* * #%L @@ -25,6 +32,10 @@ */ /** Defines how a {@link CommissionInstance} is created for a given {@link CommissionTrigger} and {@link Hierarchy}. */ +@JsonTypeInfo(use = NAME, include = PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = HierarchyConfig.class, name = "HierarchyConfig"), +}) public interface CommissionConfig { CommissionType getCommissionType(); diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionContract.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionContract.java index 3e9ecded848..e11c4abdc2f 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionContract.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionContract.java @@ -1,5 +1,13 @@ package de.metas.contracts.commission.businesslogic; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import de.metas.contracts.commission.businesslogic.algorithms.HierarchyContract; + /* * #%L * de.metas.commission @@ -23,6 +31,10 @@ */ /** Contains settings that can vary between different {@link Beneficiary} even within the same commission instance. */ +@JsonTypeInfo(use = NAME, include = PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = HierarchyContract.class, name = "HierarchyContract"), +}) public interface CommissionContract { CommissionConfig getConfig(); diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionFact.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionFact.java index 70336c1e5eb..ebd4d3dbf23 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionFact.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionFact.java @@ -2,6 +2,9 @@ import java.time.Instant; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.Builder; import lombok.NonNull; import lombok.Value; @@ -19,26 +22,35 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public - * License along with this program. If not, see + * License along with this program. If not, see * . * #L% */ @Value -@Builder + public class CommissionFact { /** This fact's timestamp; note that we need chronology, but don't care for a particular timezone. */ - @NonNull Instant timestamp; - @NonNull CommissionState state; - @NonNull CommissionPoints points; + + @JsonCreator + @Builder + private CommissionFact( + @JsonProperty("timestamp") @NonNull final Instant timestamp, + @JsonProperty("state") @NonNull final CommissionState state, + @JsonProperty("points") @NonNull final CommissionPoints points) + { + this.timestamp = timestamp; + this.state = state; + this.points = points; + } } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionInstance.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionInstance.java index b1e5086abbb..274824d835d 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionInstance.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionInstance.java @@ -1,7 +1,11 @@ package de.metas.contracts.commission.businesslogic; +import java.util.List; + import javax.annotation.Nullable; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import lombok.Builder; @@ -32,20 +36,31 @@ */ @Value -@Builder public class CommissionInstance { /** null if this instance was not (yet) persisted */ - @Nullable CommissionInstanceId id; - @NonNull CommissionTriggerData currentTriggerData; - @NonNull CommissionConfig config; /** Each instance means that commission will be paid to some {@link Beneficiary} in accordance to some commission contract and hierarchy. */ - @Singular + ImmutableList shares; + + @JsonCreator + @Builder(toBuilder = true) + private CommissionInstance( + @JsonProperty("id") @Nullable final CommissionInstanceId id, + @JsonProperty("currentTriggerData") @NonNull final CommissionTriggerData currentTriggerData, + @JsonProperty("config") @NonNull final CommissionConfig config, + @JsonProperty("shares") @Singular final List shares) + { + this.id = id; + this.currentTriggerData = currentTriggerData; + this.config = config; + this.shares = ImmutableList.copyOf(shares); + } + } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionPoints.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionPoints.java index 187c2dd1465..19929be830c 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionPoints.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionPoints.java @@ -5,6 +5,10 @@ import java.math.BigDecimal; import java.util.Collection; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; + import de.metas.util.lang.Percent; import lombok.NonNull; import lombok.Value; @@ -44,6 +48,7 @@ public static CommissionPoints of(@NonNull final String points) return of(new BigDecimal(points)); } + @JsonCreator public static CommissionPoints of(@NonNull final BigDecimal points) { return new CommissionPoints(points); @@ -63,6 +68,7 @@ private CommissionPoints(@NonNull final BigDecimal points) this.points = points; } + @JsonValue public BigDecimal toBigDecimal() { return points; @@ -95,6 +101,7 @@ public CommissionPoints subtract(@NonNull final CommissionPoints augent) return CommissionPoints.of(toBigDecimal().subtract(augent.toBigDecimal())); } + @JsonIgnore public boolean isZero() { final boolean isZero = points.signum() == 0; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionShare.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionShare.java index a8664039bd9..612b9690aa9 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionShare.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionShare.java @@ -1,9 +1,12 @@ package de.metas.contracts.commission.businesslogic; import java.util.ArrayList; +import java.util.List; import org.adempiere.exceptions.AdempiereException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import de.metas.contracts.commission.businesslogic.hierarchy.HierarchyLevel; @@ -57,12 +60,13 @@ public class CommissionShare /** Chronological list of facts that make it clear what happened when */ private final ArrayList facts; + @JsonCreator @Builder private CommissionShare( - @NonNull final CommissionContract contract, - @NonNull final HierarchyLevel level, - @NonNull final Beneficiary beneficiary, - @NonNull @Singular final ImmutableList facts) + @JsonProperty("contract") @NonNull final CommissionContract contract, + @JsonProperty("level") @NonNull final HierarchyLevel level, + @JsonProperty("beneficiary") @NonNull final Beneficiary beneficiary, + @JsonProperty("facts") @NonNull @Singular final List facts) { this.contract = contract; this.level = level; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionTriggerData.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionTriggerData.java index 79f0124a7b5..64afcf6f6b7 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionTriggerData.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/CommissionTriggerData.java @@ -4,6 +4,9 @@ import java.time.Instant; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import de.metas.invoicecandidate.InvoiceCandidateId; import lombok.Builder; import lombok.NonNull; @@ -46,12 +49,13 @@ public class CommissionTriggerData CommissionPoints invoicedPoints; @Builder + @JsonCreator private CommissionTriggerData( - @NonNull final InvoiceCandidateId invoiceCandidateId, - @NonNull final Instant timestamp, - @NonNull final CommissionPoints forecastedPoints, - @NonNull final CommissionPoints invoiceablePoints, - @NonNull final CommissionPoints invoicedPoints) + @JsonProperty("invoiceCandidateId") @NonNull final InvoiceCandidateId invoiceCandidateId, + @JsonProperty("timestamp") @NonNull final Instant timestamp, + @JsonProperty("forecastedPoints") @NonNull final CommissionPoints forecastedPoints, + @JsonProperty("invoiceablePoints") @NonNull final CommissionPoints invoiceablePoints, + @JsonProperty("invoicedPoints") @NonNull final CommissionPoints invoicedPoints) { this.invoiceCandidateId = invoiceCandidateId; this.timestamp = timestamp; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Customer.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Customer.java index b551b0e0c74..a543aeaa374 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Customer.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/Customer.java @@ -1,6 +1,9 @@ package de.metas.contracts.commission.businesslogic; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import de.metas.bpartner.BPartnerId; +import lombok.NonNull; import lombok.Value; /* @@ -29,5 +32,22 @@ @Value public class Customer { + @JsonCreator + public static Customer of(@JsonProperty("bPartnerId") @NonNull final BPartnerId bPartnerId) + { + return new Customer(bPartnerId); + } + BPartnerId bPartnerId; + + private Customer(BPartnerId bPartnerId) + { + this.bPartnerId = bPartnerId; + } + + @JsonProperty("bPartnerId") + public BPartnerId getBPartnerId() + { + return bPartnerId; + } } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyConfig.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyConfig.java index d0916febdef..9f15ca30d3b 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyConfig.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyConfig.java @@ -46,7 +46,6 @@ public static HierarchyConfig cast(@NonNull final CommissionConfig config) .setParameter("config", config); } - public boolean isSubtractLowerLevelCommissionFromBase() { return true; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyContract.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyContract.java index 83b26a52dc9..26fa6b61545 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyContract.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/algorithms/HierarchyContract.java @@ -2,6 +2,9 @@ import org.adempiere.exceptions.AdempiereException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import de.metas.contracts.commission.businesslogic.CommissionContract; import de.metas.util.lang.Percent; import lombok.NonNull; @@ -46,6 +49,12 @@ public static HierarchyContract cast(@NonNull final CommissionContract contract) .setParameter("contract", contract); } + @JsonCreator + public HierarchyContract(@JsonProperty("config") @NonNull final HierarchyConfig config) + { + this.config = config; + } + /** Note: add "Hierarchy" as method parameters if and when we have a commission type where it makes a difference. */ public Percent getCommissionPercent() { @@ -56,4 +65,7 @@ public int getPointsPrecision() { return 2; } + + + } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyLevel.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyLevel.java index 691350193e6..07575a363a6 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyLevel.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyLevel.java @@ -2,6 +2,9 @@ import static de.metas.util.Check.assumeGreaterOrEqualToZero; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + import lombok.AccessLevel; import lombok.Getter; import lombok.Value; @@ -33,6 +36,7 @@ public class HierarchyLevel { public static final HierarchyLevel ZERO = of(0); + @JsonCreator public static HierarchyLevel of(int level) { return new HierarchyLevel(level); @@ -41,6 +45,7 @@ public static HierarchyLevel of(int level) @Getter(AccessLevel.NONE) int level; + @JsonValue public int toInt() { return level; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionHierarchyFactory.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionHierarchyFactory.java index d5ccc3f5f31..fadacdd6e75 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionHierarchyFactory.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionHierarchyFactory.java @@ -66,6 +66,6 @@ private Hierarchy createFor(final BPartnerId bPartnerId, final HierarchyBuilder private HierarchyNode node(final BPartnerId bPartnerId) { - return HierarchyNode.of(new Beneficiary(bPartnerId)); + return HierarchyNode.of(Beneficiary.of(bPartnerId)); } } diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionTriggerFactory.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionTriggerFactory.java index c5cea53b865..e3a002aa787 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionTriggerFactory.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/CommissionTriggerFactory.java @@ -69,9 +69,9 @@ private Optional createForRecord(@NonNull final I_C_Invoice_C } final CommissionTrigger trigger = CommissionTrigger.builder() - .customer(new Customer(BPartnerId.ofRepoId(icRecord.getBill_BPartner_ID()))) + .customer(Customer.of(BPartnerId.ofRepoId(icRecord.getBill_BPartner_ID()))) .timestamp(TimeUtil.asInstant(icRecord.getUpdated())) - .beneficiary(new Beneficiary(salesRepId)) + .beneficiary(Beneficiary.of(salesRepId)) .commissionTriggerData(commissionTriggerDataRepository.getForInvoiceCandiateId(invoiceCandidateId)) .build(); diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepository.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepository.java index 33b46b1db60..b509eba983e 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepository.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepository.java @@ -138,7 +138,7 @@ private CommissionTriggerData createCommissionTriggerData(@NonNull final I_C_Com private CommissionShareBuilder createShareBuilder(@NonNull final I_C_Commission_Share shareRecord) { final CommissionShareBuilder share = CommissionShare.builder() - .beneficiary(new Beneficiary(BPartnerId.ofRepoId(shareRecord.getC_BPartner_SalesRep_ID()))) + .beneficiary(Beneficiary.of(BPartnerId.ofRepoId(shareRecord.getC_BPartner_SalesRep_ID()))) .contract(new HierarchyContract(new HierarchyConfig())) // TODO load the contract! .level(HierarchyLevel.of(shareRecord.getLevelHierarchy())); return share; diff --git a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionRecordStagingService.java b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionRecordStagingService.java index ef515e2dec8..25078a4f7d6 100644 --- a/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionRecordStagingService.java +++ b/de.metas.contracts/src/main/java/de/metas/contracts/commission/services/repos/CommissionRecordStagingService.java @@ -6,6 +6,8 @@ import java.util.Collection; import java.util.List; +import javax.annotation.Nullable; + import org.adempiere.ad.dao.IQueryBL; import org.adempiere.ad.dao.IQueryBuilder; import org.compiere.model.IQuery; @@ -63,18 +65,18 @@ CommissionRecords retrieveRecordsForInstanceId( final boolean onlyActive) { final IQueryBuilder instanceQueryBuilder = createInstanceQueryBuilder(onlyActive) - .addEqualsFilter(I_C_Commission_Instance.COLUMN_C_Commission_Instance_ID, commissionInstanceIds); + .addInArrayFilter(I_C_Commission_Instance.COLUMN_C_Commission_Instance_ID, commissionInstanceIds); return retrieveRecords(instanceQueryBuilder.create(), onlyActive); } CommissionRecords retrieveRecordsForInvoiceCandidateId( - @NonNull final Collection invoiceCandidateId, + @NonNull final Collection invoiceCandidateIds, final boolean onlyActive) { // ------------------ I_C_Commission_Instance final IQueryBuilder instanceQueryBuilder = createInstanceQueryBuilder(onlyActive) - .addEqualsFilter(I_C_Commission_Instance.COLUMN_C_Invoice_Candidate_ID, invoiceCandidateId); + .addInArrayFilter(I_C_Commission_Instance.COLUMN_C_Invoice_Candidate_ID, invoiceCandidateIds); return retrieveRecords(instanceQueryBuilder.create(), onlyActive); } @@ -151,10 +153,10 @@ static class CommissionRecords @Builder private CommissionRecords( - @NonNull final ImmutableListMultimap icRecordIdToInstanceRecords, - @NonNull final ImmutableMap instanceRecordIdToInstance, - @NonNull final ImmutableListMultimap instanceRecordIdToShareRecords, - @NonNull final ImmutableListMultimap shareRecordIdToFactRecords) + @Nullable final ImmutableListMultimap icRecordIdToInstanceRecords, + @Nullable final ImmutableMap instanceRecordIdToInstance, + @Nullable final ImmutableListMultimap instanceRecordIdToShareRecords, + @Nullable final ImmutableListMultimap shareRecordIdToFactRecords) { this.icRecordIdToInstanceRecords = coalesce(icRecordIdToInstanceRecords, ImmutableListMultimap.of()); this.instanceRecordIdToInstance = coalesce(instanceRecordIdToInstance, ImmutableMap.of()); diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/BeneficiaryTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/BeneficiaryTest.java new file mode 100644 index 00000000000..9a020c229ee --- /dev/null +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/BeneficiaryTest.java @@ -0,0 +1,45 @@ +package de.metas.contracts.commission.businesslogic; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import de.metas.bpartner.BPartnerId; +import de.metas.util.JSONObjectMapper; + +/* + * #%L + * de.metas.contracts + * %% + * Copyright (C) 2019 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +class BeneficiaryTest +{ + @Test + void serialize_deserialize() + { + final JSONObjectMapper objectMapper = JSONObjectMapper.forClass(Beneficiary.class); + + final Beneficiary original = Beneficiary.of(BPartnerId.ofRepoId(20)); + final String json = objectMapper.writeValueAsString(original); + + final Beneficiary result = objectMapper.readValue(json); + + assertThat(result).isEqualTo(original); + } +} \ No newline at end of file diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/CustomerTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/CustomerTest.java new file mode 100644 index 00000000000..e5eb551c347 --- /dev/null +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/CustomerTest.java @@ -0,0 +1,46 @@ +package de.metas.contracts.commission.businesslogic; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import de.metas.bpartner.BPartnerId; +import de.metas.util.JSONObjectMapper; + +/* + * #%L + * de.metas.contracts + * %% + * Copyright (C) 2019 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +class CustomerTest +{ + + @Test + void serialize_deserialize() + { + final JSONObjectMapper objectMapper = JSONObjectMapper.forClass(Customer.class); + + final Customer original = Customer.of(BPartnerId.ofRepoId(20)); + final String json = objectMapper.writeValueAsString(original); + + final Customer result = objectMapper.readValue(json); + + assertThat(result).isEqualTo(original); + } +} diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/algorithms/HierachyAlgorithmTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/algorithms/HierachyAlgorithmTest.java index 159b30d2713..1604d2c7e4e 100644 --- a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/algorithms/HierachyAlgorithmTest.java +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/algorithms/HierachyAlgorithmTest.java @@ -61,13 +61,13 @@ void createInstance() private CommissionInstance createInstance_performTest(@NonNull final InvoiceCandidateId invoiceCandiateId) { - final Beneficiary headOfSales = new Beneficiary(BPartnerId.ofRepoId(40)); + final Beneficiary headOfSales = Beneficiary.of(BPartnerId.ofRepoId(40)); final HierarchyNode headOfSalesNode = HierarchyNode.of(headOfSales); - final Beneficiary salesSupervisor = new Beneficiary(BPartnerId.ofRepoId(30)); + final Beneficiary salesSupervisor = Beneficiary.of(BPartnerId.ofRepoId(30)); final HierarchyNode salesSupervisorNode = HierarchyNode.of(salesSupervisor); - final Beneficiary salesRep = new Beneficiary(BPartnerId.ofRepoId(20)); + final Beneficiary salesRep = Beneficiary.of(BPartnerId.ofRepoId(20)); final HierarchyNode salesrepNode = HierarchyNode.of(salesRep); final Hierarchy hierarchy = Hierarchy.builder() @@ -85,7 +85,7 @@ private CommissionInstance createInstance_performTest(@NonNull final InvoiceCand final CommissionTrigger trigger = CommissionTrigger.builder() .timestamp(Instant.now()) - .customer(new Customer(BPartnerId.ofRepoId(10))) + .customer(Customer.of(BPartnerId.ofRepoId(10))) .beneficiary(salesRep) .commissionTriggerData(triggerData) .build(); diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyTest.java index a35eb5b3709..fde2469f8a1 100644 --- a/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyTest.java +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/businesslogic/hierarchy/HierarchyTest.java @@ -48,12 +48,12 @@ class HierarchyTest @BeforeEach void before() { - headOfSalesNode = HierarchyNode.of(new Beneficiary(BPartnerId.ofRepoId(40))); + headOfSalesNode = HierarchyNode.of(Beneficiary.of(BPartnerId.ofRepoId(40))); - salesSupervisorNode = HierarchyNode.of(new Beneficiary(BPartnerId.ofRepoId(30))); + salesSupervisorNode = HierarchyNode.of(Beneficiary.of(BPartnerId.ofRepoId(30))); - salesrepNode1 = HierarchyNode.of(new Beneficiary(BPartnerId.ofRepoId(20))); - salesrepNode2 = HierarchyNode.of(new Beneficiary(BPartnerId.ofRepoId(21))); + salesrepNode1 = HierarchyNode.of(Beneficiary.of(BPartnerId.ofRepoId(20))); + salesrepNode2 = HierarchyNode.of(Beneficiary.of(BPartnerId.ofRepoId(21))); hierarchy = Hierarchy.builder() .addChildren(headOfSalesNode, ImmutableList.of(salesSupervisorNode)) @@ -73,7 +73,7 @@ void test() @Test void getUpStream() { - final Iterable result = hierarchy.getUpStream(new Beneficiary(BPartnerId.ofRepoId(21))); + final Iterable result = hierarchy.getUpStream(Beneficiary.of(BPartnerId.ofRepoId(21))); final Iterator iterator = result.iterator(); assertThat(iterator.hasNext()).isTrue(); diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/CommissionHierarchyFactoryTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/CommissionHierarchyFactoryTest.java index 2a31868c9db..f2147338604 100644 --- a/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/CommissionHierarchyFactoryTest.java +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/CommissionHierarchyFactoryTest.java @@ -73,7 +73,7 @@ void createFor() private HierarchyNode node(final int id) { - return HierarchyNode.of(new Beneficiary(BPartnerId.ofRepoId(id))); + return HierarchyNode.of(Beneficiary.of(BPartnerId.ofRepoId(id))); } } diff --git a/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.java b/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.java new file mode 100644 index 00000000000..ea4e44e3972 --- /dev/null +++ b/de.metas.contracts/src/test/java/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.java @@ -0,0 +1,225 @@ +package de.metas.contracts.commission.services.repos; + +import static io.github.jsonSnapshot.SnapshotMatcher.validateSnapshots; +import static java.math.BigDecimal.TEN; +import static org.adempiere.model.InterfaceWrapperHelper.newInstance; +import static org.adempiere.model.InterfaceWrapperHelper.saveRecord; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; + +import org.adempiere.ad.wrapper.POJOLookupMap; +import org.adempiere.test.AdempiereTestHelper; +import org.compiere.util.TimeUtil; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableList; + +import de.metas.contracts.commission.businesslogic.CommissionInstance; +import de.metas.contracts.commission.businesslogic.CommissionInstanceId; +import de.metas.contracts.commission.model.I_C_Commission_Fact; +import de.metas.contracts.commission.model.I_C_Commission_Instance; +import de.metas.contracts.commission.model.I_C_Commission_Share; +import de.metas.contracts.commission.model.X_C_Commission_Fact; +import de.metas.invoicecandidate.InvoiceCandidateId; +import de.metas.util.JSONObjectMapper; +import io.github.jsonSnapshot.SnapshotMatcher; + +/* + * #%L + * de.metas.contracts + * %% + * Copyright (C) 2019 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +class CommissionInstanceRepositoryTest +{ + private static final long START_TIMESTAMP = 1568720955000L; // Tuesday, September 17, 2019 11:49:15 AM + + private static long nextTimestamp = START_TIMESTAMP; + + private static final int C_INVOICE_CANDIDATE_ID = 10; + private static final int C_BPartner_SalesRep_1_ID = 20; + private static final int C_BPartner_SalesRep_2_ID = 21; + + private static final BigDecimal ELEVEN = new BigDecimal("11"); + private static final BigDecimal TWELVE = new BigDecimal("12"); + + private CommissionInstanceRepository commissionInstanceRepository; + + @BeforeEach + void beforeEach() + { + AdempiereTestHelper.get().init(); + commissionInstanceRepository = new CommissionInstanceRepository(new CommissionRecordStagingService()); + } + + @BeforeAll + static void beforeAll() + { + SnapshotMatcher.start( + AdempiereTestHelper.SNAPSHOT_CONFIG, + AdempiereTestHelper.createSnapshotJsonFunction()); + } + + @AfterAll + static void afterAll() + { + validateSnapshots(); + } + + @Test + void getForInvoiceCandidateId() + { + createCommissionData(); + + // invoke the method under test + final ImmutableList result = commissionInstanceRepository.getForInvoiceCandidateId(InvoiceCandidateId.ofRepoId(C_INVOICE_CANDIDATE_ID)); + assertThat(result).hasSize(1); + + SnapshotMatcher.expect(result.get(0)).toMatchSnapshot(); + } + + @Test + void getForCommissionInstanceId() + { + CommissionInstanceId commissionInstanceId = createCommissionData(); + + // invoke the method under test + final CommissionInstance result = commissionInstanceRepository.getForCommissionInstanceId(commissionInstanceId); + + SnapshotMatcher.expect(result).toMatchSnapshot(); + } + + private CommissionInstanceId createCommissionData() + { + final I_C_Commission_Instance instanceRecord = newInstance(I_C_Commission_Instance.class); + instanceRecord.setC_Invoice_Candidate_ID(C_INVOICE_CANDIDATE_ID); + instanceRecord.setMostRecentTriggerTimestamp(TimeUtil.asTimestamp(createNextInstant())); + instanceRecord.setPointsBase_Forecasted(TEN); + instanceRecord.setPointsBase_Invoiceable(ELEVEN); + instanceRecord.setPointsBase_Invoiced(TWELVE); + saveRecord(instanceRecord); + + final I_C_Commission_Share shareRecord1 = newInstance(I_C_Commission_Share.class); + shareRecord1.setC_Commission_Instance_ID(instanceRecord.getC_Commission_Instance_ID()); + shareRecord1.setC_BPartner_SalesRep_ID(C_BPartner_SalesRep_1_ID); + shareRecord1.setLevelHierarchy(10); + shareRecord1.setPointsSum_Forecasted(new BigDecimal("1")); + shareRecord1.setPointsSum_Invoiceable(new BigDecimal("1.1")); + shareRecord1.setPointsSum_Invoiced(new BigDecimal("1.2")); + saveRecord(shareRecord1); + + createFactRecord(shareRecord1, X_C_Commission_Fact.COMMISSION_FACT_STATE_FORECASTED, new BigDecimal("10")); + createFactRecord(shareRecord1, X_C_Commission_Fact.COMMISSION_FACT_STATE_FORECASTED, new BigDecimal("-9")); + createFactRecord(shareRecord1, X_C_Commission_Fact.COMMISSION_FACT_STATE_INVOICEABLE, new BigDecimal("1.1")); + createFactRecord(shareRecord1, X_C_Commission_Fact.COMMISSION_FACT_STATE_INVOICED, new BigDecimal("1.2")); + + final I_C_Commission_Share shareRecord2 = newInstance(I_C_Commission_Share.class); + shareRecord2.setC_Commission_Instance_ID(instanceRecord.getC_Commission_Instance_ID()); + shareRecord2.setC_BPartner_SalesRep_ID(C_BPartner_SalesRep_2_ID); + shareRecord2.setLevelHierarchy(20); + shareRecord2.setPointsSum_Forecasted(new BigDecimal("2")); + shareRecord2.setPointsSum_Invoiceable(new BigDecimal("2.1")); + shareRecord2.setPointsSum_Invoiced(new BigDecimal("2.2")); + saveRecord(shareRecord2); + + createFactRecord(shareRecord2, X_C_Commission_Fact.COMMISSION_FACT_STATE_FORECASTED, new BigDecimal("2")); + createFactRecord(shareRecord2, X_C_Commission_Fact.COMMISSION_FACT_STATE_INVOICEABLE, new BigDecimal("2.1")); + createFactRecord(shareRecord2, X_C_Commission_Fact.COMMISSION_FACT_STATE_INVOICED, new BigDecimal("10")); + createFactRecord(shareRecord2, X_C_Commission_Fact.COMMISSION_FACT_STATE_INVOICED, new BigDecimal("-7.8")); + + return CommissionInstanceId.ofRepoId(instanceRecord.getC_Commission_Instance_ID()); + } + + private void createFactRecord(final I_C_Commission_Share shareRecord, final String state, final BigDecimal points) + { + final I_C_Commission_Fact factRecord = newInstance(I_C_Commission_Fact.class); + factRecord.setC_Commission_Share_ID(shareRecord.getC_Commission_Share_ID()); + factRecord.setCommissionFactTimestamp(createNextInstant().toString()); + factRecord.setCommission_Fact_State(state); + factRecord.setCommissionPoints(points); + saveRecord(factRecord); + } + + private Instant createNextInstant() + { + final Instant result = Instant.ofEpochMilli(nextTimestamp); + nextTimestamp += 10000; + + return result; + } + + @Test + void save() + { + final InputStream objectStream = getClass().getResourceAsStream("/de/metas/contracts/commission/services/repos/CommissionInstance.json"); + assertThat(objectStream).isNotNull(); + + final CommissionInstance commissionInstance = JSONObjectMapper.forClass(CommissionInstance.class).readValue(objectStream); + + // invoke the method under test + final CommissionInstanceId result = commissionInstanceRepository.save(commissionInstance); + + // verify the records that were stored + final List instanceRecords = POJOLookupMap.get().getRecords(I_C_Commission_Instance.class); + assertThat(instanceRecords).hasSize(1) + .extracting("C_Commission_Instance_ID", "PointsBase_Forecasted", "PointsBase_Invoiceable", "PointsBase_Invoiced", "MostRecentTriggerTimestamp.time") + .contains(tuple(result.getRepoId(), TEN, ELEVEN, TWELVE, 1568720955000L/* "2019-09-17T11:49:15Z" */)); + + final List shareRecords = POJOLookupMap.get().getRecords(I_C_Commission_Share.class); + assertThat(shareRecords).hasSize(2) + .extracting("C_Commission_Instance_ID", "LevelHierarchy", "C_BPartner_SalesRep_ID", "PointsSum_Forecasted", "PointsSum_Invoiceable", "PointsSum_Invoiced") + .contains( + tuple(result.getRepoId(), 10, 20, new BigDecimal("1"), new BigDecimal("1.1"), new BigDecimal("1.2")), + tuple(result.getRepoId(), 20, 21, new BigDecimal("2"), new BigDecimal("2.1"), new BigDecimal("2.2"))); + + final I_C_Commission_Share shareRecord1 = shareRecords.get(0); + assertThat(shareRecord1.getLevelHierarchy()).isEqualTo(10); // guard + final List factRecords1 = POJOLookupMap.get().getRecords(I_C_Commission_Fact.class, r -> r.getC_Commission_Share_ID() == shareRecord1.getC_Commission_Share_ID()); + assertThat(factRecords1).hasSize(4) + .extracting("CommissionFactTimestamp", "Commission_Fact_State", "CommissionPoints") + .contains( + tuple("2019-09-17T11:49:25Z", "FORECASTED", new BigDecimal("10")), + tuple("2019-09-17T11:49:35Z", "FORECASTED", new BigDecimal("-9")), + tuple("2019-09-17T11:49:45Z", "INVOICEABLE", new BigDecimal("1.1")), + tuple("2019-09-17T11:49:55Z", "INVOICED", new BigDecimal("1.2"))); + + final I_C_Commission_Share shareRecord2 = shareRecords.get(1); + assertThat(shareRecord2.getLevelHierarchy()).isEqualTo(20); // guard + final List factRecords2 = POJOLookupMap.get().getRecords(I_C_Commission_Fact.class, r -> r.getC_Commission_Share_ID() == shareRecord2.getC_Commission_Share_ID()); + assertThat(factRecords2).hasSize(4) + .extracting("CommissionFactTimestamp", "Commission_Fact_State", "CommissionPoints") + .contains( + tuple("2019-09-17T11:50:05Z", "FORECASTED", new BigDecimal("2")), + tuple("2019-09-17T11:50:15Z", "INVOICEABLE", new BigDecimal("2.1")), + tuple("2019-09-17T11:50:25Z", "INVOICED", new BigDecimal("10")), + tuple("2019-09-17T11:50:35Z", "INVOICED", new BigDecimal("-7.8"))); + + // final check; load the CommissionInstance from the records we jsut created and verify that it's equal to the one we got from json + final CommissionInstance reloadedInstance = commissionInstanceRepository.getForCommissionInstanceId(result); + assertThat(reloadedInstance).isEqualTo(commissionInstance.toBuilder().id(result).build()); + } +} diff --git a/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstance.json b/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstance.json new file mode 100644 index 00000000000..e17defc7e75 --- /dev/null +++ b/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstance.json @@ -0,0 +1,75 @@ +{ + "currentTriggerData" : { + "invoiceCandidateId" : 10, + "timestamp" : "2019-09-17T11:49:15Z", + "forecastedPoints" : 10, + "invoiceablePoints" : 11, + "invoicedPoints" : 12 + }, + "config" : { + "@type" : "HierarchyConfig" + }, + "shares" : [ { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig" + } + }, + "level" : 10, + "beneficiary" : { + "bPartnerId" : 20 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:49:25Z", + "state" : "FORECASTED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:49:35Z", + "state" : "FORECASTED", + "points" : -9 + }, { + "timestamp" : "2019-09-17T11:49:45Z", + "state" : "INVOICEABLE", + "points" : 1.1 + }, { + "timestamp" : "2019-09-17T11:49:55Z", + "state" : "INVOICED", + "points" : 1.2 + } ], + "forecastedPointsSum" : 1, + "invoiceablePointsSum" : 1.1, + "invoicedPointsSum" : 1.2 + }, { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig" + } + }, + "level" : 20, + "beneficiary" : { + "bPartnerId" : 21 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:50:05Z", + "state" : "FORECASTED", + "points" : 2 + }, { + "timestamp" : "2019-09-17T11:50:15Z", + "state" : "INVOICEABLE", + "points" : 2.1 + }, { + "timestamp" : "2019-09-17T11:50:25Z", + "state" : "INVOICED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:50:35Z", + "state" : "INVOICED", + "points" : -7.8 + } ], + "forecastedPointsSum" : 2, + "invoiceablePointsSum" : 2.1, + "invoicedPointsSum" : 2.2 + } ] + } \ No newline at end of file diff --git a/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.snap b/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.snap new file mode 100644 index 00000000000..eadf6960209 --- /dev/null +++ b/de.metas.contracts/src/test/resources/de/metas/contracts/commission/services/repos/CommissionInstanceRepositoryTest.snap @@ -0,0 +1,174 @@ +de.metas.contracts.commission.services.repos.CommissionInstanceRepositoryTest.getForCommissionInstanceId=[ { + "id" : 100001, + "currentTriggerData" : { + "invoiceCandidateId" : 10, + "timestamp" : "2019-09-17T11:50:45Z", + "forecastedPoints" : 10, + "invoiceablePoints" : 11, + "invoicedPoints" : 12 + }, + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "shares" : [ { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "commissionPercent" : 10, + "pointsPrecision" : 2 + }, + "level" : 10, + "beneficiary" : { + "bPartnerId" : 20 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:50:55Z", + "state" : "FORECASTED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:51:05Z", + "state" : "FORECASTED", + "points" : -9 + }, { + "timestamp" : "2019-09-17T11:51:15Z", + "state" : "INVOICEABLE", + "points" : 1.1 + }, { + "timestamp" : "2019-09-17T11:51:25Z", + "state" : "INVOICED", + "points" : 1.2 + } ], + "forecastedPointsSum" : 1, + "invoiceablePointsSum" : 1.1, + "invoicedPointsSum" : 1.2 + }, { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "commissionPercent" : 10, + "pointsPrecision" : 2 + }, + "level" : 20, + "beneficiary" : { + "bPartnerId" : 21 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:51:35Z", + "state" : "FORECASTED", + "points" : 2 + }, { + "timestamp" : "2019-09-17T11:51:45Z", + "state" : "INVOICEABLE", + "points" : 2.1 + }, { + "timestamp" : "2019-09-17T11:51:55Z", + "state" : "INVOICED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:52:05Z", + "state" : "INVOICED", + "points" : -7.8 + } ], + "forecastedPointsSum" : 2, + "invoiceablePointsSum" : 2.1, + "invoicedPointsSum" : 2.2 + } ] +} ] + + +de.metas.contracts.commission.services.repos.CommissionInstanceRepositoryTest.getForInvoiceCandidateId=[ { + "id" : 100001, + "currentTriggerData" : { + "invoiceCandidateId" : 10, + "timestamp" : "2019-09-17T11:49:15Z", + "forecastedPoints" : 10, + "invoiceablePoints" : 11, + "invoicedPoints" : 12 + }, + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "shares" : [ { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "commissionPercent" : 10, + "pointsPrecision" : 2 + }, + "level" : 10, + "beneficiary" : { + "bPartnerId" : 20 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:49:25Z", + "state" : "FORECASTED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:49:35Z", + "state" : "FORECASTED", + "points" : -9 + }, { + "timestamp" : "2019-09-17T11:49:45Z", + "state" : "INVOICEABLE", + "points" : 1.1 + }, { + "timestamp" : "2019-09-17T11:49:55Z", + "state" : "INVOICED", + "points" : 1.2 + } ], + "forecastedPointsSum" : 1, + "invoiceablePointsSum" : 1.1, + "invoicedPointsSum" : 1.2 + }, { + "contract" : { + "@type" : "HierarchyContract", + "config" : { + "@type" : "HierarchyConfig", + "commissionType" : "HIERARCHY_COMMISSION", + "subtractLowerLevelCommissionFromBase" : true + }, + "commissionPercent" : 10, + "pointsPrecision" : 2 + }, + "level" : 20, + "beneficiary" : { + "bPartnerId" : 21 + }, + "facts" : [ { + "timestamp" : "2019-09-17T11:50:05Z", + "state" : "FORECASTED", + "points" : 2 + }, { + "timestamp" : "2019-09-17T11:50:15Z", + "state" : "INVOICEABLE", + "points" : 2.1 + }, { + "timestamp" : "2019-09-17T11:50:25Z", + "state" : "INVOICED", + "points" : 10 + }, { + "timestamp" : "2019-09-17T11:50:35Z", + "state" : "INVOICED", + "points" : -7.8 + } ], + "forecastedPointsSum" : 2, + "invoiceablePointsSum" : 2.1, + "invoicedPointsSum" : 2.2 + } ] +} ] \ No newline at end of file diff --git a/de.metas.util/src/main/java/de/metas/util/lang/Percent.java b/de.metas.util/src/main/java/de/metas/util/lang/Percent.java index 92e530952ab..37ddef29480 100644 --- a/de.metas.util/src/main/java/de/metas/util/lang/Percent.java +++ b/de.metas.util/src/main/java/de/metas/util/lang/Percent.java @@ -5,6 +5,9 @@ import javax.annotation.Nullable; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + import de.metas.util.Check; import de.metas.util.NumberUtils; import lombok.AccessLevel; @@ -37,6 +40,7 @@ @Value public class Percent { + @JsonCreator public static Percent of(@NonNull final String value) { return of(new BigDecimal(value)); @@ -149,6 +153,7 @@ private Percent(@NonNull final BigDecimal valueAsBigDecimal) this.value = NumberUtils.stripTrailingDecimalZeros(valueAsBigDecimal); } + @JsonValue public BigDecimal toBigDecimal() { return value;