public BillingProcessWS(BillingProcessDTO dto) { this.id = dto.getId(); this.entityId = dto.getEntity() != null ? dto.getEntity().getId() : null; this.periodUnitId = dto.getPeriodUnit() != null ? dto.getPeriodUnit().getId() : null; this.periodValue = dto.getPeriodValue(); this.billingDate = dto.getBillingDate(); this.isReview = dto.getIsReview(); this.retriesToDo = dto.getRetriesToDo(); // invoice ID's if (!dto.getInvoices().isEmpty()) { invoiceIds = new ArrayList<Integer>(dto.getInvoices().size()); for (InvoiceDTO invoice : dto.getInvoices()) invoiceIds.add(invoice.getId()); } // order processes if (!dto.getOrderProcesses().isEmpty()) { orderProcesses = new ArrayList<OrderProcessWS>(dto.getOrderProcesses().size()); for (OrderProcessDTO process : dto.getOrderProcesses()) orderProcesses.add(new OrderProcessWS(process)); } if (!dto.getProcessRuns().isEmpty()) { // billing process runs processRuns = new ArrayList<ProcessRunWS>(dto.getProcessRuns().size()); for (ProcessRunDTO run : dto.getProcessRuns()) processRuns.add(new ProcessRunWS(run)); } }
public static Date getEndOfProcessPeriod(BillingProcessDTO process) throws SessionInternalError { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(process.getBillingDate()); cal.add(MapPeriodToCalendar.map(process.getPeriodUnit().getId()), process.getPeriodValue()); return cal.getTime(); }
public Integer findOrCreate(BillingProcessDTO dto) { billingProcess = billingProcessDas.isPresent( dto.getEntity().getId(), dto.getIsReview(), dto.getBillingDate()); if (billingProcess == null) { create(dto); } return billingProcess.getId(); }
public Integer create(BillingProcessDTO dto) { // create the record billingProcess = billingProcessDas.create( dto.getEntity(), dto.getBillingDate(), dto.getPeriodUnit().getId(), dto.getPeriodValue(), dto.getRetriesToDo()); billingProcess.setIsReview(dto.getIsReview()); processRun = processRunHome.create( billingProcess, dto.getBillingDate(), 0, new ProcessRunStatusDAS().find(Constants.PROCESS_RUN_STATUS_RINNING)); if (dto.getIsReview() == 1) { ConfigurationBL config = new ConfigurationBL(dto.getEntity().getId()); config.getEntity().setReviewStatus(Constants.REVIEW_STATUS_GENERATED); } return billingProcess.getId(); }
public BillingProcessDTOEx getDtoEx(Integer language) { BillingProcessDTOEx retValue = new BillingProcessDTOEx(); retValue.setBillingDate(billingProcess.getBillingDate()); retValue.setEntity(billingProcess.getEntity()); retValue.setId(billingProcess.getId()); retValue.setPeriodUnit(billingProcess.getPeriodUnit()); retValue.setPeriodValue(billingProcess.getPeriodValue()); retValue.setIsReview(billingProcess.getIsReview()); // now add the runs and grand total BillingProcessRunDTOEx grandTotal = new BillingProcessRunDTOEx(); int totalInvoices = 0; int runsCounter = 0; List<BillingProcessRunDTOEx> runs = new ArrayList<BillingProcessRunDTOEx>(); // go throuhg every run for (Iterator it = billingProcess.getProcessRuns().iterator(); it.hasNext(); ) { ProcessRunDTO run = (ProcessRunDTO) it.next(); BillingProcessRunBL runBL = new BillingProcessRunBL(run); BillingProcessRunDTOEx runDto = runBL.getDTO(language); runs.add(runDto); runsCounter++; // add statistic for InProgress run proccess in DTO if (run.getPaymentFinished() == null) { addRuntimeStatistic(run.getBillingProcess().getId(), language, runDto); } LOG.debug( "Run:" + run.getId() + " has " + run.getProcessRunTotals().size() + " total records"); // go over the totals, since there's one per currency for (Iterator it2 = runDto.getTotals().iterator(); it2.hasNext(); ) { // the total to process BillingProcessRunTotalDTOEx totalDto = (BillingProcessRunTotalDTOEx) it2.next(); BillingProcessRunTotalDTOEx sum = getTotal(totalDto.getCurrency(), grandTotal.getTotals()); BigDecimal totalTmp = totalDto.getTotalInvoiced().add(sum.getTotalInvoiced()); sum.setTotalInvoiced(totalTmp); totalTmp = totalDto.getTotalPaid().add(sum.getTotalPaid()); sum.setTotalPaid(totalTmp); // can't add up the not paid, because many runs will try to // get the same invoices paid, so the not paid field gets // duplicated amounts. totalTmp = sum.getTotalInvoiced().subtract(sum.getTotalPaid()); sum.setTotalNotPaid(totalTmp); // make sure this total has the currency name initialized if (sum.getCurrencyName() == null) { CurrencyBL currency = new CurrencyBL(sum.getCurrency().getId()); sum.setCurrencyName(currency.getEntity().getDescription(language)); } // add the payment method totals for (Enumeration en = totalDto.getPmTotals().keys(); en.hasMoreElements(); ) { String method = (String) en.nextElement(); BigDecimal methodTotal = new BigDecimal(totalDto.getPmTotals().get(method).toString()); if (sum.getPmTotals().containsKey(method)) { if (sum.getPmTotals().get(method) != null) { methodTotal = methodTotal.add( new BigDecimal(((Float) sum.getPmTotals().get(method)).toString())); } } sum.getPmTotals().put(method, new Float(methodTotal.floatValue())); } LOG.debug( "Added total to run dto. PMs in total:" + sum.getPmTotals().size() + " now grandTotal totals:" + grandTotal.getTotals().size()); } totalInvoices += runDto.getInvoicesGenerated(); } grandTotal.setInvoicesGenerated(new Integer(totalInvoices)); retValue.setRetries(new Integer(runsCounter)); retValue.setRuns(runs); retValue.setGrandTotal(grandTotal); retValue.setBillingDateEnd(getEndOfProcessPeriod(billingProcess)); retValue.setOrdersProcessed(new Integer(billingProcess.getOrderProcesses().size())); return retValue; }
private boolean processOrdersForUser( UserDTO user, Integer entityId, BillingProcessDTO process, boolean isReview, boolean onlyRecurring, boolean useProcessDateForInvoice, int maximumPeriods, Hashtable<TimePeriod, NewInvoiceDTO> newInvoices) { boolean includedOrders = false; Integer userId = user.getUserId(); LOG.debug("Processing orders for user " + userId); // initialize the subaccounts iterator if this user is a parent Iterator subAccountsIt = null; if (user.getCustomer().getIsParent() != null && user.getCustomer().getIsParent().intValue() == 1) { UserBL parent = new UserBL(userId); subAccountsIt = parent.getEntity().getCustomer().getChildren().iterator(); } // get the orders that might be processable for this user OrderDAS orderDas = new OrderDAS(); Collection<OrderDTO> orders = orderDas.findByUser_Status(userId, Constants.ORDER_STATUS_ACTIVE); // go through each of them, and update the DTO if it applies for (OrderDTO order : orders) { LOG.debug("Processing order :" + order.getId()); // apply any order processing filter pluggable task try { PluggableTaskManager taskManager = new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_ORDER_FILTER); OrderFilterTask task = (OrderFilterTask) taskManager.getNextClass(); boolean isProcessable = true; while (task != null) { isProcessable = task.isApplicable(order, process); if (!isProcessable) { break; // no need to keep doing more tests } task = (OrderFilterTask) taskManager.getNextClass(); } // include this order only if it complies with all the // rules if (isProcessable) { LOG.debug("Order processable"); if (onlyRecurring) { if (order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE) { includedOrders = true; LOG.debug("It is not one-timer. " + "Generating invoice"); } } else { includedOrders = true; } /* * now find if there is already an invoice being * generated for the given due date period */ // find the due date that applies to this order OrderBL orderBl = new OrderBL(); orderBl.set(order); TimePeriod dueDatePeriod = orderBl.getDueDate(); // look it up in the hashtable NewInvoiceDTO thisInvoice = (NewInvoiceDTO) newInvoices.get(dueDatePeriod); if (thisInvoice == null) { LOG.debug( "Adding new invoice for period " + dueDatePeriod + " process date:" + process.getBillingDate()); // we need a new one with this period // define the invoice date thisInvoice = new NewInvoiceDTO(); if (useProcessDateForInvoice) { thisInvoice.setDate(process.getBillingDate()); } else { thisInvoice.setDate( orderBl.getInvoicingDate(), order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE); } thisInvoice.setIsReview(isReview ? new Integer(1) : new Integer(0)); thisInvoice.setCarriedBalance(BigDecimal.ZERO); thisInvoice.setDueDatePeriod(dueDatePeriod); } else { LOG.debug("invoice found for period " + dueDatePeriod); if (!useProcessDateForInvoice) { thisInvoice.setDate( orderBl.getInvoicingDate(), order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE); } } /* * The order periods plug-in might not add any period. This should not happen * but if it does, the invoice should not be included */ if (addOrderToInvoice( entityId, order, thisInvoice, process.getBillingDate(), maximumPeriods)) { // add or replace newInvoices.put(dueDatePeriod, thisInvoice); } LOG.debug("After putting period there are " + newInvoices.size() + " periods."); // here it would be easy to update this order // to_process and // next_billable_time. I can't do that because these // fields // will be read by the following tasks, and they // will asume // they are not modified. } } catch (PluggableTaskException e) { LOG.fatal("Problems handling order filter task.", e); throw new SessionInternalError("Problems handling order filter task."); } catch (TaskException e) { LOG.fatal("Problems excecuting order filter task.", e); throw new SessionInternalError("Problems executing order filter task."); } } // for - all the orders for this user // see if there is any subaccounts to include in this invoice while (subAccountsIt != null) { // until there are no more subaccounts (subAccountsIt != null) { CustomerDTO customer = null; while (subAccountsIt.hasNext()) { customer = (CustomerDTO) subAccountsIt.next(); if (customer.getInvoiceChild() == null || customer.getInvoiceChild().intValue() == 0) { break; } else { LOG.debug("Subaccount not included in parent's invoice " + customer.getId()); customer = null; } } if (customer != null) { userId = customer.getBaseUser().getUserId(); // if the child does not have any orders to invoice, this should // not affect the current value of includedOrders if (processOrdersForUser( customer.getBaseUser(), entityId, process, isReview, onlyRecurring, useProcessDateForInvoice, maximumPeriods, newInvoices)) { // if ANY child has orders to invoice, it is enough for includedOrders to be true includedOrders = true; } LOG.debug("Now processing subaccount " + userId); } else { subAccountsIt = null; LOG.debug("No more subaccounts to process"); } } return includedOrders; }
public InvoiceDTO[] generateInvoice( BillingProcessDTO process, UserDTO user, boolean isReview, boolean onlyRecurring) throws SessionInternalError { Integer userId = user.getUserId(); Integer entityId = user.getEntity().getId(); // get the configuration boolean useProcessDateForInvoice = true; int maximumPeriods = 1; boolean paymentApplication = false; try { ConfigurationBL config = new ConfigurationBL(process.getEntity().getId()); useProcessDateForInvoice = config.getEntity().getInvoiceDateProcess() == 1; maximumPeriods = config.getEntity().getMaximumPeriods(); paymentApplication = config.getEntity().getAutoPaymentApplication() == 1; } catch (Exception e) { // swallow exception } // this contains the generated invoices, one per due date // found in the applicable purchase orders. // The key is the object TimePeriod Hashtable<TimePeriod, NewInvoiceDTO> newInvoices = new Hashtable<TimePeriod, NewInvoiceDTO>(); InvoiceDTO[] retValue = null; LOG.debug( "In generateInvoice for user " + userId + " process date:" + process.getBillingDate()); /* * Go through the orders first * This method will recursively call itself to find sub-accounts in any * level */ boolean includedOrders = processOrdersForUser( user, entityId, process, isReview, onlyRecurring, useProcessDateForInvoice, maximumPeriods, newInvoices); if (!includedOrders || newInvoices.size() == 0) { // check if invoices without orders are allowed PreferenceBL preferenceBL = new PreferenceBL(); try { preferenceBL.set(entityId, Constants.PREFERENCE_ALLOW_INVOICES_WITHOUT_ORDERS); } catch (EmptyResultDataAccessException fe) { // use default } if (preferenceBL.getInt() == 0) { LOG.debug("No applicable orders. No invoice generated (skipping invoices)."); return null; } } if (!isReview) { for (Map.Entry<TimePeriod, NewInvoiceDTO> newInvoiceEntry : newInvoices.entrySet()) { // process events before orders added to invoice processOrderToInvoiceEvents(newInvoiceEntry.getValue(), entityId); } } /* * Include those invoices that should've been paid * (or have negative balance, as credits) */ LOG.debug("Considering overdue invoices"); // find the invoice home interface InvoiceDAS invoiceDas = new InvoiceDAS(); // any of the new invoices being created could hold the overdue invoices NewInvoiceDTO holder = newInvoices.isEmpty() ? null : (NewInvoiceDTO) newInvoices.elements().nextElement(); Collection dueInvoices = invoiceDas.findWithBalanceByUser(user); LOG.debug("Processing invoices for user " + user.getUserId()); // go through each of them, and update the DTO if it applies for (Iterator it = dueInvoices.iterator(); it.hasNext(); ) { InvoiceDTO invoice = (InvoiceDTO) it.next(); if (invoice.getToProcess() == InvoiceDTO.DO_NOT_PROCESS) { LOG.debug("skipping invoice " + invoice.getId() + " because DO_NOT_PROCESS is set"); continue; } LOG.debug("Processing invoice " + invoice.getId()); // apply any invoice processing filter pluggable task try { PluggableTaskManager taskManager = new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_INVOICE_FILTER); InvoiceFilterTask task = (InvoiceFilterTask) taskManager.getNextClass(); boolean isProcessable = true; while (task != null) { isProcessable = task.isApplicable(invoice, process); if (!isProcessable) { break; // no need to keep doing more tests } task = (InvoiceFilterTask) taskManager.getNextClass(); } // include this invoice only if it complies with all the rules if (isProcessable) { // check for an invoice if (holder == null) { // Since there are no new invoices (therefore no orders), // don't let invoices with positive balances generate // an invoice. if (BigDecimal.ZERO.compareTo(invoice.getBalance()) < 0) { continue; } // no invoice/s yet (no applicable orders), so create one holder = new NewInvoiceDTO(); holder.setDate(process.getBillingDate()); holder.setIsReview(isReview ? new Integer(1) : new Integer(0)); holder.setCarriedBalance(BigDecimal.ZERO); holder.setInvoiceStatus(new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID)); // need to set a due date, so use the order default OrderBL orderBl = new OrderBL(); OrderDTO order = new OrderDTO(); order.setBaseUserByUserId(user); orderBl.set(order); TimePeriod dueDatePeriod = orderBl.getDueDate(); holder.setDueDatePeriod(dueDatePeriod); newInvoices.put(dueDatePeriod, holder); } InvoiceBL ibl = new InvoiceBL(invoice); holder.addInvoice(ibl.getDTO()); // for those invoices wiht only overdue invoices, the // currency has to be initialized if (holder.getCurrency() == null) { holder.setCurrency(invoice.getCurrency()); } else if (holder.getCurrency().getId() != invoice.getCurrency().getId()) { throw new SessionInternalError( "An invoice with different " + "currency is not supported. " + "Currency = " + holder.getCurrency().getId() + "invoice = " + invoice.getId()); } // update the amount of the new invoice that is due to // previously unpaid overdue invoices // carry the remaining balance, plus the previously carried balance to the new invoice BigDecimal balance = (invoice.getBalance() == null) ? BigDecimal.ZERO : invoice.getBalance(); BigDecimal carried = balance.add(holder.getCarriedBalance()); holder.setCarriedBalance(carried); } LOG.debug("invoice " + invoice.getId() + " result " + isProcessable); } catch (PluggableTaskException e) { LOG.fatal("Problems handling task invoice filter.", e); throw new SessionInternalError("Problems handling task invoice filter."); } catch (TaskException e) { LOG.fatal("Problems excecuting task invoice filter.", e); throw new SessionInternalError("Problems executing task invoice filter."); } } if (newInvoices.size() == 0) { // no orders or invoices for this invoice LOG.debug("No applicable orders or invoices. No invoice generated (skipping invoices)."); return null; } try { retValue = new InvoiceDTO[newInvoices.size()]; int index = 0; for (NewInvoiceDTO invoice : newInvoices.values()) { LOG.debug( "Processing invoice " + invoice.getId() + " " + invoice.getBalance() + " " + invoice.getTotal()); /* * Apply invoice composition tasks to the new invoices object */ composeInvoice(entityId, user.getUserId(), invoice); LOG.debug( " after composeInvoice " + invoice.getId() + " " + invoice.getBalance() + " " + invoice.getTotal()); if (!isReview) { // process events after orders added to invoice processOrderAddedOnInvoiceEvents(invoice, entityId); LOG.debug("processing old invoices"); for (InvoiceDTO oldInvoice : invoice.getInvoices()) { // since this invoice is being delegated, mark it as being carried forward // so that it is not re-processed later. do not clear the old balance! LOG.debug( " setting status unpaid and carried on " + oldInvoice.getId() + " " + oldInvoice.getBalance() + " " + oldInvoice.getTotal()); oldInvoice.setInvoiceStatus( new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID_AND_CARRIED)); } } /* * apply this object to the DB, generating the actual rows */ // only if the invoice generated actually has some lines in it if (invoice.areLinesGeneratedEmpty()) { LOG.warn( "User " + user.getUserId() + " had orders or invoices but" + " the invoice composition task didn't generate any lines."); continue; } // If this is a web services API call, the billing // process id is 0. Don't link to the billing process // object for API calls. retValue[index] = generateDBInvoice( user.getUserId(), invoice, (process.getId() != 0 ? process : null), Constants.ORDER_PROCESS_ORIGIN_PROCESS); // try to get this new invioce paid by previously unlinked // payments if (paymentApplication && !isReview) { PaymentBL pBL = new PaymentBL(); pBL.automaticPaymentApplication(retValue[index]); } index++; } } catch (PluggableTaskException e1) { LOG.error("Error handling pluggable tasks when composing an invoice"); throw new SessionInternalError(e1); } catch (TaskException e1) { LOG.error("Task exception when composing an invoice"); throw new SessionInternalError(e1); } catch (Exception e1) { LOG.error("Error, probably linking payments", e1); throw new SessionInternalError(e1); } InvoicesGeneratedEvent generatedEvent = new InvoicesGeneratedEvent(entityId, process.getId()); generatedEvent.addInvoices(retValue); EventManager.process(generatedEvent); return retValue; }