/* * 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); } }