com.sapienter.jbilling.server.pluggableTask.BasicCompositionTask.java Source code

Java tutorial

Introduction

Here is the source code for com.sapienter.jbilling.server.pluggableTask.BasicCompositionTask.java

Source

/*
 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.pluggableTask;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.sapienter.jbilling.common.SessionInternalError;
import com.sapienter.jbilling.server.invoice.InvoiceBL;
import com.sapienter.jbilling.server.invoice.NewInvoiceDTO;
import com.sapienter.jbilling.server.invoice.db.InvoiceDTO;
import com.sapienter.jbilling.server.invoice.db.InvoiceLineDTO;
import com.sapienter.jbilling.server.order.db.OrderDTO;
import com.sapienter.jbilling.server.order.db.OrderLineDTO;
import com.sapienter.jbilling.server.process.PeriodOfTime;
import com.sapienter.jbilling.server.user.UserBL;
import com.sapienter.jbilling.server.util.Constants;
import com.sapienter.jbilling.server.util.PreferenceBL;
import org.joda.time.DateMidnight;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * This task will copy all the lines on the orders and invoices
 * to the new invoice, considering the periods involved for each
 * order, but not the fractions of perios. It will not copy the
 * lines that are taxes.
 * The quantity and total of each line will be multiplied by the
 * amount of periods.
 * @author Emil Created on 27-Apr-2003
 */
public class BasicCompositionTask extends PluggableTask implements InvoiceCompositionTask {

    private static final Logger LOG = Logger.getLogger(BasicCompositionTask.class);

    private Locale locale = null;

