com.sfs.whichdoctor.dao.DebitDAOImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.sfs.whichdoctor.dao.DebitDAOImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2009 David Harrison.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * Contributors:
 *     David Harrison - initial API and implementation
 ******************************************************************************/
package com.sfs.whichdoctor.dao;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.TreeMap;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.sfs.beans.BuilderBean;
import com.sfs.beans.FinancialTypeBean;
import com.sfs.beans.PrivilegesBean;
import com.sfs.beans.UserBean;
import com.sfs.whichdoctor.beans.CreditBean;
import com.sfs.whichdoctor.beans.DebitBean;
import com.sfs.whichdoctor.beans.GroupBean;
import com.sfs.whichdoctor.beans.ItemBean;
import com.sfs.whichdoctor.beans.OrganisationBean;
import com.sfs.whichdoctor.beans.PaymentBean;
import com.sfs.whichdoctor.beans.PersonBean;
import com.sfs.whichdoctor.beans.ReceiptBean;
import com.sfs.whichdoctor.beans.SearchBean;
import com.sfs.whichdoctor.beans.SearchResultsBean;
import com.sfs.whichdoctor.search.WhichDoctorSearchDaoException;

/**
 * The Class DebitDAOImpl.
 */
public class DebitDAOImpl extends WhichDoctorFinancialObjectDAOImpl implements DebitDAO {

    /** The data logger. */
    private static Logger dataLogger = Logger.getLogger(DebitDAOImpl.class);

    /** The payment dao. */
    @Resource
    private PaymentDAO paymentDAO;

    /**
     * Used to get a DebitBean for a specified debitId.
     *
     * @param debitId the debit id
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean load(final int debitId) throws WhichDoctorDaoException {
        return load(debitId, new BuilderBean(), false);
    }

    /**
     * Used to get a DebitBean for a specified debitId & supplied load options.
     *
     * @param debitId the debit id
     * @param loadDetails the load details
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean load(final int debitId, final BuilderBean loadDetails) throws WhichDoctorDaoException {
        return load(debitId, loadDetails, false);
    }

    /**
     * Used to get a DebitBean for a specified debitId and supplied load
     * options. A boolean parameter identifies whether to use the default reader
     * connection or optional writer connection datasource.
     *
     * @param debitId the debit id
     * @param loadDetails the load details
     * @param useWriterConn the use writer conn
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private DebitBean load(final int debitId, final BuilderBean loadDetails, final boolean useWriterConn)
            throws WhichDoctorDaoException {

        DebitBean debit = null;

        dataLogger.info("DebitId: " + debitId + " requested");

        JdbcTemplate jdbcTemplate = this.getJdbcTemplateReader();
        if (useWriterConn) {
            jdbcTemplate = this.getJdbcTemplateWriter();
        }

        final String loadSQL = getSQL().getValue("debit/load") + " AND invoice.InvoiceId = ?";

        try {
            debit = (DebitBean) jdbcTemplate.queryForObject(loadSQL, new Object[] { debitId }, new RowMapper() {
                public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                    return loadDebit(rs, loadDetails);
                }
            });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return debit;
    }

    /**
     * Load a DebitBean for a specified debit name.
     *
     * @param strDebit the str debit
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean load(final String strDebit) throws WhichDoctorDaoException {
        return load(strDebit, new BuilderBean());
    }

    /**
     * Load a DebitBean for a specified debit name and supplied load options.
     *
     * @param strDebit the str debit
     * @param loadDetails the load details
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean load(final String strDebit, final BuilderBean loadDetails)
            throws WhichDoctorDaoException {

        dataLogger.info("Debit Name: " + strDebit + " requested");

        int debitGUID = 0;

        final String loadSQL = getSQL().getValue("debit/loadName");

        try {
            debitGUID = this.getJdbcTemplateReader().queryForInt(loadSQL, new Object[] { strDebit });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }

        if (debitGUID > 0) {
            return loadGUID(debitGUID, loadDetails);
        } else {
            throw new WhichDoctorDaoException("Sorry no debit matching " + "those details could be identified");
        }
    }

    /**
     * Load a DebitBean for a specified GUID.
     *
     * @param guid the guid
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean loadGUID(final int guid) throws WhichDoctorDaoException {
        return loadGUID(guid, new BuilderBean());
    }

    /**
     * Load a DebitBean for a specified GUID and supplied load options.
     *
     * @param guid the guid
     * @param loadDetails the load details
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean loadGUID(final int guid, final BuilderBean loadDetails) throws WhichDoctorDaoException {

        dataLogger.info("Debit GUID: " + guid + " requested");

        DebitBean debit = null;

        final String loadSQL = getSQL().getValue("debit/load") + " AND invoice.Active = true AND invoice.GUID = ?";

        try {
            debit = (DebitBean) this.getJdbcTemplateReader().queryForObject(loadSQL, new Object[] { guid },
                    new RowMapper() {
                        public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                            return loadDebit(rs, loadDetails);
                        }
                    });

        } catch (IncorrectResultSizeDataAccessException ie) {
            dataLogger.debug("No results found for search: " + ie.getMessage());
        }
        return debit;
    }

    /**
     * Refresh the calculated debit values (credit/outstanding) for the supplied
     * debit GUID value.
     *
     * @param guid the guid
     */
    public final void refreshDebitValues(final int guid) {
        try {
            final DebitBean debit = this.loadGUID(guid);
            if (debit != null) {
                refreshDebitValues(debit);
            }
        } catch (WhichDoctorDaoException wde) {
            dataLogger.error("Failed to load debit " + guid + " to refresh its values: " + wde.getMessage());
        }
    }

