/*
* Copyright (c) 2015 Ngewi Fet <ngewif@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gnucash.android.db.adapter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.DatabaseSchema.BudgetAmountEntry;
import org.gnucash.android.db.DatabaseSchema.BudgetEntry;
import org.gnucash.android.model.Budget;
import org.gnucash.android.model.BudgetAmount;
import org.gnucash.android.model.Money;
import org.gnucash.android.model.Recurrence;
import java.util.ArrayList;
import java.util.List;
/**
* Database adapter for accessing {@link org.gnucash.android.model.Budget} records
*/
public class BudgetsDbAdapter extends DatabaseAdapter<Budget>{
private RecurrenceDbAdapter mRecurrenceDbAdapter;
private BudgetAmountsDbAdapter mBudgetAmountsDbAdapter;
/**
* Opens the database adapter with an existing database
*
* @param db SQLiteDatabase object
*/
public BudgetsDbAdapter(SQLiteDatabase db, BudgetAmountsDbAdapter budgetAmountsDbAdapter,
RecurrenceDbAdapter recurrenceDbAdapter) {
super(db, BudgetEntry.TABLE_NAME, new String[]{
BudgetEntry.COLUMN_NAME,
BudgetEntry.COLUMN_DESCRIPTION,
BudgetEntry.COLUMN_RECURRENCE_UID,
BudgetEntry.COLUMN_NUM_PERIODS
});
mRecurrenceDbAdapter = recurrenceDbAdapter;
mBudgetAmountsDbAdapter = budgetAmountsDbAdapter;
}
/**
* Returns an instance of the budget database adapter
* @return BudgetsDbAdapter instance
*/
public static BudgetsDbAdapter getInstance(){
return GnuCashApplication.getBudgetDbAdapter();
}
@Override
public void addRecord(@NonNull Budget budget, UpdateMethod updateMethod) {
if (budget.getBudgetAmounts().size() == 0)
throw new IllegalArgumentException("Budgets must have budget amounts");
mRecurrenceDbAdapter.addRecord(budget.getRecurrence(), updateMethod);
super.addRecord(budget, updateMethod);
mBudgetAmountsDbAdapter.deleteBudgetAmountsForBudget(budget.getUID());
for (BudgetAmount budgetAmount : budget.getBudgetAmounts()) {
mBudgetAmountsDbAdapter.addRecord(budgetAmount, updateMethod);
}
}
@Override
public long bulkAddRecords(@NonNull List<Budget> budgetList, UpdateMethod updateMethod) {
List<BudgetAmount> budgetAmountList = new ArrayList<>(budgetList.size()*2);
for (Budget budget : budgetList) {
budgetAmountList.addAll(budget.getBudgetAmounts());
}
//first add the recurrences, they have no dependencies (foreign key constraints)
List<Recurrence> recurrenceList = new ArrayList<>(budgetList.size());
for (Budget budget : budgetList) {
recurrenceList.add(budget.getRecurrence());
}
mRecurrenceDbAdapter.bulkAddRecords(recurrenceList, updateMethod);
//now add the budgets themselves
long nRow = super.bulkAddRecords(budgetList, updateMethod);
//then add the budget amounts, they require the budgets to exist
if (nRow > 0 && !budgetAmountList.isEmpty()){
mBudgetAmountsDbAdapter.bulkAddRecords(budgetAmountList, updateMethod);
}
return nRow;
}
@Override
public Budget buildModelInstance(@NonNull Cursor cursor) {
String name = cursor.getString(cursor.getColumnIndexOrThrow(BudgetEntry.COLUMN_NAME));
String description = cursor.getString(cursor.getColumnIndexOrThrow(BudgetEntry.COLUMN_DESCRIPTION));
String recurrenceUID = cursor.getString(cursor.getColumnIndexOrThrow(BudgetEntry.COLUMN_RECURRENCE_UID));
long numPeriods = cursor.getLong(cursor.getColumnIndexOrThrow(BudgetEntry.COLUMN_NUM_PERIODS));
Budget budget = new Budget(name);
budget.setDescription(description);
budget.setRecurrence(mRecurrenceDbAdapter.getRecord(recurrenceUID));
budget.setNumberOfPeriods(numPeriods);
populateBaseModelAttributes(cursor, budget);
budget.setBudgetAmounts(mBudgetAmountsDbAdapter.getBudgetAmountsForBudget(budget.getUID()));
return budget;
}
@Override
protected @NonNull SQLiteStatement setBindings(@NonNull SQLiteStatement stmt, @NonNull final Budget budget) {
stmt.clearBindings();
stmt.bindString(1, budget.getName());
if (budget.getDescription() != null)
stmt.bindString(2, budget.getDescription());
stmt.bindString(3, budget.getRecurrence().getUID());
stmt.bindLong(4, budget.getNumberOfPeriods());
stmt.bindString(5, budget.getUID());
return stmt;
}
/**
* Fetch all budgets which have an amount specified for the account
* @param accountUID GUID of account
* @return Cursor with budgets data
*/
public Cursor fetchBudgetsForAccount(String accountUID){
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(BudgetEntry.TABLE_NAME + "," + BudgetAmountEntry.TABLE_NAME
+ " ON " + BudgetEntry.TABLE_NAME + "." + BudgetEntry.COLUMN_UID + " = "
+ BudgetAmountEntry.TABLE_NAME + "." + BudgetAmountEntry.COLUMN_BUDGET_UID);
queryBuilder.setDistinct(true);
String[] projectionIn = new String[]{BudgetEntry.TABLE_NAME + ".*"};
String selection = BudgetAmountEntry.TABLE_NAME + "." + BudgetAmountEntry.COLUMN_ACCOUNT_UID + " = ?";
String[] selectionArgs = new String[]{accountUID};
String sortOrder = BudgetEntry.TABLE_NAME + "." + BudgetEntry.COLUMN_NAME + " ASC";
return queryBuilder.query(mDb, projectionIn, selection, selectionArgs, null, null, sortOrder);
}
/**
* Returns the budgets associated with a specific account
* @param accountUID GUID of the account
* @return List of budgets for the account
*/
public List<Budget> getAccountBudgets(String accountUID) {
Cursor cursor = fetchBudgetsForAccount(accountUID);
List<Budget> budgets = new ArrayList<>();
while(cursor.moveToNext()){
budgets.add(buildModelInstance(cursor));
}
cursor.close();
return budgets;
}
/**
* Returns the sum of the account balances for all accounts in a budget for a specified time period
* <p>This represents the total amount spent within the account of this budget in a given period</p>
* @param budgetUID GUID of budget
* @param periodStart Start of the budgeting period in millis
* @param periodEnd End of the budgeting period in millis
* @return Balance of all the accounts
*/
public Money getAccountSum(String budgetUID, long periodStart, long periodEnd){
List<BudgetAmount> budgetAmounts = mBudgetAmountsDbAdapter.getBudgetAmountsForBudget(budgetUID);
List<String> accountUIDs = new ArrayList<>();
for (BudgetAmount budgetAmount : budgetAmounts) {
accountUIDs.add(budgetAmount.getAccountUID());
}
return new AccountsDbAdapter(mDb).getAccountsBalance(accountUIDs, periodStart, periodEnd);
}
}