com.premiumminds.billy.core.services.builders.impl.GenericInvoiceEntryBuilderImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.premiumminds.billy.core.services.builders.impl.GenericInvoiceEntryBuilderImpl.java

Source

/**
 * Copyright (C) 2017 Premium Minds.
 *
 * This file is part of billy core.
 *
 * billy core is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * billy core 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with billy core. If not, see <http://www.gnu.org/licenses/>.
 */
package com.premiumminds.billy.core.services.builders.impl;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Currency;
import java.util.Date;

import javax.inject.Inject;
import javax.validation.ValidationException;

import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;

import com.premiumminds.billy.core.persistence.dao.AbstractDAOGenericInvoice;
import com.premiumminds.billy.core.persistence.dao.AbstractDAOGenericInvoiceEntry;
import com.premiumminds.billy.core.persistence.dao.DAOContext;
import com.premiumminds.billy.core.persistence.dao.DAOProduct;
import com.premiumminds.billy.core.persistence.dao.DAOTax;
import com.premiumminds.billy.core.persistence.entities.ContextEntity;
import com.premiumminds.billy.core.persistence.entities.GenericInvoiceEntity;
import com.premiumminds.billy.core.persistence.entities.GenericInvoiceEntryEntity;
import com.premiumminds.billy.core.persistence.entities.ProductEntity;
import com.premiumminds.billy.core.services.Builder;
import com.premiumminds.billy.core.services.UID;
import com.premiumminds.billy.core.services.builders.GenericInvoiceEntryBuilder;
import com.premiumminds.billy.core.services.entities.Context;
import com.premiumminds.billy.core.services.entities.ShippingPoint;
import com.premiumminds.billy.core.services.entities.Tax;
import com.premiumminds.billy.core.services.entities.documents.GenericInvoiceEntry;
import com.premiumminds.billy.core.util.BillyMathContext;
import com.premiumminds.billy.core.util.BillyValidator;
import com.premiumminds.billy.core.util.DiscountType;
import com.premiumminds.billy.core.util.Localizer;
import com.premiumminds.billy.core.util.NotImplemented;
import com.premiumminds.billy.core.util.NotOnUpdate;

