Java tutorial
/******************************************************************************* * 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 com.sfs.beans.FinancialTypeBean; import com.sfs.beans.PrivilegesBean; import com.sfs.beans.UserBean; import com.sfs.dao.FinancialTypeDAO; import com.sfs.whichdoctor.beans.DebitBean; 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.SearchDAO; import com.sfs.whichdoctor.search.WhichDoctorSearchDaoException; import java.net.URL; import java.sql.Date; import java.sql.ResultSet; import java.sql.Timestamp; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.StringTokenizer; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.codehaus.xfire.client.Client; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.RowMapper; /** * The Class PaymentDAOImpl. */ public class PaymentDAOImpl extends WhichDoctorBaseDAOImpl implements PaymentDAO { /** The Constant MINIMUM_RANGE. */ private static final double MINIMUM_RANGE = 0.01; /** The Constant MAXIMUM_RANGE. */ private static final double MAXIMUM_RANGE = 999999999; /** The data logger. */ private static Logger dataLogger = Logger.getLogger(PaymentDAOImpl.class); /** The payment url. */ private String paymentUrl; /** The access id. */ private String accessId; /** The secret. */ private String secret; /** The debit dao. */ @Resource private DebitDAO debitDAO; /** The financial type dao. */ @Resource private FinancialTypeDAO financialTypeDAO; /** The search dao. */ @Resource private SearchDAO searchDAO; /** The search index dao. */ @Resource private SearchIndexDAO searchIndexDAO; /** * Sets the payment url. * * @param paymentUrlVal the new payment url */ public final void setPaymentUrl(final String paymentUrlVal) { this.paymentUrl = paymentUrlVal; } /** * Sets the access id for the web service. * * @param accessIdVal the new access id */ public final void setAccessId(final String accessIdVal) { this.accessId = accessIdVal; } /** * Sets the secret for the web service. * * @param secretVal the new secret */ public final void setSecret(final String secretVal) { this.secret = secretVal; } /** * Used to get a Collection of PaymentBeans for a specified GUID number. * * @param guid the guid * @param fullResults the full results * * @return the collection< payment bean> * * @throws WhichDoctorDaoException the which doctor dao exception */ @SuppressWarnings("unchecked") public final Collection<PaymentBean> load(final int guid, final boolean fullResults) throws WhichDoctorDaoException { dataLogger.info("Payments for GUID: " + guid + " requested"); final String loadPayment = getSQL().getValue("payment/load") + " WHERE payment.Active = true AND payment.ReferenceGUID = ?" + " AND (invoice.Active IS null OR invoice.Active = true)" + " ORDER BY payment.PersonId, payment.InvoiceId"; Collection<PaymentBean> payments = new ArrayList<PaymentBean>(); try { payments = this.getJdbcTemplateReader().query(loadPayment, new Object[] { guid }, new RowMapper() { public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException { return loadPayment(rs); } }); } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for this search:" + ie.getMessage()); } return payments; } /** * Used to get a PaymentBean for a specified PaymentId. * * @param paymentId the payment id * * @return the payment bean * * @throws WhichDoctorDaoException the which doctor dao exception */ @SuppressWarnings("unchecked") public final PaymentBean load(final int paymentId) throws WhichDoctorDaoException { dataLogger.info("PaymentId: " + paymentId + " requested"); final String loadPaymentId = getSQL().getValue("payment/load") + " WHERE payment.PaymentId = ?"; PaymentBean payment = null; try { payment = (PaymentBean) this.getJdbcTemplateReader().queryForObject(loadPaymentId, new Object[] { paymentId }, new RowMapper() { public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException { return loadPayment(rs); } }); } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for this search:" + ie.getMessage()); } return payment; } /** * Used to get a PaymentBean for a specified GUID. * * @param guid the payment guid * * @return the payment bean * * @throws WhichDoctorDaoException the which doctor dao exception */ @SuppressWarnings("unchecked") public final PaymentBean loadGUID(final int guid) throws WhichDoctorDaoException { dataLogger.info("Payment GUID: " + guid + " requested"); final String loadPaymentGUID = getSQL().getValue("payment/load") + " WHERE payment.Active = true AND payment.GUID = ?"; PaymentBean payment = null; try { payment = (PaymentBean) this.getJdbcTemplateReader().queryForObject(loadPaymentGUID, new Object[] { guid }, new RowMapper() { public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException { return loadPayment(rs); } }); } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for this search:" + ie.getMessage()); } return payment; } /** * Load outstanding debits. * * @param payment the payment * @param user the user * * @return the collection< debit bean> */ public final Collection<DebitBean> loadOutstandingDebits(final PaymentBean payment, final UserBean user) { Collection<DebitBean> debits = new ArrayList<DebitBean>(); if (payment.getPerson() != null || payment.getOrganisation() != null) { SearchBean search = this.searchDAO.initiate("debit", user); search.setLimit(0); DebitBean criteria = (DebitBean) search.getSearchCriteria(); DebitBean constraints = (DebitBean) search.getSearchConstraints(); // No cancelled debits criteria.setCancelled(false); constraints.setCancelled(false); // Debits with a positive outstanding value criteria.setOutstandingValue(MINIMUM_RANGE); constraints.setOutstandingValue(MAXIMUM_RANGE); if (payment.getPerson() != null && payment.getPerson().getGUID() > 0) { criteria.setPersonId(payment.getPerson().getGUID()); } if (payment.getOrganisation() != null && payment.getOrganisation().getGUID() > 0) { criteria.setOrganisationId(payment.getOrganisation().getGUID()); } search.setSearchCriteria(criteria); search.setSearchConstraints(constraints); try { // Perform the search SearchResultsBean results = this.searchDAO.search(search); if (results.getSearchResults() != null) { for (Object objResult : results.getSearchResults()) { DebitBean debit = (DebitBean) objResult; debits.add(debit); } } } catch (WhichDoctorSearchDaoException wse) { dataLogger.error("Error performing search for outstanding " + "debits: " + wse.getMessage()); } } return debits; } /** * Creates the PaymentBean. * * @param payment the payment * @param receipt the receipt * @param checkUser the check user * @param privileges the privileges * * @return the int * * @throws WhichDoctorDaoException the which doctor dao exception */ public final int create(final PaymentBean payment, final ReceiptBean receipt, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { payment.setActive(true); return save(payment, receipt, checkUser, privileges, "create"); } /** * Modify the PaymentBean. * * @param payment the payment * @param receipt the receipt * @param checkUser the check user * @param privileges the privileges * * @return the int * * @throws WhichDoctorDaoException the which doctor dao exception */ public final int modify(final PaymentBean payment, final ReceiptBean receipt, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { payment.setActive(true); return save(payment, receipt, checkUser, privileges, "modify"); } /** * Delete the PaymentBean. * * @param payment the payment * @param receipt the receipt * @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 PaymentBean payment, final ReceiptBean receipt, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { boolean success = false; payment.setActive(false); int paymentId = save(payment, receipt, checkUser, privileges, "delete"); if (paymentId > 0) { success = true; } return success; } /** * Process a credit card payment. * * @param cardNumber the card number * @param cardHolder the card holder * @param expiryMonth the expiry month * @param expiryYear the expiry year * @param securityCode the security code * @param totalValue the total value * @param description the description * @param purchaseComment the purchase comment * @return the string[] where: * string[0] = success/error, * string[1] = transaction reference, * string[2] = error message, * string[3] = extended error log */ public final String[] processCreditCardPayment(final String cardNumber, final String cardHolder, final int expiryMonth, final int expiryYear, final int securityCode, final double totalValue, final String description, final String purchaseComment) { String success = "", reference = "", message = "", logMessage = ""; /* Perform a web service call using XFire libraries */ try { Client client = new Client(new URL(this.paymentUrl)); dataLogger.debug("Processing payment"); Object[] result = client.invoke("process", new Object[] { accessId, secret, cardNumber, cardHolder, expiryMonth, expiryYear, securityCode, totalValue, 0, "", description, purchaseComment }); dataLogger.info("Payment processing complete. Results Object[]: " + result); String resultString = (String) result[0]; StringTokenizer st = new StringTokenizer(resultString, ","); int i = 0; while (st.hasMoreTokens()) { String token = st.nextToken(); if (StringUtils.startsWith(token, "'")) { token = StringUtils.substring(token, 1, token.length()); } if (StringUtils.endsWith(token, "'")) { token = StringUtils.substring(token, 0, token.length() - 1); } switch (i) { case 0: success = token; break; case 1: reference = token; break; case 2: message = token; break; case 3: logMessage = token; break; } i++; } dataLogger.info("Payment result: " + success + ", reference: " + reference + ", message: " + message); } catch (Exception e) { dataLogger.error("Error performing payment process web service lookup: " + e.getMessage(), e); } return new String[] { success, reference, message, logMessage }; } /** * Save. * * @param payment the payment * @param receipt the receipt * @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 PaymentBean payment, final ReceiptBean receipt, final UserBean checkUser, final PrivilegesBean privileges, final String action) throws WhichDoctorDaoException { /* Check required information within PaymentBean is present */ if (payment.getReferenceGUID() == 0) { throw new WhichDoctorDaoException("Payment requires a valid " + "Reference GUID number"); } if (receipt.getId() == 0) { throw new WhichDoctorDaoException("Payment requires a valid receipt"); } if (!privileges.getPrivilege(checkUser, "payments", action)) { throw new WhichDoctorDaoException("Insufficient user credentials " + "to create new payment entry"); } /* Get the Receipt TypeId */ int typeId = 0; try { FinancialTypeBean financialObject = this.financialTypeDAO.load("Receipt", receipt.getTypeName(), receipt.getClassName()); typeId = financialObject.getFinancialTypeId(); } catch (Exception e) { dataLogger.error("Error loading financial type for receipt: " + e.getMessage()); throw new WhichDoctorDaoException("Error loading financial type " + "for receipt: " + e.getMessage()); } if (payment.getIncomeStreamId() > 0 && payment.getNetValue() > 0) { // Debit should not exist if payment is negative This is because you // cannot attribute negative payment to a debit payment.setDebit(new DebitBean()); // As it is a negative debit the net value should be negative payment.setNetValue(0 - payment.getNetValue()); // Reset the value in order to recalculate GST (if applicable) payment.setValue(0); } int personGUID = 0; int organisationGUID = 0; if (payment.getPerson() != null) { personGUID = payment.getPerson().getGUID(); } if (payment.getOrganisation() != null) { organisationGUID = payment.getOrganisation().getGUID(); } int newDebitGUID = 0; if (payment.getDebit() != null) { // Load the debit to get the GST rate try { final DebitBean debit = this.debitDAO.loadGUID(payment.getDebit().getGUID()); if (debit != null) { payment.setGSTRate(debit.getGSTRate()); newDebitGUID = debit.getGUID(); if (personGUID == 0 && debit.getPerson() != null) { personGUID = debit.getPerson().getGUID(); } if (organisationGUID == 0 && debit.getOrganisation() != null) { organisationGUID = debit.getOrganisation().getGUID(); } } } catch (WhichDoctorDaoException wde) { dataLogger.error("Error loading debit: " + wde.getMessage()); } } int existingDebitGUID = 0; // Load the existing payment (if GUID > 0) to check if the // associated debit has changed. if (payment.getGUID() > 0) { try { final PaymentBean existingPayment = this.loadGUID(payment.getGUID()); if (existingPayment.getDebit() != null) { existingDebitGUID = existingPayment.getDebit().getGUID(); } } catch (WhichDoctorDaoException wde) { dataLogger.error("Error loading existing payment: " + wde.getMessage()); } } int paymentId = 0; Timestamp sqlTimeStamp = new Timestamp(Calendar.getInstance().getTimeInMillis()); ArrayList<Object> parameters = new ArrayList<Object>(); parameters.add(payment.getReferenceGUID()); parameters.add(personGUID); parameters.add(organisationGUID); parameters.add(newDebitGUID); parameters.add(payment.getValue()); parameters.add(payment.getNetValue()); parameters.add(payment.getIncomeStreamId()); parameters.add(payment.getGSTRate()); parameters.add(payment.getActive()); parameters.add(sqlTimeStamp); parameters.add(checkUser.getDN()); parameters.add(payment.getLogMessage(action)); try { Integer[] result = this.performUpdate("payment", payment.getGUID(), parameters, "Payment", checkUser, action); /* Set the returned guid and id values */ payment.setGUID(result[0]); paymentId = result[1]; } catch (Exception e) { dataLogger.error("Error processing payment record: " + e.getMessage()); throw new WhichDoctorDaoException("Error processing payment: " + e.getMessage()); } if (paymentId > 0) { dataLogger.info(checkUser.getDN() + " created paymentId: " + paymentId); /* Get Issued Date */ Date issued = new Date(Calendar.getInstance().getTimeInMillis()); if (receipt.getIssued() != null) { issued = new Date(receipt.getIssued().getTime()); } if (action.compareTo("delete") == 0) { /* Delete the financial summary */ this.getJdbcTemplateWriter().update(this.getSQL().getValue("payment/deleteSummary"), new Object[] { receipt.getGUID(), payment.getGUID() }); } else { /* Create or modify financial summary entry */ try { this.getJdbcTemplateWriter().update(this.getSQL().getValue("payment/editSummary"), new Object[] { receipt.getGUID(), typeId, receipt.getNumber(), receipt.getDescription(), payment.getValue(), payment.getNetValue(), receipt.getCancelled(), issued, personGUID, organisationGUID, payment.getGUID(), typeId, receipt.getDescription(), payment.getValue(), payment.getNetValue(), receipt.getCancelled(), issued, personGUID, organisationGUID }); } catch (DataAccessException dae) { dataLogger.error("Failed to modify the financial summary: " + dae.getMessage()); throw new WhichDoctorDaoException( "Failed to modify the financial summary: " + dae.getMessage()); } } /* Update the related debit's calculated values */ if (existingDebitGUID > 0) { this.debitDAO.refreshDebitValues(existingDebitGUID); } if (newDebitGUID > 0 && newDebitGUID != existingDebitGUID) { this.debitDAO.refreshDebitValues(newDebitGUID); } /* Update the search index */ if (organisationGUID > 0) { this.searchIndexDAO.updateCurrentBalanceIndex(organisationGUID, "organisation"); } else { this.searchIndexDAO.updateCurrentBalanceIndex(personGUID, "person"); } } return paymentId; } /** * Load payment. * * @param rs the rs * * @return the payment bean * * @throws SQLException the SQL exception */ private PaymentBean loadPayment(final ResultSet rs) throws SQLException { PaymentBean payment = new PaymentBean(); payment.setId(rs.getInt("PaymentId")); payment.setGUID(rs.getInt("GUID")); payment.setReferenceGUID(rs.getInt("ReferenceGUID")); if (rs.getInt("PersonId") != 0) { PersonBean person = new PersonBean(); person.setGUID(rs.getInt("PersonId")); person.setPersonIdentifier(rs.getInt("PersonIdentifier")); person.setPreferredName(rs.getString("PreferredName")); person.setFirstName(rs.getString("FirstName")); person.setLastName(rs.getString("LastName")); person.setTitle(rs.getString("Title")); payment.setPerson(person); } if (rs.getInt("OrganisationId") > 0) { OrganisationBean organisation = new OrganisationBean(); organisation.setGUID(rs.getInt("OrganisationId")); organisation.setName(rs.getString("OrganisationName")); payment.setOrganisation(organisation); } if (rs.getInt("IncomeStreamId") > 0) { /* Payment is a negative payment */ payment.setNegativePayment(true); /* Create a 'positive' value */ payment.setIncomeStream(rs.getString("IncomeStream")); payment.setIncomeStreamId(rs.getInt("IncomeStreamId")); } else { payment.setNegativePayment(false); /* See if 'positive' payment has attributed invoice */ if (rs.getInt("InvoiceId") > 0) { DebitBean debit = new DebitBean(); debit.setGUID(rs.getInt("InvoiceId")); debit.setDescription(rs.getString("Description")); debit.setTypeName(rs.getString("DebitType")); debit.setNumber(rs.getString("InvoiceNo")); debit.setAbbreviation(rs.getString("Abbreviation")); try { debit.setIssued(rs.getDate("Issued")); } catch (Exception sqe) { dataLogger.debug("Error parsing Issued value: " + sqe.getMessage()); } debit.setGSTRate(rs.getDouble("InvoiceGSTRate")); debit.setCancelled(rs.getBoolean("InvoiceCancelled")); debit.setValue(rs.getDouble("InvoiceValue")); debit.setCreditValue(rs.getDouble("InvoiceCreditValue")); debit.setNetValue(rs.getDouble("InvoiceNetValue")); payment.setDebit(debit); } } payment.setValue(rs.getDouble("Value")); payment.setNetValue(rs.getDouble("NetValue")); payment.setGSTRate(rs.getDouble("GSTRate")); payment.setActive(rs.getBoolean("Active")); try { payment.setCreatedDate(rs.getTimestamp("CreatedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading CreatedDate: " + sqe.getMessage()); } payment.setCreatedBy(rs.getString("CreatedBy")); try { payment.setModifiedDate(rs.getTimestamp("ModifiedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading ModifiedDate: " + sqe.getMessage()); } payment.setModifiedBy(rs.getString("ModifiedBy")); try { payment.setExportedDate(rs.getTimestamp("ExportedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading ExportedDate: " + sqe.getMessage()); } payment.setExportedBy(rs.getString("ExportedBy")); return payment; } }