private void init() { eLogger = EventLogger.getInstance(); billingProcessDas = new BillingProcessDAS(); // now create the run info row processRunHome = new ProcessRunDAS(); }
private void init() { eLogger = EventLogger.getInstance(); configurationDas = new BillingProcessConfigurationDAS(); }
private void init() { eLogger = EventLogger.getInstance(); invoiceDas = new InvoiceDAS(); }
/** * BasicAgeingTask * * @author Brian Cowdery * @since 28/04/11 */ public class BasicAgeingTask extends PluggableTask implements IAgeingTask { private static final Logger LOG = Logger.getLogger(BasicAgeingTask.class); private final EventLogger eLogger = EventLogger.getInstance(); private static Calendar calendar = GregorianCalendar.getInstance(); static { calendar.clear(); } private Map<Integer, Integer> gracePeriodCache = new HashMap<Integer, Integer>(); protected int getGracePeriod(Integer entityId) { if (!gracePeriodCache.containsKey(entityId)) { PreferenceBL preference = new PreferenceBL(); preference.set(entityId, Constants.PREFERENCE_GRACE_PERIOD); gracePeriodCache.put(entityId, preference.getInt()); } return gracePeriodCache.get(entityId); } /** * Review all users for the given day, and age those that have outstanding invoices over the set * number of days for an ageing step. * * @param steps ageing steps * @param today today's date * @param executorId executor id */ public void reviewAllUsers( Integer entityId, Set<AgeingEntityStepDTO> steps, Date today, Integer executorId) { LOG.debug("Reviewing users for entity " + entityId + " ..."); // go over all the users already in the ageing system for (UserDTO user : new UserDAS().findAgeing(entityId)) { ageUser(steps, user, today, executorId); } // go over the active users with payable invoices try { UserDAS userDas = new UserDAS(); InvoiceDAS invoiceDas = new InvoiceDAS(); CachedRowSet users = new UserBL().findActiveWithOpenInvoices(entityId); while (users.next()) { Integer userId = users.getInt(1); UserDTO user = userDas.find(userId); int gracePeriod = getGracePeriod(entityId); LOG.debug( "Reviewing invoices for user " + user.getId() + " using a grace period of " + gracePeriod + " days."); for (InvoiceDTO invoice : invoiceDas.findProccesableByUser(user)) { if (isInvoiceOverdue(invoice, user, gracePeriod, today)) { ageUser(steps, user, today, executorId); break; } } } } catch (SQLException e) { LOG.error("Failed to fetch users with payable invoices.", e); } catch (NamingException e) { LOG.error("Exception fetching users with payable invoices.", e); } } /** * Moves a user one step forward in the ageing process (move from active -> suspended etc.). The * user will only be moved if they have spent long enough in their present status. * * @param steps ageing steps * @param user user to age * @param today today's date * @return the resulting ageing step for the user after ageing */ public AgeingEntityStepDTO ageUser( Set<AgeingEntityStepDTO> steps, UserDTO user, Date today, Integer executorId) { LOG.debug("Ageing user " + user.getId()); Integer currentStatusId = user.getStatus().getId(); UserStatusDTO nextStatus = null; AgeingEntityStepDTO ageingStep = null; if (currentStatusId.equals(UserDTOEx.STATUS_ACTIVE)) { // send welcome message (initial step after active). nextStatus = getNextAgeingStep(steps, UserDTOEx.STATUS_ACTIVE); } else { // user already in the ageing process ageingStep = new AgeingEntityStepDAS().findStep(user.getEntity().getId(), currentStatusId); if (ageingStep != null) { // determine the next ageing step if (isAgeingRequired(user, ageingStep, today)) { nextStatus = getNextAgeingStep(steps, currentStatusId); LOG.debug( "User " + user.getId() + " needs to be aged to '" + getStatusDescription(nextStatus) + "'"); } } else { // User is in a non-existent ageing status... Either the status was removed or // the data is bad. As a workaround, just move to the next status. nextStatus = getNextAgeingStep(steps, currentStatusId); LOG.warn( "User " + user.getId() + " is in an invalid ageing step. Moving to '" + getStatusDescription(nextStatus) + "'"); } } // set status if (nextStatus != null) { setUserStatus(user, nextStatus, today, null); } else { LOG.debug("Next status is null, no further ageing steps are available."); eLogger.warning( user.getEntity().getId(), user.getUserId(), user.getUserId(), EventLogger.MODULE_USER_MAINTENANCE, EventLogger.NO_FURTHER_STEP, Constants.TABLE_BASE_USER); } return ageingStep; } /** * Returns true if the given invoice is overdue. * * @param invoice invoice to check * @param user user owning the invoice * @param gracePeriod company wide grace period * @param today today's date * @return true if invoice is overdue, false if not */ public boolean isInvoiceOverdue( InvoiceDTO invoice, UserDTO user, Integer gracePeriod, Date today) { calendar.clear(); calendar.setTime(invoice.getDueDate()); calendar.add(Calendar.DATE, gracePeriod); if (calendar.getTime().before(today)) { LOG.debug( "Invoice is overdue (due date " + invoice.getDueDate() + " + " + gracePeriod + " days grace, is before today " + today + ")"); return true; } LOG.debug( "Invoice is NOT overdue (due date " + invoice.getDueDate() + " + " + gracePeriod + " days grace is after today " + today + ")"); return false; } /** * Returns true if the user requires ageing. * * @param user user being reviewed * @param currentStep current ageing step of the user * @param today today's date * @return true if user requires ageing, false if not */ public boolean isAgeingRequired(UserDTO user, AgeingEntityStepDTO currentStep, Date today) { Date lastStatusChange = user.getLastStatusChange() != null ? user.getLastStatusChange() : user.getCreateDatetime(); calendar.clear(); calendar.setTime(lastStatusChange); calendar.add(Calendar.DATE, currentStep.getDays()); if (calendar.getTime().equals(today) || calendar.getTime().before(today)) { LOG.debug( "User status has expired (last change " + lastStatusChange + " + " + currentStep.getDays() + " days is before today " + today + ")"); return true; } LOG.debug( "User does not need to be aged (last change " + lastStatusChange + " + " + currentStep.getDays() + " days is after today " + today + ")"); return false; } /** * Removes a user from the ageing process (makes them active), ONLY if they do not still have * overdue invoices. * * @param user user to make active * @param excludedInvoiceId invoice id to ignore when determining if the user CAN be made active * @param executorId executor id */ public void removeUser(UserDTO user, Integer executorId, Integer excludedInvoiceId) { Date now = new Date(); // validate that the user actually needs a status change if (user.getStatus().getId() != UserDTOEx.STATUS_ACTIVE) { LOG.debug("User " + user.getId() + " is already active, no need to remove from ageing."); return; } // validate that the user does not still have overdue invoices try { if (new InvoiceBL().isUserWithOverdueInvoices(user.getUserId(), now, excludedInvoiceId)) { LOG.debug( "User " + user.getId() + " still has overdue invoices, cannot remove from ageing."); return; } } catch (SQLException e) { LOG.error("Exception occurred checking for overdue invoices.", e); return; } // make the status change. LOG.debug("Removing user " + user.getUserId() + " from ageing (making active)."); UserStatusDTO status = new UserStatusDAS().find(UserDTOEx.STATUS_ACTIVE); setUserStatus(user, status, now, null); } /** * Sets the user status to the given "aged" status. If the user status is already set to the aged * status no changes will be made. This method also performs an HTTP callback and sends a * notification message when a status change is made. * * <p>If the user becomes suspended and can no longer log-in to the system, all of their active * orders will be automatically suspended. * * <p>If the user WAS suspended and becomes active (and can now log-in to the system), any * automatically suspended orders will be re-activated. * * @param user user * @param status status to set * @param today today's date * @param executorId executor id */ public void setUserStatus(UserDTO user, UserStatusDTO status, Date today, Integer executorId) { // only set status if the new "aged" status is different from the users current status if (status.getId() == user.getStatus().getId()) { return; } LOG.debug("Setting user " + user.getId() + " status to '" + getStatusDescription(status) + "'"); if (executorId != null) { // this came from the gui eLogger.audit( executorId, user.getId(), Constants.TABLE_BASE_USER, user.getId(), EventLogger.MODULE_USER_MAINTENANCE, EventLogger.STATUS_CHANGE, user.getStatus().getId(), null, null); } else { // this is from a process, no executor involved eLogger.auditBySystem( user.getCompany().getId(), user.getId(), Constants.TABLE_BASE_USER, user.getId(), EventLogger.MODULE_USER_MAINTENANCE, EventLogger.STATUS_CHANGE, user.getStatus().getId(), null, null); } // make the change boolean couldLogin = user.getStatus().getCanLogin() == 1; UserStatusDTO oldStatus = user.getStatus(); user.setUserStatus(status); user.setLastStatusChange(today); // status changed to deleted, remove user if (status.getId() == UserDTOEx.STATUS_DELETED) { LOG.debug("Deleting user " + user.getId()); new UserBL(user.getId()).delete(executorId); return; } // status changed from active to suspended // suspend customer orders if (couldLogin && status.getCanLogin() == 0) { LOG.debug("User " + user.getId() + " cannot log-in to the system. Suspending active orders."); OrderBL orderBL = new OrderBL(); ScrollableResults orders = new OrderDAS().findByUser_Status(user.getId(), Constants.ORDER_STATUS_ACTIVE); while (orders.next()) { OrderDTO order = (OrderDTO) orders.get()[0]; orderBL.set(order); orderBL.setStatus(executorId, Constants.ORDER_STATUS_SUSPENDED_AGEING); } orders.close(); } // status changed from suspended to active // re-active suspended customer orders if (!couldLogin && status.getCanLogin() == 1) { LOG.debug( "User " + user.getId() + " can now log-in to the system. Activating previously suspended orders."); OrderBL orderBL = new OrderBL(); ScrollableResults orders = new OrderDAS().findByUser_Status(user.getId(), Constants.ORDER_STATUS_SUSPENDED_AGEING); while (orders.next()) { OrderDTO order = (OrderDTO) orders.get()[0]; orderBL.set(order); orderBL.setStatus(executorId, Constants.ORDER_STATUS_ACTIVE); } orders.close(); } // perform callbacks and notifications performAgeingCallback(user, oldStatus, status); sendAgeingNotification(user, oldStatus, status); // emit NewUserStatusEvent NewUserStatusEvent event = new NewUserStatusEvent( user.getCompany().getId(), user.getId(), oldStatus.getId(), status.getId()); EventManager.process(event); } protected boolean performAgeingCallback( UserDTO user, UserStatusDTO oldStatus, UserStatusDTO newStatus) { String url = null; try { PreferenceBL pref = new PreferenceBL(); pref.set(user.getEntity().getId(), Constants.PREFERENCE_URL_CALLBACK); url = pref.getString(); } catch (EmptyResultDataAccessException e) { /* ignore, no callback preference configured */ } if (url != null && url.length() > 0) { try { LOG.debug("Performing ageing HTTP callback for URL: " + url); // cook the parameters to be sent NameValuePair[] data = new NameValuePair[6]; data[0] = new NameValuePair("cmd", "ageing_update"); data[1] = new NameValuePair("user_id", String.valueOf(user.getId())); data[2] = new NameValuePair("login_name", user.getUserName()); data[3] = new NameValuePair("from_status", String.valueOf(oldStatus.getId())); data[4] = new NameValuePair("to_status", String.valueOf(newStatus.getId())); data[5] = new NameValuePair("can_login", String.valueOf(newStatus.getCanLogin())); // make the call HttpClient client = new HttpClient(); client.setConnectionTimeout(30000); PostMethod post = new PostMethod(url); post.setRequestBody(data); client.executeMethod(post); } catch (Exception e) { LOG.error("Exception occurred posting ageing HTTP callback for URL: " + url, e); return false; } } return true; } protected boolean sendAgeingNotification( UserDTO user, UserStatusDTO oldStatus, UserStatusDTO newStatus) { try { MessageDTO message = new NotificationBL() .getAgeingMessage( user.getEntity().getId(), user.getLanguage().getId(), newStatus.getId(), user.getId()); INotificationSessionBean notification = (INotificationSessionBean) Context.getBean(Context.Name.NOTIFICATION_SESSION); notification.notify(user, message); } catch (NotificationNotFoundException e) { LOG.warn( "Failed to send ageing notification. Entity " + user.getEntity().getId() + " does not have an ageing message configured for status '" + getStatusDescription(newStatus) + "'."); return false; } return true; } /** * Get the status for the next step in the ageing process, based on the users current status. * * @param steps configured ageing steps * @param currentStatusId the current user status */ public UserStatusDTO getNextAgeingStep(Set<AgeingEntityStepDTO> steps, Integer currentStatusId) { for (AgeingEntityStepDTO step : steps) { Integer stepStatusId = step.getUserStatus().getId(); if (stepStatusId.compareTo(currentStatusId) > 0) { return step.getUserStatus(); } } return null; } /** * Null safe convenience method to return the status description. * * @param status user status * @return description */ private String getStatusDescription(UserStatusDTO status) { if (status != null) { return status.getDescription(); } return null; } }