app/src/main/java/ve/com/abicelis/creditcardexpensemanager/enums/CreditCardBackground.java
public enum CreditCardBackground {
    DARK_RED(R.drawable.cc_background_dark_red, R.color.cc_foreground_red, R.color.cc_text_white),
    RED(R.drawable.cc_background_red, R.color.cc_foreground_red, R.color.cc_text_white),
    GREEN(R.drawable.cc_background_dark_green, R.color.cc_foreground_green,  R.color.cc_text_white),
    GOLD(R.drawable.cc_background_gold, R.color.cc_foreground_gold,  R.color.cc_text_black),
    ORANGE(R.drawable.cc_background_orange, R.color.cc_foreground_orange,  R.color.cc_text_white),
    LIGHT_BLUE(R.drawable.cc_background_light_blue, R.color.cc_foreground_light_blue,  R.color.cc_text_white),
    LIGHT_GRAY(R.drawable.cc_background_light_gray, R.color.cc_foreground_light_gray,  R.color.cc_text_white);

    int backgroundResId;
    int foregroundColorId;
    int textColorId;

    CreditCardBackground(@DrawableRes int backgroundResId, @ColorRes int foregroundColorId, @ColorRes int textColorId) {
        this.backgroundResId = backgroundResId;
        this.foregroundColorId = foregroundColorId;
        this.textColorId = textColorId;
    }


    public int getBackgroundResId() {
        return backgroundResId;
    }

    public int getForegroundColorId() {
        return foregroundColorId;
    }

    public int getTextColorId() {
        return textColorId;
    }

    public String getCode(){
        return this.name();
    }


    public Drawable getBackgroundDrawable(Context context) throws Resources.NotFoundException {

        Drawable backgroundDrawable = ContextCompat.getDrawable(context, backgroundResId);
        if(foregroundColorId != -1) {
            LayerDrawable ld = (LayerDrawable) backgroundDrawable;
            Drawable drawable = ld.getDrawable(1);
            drawable.mutate().setColorFilter(getForegroundColor(context), PorterDuff.Mode.MULTIPLY);
        }
        return backgroundDrawable;
    }

    public int getForegroundColor(Context context) throws Resources.NotFoundException {
        return ContextCompat.getColor(context, foregroundColorId);
    }

