Skip to content

Commit

Permalink
InvoiceProcessingServiceCompany: add ValidFrom and Percent
Browse files Browse the repository at this point in the history
Take into account ValidFrom when selecting the correct InvoiceProcessingServiceCompanyConfig

#6904
  • Loading branch information
TheBestPessimist committed Jun 26, 2020
1 parent eb7236f commit a696010
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package de.metas.invoice.invoiceProcessingServiceCompany;

import com.google.common.collect.ImmutableList;
import de.metas.bpartner.BPartnerId;
import de.metas.document.DocTypeId;
import de.metas.product.ProductId;
import de.metas.util.lang.Percent;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.Value;

import java.time.ZonedDateTime;
import java.util.Optional;

/*
* #%L
* de.metas.business
Expand All @@ -18,12 +24,12 @@
* 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
* <http://www.gnu.org/licenses/gpl-2.0.html>.
Expand All @@ -34,8 +40,6 @@
@Builder
public class InvoiceProcessingServiceCompanyConfig
{
boolean active;

@NonNull
BPartnerId serviceCompanyBPartnerId;

Expand All @@ -45,6 +49,34 @@ public class InvoiceProcessingServiceCompanyConfig
@NonNull
ProductId serviceFeeProductId;

@NonNull ZonedDateTime validFrom;

@Getter(AccessLevel.NONE)
@NonNull ImmutableList<InvoiceProcessingServiceCompanyConfigBPartnerDetails> bpartnerDetails;

public ImmutableList<BPartnerId> getBPartnerIds()
{
return bpartnerDetails.stream()
.map(InvoiceProcessingServiceCompanyConfigBPartnerDetails::getBpartnerId)
.collect(ImmutableList.toImmutableList());
}

public Optional<Percent> getFeePercentageOfGrandTotalByBpartner(@NonNull final BPartnerId bpartnerId)
{
return bpartnerDetails.stream()
.filter(it -> it.getBpartnerId().equals(bpartnerId))
.map(InvoiceProcessingServiceCompanyConfigBPartnerDetails::getPercent)
.findFirst();
}
}

@Builder
@Value
/* package */ class InvoiceProcessingServiceCompanyConfigBPartnerDetails
{
@NonNull
BPartnerId bpartnerId;

@NonNull
Percent feePercentageOfGrandTotal;
Percent percent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import de.metas.util.lang.RepoIdAware;
import lombok.Value;

import javax.annotation.Nullable;

/*
* #%L
* de.metas.business
Expand Down Expand Up @@ -38,6 +40,7 @@ public static InvoiceProcessingServiceCompanyConfigId ofRepoId(final int repoId)
return new InvoiceProcessingServiceCompanyConfigId(repoId);
}

@Nullable
public static InvoiceProcessingServiceCompanyConfigId ofRepoIdOrNull(final int repoId)
{
return repoId > 0 ? new InvoiceProcessingServiceCompanyConfigId(repoId) : null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* #%L
* de.metas.business
* %%
* Copyright (C) 2020 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
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/

package de.metas.invoice.invoiceProcessingServiceCompany;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import de.metas.bpartner.BPartnerId;
import lombok.NonNull;

import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

// TODO tbp: add unit test to see that getting stuff (and entering duplicates work)
/* package */ class InvoiceProcessingServiceCompanyConfigMap
{
private final ImmutableListMultimap<BPartnerId, InvoiceProcessingServiceCompanyConfig> bpartnersToConfigsSorted;

public InvoiceProcessingServiceCompanyConfigMap(@NonNull final List<InvoiceProcessingServiceCompanyConfig> configs)
{
final ImmutableListMultimap.Builder<BPartnerId, InvoiceProcessingServiceCompanyConfig> map = ImmutableListMultimap.builder();
for (final InvoiceProcessingServiceCompanyConfig config : configs)
{
for (final BPartnerId partnerId : config.getBPartnerIds())
{
map.put(partnerId, config);
}
}
// for ease of access and calculation: sort by ValidFrom
map.orderValuesBy(Comparator.comparing(InvoiceProcessingServiceCompanyConfig::getValidFrom));

bpartnersToConfigsSorted = map.build();
}

public Optional<InvoiceProcessingServiceCompanyConfig> getByCustomerIdAndDate(@NonNull final BPartnerId customerId, @NonNull final ZonedDateTime validFrom)
{
final ImmutableList<InvoiceProcessingServiceCompanyConfig> configs = bpartnersToConfigsSorted.get(customerId);
for (int i = configs.size() - 1; i >= 0; i--)
{
final InvoiceProcessingServiceCompanyConfig config = configs.get(i);
if (config.getValidFrom().isBefore(validFrom) || config.getValidFrom().isEqual(validFrom))
{
return Optional.of(config);
}
}

return Optional.empty();
}
}

Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package de.metas.invoice.invoiceProcessingServiceCompany;

import static org.adempiere.model.InterfaceWrapperHelper.loadOutOfTrx;

import java.util.Map;
import java.util.Optional;

import org.adempiere.ad.dao.IQueryBL;
import org.compiere.model.I_InvoiceProcessingServiceCompany;
import org.compiere.model.I_InvoiceProcessingServiceCompany_BPartnerAssignment;
import org.springframework.stereotype.Repository;

import com.google.common.collect.ImmutableMap;

import com.google.common.collect.ImmutableList;
import de.metas.bpartner.BPartnerId;
import de.metas.cache.CCache;
import de.metas.document.DocTypeId;
import de.metas.product.ProductId;
import de.metas.util.GuavaCollectors;
import de.metas.util.Services;
import de.metas.util.lang.Percent;
import lombok.NonNull;
import org.adempiere.ad.dao.IQueryBL;
import org.compiere.model.I_InvoiceProcessingServiceCompany;
import org.compiere.model.I_InvoiceProcessingServiceCompany_BPartnerAssignment;
import org.compiere.util.TimeUtil;
import org.springframework.stereotype.Repository;

import java.time.ZonedDateTime;
import java.util.Optional;

/*
* #%L
Expand All @@ -30,12 +28,12 @@
* 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
* <http://www.gnu.org/licenses/gpl-2.0.html>.
Expand All @@ -47,77 +45,53 @@ public class InvoiceProcessingServiceCompanyConfigRepository
{
private final IQueryBL queryBL = Services.get(IQueryBL.class);

private final CCache<InvoiceProcessingServiceCompanyConfigId, InvoiceProcessingServiceCompanyConfig> //
configsById = CCache.<InvoiceProcessingServiceCompanyConfigId, InvoiceProcessingServiceCompanyConfig> builder()
.tableName(I_InvoiceProcessingServiceCompany.Table_Name)
.build();

private final CCache<Integer, CustomerToConfigAssignmentMap> //
customerToConfigAssignmentsCache = CCache.<Integer, CustomerToConfigAssignmentMap> builder()
.tableName(I_InvoiceProcessingServiceCompany_BPartnerAssignment.Table_Name)
.build();
private final CCache<Integer, InvoiceProcessingServiceCompanyConfigMap> configsMapCache =
CCache.<Integer, InvoiceProcessingServiceCompanyConfigMap>builder()
.tableName(I_InvoiceProcessingServiceCompany.Table_Name)
.additionalTableNameToResetFor(I_InvoiceProcessingServiceCompany_BPartnerAssignment.Table_Name)
.build();

public Optional<InvoiceProcessingServiceCompanyConfig> getByCustomerId(@NonNull final BPartnerId customerId)
public Optional<InvoiceProcessingServiceCompanyConfig> getByCustomerId(@NonNull final BPartnerId customerId, @NonNull final ZonedDateTime evaluationDate)
{
return getConfigIdByCustomerId(customerId)
.map(this::getById)
.filter(InvoiceProcessingServiceCompanyConfig::isActive);
final InvoiceProcessingServiceCompanyConfigMap config = configsMapCache.getOrLoad(0, this::retrieveAllCompanyConfigs);
return config.getByCustomerIdAndDate(customerId, evaluationDate);
}

private Optional<InvoiceProcessingServiceCompanyConfigId> getConfigIdByCustomerId(@NonNull final BPartnerId customerId)
@NonNull
private InvoiceProcessingServiceCompanyConfigMap retrieveAllCompanyConfigs()
{
final CustomerToConfigAssignmentMap customerToConfigAssignmentMap = customerToConfigAssignmentsCache.getOrLoad(0, this::retrieveCustomerToConfigAssignmentMap);
return customerToConfigAssignmentMap.getConfigIdByCustomerId(customerId);
}

private InvoiceProcessingServiceCompanyConfig getById(@NonNull final InvoiceProcessingServiceCompanyConfigId configId)
{
return configsById.getOrLoad(configId, this::retrieveById);
}

private InvoiceProcessingServiceCompanyConfig retrieveById(@NonNull final InvoiceProcessingServiceCompanyConfigId configId)
{
final I_InvoiceProcessingServiceCompany record = loadOutOfTrx(configId, I_InvoiceProcessingServiceCompany.class);

return InvoiceProcessingServiceCompanyConfig.builder()
.active(record.isActive())
.serviceCompanyBPartnerId(BPartnerId.ofRepoId(record.getServiceCompany_BPartner_ID()))
.serviceInvoiceDocTypeId(DocTypeId.ofRepoId(record.getServiceInvoice_DocType_ID()))
.serviceFeeProductId(ProductId.ofRepoId(record.getServiceFee_Product_ID()))
.feePercentageOfGrandTotal(Percent.of(record.getFeePercentageOfGrandTotal()))
.build();
final ImmutableList<InvoiceProcessingServiceCompanyConfig> collect = queryBL.createQueryBuilder(I_InvoiceProcessingServiceCompany.class)
.addOnlyActiveRecordsFilter()
.create()
.iterateAndStream()
.map(record -> {
final ImmutableList<InvoiceProcessingServiceCompanyConfigBPartnerDetails> partnerDetails = readAllBPartnerDetails(record);

return InvoiceProcessingServiceCompanyConfig.builder()
.serviceCompanyBPartnerId(BPartnerId.ofRepoId(record.getServiceCompany_BPartner_ID()))
.serviceInvoiceDocTypeId(DocTypeId.ofRepoId(record.getServiceInvoice_DocType_ID()))
.serviceFeeProductId(ProductId.ofRepoId(record.getServiceFee_Product_ID()))
.validFrom(TimeUtil.asZonedDateTime(record.getValidFrom()))
.bpartnerDetails(partnerDetails)
.build();
})
.collect(GuavaCollectors.toImmutableList());
return new InvoiceProcessingServiceCompanyConfigMap(collect);
}

private CustomerToConfigAssignmentMap retrieveCustomerToConfigAssignmentMap()
private ImmutableList<InvoiceProcessingServiceCompanyConfigBPartnerDetails> readAllBPartnerDetails(@NonNull final I_InvoiceProcessingServiceCompany company)
{
final ImmutableMap<BPartnerId, InvoiceProcessingServiceCompanyConfigId> configIdsByCustomerId = queryBL
return queryBL
.createQueryBuilder(I_InvoiceProcessingServiceCompany_BPartnerAssignment.class)
.addOnlyActiveRecordsFilter()
.addEqualsFilter(I_InvoiceProcessingServiceCompany_BPartnerAssignment.COLUMN_InvoiceProcessingServiceCompany_ID, company.getInvoiceProcessingServiceCompany_ID())
.create()
.stream()
.collect(ImmutableMap.toImmutableMap(
record -> BPartnerId.ofRepoId(record.getC_BPartner_ID()),
record -> InvoiceProcessingServiceCompanyConfigId.ofRepoId(record.getInvoiceProcessingServiceCompany_ID())));

return new CustomerToConfigAssignmentMap(configIdsByCustomerId);
}

//
//
//

private static class CustomerToConfigAssignmentMap
{
private final ImmutableMap<BPartnerId, InvoiceProcessingServiceCompanyConfigId> configIdsByCustomerId;

private CustomerToConfigAssignmentMap(@NonNull final Map<BPartnerId, InvoiceProcessingServiceCompanyConfigId> configIdsByCustomerId)
{
this.configIdsByCustomerId = ImmutableMap.copyOf(configIdsByCustomerId);
}

public Optional<InvoiceProcessingServiceCompanyConfigId> getConfigIdByCustomerId(@NonNull final BPartnerId customerId)
{
return Optional.ofNullable(configIdsByCustomerId.get(customerId));
}
.iterateAndStream()
.map(recordBP -> InvoiceProcessingServiceCompanyConfigBPartnerDetails.builder()
.bpartnerId(BPartnerId.ofRepoId(recordBP.getC_BPartner_ID()))
.percent(Percent.of(recordBP.getFeePercentageOfGrandTotal()))
.build()
)
.collect(GuavaCollectors.toImmutableList());
}
}

0 comments on commit a696010

Please sign in to comment.