    public void apply(NewInvoiceDTO invoiceDTO, Integer userId) throws TaskException {

        /*
        Process each order being included in this invoice
         */
        for (int orderIndex = 0; orderIndex < invoiceDTO.getOrders().size(); orderIndex++) {

            OrderDTO order = (OrderDTO) invoiceDTO.getOrders().get(orderIndex);
            BigDecimal orderContribution = BigDecimal.ZERO;

            // add customer notes
            if (Integer.valueOf(1).equals(order.getNotesInInvoice()) && !StringUtils.isBlank(order.getNotes())) {
                if (invoiceDTO.getCustomerNotes() == null) {
                    invoiceDTO.setCustomerNotes(order.getNotes());
                } else {
                    invoiceDTO.setCustomerNotes(invoiceDTO.getCustomerNotes() + " " + order.getNotes());
                }
            }

            /*
            Add order lines - excluding taxes
             */
            for (OrderLineDTO orderLine : order.getLines()) {

                // skip deleted lines
                if (orderLine.getDeleted() == 1) {
                    continue;
                }

                for (PeriodOfTime period : invoiceDTO.getPeriods().get(orderIndex)) {
                    LOG.debug("Adding order line from " + order.getId() + ", quantity " + orderLine.getQuantity()
                            + ", price " + orderLine.getPrice());

                    LOG.debug("Period: " + period);

                    InvoiceLineDTO invoiceLine = null;

                    if (orderLine.getOrderLineType().getId() == Constants.ORDER_LINE_TYPE_ITEM) {
                        // compose order line description
                        String desc = composeDescription(order, period, orderLine.getDescription());

                        // determine the invoice line type (one-time, recurring, line from sub-account)
                        Integer type;
                        if (userId.equals(order.getUser().getId())) {
                            if (Constants.ORDER_PERIOD_ONCE.equals(order.getPeriodId())) {
                                type = Constants.INVOICE_LINE_TYPE_ITEM_ONETIME;
                            } else {
                                type = Constants.INVOICE_LINE_TYPE_ITEM_RECURRING;
                            }
                        } else {
                            type = Constants.INVOICE_LINE_TYPE_SUB_ACCOUNT;
                        }

                        BigDecimal periodAmount = calculatePeriodAmount(orderLine.getAmount(), period);
                        invoiceLine = new InvoiceLineDTO(null, desc, periodAmount, orderLine.getPrice(),
                                orderLine.getQuantity(), type, 0, orderLine.getItemId(), order.getUser().getId(),
                                null);

                        // link invoice line to the order that originally held the charge
                        invoiceLine.setOrderId(order.getId());
                        orderContribution = orderContribution.add(periodAmount);

                    } else if (orderLine.getOrderLineType().getId() == Constants.ORDER_LINE_TYPE_TAX) {
                        // tax items
                        int taxLineIndex = getTaxLineIndex(invoiceDTO.getResultLines(), orderLine.getDescription());
                        if (taxLineIndex >= 0) {
                            // tax already exists, add the total
                            invoiceLine = (InvoiceLineDTO) invoiceDTO.getResultLines().get(taxLineIndex);
                            BigDecimal periodAmount = calculatePeriodAmount(orderLine.getAmount(), period);
                            invoiceLine.setAmount(invoiceLine.getAmount().add(periodAmount));
                            orderContribution = orderContribution.add(periodAmount);

                        } else {
                            // tax has not yet been added, add a new invoice line
                            BigDecimal periodAmount = calculatePeriodAmount(orderLine.getAmount(), period);
                            invoiceLine = new InvoiceLineDTO(null, orderLine.getDescription(), periodAmount,
                                    orderLine.getPrice(), null, Constants.INVOICE_LINE_TYPE_TAX, 0,
                                    orderLine.getItemId(), order.getUser().getId(), null);

                            orderContribution = orderContribution.add(periodAmount);
                        }

                    } else if (orderLine.getOrderLineType().getId() == Constants.ORDER_LINE_TYPE_PENALTY) {
                        // penalty items
                        BigDecimal periodAmount = calculatePeriodAmount(orderLine.getAmount(), period);
                        invoiceLine = new InvoiceLineDTO(null, orderLine.getDescription(), periodAmount, null, null,
                                Constants.INVOICE_LINE_TYPE_PENALTY, 0, orderLine.getItemId(),
                                order.getUser().getId(), null);

                        orderContribution = orderContribution.add(periodAmount);
                    }

                    // for the invoice to make sense when it is displayed,
                    // each line has to be rounded to the decimals shown
                    invoiceDTO.addResultLine(invoiceLine);
                }
            }

            // save the order contribution
            saveOrderTotalContributionToInvoice(order.getId(), invoiceDTO, orderContribution);
        }

        /*
        add delegated invoices
         */
        for (InvoiceDTO invoice : invoiceDTO.getInvoices()) {
            // the whole invoice will be added as a single line
            // The text of this line has to be i18n
            InvoiceBL bl = new InvoiceBL(invoice.getId());
            Locale locale = getLocale(bl.getEntity().getUserId());

            ResourceBundle bundle = ResourceBundle.getBundle("entityNotifications", locale);
            SimpleDateFormat df = new SimpleDateFormat(bundle.getString("format.date"));

            StringBuilder delLine = new StringBuilder();
            delLine.append(bundle.getString("invoice.line.delegated"));
            delLine.append(" ").append(invoice.getPublicNumber()).append(" ");
            delLine.append(bundle.getString("invoice.line.delegated.due"));
            delLine.append(" ").append(df.format(invoice.getDueDate()));

            InvoiceLineDTO invoiceLine = new InvoiceLineDTO(null, delLine.toString(), invoice.getBalance(), null,
                    null, Constants.INVOICE_LINE_TYPE_DUE_INVOICE, 0, null, null, 0);

            invoiceDTO.addResultLine(invoiceLine);
        }
    }

    /**
     * Returns the index of a tax line with the matching description. Used to find an existing
     * tax line so that similar taxes can be consolidated;
     *
     * @param lines invoice lines
     * @param desc tax line description
     * @return index of tax line
     */
    protected int getTaxLineIndex(List lines, String desc) {
        for (int f = 0; f < lines.size(); f++) {
            InvoiceLineDTO line = (InvoiceLineDTO) lines.get(f);
            if (line.getTypeId() == Constants.ORDER_LINE_TYPE_TAX) {
                if (line.getDescription().equals(desc)) {
                    return f;
                }
            }
        }

        return -1;
    }