    public int getTextColor(Context context) throws Resources.NotFoundException {
        return ContextCompat.getColor(context, textColorId);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CreditCardNotFoundException.java
public class CreditCardNotFoundException extends Exception {

    public static final String DEFAULT_MESSAGE = "Credit Card does not exist";

    public CreditCardNotFoundException(String message) {
        super(message);
    }

    public CreditCardNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CouldNotInsertDataException.java
public class CouldNotInsertDataException extends Exception {

    public static final String DEFAULT_MESSAGE = "There was a problem while inserting data into the database";

    public CouldNotInsertDataException(String message) {
        super(message);
    }

    public CouldNotInsertDataException(String message, Throwable cause) {
        super(message, cause);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/enums/CreditCardType.java
public enum CreditCardType {

    AMEX("American Express"),
    DISCOVER("Discover"),
    MASTERCARD("Master-Card"),
    VISA("Visa");

    private String mFriendlyName;

    CreditCardType(String friendlyName) {
        mFriendlyName = friendlyName;
    }

    public String getCode(){
        return this.name();
    }

    public String getFriendlyName() {
        return mFriendlyName;
    }

    @Override
    public String toString() {
        return mFriendlyName;
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/enums/ExpenseType.java
public enum ExpenseType {

    ORDINARY("Ordinary", "ORD", R.color.expense_category_4),
    EXTRAORDINARY("Extraordinary", "EXT", R.color.expense_category_7);




    private String mFriendlyName;
    private String mShortName;
    private int mColor;

    ExpenseType(String friendlyName, String shortName, int color) {

        mFriendlyName = friendlyName;
        mShortName = shortName;
        mColor = color;
    }


    public String getCode(){
        return this.name();
    }

    public String getFriendlyName() {
        return mFriendlyName;
    }

    public String getShortName() {
        return mShortName;
    }

    public int getColor() {
        return mColor;
    }

    @Override
    public String toString() {
        return mFriendlyName;
    }


}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/model/CreditCard.java
public class CreditCard implements Serializable {

    private int id;
    private String cardAlias;
    private String bankName;
    private String cardNumber;
    private Currency currency;
    private CreditCardType cardType;
    private Calendar cardExpiration;
    private int closingDay;         // Fecha de corte: Fecha de cierre de operaciones del mes, para efectos de registros y cobros.
    private int dueDay;             // Fecha Limite de Pago: Fecha máxima para el próximo pago sin generar incumplimiento e intereses de mora.
    private CreditCardBackground creditCardBackground;


    private Map<Integer, CreditPeriod> creditPeriods;

    public CreditCard(String cardAlias, String bankName, String cardNumber, @NonNull Currency currency, @NonNull CreditCardType cardType, @NonNull Calendar cardExpiration, int closingDay, int dueDay, @NonNull CreditCardBackground creditCardBackground) {
        this.cardAlias = cardAlias;
        this.bankName = bankName;
        this.cardNumber = cardNumber;
        this.currency = currency;
        this.cardType = cardType;

        this.cardExpiration = Calendar.getInstance();
        this.cardExpiration.setTimeZone(cardExpiration.getTimeZone());
        this.cardExpiration.setTimeInMillis(cardExpiration.getTimeInMillis());

        this.dueDay = dueDay;
        this.closingDay = closingDay;
        this.creditCardBackground = creditCardBackground;
    }

    public CreditCard(int id, String cardAlias, String bankName, String cardNumber, @NonNull Currency currency, @NonNull CreditCardType cardType, @NonNull Calendar cardExpiration, int closingDay, int dueDay, @NonNull CreditCardBackground creditCardBackground) {
        this(cardAlias, bankName, cardNumber, currency, cardType, cardExpiration, closingDay, dueDay, creditCardBackground);
        this.id = id;
    }

    public CreditCard(int id, String cardAlias, String bankName, String cardNumber, @NonNull Currency currency, @NonNull CreditCardType cardType, @NonNull Calendar cardExpiration, int closingDay, int dueDay, @NonNull CreditCardBackground creditCardBackground, @NonNull Map<Integer, CreditPeriod> creditPeriods) {
        this(id, cardAlias, bankName, cardNumber, currency, cardType, cardExpiration, closingDay, dueDay, creditCardBackground);
        this.creditPeriods = creditPeriods;
    }



    public int getId() {
        return id;
    }


    public String getCardAlias() {
        return cardAlias;
    }

    public void setCardAlias(String cardAlias) {
        this.cardAlias = cardAlias;
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public Currency getCurrency() {
        return currency;
    }

    public void setCurrency(Currency currency) {
        this.currency = currency;
    }

    public CreditCardType getCardType() {
        return cardType;
    }

    public void setCardType(CreditCardType cardType) {
        this.cardType = cardType;
    }

    public Calendar getCardExpiration() {
        return cardExpiration;
    }

    public void setCardExpiration(Calendar cardExpiration) {
        this.cardExpiration = Calendar.getInstance();
        this.cardExpiration.setTimeZone(cardExpiration.getTimeZone());
        this.cardExpiration.setTimeInMillis(cardExpiration.getTimeInMillis());
    }

    public String getShortCardExpirationString() {
        if(cardExpiration == null)
            return "-/-";

        SimpleDateFormat formatter = new SimpleDateFormat("MM/yy", Locale.getDefault());
        return formatter.format(cardExpiration.getTime());
    }

    public String getLongCardExpirationString() {
        if(cardExpiration == null)
            return "-";

        SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());
        return formatter.format(cardExpiration.getTime());
    }

    public int getClosingDay() {
        return closingDay;
    }

    public void setClosingDay(int closingDay) {
        this.closingDay = closingDay;
    }

    public int getDueDay() {
        return dueDay;
    }

    public void setDueDay(int dueDay) {
        this.dueDay = dueDay;
    }

    public CreditCardBackground getCreditCardBackground() {
        return creditCardBackground;
    }

    public Map<Integer, CreditPeriod> getCreditPeriods() {
        return creditPeriods;
    }

    public void setCreditPeriods(Map<Integer, CreditPeriod> creditPeriods) {
        this.creditPeriods = creditPeriods;
    }

    /**
     * Returns a List of Credit Cards based on the number of existing CreditCardBackgrounds, instantiated with default values
     * @return List<CreditCard>
     */
    public static List<CreditCard> getCreditCardBackgroundTypesList(Context context) {
        List<CreditCard> creditCards = new ArrayList<>();

        String defaultAlias = context.getResources().getString(R.string.activity_add_new_cc_alias);
        String defaultBankName = context.getResources().getString(R.string.activity_add_new_cc_bank);
        String defaultCardNumber = context.getResources().getString(R.string.activity_add_new_cc_number);
        Calendar defaultExpiration = Calendar.getInstance();
        defaultExpiration.add(Calendar.YEAR, 5);

        for (CreditCardBackground c : CreditCardBackground.values()) {
            creditCards.add(new CreditCard(defaultAlias, defaultBankName, defaultCardNumber, Currency.USD, CreditCardType.AMEX, defaultExpiration, 0, 0, c));
        }

        return creditCards;
    }

    @Override
    public String toString() {
        return  "ID=" + id + "\r\n" +
                " cardAlias=" + cardAlias + "\r\n" +
                " cardNumber=" + cardNumber + "\r\n" +
                " currency=" + currency + "\r\n" +
                " cardType=" + cardType + "\r\n" +
                " cardExpiration=" + cardExpiration + "\r\n" +
                " closingDay=" + closingDay + "\r\n" +
                " dueDay=" + dueDay + "\r\n" +
                " creditPeriods=" + creditPeriods;
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/model/Expense.java
public class Expense implements Serializable {

    private int id;
    private String description;
    byte[] thumbnail;
    String fullImagePath;
    BigDecimal amount;
    Currency currency;
    Calendar date;
    ExpenseCategory expenseCategory;
    ExpenseType expenseType;


    public Expense (@NonNull String description, @Nullable byte[] thumbnail, @Nullable String fullImagePath, @NonNull BigDecimal amount, @NonNull Currency currency, @NonNull Calendar date, @NonNull ExpenseCategory expenseCategory, @NonNull ExpenseType expenseType) {
        this.description = description;
        this.fullImagePath = fullImagePath;
        this.amount = amount;
        this.currency = currency;
        this.expenseCategory = expenseCategory;
        this.expenseType = expenseType;

        this.thumbnail = thumbnail;
        if(this.thumbnail != null && this.thumbnail.equals(new byte[0]) )   //if thumbnail is empty byte array -> null
            this.thumbnail = null;

        this.date = Calendar.getInstance();
        this.date.setTimeZone(date.getTimeZone());
        this.date.setTimeInMillis(date.getTimeInMillis());
    }

    public Expense (int id, String description, byte[] thumbnail, String fullImagePath, BigDecimal amount, Currency currency, Calendar date, ExpenseCategory expenseCategory, ExpenseType expenseType) {
        this(description, thumbnail, fullImagePath, amount, currency, date, expenseCategory, expenseType);
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getDescription() {
        return description;
    }

    public byte[] getThumbnail() {
        return thumbnail;
    }

    public String getFullImagePath() {
        return fullImagePath;
    }

    public Bitmap getFullImage() throws FileNotFoundException {
        Bitmap fullImage = BitmapFactory.decodeFile(fullImagePath);

        if(fullImage == null)
            throw new FileNotFoundException("This image does not exist, has probably been deleted");

        return fullImage;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public Currency getCurrency() {
        return currency;
    }

    public Calendar getDate() {
        return date;
    }


    public ExpenseCategory getExpenseCategory() {
        return expenseCategory;
    }

    public ExpenseType getExpenseType() {
        return expenseType;
    }



    @Override
    public String toString() {
        return  "ID=" + id + "\r\n" +
                " description=" + description + "\r\n" +
                " amount=" + amount + "\r\n" +
                " currency=" + currency + "\r\n" +
                " date=" + date + "\r\n" +
                " expenseCategory=" + expenseCategory + "\r\n" +
                " expenseType=" + expenseType;
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CouldNotGetDataException.java
public class CouldNotGetDataException extends Exception {

    public static final String DEFAULT_MESSAGE = "There was a problem while getting the data from the database";

    public CouldNotGetDataException(String message) {
        super(message);
    }

    public CouldNotGetDataException(String message, Throwable cause) {
        super(message, cause);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/enums/ExpenseCategory.java
public enum ExpenseCategory {

    FOOD(0, "Food", R.color.expense_category_1),
    LEISURE(1, "Leisure", R.color.expense_category_2),
    ENTERTAINMENT(2, "Entertainment", R.color.expense_category_3),
    CLOTHING(3, "Clothing", R.color.expense_category_4),
    EDUCATION(4, "Education", R.color.expense_category_5);



    private int mIndex;
    private String mFriendlyName;
    private int mColor;

    ExpenseCategory(int index, String friendlyName, int color) {

        mIndex = index;
        mFriendlyName = friendlyName;
        mColor = color;
    }


    public String getCode(){
        return this.name();
    }

    public int getColor() {
        return mColor;
    }

    public String getFriendlyName() {
        return mFriendlyName;
    }

    @Override
    public String toString() {
        return mFriendlyName;
    }

    public int getIndex() {
       return mIndex;
    }

    public static ExpenseCategory getByExpenseCategoryId(int expenseCategoryId) {

        for(ExpenseCategory expenseCategory : ExpenseCategory.values()) {
            if(expenseCategory.getIndex() == expenseCategoryId)
                return expenseCategory;
        }
        throw new IllegalArgumentException(String.valueOf(expenseCategoryId));
    }

}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CouldNotUpdateDataException.java
public class CouldNotUpdateDataException extends Exception {

    public static final String DEFAULT_MESSAGE = "There was a problem while updating data on the database";

    public CouldNotUpdateDataException(String message) {
        super(message);
    }

    public CouldNotUpdateDataException(String message, Throwable cause) {
        super(message, cause);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/model/Payment.java
public class Payment {

    private int id;
    private String description;
    BigDecimal amount;
    Currency currency;
    Calendar date;


    public Payment (int id, String description, BigDecimal amount, Currency currency, Calendar date) {
        this.id = id;
        this.description = description;
        this.amount = amount;
        this.currency = currency;

        this.date = Calendar.getInstance();
        this.date.setTimeZone(date.getTimeZone());
        this.date.setTimeInMillis(date.getTimeInMillis());
    }

    public int getId() {
        return id;
    }

    public String getDescription() {
        return description;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public Currency getCurrency() {
        return currency;
    }

    public Calendar getDate() {
        return date;
    }


    @Override
    public String toString() {
        return  "ID=" + id + "\r\n" +
                " description=" + description + "\r\n" +
                " amount=" + amount + "\r\n" +
                " currency=" + currency + "\r\n" +
                " date=" + date;
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CouldNotDeleteDataException.java
public class CouldNotDeleteDataException extends Exception {

    public static final String DEFAULT_MESSAGE = "There was a problem while deleting the data from the database";

    public CouldNotDeleteDataException(String message) {
        super(message);
    }

    public CouldNotDeleteDataException(String message, Throwable cause) {
        super(message, cause);
    }
}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/enums/Currency.java
public enum Currency implements Serializable {

    USD("US Dollar"),
    CAD("Canadian Dollar"),
    VEF("Venezuelan Bolivar"),
    EUR("Euro"),
    MEX("Mexican Peso"),
    ARS("Argentine Peso"),
    BRL("Brazilian Real"),
    CLP("Chilean Peso"),
    COL("Colombian Peso"),
    UYU("Uruguayan Peso"),
    PEN("Peruvian Sol"),
    PYG("Paraguayan guarani"),
    ;

    private String mFriendlyName;

    Currency(String friendlyName) {
        mFriendlyName = friendlyName;
    }

    public String getCode(){
        return this.name();
    }

    public String getFriendlyName() {
        return mFriendlyName;
    }


    @Override
    public String toString() {
        return mFriendlyName;
    }

}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/model/CreditPeriod.java
public class CreditPeriod {

    public static final int PERIOD_NAME_LARGEST_MONTH = 0;
    public static final int PERIOD_NAME_COMPLETE = 1;
    public static final int PERIOD_NAME_COMPLETE_NUMERIC = 2;

    private int id = -1;
    private String periodName;
    private int periodNameStyle;
    private Calendar startDate;
    private Calendar endDate;
    private BigDecimal creditLimit;

    private List<Expense> expenses = new ArrayList<>();
    private List<Payment> payments = new ArrayList<>();


    public CreditPeriod(int periodNameStyle, Calendar startDate, Calendar endDate, BigDecimal creditLimit) {
        this.periodNameStyle = periodNameStyle;
        this.creditLimit = creditLimit;

        this.startDate = Calendar.getInstance();
        this.startDate.setTimeZone(startDate.getTimeZone());
        this.startDate.setTimeInMillis(startDate.getTimeInMillis());

        this.endDate = Calendar.getInstance();
        this.endDate.setTimeZone(endDate.getTimeZone());
        this.endDate.setTimeInMillis(endDate.getTimeInMillis());
    }

    public CreditPeriod(int id, int periodNameStyle, Calendar startDate, Calendar endDate, BigDecimal creditLimit) {
        this(periodNameStyle, startDate, endDate, creditLimit);

        this.id = id;
    }

    public CreditPeriod(int id, int periodNameStyle, Calendar startDate, Calendar endDate, BigDecimal creditLimit, List<Expense> expenses, List<Payment> payments) {
        this(id, periodNameStyle, startDate, endDate, creditLimit);

        this.expenses = expenses;
        this.payments = payments;
    }



    public int getId() {
        return id;
    }



    /**
     * returns periodName String according to periodNameStyle, which can be either
     * PERIOD_NAME_LARGEST_MONTH    -> "February", "March"  or
     * PERIOD_NAME_COMPLETE         -> "Feb 4 - Mar 3"      or
     * PERIOD_NAME_COMPLETE_NUMERIC -> "02/04 - 03/03"
     */
    public String getPeriodName() {
        if(periodName == null) {
            StringBuilder sb = new StringBuilder(40);
            switch(periodNameStyle) {
                case PERIOD_NAME_LARGEST_MONTH:
                    if (getStartDate().get(Calendar.DAY_OF_MONTH) <= 15)
                        periodName = getStartDate().getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
                    else
                        periodName = getEndDate().getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
                    break;

                case PERIOD_NAME_COMPLETE:
                    sb.append(getStartDate().getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()));
                    sb.append(" ");
                    sb.append(getStartDate().get(Calendar.DAY_OF_MONTH));
                    sb.append(" - ");
                    sb.append(getEndDate().getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()));
                    sb.append(" ");
                    sb.append(getEndDate().get(Calendar.DAY_OF_MONTH));
                    periodName = sb.toString();
                    break;

                case PERIOD_NAME_COMPLETE_NUMERIC:
                    sb.append(getStartDate().get(Calendar.MONTH) + 1);
                    sb.append(" ");
                    sb.append(getStartDate().get(Calendar.DAY_OF_MONTH));
                    sb.append(" - ");
                    sb.append(getEndDate().get(Calendar.MONTH) + 1);
                    sb.append(" ");
                    sb.append(getEndDate().get(Calendar.DAY_OF_MONTH));
                    periodName = sb.toString();
                    break;

                default:
                    periodName = "ERROR: undefined periodNameStyle";
            }
        }

        return periodName;
    }

    public int getPeriodNameStyle() {
        return periodNameStyle;
    }

    public Calendar getStartDate() {
        return startDate;
    }

    public Calendar getEndDate() {
        return endDate;
    }

    public BigDecimal getCreditLimit() {
        return creditLimit;
    }

    public void setCreditLimit(BigDecimal creditLimit) {
        this.creditLimit = new BigDecimal(creditLimit.toPlainString());
    }

    public List<Expense> getExpenses() {
        return expenses;
    }

    public void setExpenses(List<Expense> expenses) {
        this.expenses = expenses;
    }

    public List<Payment> getPayments() {
        return payments;
    }

    public void setPayments(List<Payment> payments) {
        this.payments = payments;
    }

    /**
     * Returns the sum of all the expenses, minus all the payments for this CreditPeriod
     * Positive value means there are unpaid expenses
     * @return
     */
    public BigDecimal getExpensesTotal() {
        BigDecimal balance = new BigDecimal(0);

        for (Expense e: expenses) {
            balance = balance.add(e.getAmount());
        }

        for (Payment p : payments) {
            balance = balance.subtract(p.getAmount());
        }

        return balance;
    }

    /**
     * Returns the sum of all the expenses, minus all the payments for this CreditPeriod
     * Positive value means there are unpaid expenses
     * @return
     */
    public List<BigDecimal> getExpensesByCategory() {

        int categoryIndex;
        int numCategories = ExpenseCategory.values().length;
        List<BigDecimal> expenseByCategory = new ArrayList<>();

        //Initialize expenseByCategory
        for (int i = 0; i < numCategories; ++i) {
            expenseByCategory.add(new BigDecimal(0));
        }

        //Iterate through expenses, accumulate expenses by category inside expenseByCategory list
        for (Expense e : expenses) {
            categoryIndex = e.getExpenseCategory().getIndex();
            BigDecimal currentVal = expenseByCategory.get(categoryIndex);
            expenseByCategory.set(categoryIndex, currentVal.add(e.amount));
        }

        return expenseByCategory;
    }


    /**
     * Returns creditLimit - expensesTotal = the available credit
     */
    public BigDecimal getAvailableCredit() {
        return getCreditLimit().subtract(getExpensesTotal());
    }


    /**
     * Returns the days in this period, startDate - endDate
     */
    public int getTotalDaysInPeriod() {

        // Create copies so we don't update the original calendars.
        Calendar start = Calendar.getInstance();
        start.setTimeZone(startDate.getTimeZone());
        start.setTimeInMillis(startDate.getTimeInMillis());

        Calendar end = Calendar.getInstance();
        end.setTimeZone(endDate.getTimeZone());
        end.setTimeInMillis(endDate.getTimeInMillis());

        // Add one millisecond so that the subtraction below works.
        // If end = Aug 5 23:59:59 we add one millisecond
        // so end = Aug 6 00:00:00 so that Aug 5 counts
        end.add(Calendar.MILLISECOND, 1);


        // At this point, each calendar is set to midnight on
        // their respective days. Now use TimeUnit.MILLISECONDS to
        // compute the number of full days between the two of them.
        int days = (int)TimeUnit.MILLISECONDS.toDays(Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
        return days;
    }

    public List<DailyExpense> getDailyExpenses() {
        List<DailyExpense> dailyExpenses = new ArrayList<>();

        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(startDate.getTimeZone());
        cal.setTimeInMillis(startDate.getTimeInMillis());

        for (int i = 0; i < getTotalDaysInPeriod(); i++) {
            dailyExpenses.add(i, new DailyExpense(cal, new BigDecimal(0)));

            cal.add(Calendar.DAY_OF_MONTH, 1);
        }

        for (Expense expense: expenses) {
            int index = getDateIndex(expense.getDate());
            dailyExpenses.get(index).addToAmount(expense.getAmount());
        }

        return dailyExpenses;
    }


    public List<DailyExpense> getAccumulatedDailyExpenses() {
        List<DailyExpense> dailyAccumulatedExpenses = getDailyExpenses();
        BigDecimal accumulatedExpenses = new BigDecimal(0);

        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(startDate.getTimeZone());
        cal.setTimeInMillis(startDate.getTimeInMillis());

        for (int i = 0; i < getTotalDaysInPeriod(); i++) {

            accumulatedExpenses = accumulatedExpenses.add(dailyAccumulatedExpenses.get(i).getAmount());
            dailyAccumulatedExpenses.remove(i);
            dailyAccumulatedExpenses.add(i, new DailyExpense(cal, accumulatedExpenses));

            cal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return dailyAccumulatedExpenses;
    }


    private Calendar getStartOfDay(Calendar date) {

        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(date.getTimeZone());
        cal.setTimeInMillis(date.getTimeInMillis());

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal;
    }

    public int getDateIndex(Calendar dateToBeIndexed) {

        // Create copies so we don't update the original calendars.
        Calendar start = Calendar.getInstance();
        start.setTimeZone(startDate.getTimeZone());
        start.setTimeInMillis(startDate.getTimeInMillis());

        Calendar end = Calendar.getInstance();
        end.setTimeZone(dateToBeIndexed.getTimeZone());
        end.setTimeInMillis(dateToBeIndexed.getTimeInMillis());

        // Set the copies to be at midnight, but keep the day information.
        start.set(Calendar.HOUR_OF_DAY, 0);
        start.set(Calendar.MINUTE, 0);
        start.set(Calendar.SECOND, 0);
        start.set(Calendar.MILLISECOND, 0);
        end.set(Calendar.HOUR_OF_DAY, 0);
        end.set(Calendar.MINUTE, 0);
        end.set(Calendar.SECOND, 0);
        end.set(Calendar.MILLISECOND, 0);

        // At this point, each calendar is set to midnight on
        // their respective days. Now use TimeUnit.MILLISECONDS to
        // compute the number of full days between the two of them.


        int days = (int) TimeUnit.MILLISECONDS.toDays(Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
        return days;
    }

    @Override
    public String toString() {
        return  "ID=" + id + "\r\n" +
                " periodName=" + periodName + "\r\n" +
                " periodNameStyle=" + periodNameStyle + "\r\n" +
                " startDate=" + startDate.getTimeInMillis() + "\r\n" +
                " endDate=" + endDate.getTimeInMillis() + "\r\n" +
                " creditLimit=" + creditLimit;
    }

}
app/src/main/java/ve/com/abicelis/creditcardexpensemanager/exceptions/CreditPeriodNotFoundException.java
public class CreditPeriodNotFoundException extends Exception {

    public static final String DEFAULT_MESSAGE = "Credit Period does not exist";

    public CreditPeriodNotFoundException(String message) {
        super(message);
    }

    public CreditPeriodNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}


Question: package ve.com.abicelis.creditcardexpensemanager.database;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.math.BigDecimal;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ve.com.abicelis.creditcardexpensemanager.enums.CreditCardBackground;
import ve.com.abicelis.creditcardexpensemanager.enums.CreditCardType;
import ve.com.abicelis.creditcardexpensemanager.enums.Currency;
import ve.com.abicelis.creditcardexpensemanager.enums.ExpenseCategory;
import ve.com.abicelis.creditcardexpensemanager.enums.ExpenseType;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CouldNotDeleteDataException;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CouldNotUpdateDataException;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CreditCardNotFoundException;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CreditPeriodNotFoundException;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CouldNotGetDataException;
import ve.com.abicelis.creditcardexpensemanager.exceptions.CouldNotInsertDataException;
import ve.com.abicelis.creditcardexpensemanager.model.CreditCard;
import ve.com.abicelis.creditcardexpensemanager.model.CreditPeriod;
import ve.com.abicelis.creditcardexpensemanager.model.Expense;
import ve.com.abicelis.creditcardexpensemanager.model.Payment;




/**
 * Created by Alex on 8/8/2016.
 */
public class ExpenseManagerDAO {

    private ExpenseManagerDbHelper mDatabaseHelper;

    public ExpenseManagerDAO(Context context) {
        mDatabaseHelper = new ExpenseManagerDbHelper(context);

    }

    /**
     * Returns a List of stored Credit Cards.
     * Note: The Credit Cards will not contain CreditPeriods, Expenses or Payments.
     */
    public List<CreditCard> getCreditCardList() {

        List<CreditCard> creditCards = new ArrayList<>();
        SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();

        Cursor cursor = db.query(ExpenseManagerContract.CreditCardTable.TABLE_NAME, null, null, null, null, null, null);

        try {
            while (cursor.moveToNext()) {
                creditCards.add(getCreditCardFromCursor(cursor));
            }
        } finally {
            cursor.close();
        }

        return creditCards;
    }

    /**
     * Returns a List of CreditPeriods associated with ccardId.
     * Note: The periods will not contain Expenses or Payments.
     * @param cardId The ID of the Credit Card
     */
    public List<CreditPeriod> getCreditPeriodListFromCard(int cardId) {
        List<CreditPeriod> creditPeriods = new ArrayList<>();
        SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();

        Cursor cursor =  db.query(ExpenseManagerContract.CreditPeriodTable.TABLE_NAME, null,
                ExpenseManagerContract.CreditPeriodTable.COLUMN_NAME_FOREIGN_KEY_CREDIT_CARD.getName() + " == " + cardId, null, null, null,
                ExpenseManagerContract.CreditPeriodTable.COLUMN_NAME_START_DATE.getName() + " DESC");

        try {
            while (cursor.moveToNext()) {
                creditPeriods.add(getCreditPeriodFromCursor(cursor));
            }
        } finally {
            cursor.close();
        }

        return creditPeriods;
    }



    /**
     * Finds a CreditCard with the supplied cardId, will not return creditPeriods associated with
     * the card
     * @param cardId The ID of the Credit Card
     */
    public CreditCard getCreditCard(int cardId) throws CreditCardNotFoundException {
        String[] selectionArgs = new String[]{String.valueOf(cardId)};
        SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();

        Cursor cursor =  db.query(ExpenseManagerContract.CreditCardTable.TABLE_NAME, null,
                ExpenseManagerContract.CreditCardTable._ID + " =? ", selectionArgs, null, null, null);

        if(cursor.getCount() == 0)
            throw new CreditCardNotFoundException("Specified Credit Card not found in the database. Passed value=" + cardId);
        if(cursor.getCount() > 1)
            throw new CreditCardNotFoundException("Database UNIQUE constraint failure, more than one record found. Passed value=" + cardId);

        cursor.moveToNext();
        return getCreditCardFromCursor(cursor);
    }

    /**
     * Returns a Credit Card with the supplied cardId and the specified periodIndex.
     * Note: PeriodIndex must be either zero (returns credit period contaning current date) or a
     * negative value (-1 returns previous credit period, and so on)
     * @param cardId The ID of the Credit Card
     * @param periodIndex An integer <= 0
     */
    public CreditCard getCreditCardWithCreditPeriod(int cardId, int periodIndex) throws CreditCardNotFoundException, CreditPeriodNotFoundException, InvalidParameterException{
        if(periodIndex > 0)
            throw new InvalidParameterException("PeriodIndex must be >=0. Passed value=" + periodIndex);

        CreditCard creditCard = getCreditCard(cardId);

        //Get ordered (DESC) list of credit periods
        List<CreditPeriod> creditPeriods = getCreditPeriodListFromCard(cardId);

        //Find the creditPeriod according to periodIndex
        CreditPeriod cp = getCreditPeriodFromPeriodIndex(creditPeriods, periodIndex, creditCard.getClosingDay());

        //Create Map and set it on the Credit Card
        Map<Integer, CreditPeriod> creditPeriodMap = new HashMap<>();
        creditPeriodMap.put(periodIndex, cp);
        creditCard.setCreditPeriods(creditPeriodMap);

        return creditCard;
    }


    /**
     * Returns a Credit Card with the supplied cardId and as many credit periods as numPeriods
     * indicates. Counting backwards starting on periodIndex.
     * Note: PeriodIndex must be either zero (current creditPriod) or a
     * negative value (-1 refers to the previous credit period, and so on)
     * Note2: Passing periodIndex=-1, numPeriods=3 will return three periods (Period -1, -2 and -3)
     * @param cardId The ID of the Credit Card
     * @param periodIndex An integer <= 0
     * @param numPeriods An integer >= 1
     */
    public CreditCard getCreditCardWithCreditPeriodRange(int cardId, int periodIndex, int numPeriods) throws CreditCardNotFoundException, CreditPeriodNotFoundException, InvalidParameterException {
        if (periodIndex > 0)
            throw new InvalidParameterException("PeriodIndex must be less or equal than 0. Passed value=" + periodIndex);
        if (numPeriods <= 0)
            throw new InvalidParameterException("NumPeriods must be more or equal than 1. Passed value=" + periodIndex);

        CreditCard creditCard = getCreditCard(cardId);

        //Get ordered (DESC) list of credit periods
        List<CreditPeriod> creditPeriods = getCreditPeriodListFromCard(cardId);

        Map<Integer, CreditPeriod> creditPeriodMap = new HashMap<>();

        //Find the creditPeriod according to periodIndex
        for (int i = periodIndex; i > (periodIndex-numPeriods); --i) {
            CreditPeriod cp = getCreditPeriodFromPeriodIndex(creditPeriods, i, creditCard.getClosingDay());
            creditPeriodMap.put(i, cp);
        }

        //Set creditPeriods in creditCard and return
        creditCard.setCreditPeriods(creditPeriodMap);
        return creditCard;
    }


    /**
     * Returns a Credit Period from a List of creditPeriods which complies with the supplied
     * periodIndex and closingDay.
     * Note: PeriodIndex must be either zero (returns credit period contaning current date) or a
     * negative value (-1 returns previous credit period, and so on)
     * Note2: closingDay must be between 1 and 28.
     * @param creditPeriods List of Credit Periods to search into
     * @param periodIndex An integer <= 0
     * @param closingDay An integer between 1 and 28
     */
    private CreditPeriod getCreditPeriodFromPeriodIndex(List<CreditPeriod> creditPeriods, int periodIndex, int closingDay) throws CreditPeriodNotFoundException, InvalidParameterException {
        if(closingDay < 1 || closingDay > 28)
            throw new InvalidParameterException("ClosingDay must be between 1 and 28. Passed value=" + closingDay);
        if(periodIndex > 0)
            throw new InvalidParameterException("PeriodIndex must be >=0. Passed value=" + periodIndex);
        if(creditPeriods == null || creditPeriods.size() == 0)
            throw new InvalidParameterException("creditPeriods is null or empty");

        //Get a day which will be between the starting and ending dates of the wanted creditPeriod
        Calendar day = Calendar.getInstance();
        if(periodIndex != 0)
            day.add(Calendar.MONTH, periodIndex);

        for (CreditPeriod cp : creditPeriods) {
            if(cp.getStartDate().compareTo(day) <=0 && cp.getEndDate().compareTo(day) >=0) {
                cp.setExpenses(getExpensesFromCreditPeriod(cp.getId()));
                cp.setPayments(getPaymentsFromCreditPeriod(cp.getId()));
                return cp;

            }
        }

        throw new CreditPeriodNotFoundException("Credit period (index " + periodIndex + ") not found in passed creditPeriods List");
    }

    /**
     * Returns a Credit Period with the supplied periodId, containing its Expenses and Payments
     * @param periodId The ID of the Credit Period
     */
    public CreditPeriod getCreditPeriodFromPeriodId(int periodId) throws CreditPeriodNotFoundException {
        SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
        CreditPeriod creditPeriod;

        Cursor cursor =  db.query(ExpenseManagerContract.CreditPeriodTable.TABLE_NAME, null, ExpenseManagerContract.CreditPeriodTable._ID + " == " + periodId, null, null, null, null);

        if(cursor.getCount() == 0)
            throw new CreditPeriodNotFoundException("Specified Credit Period not found in the database. Passed value=" + periodId);
        if(cursor.getCount() > 1)
            throw new CreditPeriodNotFoundException("Database UNIQUE constraint failure, more than one record found. Passed Value=" + periodId);

        try{
            cursor.moveToNext();
            creditPeriod = getCreditPeriodFromCursor(cursor);
            creditPeriod.setExpenses(getExpensesFromCreditPeriod(periodId));
            creditPeriod.setPayments(getPaymentsFromCreditPeriod(periodId));
        } finally {
            cursor.close();
        }

        return creditPeriod;
    }


    /**
     * Returns a List of Expenses, given a creditPeriodId
     * @param periodId the Id of the Credit period
     */
Answer:     public List<Expense> getExpensesFromCreditPeriod(int periodId) {