public class GenericInvoiceEntryBuilderImpl<TBuilder extends GenericInvoiceEntryBuilderImpl<TBuilder, TEntry, TDAOEntry, TDAOInvoice>, TEntry extends GenericInvoiceEntry, TDAOEntry extends AbstractDAOGenericInvoiceEntry<?>, TDAOInvoice extends AbstractDAOGenericInvoice<?>>
        extends AbstractBuilder<TBuilder, TEntry> implements GenericInvoiceEntryBuilder<TBuilder, TEntry> {

    protected static final Localizer LOCALIZER = new Localizer("com/premiumminds/billy/core/i18n/FieldNames");

    protected TDAOEntry daoEntry;
    protected TDAOInvoice daoInvoice;
    protected DAOTax daoTax;
    protected DAOProduct daoProduct;
    protected DAOContext daoContext;

    protected Context context;

    @Inject
    public GenericInvoiceEntryBuilderImpl(TDAOEntry daoEntry, TDAOInvoice daoInvoice, DAOTax daoTax,
            DAOProduct daoProduct, DAOContext daoContext) {
        super(daoEntry);
        this.daoEntry = daoEntry;
        this.daoInvoice = daoInvoice;
        this.daoTax = daoTax;
        this.daoProduct = daoProduct;
        this.daoContext = daoContext;
    }

    @Override
    @NotOnUpdate
    public <T extends ShippingPoint> TBuilder setShippingOrigin(Builder<T> originBuilder) {
        BillyValidator.notNull(originBuilder,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.entry_shipping_origin"));
        this.getTypeInstance().setShippingOrigin(originBuilder.build());
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public <T extends ShippingPoint> TBuilder setShippingDestination(Builder<T> destinationBuilder) {
        BillyValidator.notNull(destinationBuilder,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.entry_shipping_destination"));
        this.getTypeInstance().setShippingDestination(destinationBuilder.build());
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setProductUID(UID productUID) {
        BillyValidator.notNull(productUID, "field.product");
        ProductEntity p = this.daoProduct.get(productUID);
        BillyValidator.found(p, "field.product");
        this.getTypeInstance().setProduct(p);

        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setCurrency(Currency currency) {
        BillyValidator.notNull(currency, "field.currency");
        this.getTypeInstance().setCurrency(currency);

        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setQuantity(BigDecimal quantity) {
        Validate.isTrue(quantity.compareTo(BigDecimal.ZERO) > 0, "The quantity must be positive"); // TODO message
        this.getTypeInstance().setQuantity(quantity);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setUnitOfMeasure(String unit) {
        BillyValidator.notBlank(unit, "field.unit");
        this.getTypeInstance().setUnitOfMeasure(unit);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setTaxPointDate(Date date) {
        BillyValidator.notNull(date, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.tax_point_date"));
        this.getTypeInstance().setTaxPointDate(date);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder addDocumentReferenceUID(UID referenceUID) {
        BillyValidator.notNull(referenceUID, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.reference"));
        GenericInvoiceEntity d = this.daoInvoice.get(referenceUID);
        BillyValidator.found(d, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.reference"));
        this.getTypeInstance().getDocumentReferences().add(d);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setDescription(String description) {
        BillyValidator.notBlank(description,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.description"));
        this.getTypeInstance().setDescription(description);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setShippingCostsAmount(BigDecimal amount) {
        BillyValidator.notNull(amount,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.shipping_costs_amount"));
        Validate.isTrue(amount.compareTo(BigDecimal.ZERO) >= 0);
        this.getTypeInstance().setShippingCostsAmount(amount);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setAmountType(AmountType type) {
        this.getTypeInstance().setAmountType(type);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setUnitAmount(AmountType type, BigDecimal amount) {
        BillyValidator.notNull(type, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.unit_amount_type"));
        BillyValidator.notNull(amount,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.unit_gross_amount"));

        switch (type) {
        case WITH_TAX:
            this.getTypeInstance().setUnitAmountWithTax(amount);
            this.getTypeInstance().setUnitAmountWithoutTax(null);
            break;
        case WITHOUT_TAX:
            this.getTypeInstance().setUnitAmountWithoutTax(amount);
            this.getTypeInstance().setUnitAmountWithTax(null);
            break;
        }
        this.getTypeInstance().setAmountType(type);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setContextUID(UID uidContext) {
        BillyValidator.notNull(uidContext,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.entry_context"));
        ContextEntity c = this.daoContext.get(uidContext);
        BillyValidator.found(c, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.entry_context"));
        this.context = c;
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setTaxExemptionReason(String exemptionReason) {
        BillyValidator.notBlank(exemptionReason,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.tax_exemption_reason"));
        this.getTypeInstance().setTaxExemptionReason(exemptionReason);
        return this.getBuilder();
    }

    @Override
    @NotOnUpdate
    public TBuilder setTaxExemptionCode(String exemptionCode) {
        BillyValidator.notBlank(exemptionCode,
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.tax_exemption_code"));
        this.getTypeInstance().setTaxExemptionCode(exemptionCode);
        return this.getBuilder();
    }

    @NotImplemented
    @Deprecated
    @Override
    public TBuilder setDiscounts(DiscountType type, BigDecimal... discounts) {
        BillyValidator.notNull(type, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.discount_type"));
        BillyValidator.notNull(discounts, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.discount"));
        Validate.notEmpty(discounts, GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.discount_type"));

        return this.getBuilder();
    }

    @Override
    protected void validateInstance() throws ValidationException {
        this.validateValues();

        GenericInvoiceEntry i = this.getTypeInstance();
        BillyValidator.mandatory(i.getDescription(),
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.description"));
        BillyValidator.mandatory(i.getTaxPointDate(),
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.tax_point_date"));
        BillyValidator.mandatory(i.getCurrency(),
                GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.currency"));

        if (i.getAmountType().compareTo(AmountType.WITH_TAX) == 0) {
            BillyValidator.mandatory(i.getUnitAmountWithoutTax(),
                    GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.unit_gross_amount"));
        } else {
            BillyValidator.mandatory(i.getUnitAmountWithoutTax(),
                    GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("field.unit_gross_amount"));
        }
    }

    protected void validateValues() throws ValidationException {
        MathContext mc = BillyMathContext.get();

        GenericInvoiceEntryEntity e = this.getTypeInstance();

        for (Tax t : e.getProduct().getTaxes()) {
            if (this.daoContext.isSubContext(t.getContext(), this.context)) {
                Date taxDate = e.getTaxPointDate() == null ? new Date() : e.getTaxPointDate();
                if (DateUtils.isSameDay(t.getValidTo(), taxDate) || t.getValidTo().after(taxDate)) {
                    e.getTaxes().add(t);
                }
            }
        }
        if (e.getTaxes().isEmpty()) {
            throw new ValidationException(
                    GenericInvoiceEntryBuilderImpl.LOCALIZER.getString("exception.invalid_taxes"));
        }

        e.setUnitDiscountAmount(BigDecimal.ZERO); // TODO

        if (e.getUnitAmountWithTax() != null) {
            BigDecimal unitAmountWithoutTax = e.getUnitAmountWithTax();
            BigDecimal unitTaxAmount = BigDecimal.ZERO;
            for (Tax t : this.getTypeInstance().getTaxes()) {
                switch (t.getTaxRateType()) {
                case FLAT:
                    unitAmountWithoutTax = unitAmountWithoutTax.subtract(t.getValue(), mc);
                    unitTaxAmount = unitTaxAmount.add(t.getValue(), mc);
                    break;
                case PERCENTAGE:
                    unitAmountWithoutTax = e.getUnitAmountWithTax().divide(
                            BigDecimal.ONE.add(t.getPercentageRateValue().divide(new BigDecimal("100"), mc), mc),
                            mc);
                    unitTaxAmount = unitTaxAmount.add(e.getUnitAmountWithTax().subtract(unitAmountWithoutTax, mc),
                            mc);

                    break;
                default:
                    break;
                }
            }
            e.setUnitAmountWithoutTax(unitAmountWithoutTax);
            e.setUnitTaxAmount(unitTaxAmount);

            // Minus discounts
            e.setUnitAmountWithoutTax(unitAmountWithoutTax.subtract(e.getUnitDiscountAmount(), mc));
        } else {
            BigDecimal unitAmountWithTax = e.getUnitAmountWithoutTax();
            BigDecimal unitTaxAmount = BigDecimal.ZERO;

            for (Tax t : this.getTypeInstance().getTaxes()) {
                switch (t.getTaxRateType()) {
                case FLAT:
                    unitAmountWithTax = unitAmountWithTax.add(t.getValue(), mc);
                    unitTaxAmount = unitTaxAmount.add(t.getValue(), mc);
                    break;
                case PERCENTAGE:
                    unitTaxAmount = unitTaxAmount.add(e.getUnitAmountWithoutTax()
                            .multiply(t.getPercentageRateValue(), mc).divide(new BigDecimal("100"), mc), mc);
                    unitAmountWithTax = unitAmountWithTax.add(unitTaxAmount, mc);
                    break;
                default:
                    break;
                }
            }

            e.setUnitAmountWithTax(unitAmountWithTax);
            e.setUnitTaxAmount(unitTaxAmount);

        }

        e.setAmountWithTax(this.getTypeInstance().getUnitAmountWithTax().multiply(e.getQuantity(), mc));
        e.setAmountWithoutTax(this.getTypeInstance().getUnitAmountWithoutTax().multiply(e.getQuantity(), mc));
        e.setTaxAmount(this.getTypeInstance().getUnitTaxAmount().multiply(e.getQuantity(), mc));
        e.setDiscountAmount(this.getTypeInstance().getUnitDiscountAmount().multiply(e.getQuantity(), mc));
    }

    @SuppressWarnings("unchecked")
    @Override
    protected GenericInvoiceEntryEntity getTypeInstance() {
        return (GenericInvoiceEntryEntity) super.getTypeInstance();
    }

}