DutySchedule.java

package com.tradecloud.domain.duties;

import com.tradecloud.common.base.PersistenceBase;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import javax.persistence.*;
import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.math.BigDecimal;

/**
 * Base class for duty schedules. This class contains common fields required by
 * all schedules and should be overridden by specific duty schedules as
 * required.
 */
@MappedSuperclass
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "DutySchedule")
public abstract class DutySchedule extends PersistenceBase implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * The descriptive name attached to a tariff line indicating the product to
     * which it applies.
     */
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "part1", column = @Column(name = "tariffheadingpart1", length = 4)),
            @AttributeOverride(name = "part2", column = @Column(name = "tariffheadingpart2", length = 2)),
            @AttributeOverride(name = "part3", column = @Column(name = "tariffheadingpart3", length = 2)),
            @AttributeOverride(name = "part4", column = @Column(name = "tariffheadingpart4", length = 2)),
            @AttributeOverride(name = "part5", column = @Column(name = "tariffheadingpart5", length = 2))})
    @XmlElement(name = "TariffHeading")
    private TariffHeading tariffHeading;

    /**
     * An indicator to allow the tariff expert to mark the duty information as
     * complete or valid.
     */
    @XmlAttribute
    private boolean valid;

    /**
     * Transient field to indicate if this duty schedule can be validated (using
     * {@link #setValid(boolean)}) or not.
     */
    @Transient
    @XmlTransient
    private boolean validationDisabled;

    public DutySchedule() {
        tariffHeading = new TariffHeading();
    }

    protected DutySchedule(DutySchedule dutySchedule) {
        tariffHeading = new TariffHeading(dutySchedule.getTariffHeading());
        valid = dutySchedule.valid;
        validationDisabled = dutySchedule.validationDisabled;
    }

    public TariffHeading getTariffHeading() {
        if (tariffHeading == null) {
            // Products will require a non-null TariffHeading, not maintained by
            // Product catalogue
            tariffHeading = new TariffHeading();
        }
        return tariffHeading;
    }

    public void setTariffHeading(TariffHeading tariffHeading) {
        this.tariffHeading = tariffHeading;
    }

    public boolean isValid() {
        return valid;
    }

    public void setValid(boolean valid) {
        this.valid = valid;
    }

    public boolean isValidationDisabled() {
        return validationDisabled;
    }

    public void setValidationDisabled(boolean validationDisabled) {
        this.validationDisabled = validationDisabled;
    }

    protected boolean validOtherDuty(boolean checkProof) {
        OtherDuty otherDuty1 = getDutyRate().getOtherDuty1();

        boolean validOtherDuty = false;

        if (otherDuty1 != null) {
            BigDecimal proof = otherDuty1.getProof();
            DutyUnit unit = otherDuty1.getUnit();
            BigDecimal additionalValue = otherDuty1.getAdditionalValue();
            BigDecimal value = otherDuty1.getValue();

            validOtherDuty = (proof != null || !checkProof) &&
                    unit != null &&
                    additionalValue != null &&
                    value != null && value.compareTo(BigDecimal.ZERO) == 1;
        }

        return validOtherDuty;
    }

    @Override
    public DutySchedule clone() {
        DutySchedule clone = (DutySchedule) super.clone();
        clone.setTariffHeading(tariffHeading != null ? tariffHeading.clone() : null);
        clone.setValid(valid);

        return clone;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("tariffHeading", tariffHeading).append("valid", valid).toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof DutySchedule)) {
            return false;
        }
        DutySchedule other = (DutySchedule) obj;
        return new EqualsBuilder().append(tariffHeading, other.tariffHeading).append(valid, other.valid).isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(tariffHeading).append(valid).toHashCode();
    }

    /**
     * Validates that the part field named partName is less than or equal to the
     * supplied maxLength.
     *
     * @param part      the part field to validate
     * @param maxLength the max length the supplied part field is allowed
     * @return
     */

    protected boolean isPartLengthValid(String part, int maxLength) {
        return !(part != null && part.trim().length() > maxLength);
    }

    /**
     * Validates that the
     * Indicates whether the minimum data is present in this tariff heading to
     * allow it to be validated. The rules are that part1 must at least 3
     * digits long and part2 must be at least 1 digit long.
     *
     * @return true if the minimum requirements are present, false otherwise
     */
    public boolean minimumTariffHeadingPresent(String part1, String part2, int PART1_MAX_LENGTH) {
        return part1 != null && part1.trim().length() == PART1_MAX_LENGTH && part2 != null && part2.trim().length() >= 1;
    }

    public abstract AbstractDutyRate getDutyRate();

    protected abstract int getPart1MaxLength();

    protected abstract boolean getDoCheckProof();

    protected abstract boolean getDoCheckValue();

    protected abstract boolean getDoCheckPercentage();

    protected boolean getOnlyRequiresHeading() {
        return false;
    }

    public boolean eligibleForValidation() {
        String part1 = getTariffHeading().getPart1();
        String part2 = getTariffHeading().getPart2();

        boolean hasHeading = getTariffHeading() != null;
        boolean lengthValid = false;
        boolean minimumHeadingPresent = false;
        boolean positivePercentage = false;
        boolean positiveDutyRate = false;
        boolean hasStatisticalOtherDuty1 = false;

        boolean doCheckProof = getDoCheckProof();
        boolean doCheckPercentage = getDoCheckPercentage();
        boolean doCheckValue = getDoCheckValue();
        boolean onlyRequiresHeading = getOnlyRequiresHeading();

        if (hasHeading) {
            lengthValid = isPartLengthValid(part1, getPart1MaxLength());

            if (lengthValid) {
                minimumHeadingPresent = minimumTariffHeadingPresent(part1, part2, getPart1MaxLength());

                if (doCheckValue && getDutyRate().getValue() != null) {
                    positiveDutyRate = getDutyRate().getValue().compareTo(BigDecimal.ZERO) >= 0;
                }

                if (doCheckPercentage && getDutyRate().getPercentage() != null) {
                    positivePercentage = getDutyRate().getPercentage().compareTo(BigDecimal.ZERO) >= 0;
                }

                hasStatisticalOtherDuty1 = validOtherDuty(doCheckProof);
            }
        }

        return lengthValid && minimumHeadingPresent &&
                (onlyRequiresHeading || positivePercentage || positiveDutyRate || hasStatisticalOtherDuty1);
    }

    public abstract String getType();
}