All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ligoj.app.plugin.prov.QuoteRelated Maven / Gradle / Ivy

The newest version!
/*
 * Licensed under MIT (https://github.com/ligoj/ligoj/blob/master/LICENSE)
 */
package org.ligoj.app.plugin.prov;

import java.math.BigDecimal;
import java.util.Optional;
import java.util.function.Function;

import jakarta.persistence.EntityNotFoundException;

import org.apache.commons.lang3.ObjectUtils;
import org.ligoj.app.plugin.prov.dao.ProvQuoteRepository;
import org.ligoj.app.plugin.prov.model.AbstractQuote;
import org.ligoj.app.plugin.prov.model.Costed;
import org.ligoj.app.plugin.prov.model.ProvQuote;
import org.ligoj.app.resource.subscription.SubscriptionResource;
import org.ligoj.bootstrap.core.dao.RestRepository;

/**
 * An object related to a quote. Handle cost and association validation against the quote.
 *
 * @param  The related entity.
 */
public interface QuoteRelated {

	/**
	 * Average hours in one month.
	 */
	double HOURS_BY_MONTH = 24d * 365d / 12d;

	/**
	 * Return the {@link SubscriptionResource} instance. Used to resolve the related subscription and validate the
	 * visibility.
	 *
	 * @return The {@link SubscriptionResource} instance.
	 */
	SubscriptionResource getSubscriptionResource();

	/**
	 * Return the {@link ProvQuoteRepository} instance. Used to resolve the right quote.
	 *
	 * @return The {@link ProvQuoteRepository} instance.
	 */
	ProvQuoteRepository getRepository();

	/**
	 * Return the quote associated to the given subscription. The visibility is checked.
	 *
	 * @param subscription The linked subscription.
	 * @return The quote if the visibility has been checked.
	 */
	default ProvQuote getQuoteFromSubscription(final int subscription) {
		return getRepository().findBy("subscription", getSubscriptionResource().checkVisible(subscription));
	}

	/**
	 * Request a cost update of the given entity and report the delta to the global cost. The changes are persisted.
	 *
	 * @param repository  The repository of the entity holding the cost.
	 * @param entity      The entity holding the cost.
	 * @param costUpdater The function used to compute the new cost.
	 * @param          The entity type holding the cost.
	 * @return The new computed cost.
	 */
	default  UpdatedCost newUpdateCost(final RestRepository repository, final T entity,
			final Function costUpdater) {

		// Update the total cost, applying the delta cost
		final var floatingCost = addCost(entity, costUpdater);
		repository.saveAndFlush(entity);

		final var cost = new UpdatedCost(entity.getId());
		cost.setCost(floatingCost);
		cost.setTotal(entity.getConfiguration().toFloating());
		return cost;
	}

	/**
	 * Add a cost to the quote related to given resource entity. The global cost is not deeply computed, only delta is
	 * applied.
	 *
	 * @param entity      The configured entity, related to a quote.
	 * @param costUpdater The function used to compute the new cost.
	 * @param          The entity type holding the cost.
	 * @return The new computed cost.
	 */
	default  Floating addCost(final T entity, final Function costUpdater) {
		// Save the previous costs
		final double oldCost = ObjectUtils.defaultIfNull(entity.getCost(), 0d);
		final double oldMaxCost = ObjectUtils.defaultIfNull(entity.getMaxCost(), 0d);
		final double oldCo2 = ObjectUtils.defaultIfNull(entity.getCo2(), 0d);
		final double oldMaxCo2 = ObjectUtils.defaultIfNull(entity.getMaxCo2(), 0d);

		final double oldInitial = ObjectUtils.defaultIfNull(entity.getInitialCost(), 0d);
		final double oldMaxInitial = ObjectUtils.defaultIfNull(entity.getMaxInitialCost(), 0d);

		// Process the update of this entity
		final var newCost = costUpdater.apply(entity);

		// Report the delta to the quote
		addCost(entity, oldCost, oldMaxCost, oldInitial, oldMaxInitial, oldCo2, oldMaxCo2);
		return newCost;
	}

