nl.strohalm.cyclos.setup.migrations.version3_6.ClosedAccountBalancesMigration.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.setup.migrations.version3_6.ClosedAccountBalancesMigration.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos 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 General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.setup.migrations.version3_6;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;

import nl.strohalm.cyclos.entities.accounts.transactions.Payment;
import nl.strohalm.cyclos.setup.TraceableMigration;
import nl.strohalm.cyclos.utils.JDBCWrapper;

import org.apache.commons.lang.time.DateUtils;

/**
 * A migration class which replaces the old AccountStatus by the new ClosedAccountBalance approach
 * 
 * @author luis
 */
public class ClosedAccountBalancesMigration implements TraceableMigration {

    @Override
    public int execute(final JDBCWrapper jdbc) throws SQLException {

        // First, ensure the account status still exists, ie, not already migrated
        ResultSet accountStatusExists = null;
        try {
            accountStatusExists = jdbc.query("select 1 from account_status limit 1");
        } catch (final SQLException e) {
            // The already does not exists. Exit.
            return 0;
        } finally {
            JDBCWrapper.closeQuietly(accountStatusExists);
        }

        // Populate the account limit logs from both the account status and the pending account status tables
        jdbc.execute(
                "insert into account_limit_logs " + " (account_id, date, by_id, credit_limit, upper_credit_limit) "
                        + " select account_id, date, credit_limit_by_id, credit_limit, upper_credit_limit"
                        + " from account_status" + " where credit_limit_by_id is not null");

        jdbc.execute(
                "insert into account_limit_logs " + " (account_id, date, by_id, credit_limit, upper_credit_limit) "
                        + " select account_id, date, by_id, lower_limit, upper_limit"
                        + " from pending_account_status" + " where type = 'lim'");

        // Populate the amount_reservations table from pending transfers, scheduled payments which reserves the total amount and their installments
        jdbc.execute("insert into amount_reservations" + " (subclass, account_id, date, amount, transfer_id)"
                + " select 'P', from_account_id, date, amount, id " + " from transfers t " + " where t.status = ? ",
                Payment.Status.PENDING.getValue());
        jdbc.execute(
                "insert into amount_reservations" + " (subclass, account_id, date, amount, scheduled_payment_id)"
                        + " select 'S', from_account_id, date, amount, id " + " from scheduled_payments "
                        + " where reserve_amount = true ");
        jdbc.execute(
                "insert into amount_reservations" + " (subclass, account_id, date, amount, transfer_id)"
                        + " select 'I', t.from_account_id, ifnull(t.process_date, t.date), -t.amount, t.id "
                        + " from transfers t inner join scheduled_payments sp on t.scheduled_payment_id = sp.id"
                        + " where sp.reserve_amount = true and t.status <> ? ",
                Payment.Status.SCHEDULED.getValue());

        // Iterate each account
        int results = 0;
        final ResultSet accounts = jdbc.query("select id, creation_date from accounts");
        try {
            while (accounts.next()) {
                final long accountId = accounts.getLong("id");
                final Date creationDate = new Date(DateUtils
                        .truncate(accounts.getTimestamp("creation_date"), Calendar.DAY_OF_MONTH).getTime());
                // Get, by day, each diff, either for balance or reserved amount
                ResultSet diffs = jdbc.query(" select * from ( "
                        + "     select 'B' as type, b.date, b.balance as diff" + "     from ( "
                        + "         select date(date) as date, sum(amount) as balance " + "         from ( "
                        + "             select t.process_date as date, "
                        + "             case when t.chargeback_of_id is null then "
                        + "                 case when t.from_account_id = ? then -t.amount else t.amount end "
                        + "             else "
                        + "                 case when t.to_account_id = ? then t.amount else -t.amount end "
                        + "             end as amount " + "             from transfers t "
                        + "             where (t.from_account_id = ? or t.to_account_id = ?) "
                        + "               and t.process_date is not null " + "         ) t "
                        + "         group by date(date) " + "     ) b " + "     union "
                        + "     select 'R', date(r.date), sum(r.amount) " + "     from amount_reservations r "
                        + "     where r.account_id = ? " + "     group by date(r.date) " + " ) t "
                        + " where date < current_date() " + " order by date", accountId, accountId, accountId,
                        accountId, accountId);
                Date lastDate = creationDate;
                double balance = 0;
                double reserved = 0;
                try {
                    boolean hasData = false;
                    while (diffs.next()) {
                        hasData = true;
                        boolean isBalance = "B".equals(diffs.getString("type"));
                        Date date = diffs.getDate("date");
                        double diff = diffs.getDouble("diff");
                        if (!lastDate.equals(date)) {
                            // Insert a closed balance when the date changes
                            results += jdbc.execute(
                                    "insert into closed_account_balances (date, account_id, balance, reserved) values (?, ?, ?, ?)",
                                    nextDay(lastDate), accountId, balance, reserved);
                        }
                        if (isBalance) {
                            balance += diff;
                        } else {
                            reserved += diff;
                        }
                        lastDate = date;
                    }
                    if (hasData) {
                        // There is a last closed balance to insert
                        results += jdbc.execute(
                                "insert into closed_account_balances (date, account_id, balance, reserved) values (?, ?, ?, ?)",
                                nextDay(lastDate), accountId, balance, reserved);
                    }
                } finally {
                    JDBCWrapper.closeQuietly(diffs);
                }
                // Set the last closing date
                jdbc.execute("update accounts set last_closing_date = ? where id = ?", lastDate, accountId);
            }
        } finally {
            JDBCWrapper.closeQuietly(accounts);
        }

        // Now it is safe to drop the account_status table
        jdbc.execute("drop table account_status");
        jdbc.execute("drop table pending_account_status");

        return results;
    }

    private Date nextDay(final Date date) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return new Date(calendar.getTimeInMillis());
    }
}