/*
jBilling - The Enterprise Open Source Billing System
Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde
This file is part of jbilling.
jbilling is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jbilling 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with jbilling. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sapienter.jbilling.server.process;
import java.math.BigDecimal;
import java.util.*;
import java.sql.SQLException;
import java.io.IOException;
import org.apache.log4j.Logger;
import com.sapienter.jbilling.common.SessionInternalError;
import com.sapienter.jbilling.common.Constants;
import com.sapienter.jbilling.server.item.CurrencyBL;
import com.sapienter.jbilling.server.payment.db.PaymentMethodDAS;
import com.sapienter.jbilling.server.process.db.*;
import com.sapienter.jbilling.server.user.db.UserDTO;
import com.sapienter.jbilling.server.list.ResultList;
import com.sapienter.jbilling.server.order.OrderSQL;
import com.sapienter.jbilling.server.notification.NotificationBL;
import com.sapienter.jbilling.server.notification.NotificationNotFoundException;
import com.sapienter.jbilling.server.notification.MessageDTO;
import com.sapienter.jbilling.server.notification.INotificationSessionBean;
import com.sapienter.jbilling.server.util.Context;
import javax.sql.rowset.CachedRowSet;
import javax.mail.MessagingException;
public class BillingProcessRunBL extends ResultList implements ProcessSQL {
private ProcessRunDAS processRunDas = null;
private ProcessRunUserDAS processRunUserDas = null;
private ProcessRunTotalDAS processRunTotalDas = null;
private ProcessRunTotalPmDAS billingProcessRunTotalPmDas = null;
private ProcessRunDTO billingProcessRun = null;
private static final Logger LOG = Logger.getLogger(BillingProcessRunBL.class);
public class DateComparator implements Comparator {
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object o1, Object o2) {
ProcessRunDTO a = (ProcessRunDTO) o1;
ProcessRunDTO b = (ProcessRunDTO) o2;
if (a.getStarted().after(b.getStarted())) {
return 1;
} else if (a.getStarted().before(b.getStarted())) {
return -1;
}
return 0;
}
}
public BillingProcessRunBL(Integer billingProcessRunId) {
init();
set(billingProcessRunId);
}
public BillingProcessRunBL() {
init();
}
public BillingProcessRunBL(ProcessRunDTO run) {
init();
billingProcessRun = run;
}
private void init() {
processRunDas = new ProcessRunDAS();
processRunTotalDas = new ProcessRunTotalDAS();
billingProcessRunTotalPmDas = new ProcessRunTotalPmDAS();
processRunUserDas = new ProcessRunUserDAS();
}
public ProcessRunDTO getEntity() {
return billingProcessRun;
}
public void set(Integer id) {
billingProcessRun = processRunDas.find(id);
}
/**
* Finds the run based on the process id. Assumes that there is
* only one run associated with the process.
* This method is called asynch by the MDBs.
* @param id
*/
public void setProcess(Integer id) {
BillingProcessBL bl = new BillingProcessBL(id);
if (bl.getEntity().getProcessRuns().size() != 1) {
throw new SessionInternalError("Process " + id +
" should have 1 run. It has " + bl.getEntity().getProcessRuns().size());
} else {
billingProcessRun = bl.getEntity().getProcessRuns().iterator().next();
}
}
public Integer create(BillingProcessDTO process, Date runDate) {
if (runDate == null) {
throw new SessionInternalError("run date can't be null");
}
billingProcessRun = processRunDas.create(process, runDate, 0,
new ProcessRunStatusDAS().find(Constants.PROCESS_RUN_STATUS_RINNING));
return billingProcessRun.getId();
}
public CachedRowSet findSucceededUsersList() throws SQLException {
prepareStatement(ProcessSQL.findSucceededUsers);
cachedResults.setInt(1, billingProcessRun.getId());
execute();
conn.close();
return cachedResults;
}
public CachedRowSet findFailedUsersList() throws SQLException {
prepareStatement(ProcessSQL.findFailedUsers);
cachedResults.setInt(1, billingProcessRun.getId());
execute();
conn.close();
return cachedResults;
}
public void notifyProcessRunFailure(Integer entityId, int failedUsersCount)
throws SessionInternalError {
LOG.debug("Sending process run failure notification.");
try {
new BillingProcessDAS().reset();
ProcessRunDTO processRunDTO = processRunDas.find(billingProcessRun.getId());
String[] params = new String[] {
entityId.toString(),
processRunDTO.getStarted().toString(),
processRunDTO.getFinished().toString(),
Integer.toString(failedUsersCount)
};
NotificationBL.sendSapienterEmail(entityId, "process.run_failed", null, params);
} catch (MessagingException e) {
LOG.warn("Could not send email.", e);
} catch (IOException e) {
LOG.warn("Could not send email.", e);
}
}
/**
* Adds the payment total to the run totals
* @param currencyId
* @param methodId
* @param total
* @param ok
*/
public void updateNewPayment(Integer currencyId, Integer methodId,
BigDecimal total, boolean ok) {
// update the payments total
ProcessRunTotalDTO totalRow = findOrCreateTotal(currencyId);
BigDecimal tmpValue = null;
if (ok) {
totalRow.setTotalPaid(totalRow.getTotalPaid().add(total));
// the payment is good, update the method total as well
ProcessRunTotalPmDTO pm = findOrCreateTotalPM(methodId, totalRow);
pm.setTotal(pm.getTotal().add(total));
// link it to the payment method table
PaymentMethodDAS paymentMethodHome = new PaymentMethodDAS();
pm.setPaymentMethod(paymentMethodHome.find(methodId));
} else {
totalRow.setTotalNotPaid(totalRow.getTotalNotPaid().add(total));
}
}
/**
* Adds an invoice to the run totals
*/
public void updateTotals(Integer billingProcessId) {
for (Iterator it = new BillingProcessDAS().getCountAndSum(billingProcessId); it.hasNext();) {
Object[] row = (Object[]) it.next();
// add the total to the total invoiced
ProcessRunTotalDTO totalRow = findOrCreateTotal((Integer) row[2]);
billingProcessRun.setInvoicesGenerated(billingProcessRun.getInvoicesGenerated() + ((Long) row[0]).intValue());
totalRow.setTotalInvoiced(((BigDecimal) row[1]));
LOG.debug("updating invoice run total version " + totalRow.getVersionNum());
}
}
private ProcessRunTotalDTO findOrCreateTotal(Integer currencyId) {
ProcessRunTotalDTO ret = processRunTotalDas.getByCurrency(billingProcessRun, currencyId);
if (ret == null) { // not present for this currency
ret = processRunTotalDas.create(billingProcessRun, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, currencyId);
}
return ret;
}
private ProcessRunTotalPmDTO findOrCreateTotalPM(Integer methodId, ProcessRunTotalDTO total) {
ProcessRunTotalPmDTO ret = billingProcessRunTotalPmDas.getByMethod(methodId, total);
if (ret == null) { // not present for this currency
ret = billingProcessRunTotalPmDas.create(BigDecimal.ZERO);
// link it to the total row
total.getTotalsPaymentMethod().add(ret);
ret.setProcessRunTotal(total);
}
return ret;
}
// called when the run is over, to update the dates only
public void updateFinished(Integer processRunStatusId) {
// get the very latest version
billingProcessRun = processRunDas.findForUpdate(billingProcessRun.getId());
billingProcessRun.setFinished(Calendar.getInstance().getTime());
billingProcessRun.setStatus(new ProcessRunStatusDAS().find(processRunStatusId));
LOG.debug("updating run " + billingProcessRun.getId() +" version " + billingProcessRun.getVersionNum());
billingProcessRun = processRunDas.save(billingProcessRun);
}
public ProcessRunUserDTO addProcessRunUser(Integer userId, Integer status) {
ProcessRunUserDTO processRunUser = processRunUserDas.getUser(billingProcessRun.getId(), userId);
if(processRunUser == null) {
return processRunUserDas.create(billingProcessRun.getId(), userId,
status, Calendar.getInstance().getTime());
}
processRunUser.setStatus(status);
return processRunUser;
}
public void updatePaymentsFinished() {
// get the very latest version
billingProcessRun = processRunDas.findForUpdate(billingProcessRun.getId());
billingProcessRun.setPaymentFinished(Calendar.getInstance().getTime());
LOG.debug("updating payments run " + billingProcessRun.getId() +" version " + billingProcessRun.getVersionNum());
billingProcessRun = processRunDas.save(billingProcessRun);
}
public BillingProcessRunDTOEx getDTO(Integer language) {
BillingProcessRunDTOEx dto = new BillingProcessRunDTOEx();
dto.setId(billingProcessRun.getId());
dto.setFinished(billingProcessRun.getFinished());
dto.setInvoicesGenerated(billingProcessRun.getInvoicesGenerated());
dto.setStarted(billingProcessRun.getStarted());
dto.setRunDate(billingProcessRun.getRunDate());
dto.setPaymentFinished(billingProcessRun.getPaymentFinished());
ProcessRunStatusDTO statusRow = new ProcessRunStatusDAS().find(billingProcessRun.getStatus().getId());
dto.setStatusStr(statusRow.getDescription(language));
ProcessRunUserDAS processRunUserDAS = new ProcessRunUserDAS();
dto.setUsersSucceeded(processRunUserDAS.findSuccessfullUsersCount(billingProcessRun.getId()));
dto.setUsersFailed(processRunUserDAS.findFailedUsersCount(billingProcessRun.getId()));
// now the totals
if (!billingProcessRun.getProcessRunTotals().isEmpty()) {
for (Iterator tIt = billingProcessRun.getProcessRunTotals().iterator();
tIt.hasNext();) {
ProcessRunTotalDTO totalRow =
(ProcessRunTotalDTO) tIt.next();
BillingProcessRunTotalDTOEx totalDto = getTotalDTO(totalRow,
language);
dto.getTotals().add(totalDto);
}
}
return dto;
}
public BillingProcessRunTotalDTOEx getTotalDTO(
ProcessRunTotalDTO row, Integer languageId) {
BillingProcessRunTotalDTOEx retValue =
new BillingProcessRunTotalDTOEx();
retValue.setCurrency(row.getCurrency());
retValue.setId(row.getId());
retValue.setTotalInvoiced(row.getTotalInvoiced());
retValue.setTotalNotPaid(row.getTotalNotPaid());
retValue.setTotalPaid(row.getTotalPaid());
// now go over the totals by payment method
Hashtable totals = new Hashtable();
for (Iterator it = row.getTotalsPaymentMethod().iterator();
it.hasNext();) {
ProcessRunTotalPmDTO pmTotal =
(ProcessRunTotalPmDTO) it.next();
totals.put(pmTotal.getPaymentMethod().getDescription(languageId),
pmTotal.getTotal());
}
retValue.setPmTotals(totals);
// add the currency name, it's handy on the client side
CurrencyBL currency = new CurrencyBL(retValue.getCurrency().getId());
retValue.setCurrencyName(currency.getEntity().getDescription(
languageId));
return retValue;
}
public void updatePaymentsStatistic(Integer runId) {
BillingProcessRunBL run = new BillingProcessRunBL(runId);
for (Iterator it = new BillingProcessDAS().getSuccessfulProcessCurrencyMethodAndSum(run.getEntity().getBillingProcess().getId()); it.hasNext();) {
Object[] row = (Object[]) it.next();
run.updateNewPayment((Integer) row[0], (Integer) row[1], (BigDecimal) row[2], true);
}
for (Iterator it = new BillingProcessDAS().getFailedProcessCurrencyAndSum(run.getEntity().getBillingProcess().getId()); it.hasNext();) {
Object[] row = (Object[]) it.next();
run.updateNewPayment((Integer) row[0], null, (BigDecimal) row[1], false);
}
}
public List<Integer> findSuccessfullUsers() {
return new ProcessRunUserDAS().findSuccessfullUserIds(billingProcessRun.getId());
}
}