    /**
     * Saves the amount the order contributed to the invoice total.
     *
     * @param orderId order id
     * @param invoiceDTO new invoice
     * @param amount amount contributed by order
     */
    protected void saveOrderTotalContributionToInvoice(Integer orderId, NewInvoiceDTO invoiceDTO,
            BigDecimal amount) {
        Map<Integer, BigDecimal> orderTotalContributions = invoiceDTO.getOrderTotalContributions();
        BigDecimal total = orderTotalContributions.get(orderId);
        if (total == null) {
            total = amount;
        } else {
            total = total.add(amount);
        }
        orderTotalContributions.put(orderId, total);
    }

    /**
     * Composes the actual invoice line description based off of set entity preferences and the
     * order period being processed.
     *
     * @param order order being processed
     * @param period period of time being processed
     * @param desc original order line description
     * @return invoice line description
     */
    public String composeDescription(OrderDTO order, PeriodOfTime period, String desc) {
        Locale locale = getLocale(order.getBaseUserByUserId().getId());
        ResourceBundle bundle = ResourceBundle.getBundle("entityNotifications", locale);

        StringBuilder lineDescription = new StringBuilder();
        lineDescription.append(desc);

        /*
        append the billing period to the order line for non one-time orders
         */
        if (order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE) {
            // period ends at midnight of the next day (E.g., Oct 1 00:00, effectivley end-of-day Sept 30th).
            // subtract 1 day from the end so the period print out looks human readable
            DateMidnight start = period.getDateMidnightStart();
            DateMidnight end = period.getDateMidnightEnd().minusDays(1);

            DateTimeFormatter dateFormat = DateTimeFormat.forPattern(bundle.getString("format.date"));

            LOG.debug("Composing for period " + start + " to " + end);
            LOG.debug("Using date format: " + bundle.getString("format.date"));

            // now add this to the line
            lineDescription.append(" ");
            lineDescription.append(bundle.getString("invoice.line.period"));
            lineDescription.append(" ");

            lineDescription.append(dateFormat.print(start));
            lineDescription.append(" ");
            lineDescription.append(bundle.getString("invoice.line.to"));
            lineDescription.append(" ");
            lineDescription.append(dateFormat.print(end));

        }

        /*
        optionally append the order id if the entity has the preference set
         */
        if (appendOrderId(order.getBaseUserByUserId().getCompany().getId())) {
            lineDescription.append(bundle.getString("invoice.line.orderNumber"));
            lineDescription.append(" ");
            lineDescription.append(order.getId().toString());
        }

        return lineDescription.toString();
    }

    /**
     * Calculates a price based on the period of time used.
     *
     * This plug-in does not do any period price calculation. The given price will be returned
     * untouched (see {@link com.sapienter.jbilling.server.process.task.DailyProRateCompositionTask}).
     *
     * @param fullPrice full line price
     * @param period period to calculate amount for
     * @return period price
     */
    public BigDecimal calculatePeriodAmount(BigDecimal fullPrice, PeriodOfTime period) {
        return fullPrice;
    }

    /**
     * Gets the locale for the give user.
     *
     * @param userId user to get locale for
     * @return users locale
     */
    protected Locale getLocale(Integer userId) {
        if (locale == null) {
            try {
                UserBL user = new UserBL(userId);
                locale = user.getLocale();
            } catch (Exception e) {
                throw new SessionInternalError("Exception occurred determining user locale for composition.", e);
            }
        }

        return locale;
    }

    /**
     * Returns true if the given entity wants the order ID appended to the invoice line description.
     *
     * @param entityId entity id
     * @return true if order ID should be appended, false if not.
     */
    protected boolean appendOrderId(Integer entityId) {
        PreferenceBL pref = new PreferenceBL();
        try {
            pref.set(entityId, Constants.PREFERENCE_ORDER_IN_INVOICE_LINE);
        } catch (Exception e) {
            /* use default value */
        }

        return pref.getInt() == 1;
    }
}