    /**
     * Refresh the calculated values (credit/outstanding) for all debits stored
     * in the system.
     */
    public final void refreshAllDebitValues() {
        // Perform a search for all debits, for each entry refresh and
        // update the creditValue and outstandingValues.
        Collection<DebitBean> debits = new ArrayList<DebitBean>();

        SearchBean search = this.getSearchDAO().initiate("debit", null);
        search.setLimit(0);

        try {
            SearchResultsBean results = this.getSearchDAO().search(search);
            if (results.getSearchResults() != null) {
                for (Object objDebit : results.getSearchResults()) {
                    DebitBean debit = (DebitBean) objDebit;
                    debits.add(debit);
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing debit search: " + wse.getMessage());
        }

        for (DebitBean debit : debits) {
            refreshDebitValues(debit);
            dataLogger.error("Refreshed the calculated values for: " + debit.getGUID());
        }
    }

    /**
     * A private function that refreshes the calculated debit values
     * (credit/outstanding) for the supplied debit object.
     *
     * @param debit the debit
     */
    private void refreshDebitValues(final DebitBean debit) {
        try {
            Double[] calculatedValues = getCalculatedValues(debit);

            final double creditValue = calculatedValues[0];
            final double outstandingValue = calculatedValues[1];

            /* Modify the financial summary */
            this.getJdbcTemplateWriter().update(this.getSQL().getValue("debit/updateValues"),
                    new Object[] { creditValue, outstandingValue, debit.getGUID() });
        } catch (DataAccessException dae) {
            dataLogger.error(
                    "Failed to update calculated values for GUID " + debit.getGUID() + ": " + dae.getMessage());
        }
    }

    /**
     * Creates the DebitBean.
     *
     * @param debit the debit
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean create(final DebitBean debit, final UserBean checkUser, final PrivilegesBean privileges)
            throws WhichDoctorDaoException {
        debit.setActive(true);
        int debitId = save(debit, checkUser, privileges, "create");

        return load(debitId, new BuilderBean(), true);
    }

    /**
     * Modify the DebitBean.
     *
     * @param debit the debit
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return the debit bean
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final DebitBean modify(final DebitBean debit, final UserBean checkUser, final PrivilegesBean privileges)
            throws WhichDoctorDaoException {
        debit.setActive(true);
        int debitId = save(debit, checkUser, privileges, "modify");

        return load(debitId, new BuilderBean(), true);
    }

    /**
     * Delete the DebitBean.
     *
     * @param debit the debit
     * @param checkUser the check user
     * @param privileges the privileges
     *
     * @return true, if successful
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    public final boolean delete(final DebitBean debit, final UserBean checkUser, final PrivilegesBean privileges)
            throws WhichDoctorDaoException {

        boolean success = false;

        if (privileges.getPrivilege(checkUser, "invoices", "delete")) {
            // Before deleting the debit remove its child objects
            final BuilderBean loadDetails = new BuilderBean();
            loadDetails.setParameter("LOAD_ALL", true);
            try {
                final DebitBean deleteObj = this.loadGUID(debit.getGUID(), loadDetails);
                deleteAssociatedObjects(deleteObj, checkUser, privileges);

            } catch (WhichDoctorDaoException wde) {
                dataLogger.error("Error deleting children: " + wde.getMessage(), wde);
                throw new WhichDoctorDaoException("Error deleting children objects: " + wde.getMessage());
            }
        }

        debit.setActive(false);
        int debitDeleted = save(debit, checkUser, privileges, "delete");
        if (debitDeleted > 0) {
            success = true;
        }
        return success;
    }

    /**
     * Save the updated DebitBean.
     *
     * @param debit the debit
     * @param checkUser the check user
     * @param privileges the privileges
     * @param action the action
     *
     * @return the int
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private int save(final DebitBean debit, final UserBean checkUser, final PrivilegesBean privileges,
            final String action) throws WhichDoctorDaoException {

        /* Create invoice requires all the essential invoice information */
        if (debit.getDescription() == null) {
            throw new NullPointerException("Debit description field cannot be null");
        }
        if (StringUtils.isBlank(debit.getDescription())) {
            throw new WhichDoctorDaoException("Debit description field cannot " + "be an empty string");
        }
        if (debit.getTypeName() == null) {
            throw new WhichDoctorDaoException("Debit requires a debit type");
        }
        if (StringUtils.isBlank(debit.getTypeName())) {
            throw new WhichDoctorDaoException("Debit requires a debit type");
        }
        if (StringUtils.equals(debit.getTypeName(), "Null")) {
            throw new WhichDoctorDaoException("Debit requires a debit type");
        }
        if (debit.getPersonId() == 0 && debit.getOrganisationId() == 0) {
            throw new WhichDoctorDaoException("Debit must be attributed to " + "a person or organisation");
        }
        if (!privileges.getPrivilege(checkUser, "invoices", action)) {
            throw new WhichDoctorDaoException("Insufficient user credentials to " + action + " debit");
        }

        int typeId = 0;
        try {
            FinancialTypeBean financialObject = this.financialTypeDAO.load("Debit", debit.getTypeName(),
                    debit.getClassName());
            typeId = financialObject.getFinancialTypeId();
        } catch (Exception e) {
            dataLogger.error("Error loading financial type for debit: " + e.getMessage());
            throw new WhichDoctorDaoException("Debit requires a valid type");
        }

        Double[] calculatedValues = getCalculatedValues(debit);
        final double creditValue = calculatedValues[0];
        final double outstandingValue = calculatedValues[1];

        // Load the existing debit (if one exists) to see if the GST rate has changed
        double existingGSTRate = debit.getGSTRate();
        if (debit.getGUID() > 0) {
            try {
                final DebitBean existingDebit = this.loadGUID(debit.getGUID());
                existingGSTRate = existingDebit.getGSTRate();
            } catch (WhichDoctorDaoException wde) {
                dataLogger.error("Error loading existing debit: " + wde.getMessage());
            }
        }

        /* Check if the debit number exists, if not create one */
        debit.setNumber(this.checkNumber("debit", debit.getNumber(), debit.getIssued()));

        int debitId = 0;

        /* Does a credit number need to be generated */
        Date issued = new Date(Calendar.getInstance().getTimeInMillis());
        if (debit.getIssued() != null) {
            issued = new Date(debit.getIssued().getTime());
        }

        Timestamp sqlTimeStamp = new Timestamp(Calendar.getInstance().getTimeInMillis());

        ArrayList<Object> parameters = new ArrayList<Object>();
        parameters.add(debit.getDescription());
        parameters.add(debit.getNumber());
        parameters.add(debit.getGSTRate());
        parameters.add(debit.getCancelled());
        parameters.add(debit.getValue());
        parameters.add(debit.getNetValue());
        parameters.add(creditValue);
        parameters.add(outstandingValue);
        parameters.add(typeId);
        parameters.add(debit.getPersonId());
        parameters.add(debit.getOrganisationId());
        parameters.add(issued);
        parameters.add(debit.getPaymentDue());
        parameters.add(debit.getLatePaymentFee());
        parameters.add(debit.getLatePaymentDate());
        parameters.add(debit.getRecommendedDonation());
        parameters.add(debit.getActive());
        parameters.add(sqlTimeStamp);
        parameters.add(checkUser.getDN());
        parameters.add(debit.getLogMessage(action));

        try {
            Integer[] result = this.performUpdate("debit", debit.getGUID(), parameters, "Debit", checkUser, action);
            /* Set the returned guid and id values */
            debit.setGUID(result[0]);
            debitId = result[1];
        } catch (Exception e) {
            dataLogger.error("Error processing debit record: " + e.getMessage());
            throw new WhichDoctorDaoException("Error processing debit record: " + e.getMessage());
        }

        if (debitId > 0) {
            dataLogger.info(checkUser.getDN() + " created debitId: " + String.valueOf(debitId));

            // Update the financial summary
            updateFinancialSummary(action, debit, typeId, issued);

            /* If the existing GST rate is different to the new rate update payments */
            if (debit.getGUID() > 0 && existingGSTRate != debit.getGSTRate()) {
                updateReceiptGst(debit.getGUID(), debit.getGSTRate(), checkUser, privileges);
            }

            /* Update the search index */
            if (debit.getOrganisationId() > 0) {
                this.getSearchIndexDAO().updateCurrentBalanceIndex(debit.getOrganisationId(), "organisation");
            } else {
                this.getSearchIndexDAO().updateCurrentBalanceIndex(debit.getPersonId(), "person");
            }
        }
        return debitId;
    }

    /**
     * Update the financial summary.
     *
     * @param action the action
     * @param debit the debit
     * @param typeId the type id
     * @param issued the issued
     *
     * @throws WhichDoctorDaoException the which doctor dao exception
     */
    private void updateFinancialSummary(final String action, final DebitBean debit, final int typeId,
            final Date issued) throws WhichDoctorDaoException {

        try {
            if (StringUtils.equals(action, "delete")) {
                this.getJdbcTemplateWriter().update(this.getSQL().getValue("debit/deleteSummary"),
                        new Object[] { debit.getGUID() });
            } else {
                /* Create or modify the financial summary */
                this.getJdbcTemplateWriter().update(this.getSQL().getValue("debit/editSummary"),
                        new Object[] { debit.getGUID(), typeId, debit.getNumber(), debit.getDescription(),
                                debit.getValue(), debit.getNetValue(), debit.getCancelled(), issued,
                                debit.getPersonId(), debit.getOrganisationId(),

                                typeId, debit.getNumber(), debit.getDescription(), debit.getValue(),
                                debit.getNetValue(), debit.getCancelled(), issued, debit.getPersonId(),
                                debit.getOrganisationId() });
            }
        } catch (DataAccessException dae) {
            dataLogger.error("Failed to modify the financial summary: " + dae.getMessage());
            throw new WhichDoctorDaoException("Failed to modify the financial summary: " + dae.getMessage());
        }
    }

    /**
     * Delete the objects associated with the supplied DebitBean.
     *
     * @param deleteObjects the delete objects
     * @param checkUser the check user
     * @param privileges the privileges
     */
    private void deleteAssociatedObjects(final DebitBean deleteObjects, final UserBean checkUser,
            final PrivilegesBean privileges) {

        deleteMemos(deleteObjects, checkUser, privileges);
        deleteGroups(deleteObjects, checkUser, privileges);
    }

    /**
     * Update the associated receipt GST values.
     *
     * @param guid the guid
     * @param gst the gst
     * @param checkUser the check user
     * @param privileges the privileges
     */
    private void updateReceiptGst(final int guid, final double gst, final UserBean checkUser,
            final PrivilegesBean privileges) {

        final Collection<ReceiptBean> receipts = this.loadReceipts(guid);

        for (ReceiptBean receipt : receipts) {
            if (receipt.getPayments() != null) {
                for (PaymentBean payment : receipt.getPayments()) {
                    payment.setGSTRate(gst);
                    // Reset the value amount to recalculate
                    payment.setValue(0);

                    try {
                        this.paymentDAO.modify(payment, receipt, checkUser, privileges);
                    } catch (WhichDoctorDaoException wde) {
                        dataLogger.error("Error updating payment GST value: " + wde.getMessage());
                    }
                }
            }
        }
    }

    /**
     * Load debit.
     *
     * @param rs the rs
     * @param loadDetails the load details
     *
     * @return the debit bean
     *
     * @throws SQLException the SQL exception
     */
    private DebitBean loadDebit(final ResultSet rs, final BuilderBean loadDetails) throws SQLException {

        DebitBean debit = new DebitBean();

        // Create resource bean and fill with dataset info.
        debit.setId(rs.getInt("InvoiceId"));
        debit.setGUID(rs.getInt("GUID"));

        debit.setAbbreviation(rs.getString("Abbreviation"));
        debit.setNumber(rs.getString("InvoiceNo"));

        debit.setDescription(rs.getString("Description"));
        debit.setPersonId(rs.getInt("PersonId"));

        if (debit.getPersonId() > 0) {
            debit.setPerson(loadPerson(rs, debit.getPersonId(), loadDetails));
        }

        debit.setOrganisationId(rs.getInt("OrganisationId"));
        if (debit.getOrganisationId() > 0) {
            debit.setOrganisation(loadOrganisation(rs, debit.getOrganisationId(), loadDetails));
        }

        debit.setValue(rs.getDouble("Value"));
        debit.setNetValue(rs.getDouble("NetValue"));

        debit.setCreditValue(rs.getDouble("CreditValue"));
        debit.setOutstandingValue(rs.getDouble("OutstandingValue"));

        debit.setGSTRate(rs.getDouble("GSTRate"));

        try {
            debit.setIssued(rs.getDate("Issued"));
        } catch (SQLException e) {
            debit.setIssued(null);
        }

        try {
            debit.setPaymentDue(rs.getDate("PaymentDue"));
        } catch (SQLException e) {
            dataLogger.debug("Error reading PaymentDue");
        }

        debit.setLatePaymentFee(rs.getDouble("LatePaymentFee"));

        try {
            debit.setLatePaymentDate(rs.getDate("LatePaymentDate"));
        } catch (SQLException e) {
            dataLogger.debug("Error reading LatePaymentDate");
        }

        debit.setRecommendedDonation(rs.getDouble("RecommendedDonation"));

        debit.setCancelled(rs.getBoolean("Cancelled"));

        debit.setTypeName(rs.getString("Type"));
        debit.setClassName(rs.getString("InvoiceType"));

        if (loadDetails.getBoolean("CREDITS")) {
            // Perform a search for credits associated with this debit
            Collection<CreditBean> loadedCredits = loadAllCredits(debit.getGUID());

            debit.setCredits(getCredits(loadedCredits));
            debit.setRefunds(getRefunds(loadedCredits));
        }

        if (loadDetails.getBoolean("RECEIPTS")) {
            // Perform a search for receipts associated with this debit
            debit.setReceipts(loadReceipts(debit.getGUID()));
        }

        debit.setSecurity(rs.getString("Security"));
        debit.setActive(rs.getBoolean("Active"));

        if (loadDetails.getBoolean("HISTORY")) {
            try {
                debit.setCreatedDate(rs.getTimestamp("CreatedDate"));
            } catch (SQLException e) {
                dataLogger.debug("Error reading CreatedDate");
            }
            debit.setCreatedBy(rs.getString("CreatedBy"));
            try {
                debit.setModifiedDate(rs.getTimestamp("ModifiedDate"));
            } catch (SQLException e) {
                dataLogger.debug("Error reading ModifiedDate");
            }
            debit.setModifiedBy(rs.getString("ModifiedBy"));
            try {
                debit.setExportedDate(rs.getTimestamp("ExportedDate"));
            } catch (SQLException e) {
                dataLogger.debug("Error reading ExportedDate");
            }
            debit.setExportedBy(rs.getString("ExportedBy"));
        }

        if (loadDetails.getBoolean("TAGS")) {
            try {
                debit.setTags(this.getTagDAO().load(debit.getGUID(), loadDetails.getString("USERDN"), true));
            } catch (Exception e) {
                dataLogger.error("Error loading tags for debit: " + e.getMessage());
            }
        }

        if (loadDetails.getBoolean("MEMO")) {
            try {
                debit.setMemo(this.getMemoDAO().load(debit.getGUID(), loadDetails.getBoolean("MEMO_FULL")));
            } catch (Exception e) {
                dataLogger.error("Error loading memos for debit: " + e.getMessage());
            }
        }

        if (loadDetails.getBoolean("GROUPS")) {
            debit.setGroups(loadGroups(debit.getGUID()));
        }

        if (loadDetails.getBoolean("CREATED")) {
            UserBean user = new UserBean();
            user.setDN(rs.getString("CreatedBy"));
            user.setPreferredName(rs.getString("CreatedFirstName"));
            user.setLastName(rs.getString("CreatedLastName"));
            debit.setCreatedUser(user);
        }
        if (loadDetails.getBoolean("MODIFIED")) {
            UserBean user = new UserBean();
            user.setDN(rs.getString("ModifiedBy"));
            user.setPreferredName(rs.getString("ModifiedFirstName"));
            user.setLastName(rs.getString("ModifiedLastName"));
            debit.setModifiedUser(user);
        }
        if (loadDetails.getBoolean("EXPORTED")) {
            UserBean user = new UserBean();
            user.setDN(rs.getString("ExportedBy"));
            user.setPreferredName(rs.getString("ExportedFirstName"));
            user.setLastName(rs.getString("ExportedLastName"));
            debit.setExportedUser(user);
        }
        return debit;
    }

    /**
     * Load credits related to the supplied credit GUID.
     *
     * @param guid the guid
     *
     * @return the collection< credit bean>
     */
    private Collection<CreditBean> loadAllCredits(final int guid) {
        Collection<CreditBean> credits = new ArrayList<CreditBean>();
        try {
            SearchBean search = this.getSearchDAO().initiate("credit", null);
            search.setLimit(0);

            CreditBean criteria = (CreditBean) search.getSearchCriteria();
            final DebitBean debit = new DebitBean();
            debit.setGUID(guid);
            criteria.setDebit(debit);
            search.setSearchCriteria(criteria);

            SearchResultsBean results = this.getSearchDAO().search(search);

            if (results != null) {
                for (Object objCredit : results.getSearchResults()) {
                    CreditBean credit = (CreditBean) objCredit;
                    credits.add(credit);
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing search for related credits: " + wse.getMessage());
        }
        return credits;
    }

    /**
     * Gets the credits.
     *
     * @param creditArray the credit array
     * @return the credits
     */
    private Collection<CreditBean> getCredits(final Collection<CreditBean> creditArray) {
        Collection<CreditBean> credits = new ArrayList<CreditBean>();

        if (creditArray != null) {
            for (CreditBean credit : creditArray) {
                if (StringUtils.equalsIgnoreCase(credit.getClassName(), "Credit")) {
                    credits.add(credit);
                }
            }
        }
        return credits;
    }

    /**
     * Gets the refunds.
     *
     * @param creditArray the credit array
     * @return the refunds
     */
    private Collection<CreditBean> getRefunds(final Collection<CreditBean> creditArray) {
        Collection<CreditBean> refunds = new ArrayList<CreditBean>();

        if (creditArray != null) {
            for (CreditBean credit : creditArray) {
                if (!StringUtils.equalsIgnoreCase(credit.getClassName(), "Credit")) {
                    refunds.add(credit);
                }
            }
        }
        return refunds;
    }

    /**
     * Load receipts related to the supplied debit GUID.
     *
     * @param guid the guid
     *
     * @return the collection< receipt bean>
     */
    private Collection<ReceiptBean> loadReceipts(final int guid) {
        Collection<ReceiptBean> receipts = new ArrayList<ReceiptBean>();
        try {
            SearchBean search = this.getSearchDAO().initiate("receipt", null);
            search.setLimit(0);

            ReceiptBean criteria = (ReceiptBean) search.getSearchCriteria();
            final PaymentBean payment = new PaymentBean();
            final DebitBean debit = new DebitBean();
            debit.setGUID(guid);
            payment.setDebit(debit);

            Collection<PaymentBean> payments = new ArrayList<PaymentBean>();
            payments.add(payment);

            criteria.setPayments(payments);
            search.setSearchCriteria(criteria);

            BuilderBean details = new BuilderBean();
            details.setParameter("PAYMENT", true);

            SearchResultsBean results = this.getSearchDAO().search(search, details);

            if (results != null) {
                for (Object objReceipt : results.getSearchResults()) {
                    ReceiptBean receipt = (ReceiptBean) objReceipt;
                    receipts.add(receipt);
                }
            }
        } catch (WhichDoctorSearchDaoException wse) {
            dataLogger.error("Error performing search for related receipts: " + wse.getMessage());
        }
        return receipts;
    }

    /**
     * Gets the calculated values.
     *
     * @param debit the debit
     *
     * @return the calculated values
     */
    private Double[] getCalculatedValues(final DebitBean debit) {
        double debitValue = 0;
        double creditValue = 0;
        double receiptValue = 0;
        double outstandingValue = 0;

        if (debit != null && !debit.getCancelled()) {
            debitValue = debit.getNetValue();
        }
        if (debit != null && debit.getGUID() > 0) {
            Collection<CreditBean> credits = loadAllCredits(debit.getGUID());
            Collection<ReceiptBean> receipts = loadReceipts(debit.getGUID());

            if (receipts != null) {
                for (ReceiptBean receipt : receipts) {
                    if (!receipt.getCancelled() && receipt.getPayments() != null) {
                        for (PaymentBean payment : receipt.getPayments()) {
                            if (payment.getDebit() != null) {
                                final int debitGUID = payment.getDebit().getGUID();
                                if (debitGUID == debit.getGUID()) {
                                    receiptValue += payment.getNetValue();
                                }
                            }
                        }
                    }
                }
            }
            if (credits != null) {
                for (CreditBean credit : credits) {
                    if (!credit.getCancelled()) {
                        if (!StringUtils.equalsIgnoreCase(credit.getClassName(), "Credit")) {
                            // Cash credit, subtract from receipt value
                            receiptValue -= credit.getNetValue();
                        } else {
                            creditValue += credit.getNetValue();
                        }
                    }
                }
            }
        }

        outstandingValue = debitValue - creditValue - receiptValue;

        if (dataLogger.isDebugEnabled()) {
            dataLogger.debug("Debit value: " + debitValue);
            dataLogger.debug("Credit value: " + creditValue);
            dataLogger.debug("Receipt value: " + receiptValue);
            dataLogger.debug("Outstanding value: " + outstandingValue);
        }

        return new Double[] { creditValue, outstandingValue };
    }

    /**
     * Load person.
     *
     * @param rs the rs
     * @param personId the person id
     * @param loadDetails the load details
     *
     * @return the person bean
     *
     * @throws SQLException the SQL exception
     */
    private PersonBean loadPerson(final ResultSet rs, final int personId, final BuilderBean loadDetails)
            throws SQLException {

        PersonBean person = new PersonBean();
        person.setPersonIdentifier(rs.getInt("PersonIdentifier"));
        person.setPreferredName(rs.getString("PreferredName"));
        person.setFirstName(rs.getString("FirstName"));
        person.setLastName(rs.getString("LastName"));
        person.setGUID(personId);
        person.setTitle(rs.getString("Title"));
        person.setGender(rs.getString("Gender"));

        // Load address
        if (loadDetails.getBoolean("ADDRESS")) {
            try {
                person.setAddress(this.getAddressDAO().load(person.getGUID(), false,
                        loadDetails.getString("ADDRESS_CLASS"), loadDetails.getString("ADDRESS_TYPE")));
            } catch (Exception e) {
                dataLogger.error("Error loading address for credit: " + e.getMessage());
            }
        }
        return person;
    }

    /**
     * Load organisation.
     *
     * @param rs the rs
     * @param organisationId the organisation id
     * @param loadDetails the load details
     *
     * @return the organisation bean
     *
     * @throws SQLException the SQL exception
     */
    private OrganisationBean loadOrganisation(final ResultSet rs, final int organisationId,
            final BuilderBean loadDetails) throws SQLException {

        OrganisationBean organisation = new OrganisationBean();
        organisation.setGUID(organisationId);
        organisation.setName(rs.getString("OrganisationName"));
        /* Load address */
        if (loadDetails.getBoolean("ADDRESS")) {
            try {
                organisation.setAddress(this.getAddressDAO().load(organisation.getGUID(), false,
                        loadDetails.getString("ADDRESS_CLASS"), loadDetails.getString("ADDRESS_TYPE")));
            } catch (Exception e) {
                dataLogger.error("Error loading address for credit: " + e.getMessage());
            }
        }
        return organisation;
    }

    /**
     * Load groups.
     *
     * @param guid the guid
     *
     * @return the array list< group bean>
     */
    private ArrayList<GroupBean> loadGroups(final int guid) {

        // Create new SearchBean of type Debit and default values
        SearchResultsBean results = new SearchResultsBean();
        SearchBean groupSearch = this.getSearchDAO().initiate("group", null);
        groupSearch.setLimit(0);

        GroupBean searchCriteria = (GroupBean) groupSearch.getSearchCriteria();
        searchCriteria.setObjectType("Debits");
        ItemBean item = new ItemBean();
        item.setObject2GUID(guid);
        TreeMap<String, ItemBean> items = new TreeMap<String, ItemBean>();
        items.put("Debit", item);
        searchCriteria.setItems(items);

        groupSearch.setSearchCriteria(searchCriteria);

        try {
            BuilderBean loadGroup = new BuilderBean();
            loadGroup.setParameter("ITEMS", true);
            loadGroup.setParameter("REFERENCEID", String.valueOf(guid));
            results = this.getSearchDAO().search(groupSearch, loadGroup);
        } catch (Exception e) {
            dataLogger.error("Error loading groups for debit: " + e.getMessage());
        }

        ArrayList<GroupBean> groups = new ArrayList<GroupBean>();
        for (Object group : results.getSearchResults()) {
            groups.add((GroupBean) group);
        }
        return groups;
    }
}