static void updateStatusFinished( OrderDTO order, Date startOfBillingPeriod, Date endOfBillingPeriod) throws SessionInternalError { // all one timers are done if (order.getOrderPeriod().getId() == Constants.ORDER_PERIOD_ONCE) { OrderBL orderBL = new OrderBL(order); orderBL.setStatus(null, Constants.ORDER_STATUS_FINISHED); } else { // recursive orders get more complicated // except those that are immortal :) if (order.getActiveUntil() == null) { return; } // see until when the incoming process will cover // compare if this is after the order exipres Logger log = Logger.getLogger(BillingProcessBL.class); log.debug( "order " + order.getId() + "end of bp " + endOfBillingPeriod + " active until " + order.getActiveUntil()); if (endOfBillingPeriod.compareTo(Util.truncateDate(order.getActiveUntil())) >= 0) { OrderBL orderBL = new OrderBL(order); orderBL.setStatus(null, Constants.ORDER_STATUS_FINISHED); } } }
public static void updateNextBillableDay(OrderDTO order, Date end) throws SessionInternalError { // if this order won't be process ever again, the // it shouldn't have a next billable day if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) { order.setNextBillableDay(null); } else { order.setNextBillableDay(end); } }
public void removeOrder(Integer itemId) { List<OrderLineDTO> list = new OrderLineDAS().findByUserItem(order.getBaseUserByUserId().getId(), itemId); for (OrderLineDTO line : list) { LOG.debug("Deleting order %s", line.getPurchaseOrder().getId()); // who is the executor? we'll use the owner.. she is cancelling new OrderBL(line.getPurchaseOrder()).delete(order.getBaseUserByUserId().getId()); } }
/** * Adds a quantity of items to the given order for the given item id. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add * @param persist save changes immediately if true */ public static void addItem(OrderDTO order, Integer itemId, BigDecimal quantity, boolean persist) { UserBL user = new UserBL(order.getUserId()); addItem( itemId, quantity, user.getLanguage(), order.getUserId(), order.getCurrencyId(), order, null, persist); }
private static void addItem( Integer itemID, BigDecimal quantity, Integer language, Integer userId, Integer currencyId, OrderDTO newOrder, OrderLineDTO myLine, boolean persist) { if (persist) throw new IllegalArgumentException("persist is oboleted"); // TODO remove the argument // check if the item is already in the order OrderLineDTO line = (OrderLineDTO) newOrder.getLine(itemID); if (myLine == null) { myLine = new OrderLineDTO(); ItemDTO item = new ItemDTO(); item.setId(itemID); myLine.setItem(item); myLine.setQuantity(quantity); } populateWithSimplePrice( newOrder, myLine, language, userId, currencyId, itemID, CommonConstants.BIGDECIMAL_SCALE); myLine.setDefaults(); // create a new line if an existing line does not exist // if the line has a different description than the existing, treat it as a new line if (line == null || (myLine.getDescription() != null && !myLine.getDescription().equals(line.getDescription()))) { OrderLineDTO newLine = new OrderLineDTO(myLine); newOrder.getLines().add(newLine); newLine.setPurchaseOrder(newOrder); // save the order (with the new line). Otherwise // the diff line will have a '0' for the order id and the // saving of the mediation record lines gets really complicated if (persist) { new OrderDAS().save(newOrder); } } else { // the item is there, I just have to update the quantity line.setQuantity(line.getQuantity().add(quantity)); // and also the total amount for this order line line.setAmount( line.getAmount() .setScale(CommonConstants.BIGDECIMAL_SCALE, CommonConstants.BIGDECIMAL_ROUND)); line.setAmount(line.getAmount().add(myLine.getAmount())); } }
private void createOrderProcess( NewInvoiceDTO newInvoice, InvoiceDTO invoice, BillingProcessDTO process, Integer origin) throws SessionInternalError { LOG.debug("Generating order process records..."); // update the orders involved, now that their old data is not needed // anymore for (int f = 0; f < newInvoice.getOrders().size(); f++) { OrderDTO order = (OrderDTO) newInvoice.getOrders().get(f); LOG.debug(" ... order " + order.getId()); // this will help later List<PeriodOfTime> periodsList = newInvoice.getPeriods().get(f); Date startOfBillingPeriod = (Date) periodsList.get(0).getStart(); Date endOfBillingPeriod = periodsList.get(periodsList.size() - 1).getEnd(); Integer periods = (Integer) newInvoice.getPeriods().get(f).size(); // We don't update orders if this is just a review if (newInvoice.getIsReview().intValue() == 0) { // update the to_process if applicable updateStatusFinished(order, startOfBillingPeriod, endOfBillingPeriod); // update this order process field updateNextBillableDay(order, endOfBillingPeriod); } // create the period and update the order-invoice relationship try { OrderProcessDAS das = new OrderProcessDAS(); OrderProcessDTO orderProcess = new OrderProcessDTO(); orderProcess.setPeriodStart(startOfBillingPeriod); orderProcess.setPeriodEnd(endOfBillingPeriod); orderProcess.setIsReview(newInvoice.getIsReview()); orderProcess.setPurchaseOrder(order); InvoiceDAS invDas = new InvoiceDAS(); orderProcess.setInvoice(invDas.find(invoice.getId())); BillingProcessDAS proDas = new BillingProcessDAS(); orderProcess.setBillingProcess(process != null ? proDas.find(process.getId()) : null); orderProcess.setPeriodsIncluded(periods); orderProcess.setOrigin(origin); orderProcess = das.save(orderProcess); LOG.debug( "created order process id " + orderProcess.getId() + " for order " + order.getId()); } catch (Exception e) { throw new SessionInternalError(e); } } }
/** * Adds a quantity of items to the given order for the given item id. Use the given price for the * addition. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add */ public static void addItem(OrderDTO order, Integer itemId, Integer quantity, BigDecimal price) { UserBL user = new UserBL(order.getUserId()); OrderLineDTO line = new OrderLineDTO(); line.setItemId(itemId); line.setQuantity(quantity); line.setPrice(price); addItem( itemId, new BigDecimal(quantity), user.getLanguage(), order.getUserId(), order.getCurrencyId(), order, line, false); }
public OrderLineDTO addItem(Integer itemID, BigDecimal quantity) throws TaskException { LOG.debug("Adding item %s q: %s", itemID, quantity); BasicItemManager helper = new BasicItemManager(); OrderLineDTO oldLine = order.getLine(itemID); FactHandle h = null; if (oldLine != null) { h = handlers.get(oldLine); } helper.addItem( itemID, quantity, language, userId, entityId, currencyId, order, records, lines, false, null, null); OrderLineDTO retValue = helper.getLatestLine(); if (h != null) { LOG.debug("updating"); session.update(h, retValue); } else { LOG.debug("inserting"); handlers.put(retValue, session.insert(retValue)); } LOG.debug("Now order line is %s , hash: %s", retValue, retValue.hashCode()); return retValue; }
private boolean isOrderProvisionable(OrderDTO order) { if (order != null) { Date today = new Date(); if (order.getOrderStatus() != null && order.getOrderStatus().getId() == Constants.ORDER_STATUS_ACTIVE) { if (order.getActiveSince() != null && order.getActiveSince().before(today) && order.getActiveUntil() != null && order.getActiveUntil().after(today)) { return true; } } } return false; }
public void process(Event event) throws PluggableTaskException { if (event instanceof SubscriptionActiveEvent) { SubscriptionActiveEvent activeEvent = (SubscriptionActiveEvent) event; LOG.debug("Processing order " + activeEvent.getOrder().getId() + " subscription activation"); doActivate(activeEvent.getOrder(), activeEvent.getOrder().getLines()); } else if (event instanceof SubscriptionInactiveEvent) { SubscriptionInactiveEvent inactiveEvent = (SubscriptionInactiveEvent) event; LOG.debug( "Processing order " + inactiveEvent.getOrder().getId() + " subscription deactivation"); doDeactivate(inactiveEvent.getOrder(), inactiveEvent.getOrder().getLines()); } else if (event instanceof NewQuantityEvent) { NewQuantityEvent quantityEvent = (NewQuantityEvent) event; if (BigDecimal.ZERO.compareTo(quantityEvent.getOldQuantity()) != 0 && BigDecimal.ZERO.compareTo(quantityEvent.getNewQuantity()) != 0) { LOG.debug("Order line quantities did not change, no provisioning necessary."); return; } OrderDTO order = new OrderBL(quantityEvent.getOrderId()).getEntity(); if (!isOrderProvisionable(order)) { LOG.warn("Order is not active and cannot be provisioned."); return; } if (BigDecimal.ZERO.compareTo(quantityEvent.getOldQuantity()) == 0) { LOG.debug("Processing order " + order.getId() + " activation"); doActivate(order, Arrays.asList(quantityEvent.getOrderLine())); } if (BigDecimal.ZERO.compareTo(quantityEvent.getNewQuantity()) == 0) { LOG.debug("Processing order " + order.getId() + " deactivation"); doDeactivate(order, Arrays.asList(quantityEvent.getOrderLine())); } } else { throw new PluggableTaskException("Cannot process event " + event); } }
/** * Returns an order line with everything correctly initialized. It does not call plug-ins to set * the price * * @param order * @param language * @param userId * @param currencyId * @param precision * @return */ private static void populateWithSimplePrice( OrderDTO order, OrderLineDTO line, Integer language, Integer userId, Integer currencyId, Integer itemId, Integer precision) { ItemBL itemBl = new ItemBL(itemId); ItemDTO item = itemBl.getEntity(); // it takes the line type of the first category this item belongs too... // TODO: redo, when item categories are redone Integer type = item.getItemTypes().iterator().next().getOrderLineTypeId(); Boolean editable = OrderBL.lookUpEditable(type); if (line.getDescription() == null) { line.setDescription(item.getDescription(language)); } if (line.getQuantity() == null) { line.setQuantity(new BigDecimal(1)); } if (line.getPrice() == null) { BigDecimal price = item.getPercentage(); if (price == null) { Date pricingDate = order != null ? order.getPricingDate() : null; price = itemBl.getPriceByCurrency( pricingDate, item, userId, currencyId); // basic price, ignoring current usage and // and quantity purchased for price calculations } line.setPrice(price); } if (line.getAmount() == null) { BigDecimal additionAmount = item.getPercentage(); // percentage ignores the quantity if (additionAmount == null) { // normal price, multiply by quantity additionAmount = line.getPrice().setScale(precision, CommonConstants.BIGDECIMAL_ROUND); additionAmount = additionAmount.multiply(line.getQuantity()); } line.setAmount(additionAmount.setScale(precision, CommonConstants.BIGDECIMAL_ROUND)); } line.setCreateDatetime(null); line.setDeleted(0); line.setTypeId(type); line.setEditable(editable); line.setItem(item); }
protected void processRules(OrderDTO order, Integer userId) throws TaskException { rulesMemoryContext.add(order); // add OrderDTO to rules memory context order.setCurrency(new CurrencyDAS().find(order.getCurrency().getId())); if (order.getCreateDate() == null) { order.setCreateDate(new Date()); } // needed for calls to 'rateOrder' if (order.getPricingFields() != null) { for (PricingField field : order.getPricingFields()) { rulesMemoryContext.add(field); } } try { UserDTOEx user = DTOFactory.getUserDTOEx(userId); rulesMemoryContext.add(user); ContactBL contact = new ContactBL(); contact.set(userId); ContactDTOEx contactDTO = contact.getDTO(); rulesMemoryContext.add(contactDTO); } catch (Exception e) { throw new TaskException(e); } executeRules(); }
@SuppressWarnings("unchecked") private void processOrderAddedOnInvoiceEvents(NewInvoiceDTO newInvoice, Integer entityId) { List<OrderDTO> orders = newInvoice.getOrders(); List<List<PeriodOfTime>> periods = newInvoice.getPeriods(); for (int i = 0; i < orders.size(); i++) { OrderDTO order = orders.get(i); Integer userId = findUserId(order); for (PeriodOfTime period : periods.get(i)) { LOG.info("Number of orders in map: " + newInvoice.getOrderTotalContributions().size()); LOG.info("Map: " + newInvoice.getOrderTotalContributions()); OrderAddedOnInvoiceEvent newEvent = new OrderAddedOnInvoiceEvent( entityId, userId, order, newInvoice.getOrderTotalContributions().get(order.getId())); newEvent.setStart(period.getStart()); newEvent.setEnd(period.getEnd()); EventManager.process(newEvent); } } }
private Integer findUserId(OrderDTO order) { UserDTO user = order.getUser(); // while this user has a parent and the flag is off, keep looking while (user.getCustomer().getParent() != null && (user.getCustomer().getInvoiceChild() == null || user.getCustomer().getInvoiceChild() == 0)) { // go up one level LOG.debug("finding parent for invoicing. Now " + user.getUserId()); user = user.getCustomer().getParent().getBaseUser(); } return user.getUserId(); }
/** * Adds or updates an order line. Calculates a percentage item order line amount based on the * amount of another order line. This is added to the existing percentage order line's amount. */ public OrderLineDTO percentageOnOrderLine(Integer percentageItemId, OrderLineDTO line) throws TaskException { // try to get percentage item order line OrderLineDTO percentageLine = order.getLine(percentageItemId); if (percentageLine == null) { // add percentage item percentageLine = addItem(percentageItemId); percentageLine.setAmount(BigDecimal.ZERO); percentageLine.setTotalReadOnly(true); } // now add the percentage amount based on the order line item amount BigDecimal percentage = percentageLine.getItem().getPercentage(); BigDecimal base = line.getPrice().multiply(line.getQuantity()); BigDecimal result = base.divide(new BigDecimal("100"), Constants.BIGDECIMAL_SCALE, Constants.BIGDECIMAL_ROUND) .multiply(percentage) .add(percentageLine.getAmount()); percentageLine.setAmount(result); return percentageLine; }
public OrderDTO createOrder(Integer itemId, BigDecimal quantity) throws TaskException { // copy the current order OrderDTO newOrder = new OrderDTO(order); newOrder.setId(0); newOrder.setVersionNum(null); // the period needs to be in the session newOrder.setOrderPeriodId(order.getOrderPeriod().getId()); // the status should be active newOrder.setOrderStatus( new OrderStatusDAS() .find( new OrderStatusDAS().getDefaultOrderStatusId(OrderStatusFlag.INVOICE, entityId))); // but without the lines newOrder.getLines().clear(); // but do get the new line in OrderManager helper = new OrderManager(newOrder, language, userId, entityId, currencyId); OrderLineDTO newLine = helper.addItem(itemId, quantity); newLine.setPurchaseOrder(newOrder); newLine.setDefaults(); return new OrderDAS().save(newOrder); }
public static void addLine(OrderDTO order, OrderLineDTO line, boolean persist) { if (persist) throw new IllegalArgumentException("persist is oboleted"); // TODO remove the argument UserBL user = new UserBL(order.getUserId()); OrderLineDTO oldLine = order.getLine(line.getItemId()); if (oldLine != null) { // get a copy of the old line oldLine = new OrderLineDTO(oldLine); } addItem( line.getItemId(), line.getQuantity(), user.getLanguage(), order.getUserId(), order.getCurrencyId(), order, line, persist); if (persist) { // generate NewQuantityEvent OrderLineDTO newLine = order.getLine(line.getItemId()); OrderBL orderBl = new OrderBL(); List<OrderLineDTO> oldLines = new ArrayList<OrderLineDTO>(1); List<OrderLineDTO> newLines = new ArrayList<OrderLineDTO>(1); if (oldLine != null) { oldLines.add(oldLine); } newLines.add(newLine); LOG.debug("Old line: %s", oldLine); LOG.debug("New line: %s", newLine); orderBl.checkOrderLineQuantities( oldLines, newLines, user.getEntity().getEntity().getId(), order.getId(), true); } }
private LineItem getLineItem( Integer itemId, InvoiceLineDTO invoiceLine, String uniqueTrackingCode, Integer userId) throws TaskException { // Get the meta field names String secondaryZipCodeExtensionFieldname = getParameter(SECONDARY_ZIP_CODE_EXTN_FIELDNAME, "Secondary Zip code extension"); String secondaryZipCodeFieldname = getParameter(SECONDARY_ZIP_CODE_FIELDNAME, "Secondary Zip code"); String billingZipCodeFieldname = getParameter(BILLING_ZIP_CODE_FIELDNAME, "Billing Zip code extension"); String regulatoryCodeFieldname = getParameter(REGULATORY_CODE_FIELDNAME, "Regulatory Code"); String salesTypeCodeFieldname = getParameter(SALES_TYPE_CODE_FIELDNAME, "Sales Type Code"); String taxExemptionCodeFieldname = getParameter(TAX_EXEMPTION_CODE_FIELDNAME, "Tax exemption code"); String transactionTypeCodeFieldname = getParameter(TRANSACTION_TYPE_CODE_FIELDNAME, "Transaction Type Code"); LineItem lineItem = new LineItem(); lineItem.setBillToNumber(""); // TODO: need to be addressed ? String customerNumber = null; List<NewInvoiceContext.OrderContext> orders = invoice.getOrders(); // We need to get the fresh item from the database because // the item in the invoiceLine doesn't yet contain meta fields. ItemDTO item = new ItemDAS().find(itemId); OrderDTO orderDTO = null; UserDTO invoiceToUser = null; for (NewInvoiceContext.OrderContext orderCtx : orders) { if (orderCtx.order.getId().intValue() == invoiceLine.getOrder().getId()) { orderDTO = orderCtx.order; break; } } if (null == orderDTO) { orderDTO = orders.get(0).order; } invoiceToUser = new UserDAS().find(userId); customerNumber = invoiceToUser.getCustomer().getId() + ""; lineItem.setCustomerNumber(customerNumber); lineItem.setInvoiceNumber("JB" + uniqueTrackingCode); lineItem.setLineNumber(""); // TODO: need to be addressed ? lineItem.setOrigNumber(""); // TODO: need to be addressed ? MetaFieldValue<String> p2PPlus4 = invoiceToUser.getCustomer().getMetaField(secondaryZipCodeExtensionFieldname); if (p2PPlus4 != null) { lineItem.setP2PPlus4(p2PPlus4.getValue()); } else { lineItem.setP2PPlus4(""); } MetaFieldValue<String> p2PZipcode = invoiceToUser.getCustomer().getMetaField(secondaryZipCodeFieldname); if (p2PZipcode != null) { lineItem.setP2PZipcode(p2PZipcode.getValue()); } else { lineItem.setP2PZipcode(""); } MetaFieldValue<String> plus4 = invoiceToUser.getCustomer().getMetaField(billingZipCodeFieldname); if (plus4 != null) { lineItem.setPlus4(plus4.getValue()); } else { lineItem.setPlus4(""); } LOG.debug("Meta fields: p2PPlus4: %s, p2PZipcode: %s, plus4:%s", p2PPlus4, p2PZipcode, plus4); MetaFieldValue<String> regulatoryCode = null; regulatoryCode = item.getMetaField(regulatoryCodeFieldname); if (regulatoryCode == null || regulatoryCode.getValue() == null || regulatoryCode.getValue().isEmpty()) { lineItem.setRegulatoryCode("00"); } else { lineItem.setRegulatoryCode(regulatoryCode.getValue()); } lineItem.setRevenue(invoiceLine.getAmount().floatValue()); MetaFieldValue<String> salesTypeCode = orderDTO.getMetaField(salesTypeCodeFieldname); if (salesTypeCode == null || salesTypeCode.getValue() == null || salesTypeCode.getValue().isEmpty()) { lineItem.setSalesTypeCode("R"); } else { lineItem.setSalesTypeCode(salesTypeCode.getValue()); } lineItem.setSeconds( invoiceLine.getQuantity() != null ? invoiceLine.getQuantity().intValue() : 0); List<String> taxExemptionCodeList = new ArrayList<String>(); // First get the tax exemption code from the customer MetaFieldValue<String> taxExemptionCode = invoiceToUser.getCustomer().getMetaField(taxExemptionCodeFieldname); LOG.debug("Tax exemption code from customer: %s", taxExemptionCode); if (!(taxExemptionCode != null && taxExemptionCode.getValue() != null && !taxExemptionCode.getValue().isEmpty())) { taxExemptionCode = item.getMetaField(taxExemptionCodeFieldname); LOG.debug("Tax exemption code from product: %s", taxExemptionCode); } if (taxExemptionCode == null) { LOG.debug("Setting tax exemption code to be 00"); taxExemptionCodeList.add("00"); } else { taxExemptionCodeList.add(taxExemptionCode.getValue()); } LOG.debug( "Meta fields: regulatoryCode: %s, salesTypeCode: %s, taxExemptionCode: %s", regulatoryCode, salesTypeCode, taxExemptionCode); lineItem.setTaxExemptionCodeList(taxExemptionCodeList); lineItem.setTaxIncludedCode("0"); lineItem.setTermNumber(""); // TODO: Need to check if trans date will be current date or based on data year and data month ? lineItem.setTransDate("07-10-2012"); MetaFieldValue<String> transTypeCode = null; transTypeCode = item.getMetaField(transactionTypeCodeFieldname); if (transTypeCode == null || transTypeCode.getValue() == null || transTypeCode.getValue().isEmpty()) { throw new SessionInternalError( "No valid transaction type code found on the product", new String[] {"ItemDTOEx,transTypeCode,no.valid.transactionTypeCode.on.product"}); } lineItem.setTransTypeCode(transTypeCode.getValue()); lineItem.setUnits(invoiceLine.getQuantity() != null ? invoiceLine.getQuantity().intValue() : 0); lineItem.setUnitType("00"); if (invoiceToUser.getContact().getPostalCode() != null && plus4 != null && plus4.getValue() != null && !plus4.getValue().isEmpty()) { lineItem.setZipcode(invoiceToUser.getContact().getPostalCode()); lineItem.setTaxSitusRule("05"); } else if (invoiceToUser.getContact().getPostalCode() != null && (plus4 == null || plus4.getValue() == null || plus4.getValue().isEmpty())) { lineItem.setZipcode(invoiceToUser.getContact().getPostalCode()); lineItem.setPlus4("0000"); lineItem.setTaxSitusRule("05"); } return lineItem; }
/** * This will remove all the records (sql delete, not just flag them). It will also update the * related orders if applicable */ public void delete(Integer executorId) throws SessionInternalError { if (invoice == null) { throw new SessionInternalError("An invoice has to be set before delete"); } // prevent a delegated Invoice from being deleted if (invoice.getDelegatedInvoiceId() != null && invoice.getDelegatedInvoiceId().intValue() > 0) { SessionInternalError sie = new SessionInternalError("A carried forward Invoice cannot be deleted"); sie.setErrorMessages( new String[] {"InvoiceDTO,invoice,invoice.error.fkconstraint," + invoice.getId()}); throw sie; } // start by updating purchase_order.next_billable_day if applicatble // for each of the orders included in this invoice for (OrderProcessDTO orderProcess : (Collection<OrderProcessDTO>) invoice.getOrderProcesses()) { OrderDTO order = orderProcess.getPurchaseOrder(); if (order.getNextBillableDay() == null) { // the next billable day doesn't need updating if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) { OrderBL orderBL = new OrderBL(order); orderBL.setStatus(null, Constants.ORDER_STATUS_ACTIVE); } continue; } // only if this invoice is the responsible for the order's // next billable day if (order.getNextBillableDay().equals(orderProcess.getPeriodEnd())) { order.setNextBillableDay(orderProcess.getPeriodStart()); if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) { OrderBL orderBL = new OrderBL(order); orderBL.setStatus(null, Constants.ORDER_STATUS_ACTIVE); } } } // go over the order process records again just to delete them // we are done with this order, delete the process row for (OrderProcessDTO orderProcess : (Collection<OrderProcessDTO>) invoice.getOrderProcesses()) { OrderDTO order = orderProcess.getPurchaseOrder(); OrderProcessDAS das = new OrderProcessDAS(); order.getOrderProcesses().remove(orderProcess); das.delete(orderProcess); } invoice.getOrderProcesses().clear(); // get rid of the contact associated with this invoice try { ContactBL contact = new ContactBL(); if (contact.setInvoice(invoice.getId())) { contact.delete(); } } catch (Exception e1) { LOG.error("Exception deleting the contact of an invoice", e1); } // remove the payment link/s PaymentBL payment = new PaymentBL(); Iterator<PaymentInvoiceMapDTO> it = invoice.getPaymentMap().iterator(); while (it.hasNext()) { PaymentInvoiceMapDTO map = it.next(); payment.removeInvoiceLink(map.getId()); invoice.getPaymentMap().remove(map); // needed because the collection has changed it = invoice.getPaymentMap().iterator(); } // log that this was deleted, otherwise there will be no trace if (executorId != null) { eLogger.audit( executorId, invoice.getBaseUser().getId(), Constants.TABLE_INVOICE, invoice.getId(), EventLogger.MODULE_INVOICE_MAINTENANCE, EventLogger.ROW_DELETED, null, null, null); } // before delete the invoice most delete the reference in table // PAYMENT_INVOICE new PaymentInvoiceMapDAS().deleteAllWithInvoice(invoice); Set<InvoiceDTO> invoices = invoice.getInvoices(); if (invoices.size() > 0) { for (InvoiceDTO delegate : invoices) { // set status to unpaid as against carried delegate.setInvoiceStatus(new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID)); // remove delegated invoice link delegate.setInvoice(null); getHome().save(delegate); } } // now delete the invoice itself getHome().delete(invoice); getHome().flush(); }
private boolean addOrderToInvoice( Integer entityId, OrderDTO order, NewInvoiceDTO newInvoice, Date processDate, int maxPeriods) throws SessionInternalError, TaskException, PluggableTaskException { // require the calculation of the period dates PluggableTaskManager taskManager = new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_ORDER_PERIODS); OrderPeriodTask optask = (OrderPeriodTask) taskManager.getNextClass(); if (optask == null) { throw new SessionInternalError( "There has to be " + "one order period pluggable task configured"); } Date start = optask.calculateStart(order); Date end = optask.calculateEnd(order, processDate, maxPeriods, start); List<PeriodOfTime> periods = optask.getPeriods(); // there isn't anything billable from this order if (periods.size() == 0) { return false; } if (start != null && end != null && start.after(end)) { // how come it starts after it ends ??? throw new SessionInternalError( "Calculated for " + "order " + order.getId() + " a period that" + " starts after it ends:" + start + " " + end); } // add this order to the invoice being created newInvoice.addOrder(order, start, end, periods); // prepaid orders shouldn't have to be included // past time. if (order.getBillingTypeId().compareTo(Constants.ORDER_BILLING_PRE_PAID) == 0 && start != null && // it has to be recursive too processDate.after(start)) { eLogger.warning( entityId, order.getBaseUserByUserId().getId(), order.getId(), EventLogger.MODULE_BILLING_PROCESS, EventLogger.BILLING_PROCESS_UNBILLED_PERIOD, Constants.TABLE_PUCHASE_ORDER); LOG.warn("Order " + order.getId() + " is prepaid " + "but has past time not billed."); } // initialize the currency of the new invoice if (newInvoice.getCurrency() == null) { newInvoice.setCurrency(order.getCurrency()); } else { // now we are not supporting orders with different // currencies in the same invoice. Later this could be done if (newInvoice.getCurrency().getId() != order.getCurrency().getId()) { throw new SessionInternalError( "Orders with different " + "currencies not supported in one invoice. " + "Currency = " + newInvoice.getCurrency().getId() + "order = " + order.getId()); } } return true; }
protected void processRules(OrderDTO newOrder) throws TaskException { // now we have the line with good defaults, the order and the item // These have to be visible to the rules KnowledgeBase knowledgeBase; try { knowledgeBase = readKnowledgeBase(); } catch (Exception e) { throw new TaskException(e); } session = knowledgeBase.newStatefulKnowledgeSession(); List<Object> rulesMemoryContext = new ArrayList<Object>(); rulesMemoryContext.add(helperOrder); // add OrderDTO to rules memory context newOrder.setCurrency(new CurrencyDAS().find(newOrder.getCurrency().getId())); if (newOrder.getCreateDate() == null) { newOrder.setCreateDate(new Date()); } rulesMemoryContext.add(newOrder); for (OrderLineDTO line : newOrder.getLines()) { if (line.getItem() != null) { ItemBL item = new ItemBL(line.getItemId()); rulesMemoryContext.add( item.getDTO( helperOrder.getLanguage(), helperOrder.getUserId(), helperOrder.getEntityId(), helperOrder.getCurrencyId())); } rulesMemoryContext.add(line); } if (newOrder.getPricingFields() != null && newOrder.getPricingFields().size() > 0) { for (PricingField pf : newOrder.getPricingFields()) { rulesMemoryContext.add(pf); } } try { Integer userId = newOrder.getBaseUserByUserId().getId(); UserDTOEx user = DTOFactory.getUserDTOEx(userId); rulesMemoryContext.add(user); ContactBL contact = new ContactBL(); contact.set(userId); ContactDTOEx contactDTO = contact.getDTO(); rulesMemoryContext.add(contactDTO); // Add the subscriptions OrderBL order = new OrderBL(); for (OrderDTO myOrder : order.getActiveRecurringByUser(userId)) { for (OrderLineDTO myLine : myOrder.getLines()) { rulesMemoryContext.add(new Subscription(myLine)); } } } catch (Exception e) { throw new TaskException(e); } session.setGlobal("order", helperOrder); // then execute the rules executeStatefulRules(session, rulesMemoryContext); }
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; }
public void removeItem(Integer itemId) { removeObject(order.getLine(itemId)); order.removeLine(itemId); }