MoneyWiseValidateSecurity.java

/*
 * MoneyWise: Finance Application
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.moneywise.data.validate;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.metis.field.MetisFieldRequired;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataValidator.MoneyWiseDataValidatorAutoCorrect;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee.MoneyWisePayeeList;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityDataMap;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityList;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityInfoSet;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWisePayeeClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType.MoneyWiseSecurityTypeList;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;

import java.util.Iterator;

/**
 * Validator for Security.
 */
public class MoneyWiseValidateSecurity
        extends MoneyWiseValidateAccount<MoneyWiseSecurity>
        implements MoneyWiseDataValidatorAutoCorrect<MoneyWiseSecurity> {
    /**
     * New Account name.
     */
    private static final String NAME_NEWACCOUNT = MoneyWiseBasicResource.SECURITY_NEWACCOUNT.getValue();

    /**
     * The infoSet validator.
     */
    private final MoneyWiseValidateSecurityInfoSet theInfoSet;

    /**
     * Constructor.
     */
    MoneyWiseValidateSecurity() {
        theInfoSet = new MoneyWiseValidateSecurityInfoSet();
    }

    @Override
    public void setEditSet(final PrometheusEditSet pEditSet) {
        super.setEditSet(pEditSet);
        theInfoSet.storeEditSet(pEditSet);
    }

    @Override
    public void validate(final PrometheusDataItem pSecurity) {
        final MoneyWiseSecurity mySecurity = (MoneyWiseSecurity) pSecurity;
        final MoneyWiseSecurityList myList = mySecurity.getList();
        final MoneyWisePayee myParent = mySecurity.getParent();
        final MoneyWiseSecurityType mySecType = mySecurity.getCategory();
        final MoneyWiseCurrency myCurrency = mySecurity.getAssetCurrency();
        final String mySymbol = mySecurity.getSymbol();

        /* Validate base components */
        super.validate(pSecurity);

        /* SecurityType must be non-null */
        if (mySecType == null) {
            pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseBasicResource.CATEGORY_NAME);
        } else {
            /* Access the class */
            final MoneyWiseSecurityClass myClass = mySecType.getSecurityClass();

            /* SecurityType must be enabled */
            if (!mySecType.getEnabled()) {
                pSecurity.addError(PrometheusDataItem.ERROR_DISABLED, MoneyWiseBasicResource.CATEGORY_NAME);
            }

            /* If the SecurityType is singular */
            if (myClass.isSingular()) {
                /* Count the elements of this class */
                final MoneyWiseSecurityDataMap myMap = myList.getDataMap();
                if (!myMap.validSingularCount(myClass)) {
                    pSecurity.addError(PrometheusDataItem.ERROR_MULT, MoneyWiseBasicResource.CATEGORY_NAME);
                }
            }
        }

        /* Currency must be non-null and enabled */
        if (myCurrency == null) {
            pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseStaticDataType.CURRENCY);
        } else if (!myCurrency.getEnabled()) {
            pSecurity.addError(PrometheusDataItem.ERROR_DISABLED, MoneyWiseStaticDataType.CURRENCY);
        }

        /* Parent must be non-null */
        if (myParent == null) {
            pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseBasicResource.ASSET_PARENT);
        } else {
            /* If we are open then parent must be open */
            if (!mySecurity.isClosed() && Boolean.TRUE.equals(myParent.isClosed())) {
                pSecurity.addError(ERROR_PARCLOSED, MoneyWiseBasicResource.ASSET_CLOSED);
            }

            /* Check class */
            if (mySecType != null) {
                /* Access the classes */
                final MoneyWiseSecurityClass myClass = mySecType.getSecurityClass();
                final MoneyWisePayeeClass myParClass = myParent.getCategoryClass();

                /* Parent must be suitable */
                if (!myParClass.canParentSecurity(myClass)) {
                    pSecurity.addError(ERROR_BADPARENT, MoneyWiseBasicResource.ASSET_PARENT);
                }
            }
        }

        /* If we have a securityType */
        if (mySecType != null) {
            /* Check symbol rules */
            if (mySecType.getSecurityClass().needsSymbol()) {
                if (mySymbol == null) {
                    pSecurity.addError(PrometheusDataItem.ERROR_MISSING, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
                } else if (!myList.validSymbolCount(mySymbol)) {
                    pSecurity.addError(PrometheusDataItem.ERROR_DUPLICATE, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
                }
            } else if (mySymbol != null) {
                pSecurity.addError(PrometheusDataItem.ERROR_EXIST, MoneyWiseSecurityInfoSet.getFieldForClass(MoneyWiseAccountInfoClass.SYMBOL));
            }
        }

        /* If we have an infoSet */
        if (mySecurity.getInfoSet() != null) {
            /* Validate the InfoSet */
            theInfoSet.validate(mySecurity.getInfoSet());
        }

        /* Set validation flag */
        if (!pSecurity.hasErrors()) {
            pSecurity.setValidEdit();
        }
    }

    @Override
    public void validateName(final MoneyWiseAssetBase pSecurity,
                             final String pName) {
        /* Perform basic checks */
        super.validateName(pSecurity, pName);

        /* Check that the name is not a reserved name */
        if (pName.equals(MoneyWiseSecurityHolding.SECURITYHOLDING_NEW)
                || pName.equals(MoneyWiseValidatePortfolio.NAME_CASHACCOUNT)) {
            pSecurity.addError(ERROR_RESERVED, PrometheusDataResource.DATAITEM_FIELD_NAME);
        }
    }

    /**
     * Determine if an infoSet class is required.
     *
     * @param pClass the infoSet class
     * @return the status
     */
    public MetisFieldRequired isClassRequired(final MoneyWiseAccountInfoClass pClass) {
        return theInfoSet.isClassRequired(pClass);
    }

    @Override
    public void setDefaults(final MoneyWiseSecurity pSecurity) throws OceanusException {
        /* Set values */
        final MoneyWiseSecurityList myList = pSecurity.getList();
        pSecurity.setName(getUniqueName(myList, NAME_NEWACCOUNT));
        pSecurity.setCategory(getDefaultSecurityType());
        pSecurity.setAssetCurrency(getReportingCurrency());
        pSecurity.setSymbol(pSecurity.getName());
        pSecurity.setClosed(Boolean.FALSE);
        autoCorrect(pSecurity);
    }

    @Override
    public void autoCorrect(final MoneyWiseSecurity pSecurity) throws OceanusException {
        /* Access category class and parent */
        final MoneyWiseSecurityClass myClass = pSecurity.getCategoryClass();
        final MoneyWisePayee myParent = pSecurity.getParent();

        /* Ensure that we have a valid parent */
        if (myParent == null
                || myParent.getCategoryClass().canParentSecurity(myClass)) {
            pSecurity.setParent(getDefaultParent(pSecurity));
        }

        /* autoCorrect the infoSet */
        theInfoSet.autoCorrect(pSecurity.getInfoSet());
    }

    /**
     * Obtain security type for new security account.
     *
     * @return the security type
     */
    private MoneyWiseSecurityType getDefaultSecurityType() {
        /* loop through the security types */
        final MoneyWiseSecurityTypeList myTypes
                = getEditSet().getDataList(MoneyWiseStaticDataType.SECURITYTYPE, MoneyWiseSecurityTypeList.class);
        final Iterator<MoneyWiseSecurityType> myIterator = myTypes.iterator();
        while (myIterator.hasNext()) {
            final MoneyWiseSecurityType myType = myIterator.next();

            /* Ignore deleted types */
            if (!myType.isDeleted()) {
                return myType;
            }
        }

        /* Return no category */
        return null;
    }

    /**
     * Obtain default parent for new security.
     *
     * @param pSecurity the security
     * @return the default parent
     */
    private MoneyWisePayee getDefaultParent(final MoneyWiseSecurity pSecurity) {
        /* Access details */
        final MoneyWisePayeeList myPayees = getEditSet().getDataList(MoneyWiseBasicDataType.PAYEE, MoneyWisePayeeList.class);
        final MoneyWiseSecurityClass myClass = pSecurity.getCategoryClass();

        /* loop through the payees */
        final Iterator<MoneyWisePayee> myIterator = myPayees.iterator();
        while (myIterator.hasNext()) {
            final MoneyWisePayee myPayee = myIterator.next();

            /* Ignore deleted and closed payees */
            if (myPayee.isDeleted() || Boolean.TRUE.equals(myPayee.isClosed())) {
                continue;
            }

            /* If the payee can parent */
            if (myPayee.getCategoryClass().canParentSecurity(myClass)) {
                return myPayee;
            }
        }

        /* Return no payee */
        return null;
    }
}