	/**
	 * Add a cost to the quote related to given resource entity. The global cost is not deeply computed, only delta is
	 * applied.
	 *
	 * @param entity        The configured entity, related to a quote.
	 * @param old           The old entity's cost.
	 * @param oldMax        The old maximum entity's cost.
	 * @param oldInitial    The old initial entity's cost.
	 * @param oldMaxInitial The old maximum initial entity's cost.
	 * @param oldCo2        The old entity's CO2.
	 * @param oldMaxCo2     The old maximum entity's CO2.
	 * @param            The entity type holding the cost.
	 */
	default  void addCost(final T entity, final double old, final double oldMax,
			final double oldInitial, final double oldMaxInitial, final double oldCo2, final double oldMaxCo2) {
		final var quote = entity.getConfiguration();
		final var delta = entity.getCost() - old;
		final var maxDelta = entity.getMaxCost() - oldMax;

		final var deltaCo2 = entity.getCo2() - oldCo2;
		final var maxDeltaCo2 = entity.getMaxCo2() - oldMaxCo2;

		final var deltaI = entity.getInitialCost() - oldInitial;
		final var maxDeltaI = entity.getMaxInitialCost() - oldMaxInitial;
		if ((Math.abs(delta) + Math.abs(maxDelta) + Math.abs(deltaI) + Math.abs(maxDeltaI)) + Math.abs(deltaCo2)
				+ Math.abs(maxDeltaCo2) != 0) {
			// Report the delta to the quote
			synchronized (quote) {
				// Recurring part
				quote.setCostNoSupport(round(quote.getCostNoSupport() + delta));
				quote.setMaxCostNoSupport(round(quote.getMaxCostNoSupport() + maxDelta));
				quote.setCo2(round(quote.getCo2() + deltaCo2));
				quote.setMaxCo2(round(quote.getMaxCo2() + maxDeltaCo2));

				// Initial part
				quote.setInitialCost(round(quote.getInitialCost() + deltaI));
				quote.setMaxInitialCost(round(quote.getMaxInitialCost() + maxDeltaI));
			}
		}
	}

	/**
	 * Update the quote's cost minimal and maximal values.
	 *
	 * @param quote The quote entity.
	 * @param fc    The cost to add. Can be a negative value.
	 * @return The formal {@code fc} parameter.
	 */
	default Floating addCost(final ProvQuote quote, final Floating fc) {
		synchronized (quote) {
			// Recurring part
			quote.setCostNoSupport(round(quote.getCostNoSupport() + fc.getMin()));
			quote.setMaxCostNoSupport(round(quote.getMaxCostNoSupport() + fc.getMax()));
			quote.setCo2(round(quote.getCo2() + fc.getMinCo2()));
			quote.setMaxCo2(round(quote.getMaxCo2() + fc.getMaxCo2()));

			// Initial part
			quote.setInitialCost(round(quote.getInitialCost() + fc.getInitial()));
			quote.setMaxInitialCost(round(quote.getMaxInitialCost() + fc.getMaxInitial()));
		}
		return fc;
	}

	/**
	 * Round a cost to eliminate floating point artifact, and without required {@link BigDecimal} usage (not yet)
	 *
	 * @param value The value to round.
	 * @return The rounded value with 4 decimals.
	 */
	default double round(final double value) {
		return Floating.round(value);
	}

	/**
	 * Update the actual monthly cost of given resource.
	 *
	 * @param qr           The {@link AbstractQuote} to update cost.
	 * @param costProvider The cost provider.
	 * @param           The entity type holding the cost.
	 * @return The new (min/max) cost.
	 */
	default > Floating updateCost(final T qr, final Function costProvider) {
		final var cost = costProvider.apply(qr);
		qr.setCost(round(cost.getMin()));
		qr.setMaxCost(round(cost.getMax()));
		qr.setCo2(round(cost.getMinCo2()));
		qr.setMaxCo2(round(cost.getMaxCo2()));
		qr.setInitialCost(round(cost.getInitial()));
		qr.setMaxInitialCost(round(cost.getMaxInitial()));
		return new Floating(qr.getCost(), qr.getMaxCost(), qr.getInitialCost(), qr.getMaxInitialCost(),
				qr.isUnboundCost(), qr.getCo2(), qr.getMaxCo2());
	}

	/**
	 * Check and return the non null object.
	 *
	 * @param object The object to test.
	 * @param name   The object name. Used for the exception when null.
	 * @param     The entity type.
	 * @return the {@code object} when not null.
	 */
	default  T assertFound(final T object, final String name) {
		// Find the scoped location
		return Optional.ofNullable(object).orElseThrow(() -> new EntityNotFoundException(name));
	}

	/**
	 * Refresh the resources and the related cost. This is a full optimization lookup of the best prices.
	 * Note only the given entity is updated, the related quote's cost is not updated.
	 *
	 * @param costed The entity to refresh.
	 * @return The new computed price.
	 */
	Floating refresh(final C costed);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy