/*
* Copyright (C) 2012-2016 The Android Money Manager Ex Project Team
*
* This program 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 3
* of the License, or (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.money.manager.ex.recurring.transactions;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.codetroopers.betterpickers.calendardatepicker.CalendarDatePickerDialogFragment;
import com.money.manager.ex.Constants;
import com.money.manager.ex.MoneyManagerApplication;
import com.money.manager.ex.R;
import com.money.manager.ex.common.MmxBaseFragmentActivity;
import com.money.manager.ex.common.events.AmountEnteredEvent;
import com.money.manager.ex.core.MenuHelper;
import com.money.manager.ex.core.NumericHelper;
import com.money.manager.ex.core.UIHelper;
import com.money.manager.ex.database.ISplitTransaction;
import com.money.manager.ex.datalayer.SplitRecurringCategoriesRepository;
import com.money.manager.ex.domainmodel.RecurringTransaction;
import com.money.manager.ex.domainmodel.SplitRecurringCategory;
import com.money.manager.ex.servicelayer.RecurringTransactionService;
import com.money.manager.ex.datalayer.AccountRepository;
import com.money.manager.ex.datalayer.RecurringTransactionRepository;
import com.money.manager.ex.transactions.EditTransactionCommonFunctions;
import com.money.manager.ex.core.Core;
import com.money.manager.ex.core.TransactionTypes;
import com.money.manager.ex.transactions.events.DialogNegativeClickedEvent;
import com.money.manager.ex.transactions.events.DialogPositiveClickedEvent;
import com.money.manager.ex.utils.MmxDate;
import com.money.manager.ex.utils.MmxDateTimeUtils;
import com.shamanland.fonticon.FontIconView;
import com.squareup.sqlbrite.BriteDatabase;
import org.greenrobot.eventbus.Subscribe;
import org.parceler.Parcels;
import java.util.Date;
import javax.inject.Inject;
import icepick.State;
import timber.log.Timber;
/**
* Recurring transactions are stored in BillsDeposits table.
*/
public class RecurringTransactionEditActivity
extends MmxBaseFragmentActivity {
public static final String KEY_MODEL = "RecurringTransactionEditActivity:Model";
public static final String KEY_BILL_DEPOSITS_ID = "RepeatingTransaction:BillDepositsId";
public static final String KEY_ACCOUNT_ID = "RepeatingTransaction:AccountId";
public static final String KEY_TO_ACCOUNT_NAME = "RepeatingTransaction:ToAccountName";
public static final String KEY_TRANS_CODE = "RepeatingTransaction:TransCode";
public static final String KEY_TRANS_STATUS = "RepeatingTransaction:TransStatus";
public static final String KEY_PAYEE_NAME = "RepeatingTransaction:PayeeName";
public static final String KEY_CATEGORY_NAME = "RepeatingTransaction:CategoryName";
public static final String KEY_SUBCATEGORY_NAME = "RepeatingTransaction:SubCategoryName";
public static final String KEY_NOTES = "RepeatingTransaction:Notes";
public static final String KEY_TRANS_NUMBER = "RepeatingTransaction:TransNumber";
public static final String KEY_SPLIT_TRANSACTION = "RepeatingTransaction:SplitCategory";
public static final String KEY_SPLIT_TRANSACTION_DELETED = "RepeatingTransaction:SplitTransactionDeleted";
public static final String TAG_DATEPICKER = "DatePicker";
@Inject BriteDatabase database;
@Inject MmxDateTimeUtils dateUtils;
@State String mIntentAction;
private RecurringTransactionViewHolder mViewHolder;
private EditTransactionCommonFunctions mCommon;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_recurring_transaction);
MoneyManagerApplication.getApp().iocComponent.inject(this);
RecurringTransaction tx = initializeModel();
mCommon = new EditTransactionCommonFunctions(this, tx, database);
mCommon.initializeToolbar();
// manage update instance
if (savedInstanceState != null) {
restoreInstanceState(savedInstanceState);
}
// manage intent
if (getIntent() != null) {
if (savedInstanceState == null) {
String action = getIntent().getAction();
if (action != null && action.equals(Intent.ACTION_EDIT)) {
int id = getIntent().getIntExtra(KEY_BILL_DEPOSITS_ID, Constants.NOT_SET);
// select data transaction
loadRecurringTransaction(id);
} else {
mCommon.transactionEntity.setAccountId(getIntent().getIntExtra(KEY_ACCOUNT_ID, Constants.NOT_SET));
}
}
mIntentAction = getIntent().getAction();
// set title
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(Intent.ACTION_INSERT.equals(mIntentAction)
? R.string.new_repeating_transaction : R.string.edit_repeating_transaction);
}
}
// Controls
initializeViewHolder();
initializeControls();
// refresh user interface
mCommon.onTransactionTypeChanged(mCommon.transactionEntity.getTransactionType());
mCommon.showPayeeName();
mCommon.displayCategoryName();
showPaymentsLeft();
mCommon.setDirty(false);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mCommon.onActivityResult(requestCode, resultCode, data);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuHelper helper = new MenuHelper(this, menu);
helper.addSaveToolbarIcon();
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return onActionCancelClick();
case MenuHelper.save:
return onActionDoneClick();
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_MODEL, Parcels.wrap(mCommon.transactionEntity));
// update the state interface
outState.putString(KEY_TO_ACCOUNT_NAME, mCommon.mToAccountName);
outState.putString(KEY_TRANS_CODE, mCommon.getTransactionType());
outState.putString(KEY_TRANS_STATUS, mCommon.transactionEntity.getStatus());
outState.putString(KEY_PAYEE_NAME, mCommon.payeeName);
outState.putString(KEY_CATEGORY_NAME, mCommon.categoryName);
outState.putString(KEY_SUBCATEGORY_NAME, mCommon.subCategoryName);
outState.putString(KEY_TRANS_NUMBER, mCommon.viewHolder.edtTransNumber.getText().toString());
outState.putParcelable(KEY_SPLIT_TRANSACTION, Parcels.wrap(mCommon.mSplitTransactions));
outState.putParcelable(KEY_SPLIT_TRANSACTION_DELETED, Parcels.wrap(mCommon.mSplitTransactionsDeleted));
outState.putString(KEY_NOTES, String.valueOf(mCommon.viewHolder.edtNotes.getTag()));
}
@Override
public boolean onActionCancelClick() {
return mCommon.onActionCancelClick();
}
@Override
public void onBackPressed() {
onActionCancelClick();
}
@Override
public boolean onActionDoneClick() {
if (saveData()) {
// set result ok, send broadcast to update widgets and finish activity
setResult(RESULT_OK);
finish();
}
return super.onActionDoneClick();
}
// Events
@Subscribe
public void onEvent(AmountEnteredEvent event) {
int id = Integer.parseInt(event.requestId);
mCommon.onFinishedInputAmountDialog(id, event.amount);
}
@Subscribe
public void onEvent(DialogPositiveClickedEvent event) {
mCommon.confirmDeletingCategories();
}
@Subscribe
public void onEvent(DialogNegativeClickedEvent event) {
mCommon.cancelChangingTransactionToTransfer();
}
// Public
/**
* refresh the UI control Payments Left
*/
public void showPaymentsLeft() {
Recurrence recurrence = getRecurringTransaction().getRecurrence();
// Recurrence label
mViewHolder.recurrenceLabel.setText((recurrence == Recurrence.IN_X_DAYS) || (recurrence == Recurrence.IN_X_MONTHS)
? R.string.activates : R.string.occurs);
// Payments Left header
mViewHolder.paymentsLeftTextView.setVisibility(recurrence.getValue() > 0 ? View.VISIBLE : View.GONE);
mViewHolder.paymentsLeftTextView.setText(recurrence.getValue() >= 11 ? R.string.activates : R.string.payments_left);
// Payments Left input
mViewHolder.paymentsLeftEditText.setVisibility(recurrence.getValue() > 0 ? View.VISIBLE : View.GONE);
mViewHolder.paymentsLeftEditText.setHint(recurrence.getValue() >= 11 ? R.string.activates : R.string.payments_left);
Integer occurrences = getRecurringTransaction().getPaymentsLeft();
if (occurrences == null) {
occurrences = Constants.NOT_SET;
getRecurringTransaction().setPaymentsLeft(Constants.NOT_SET);
}
String value = occurrences == Constants.NOT_SET
? "∞"
: Integer.toString(occurrences);
mViewHolder.paymentsLeftEditText.setText(value);
// if (mRecurringTransaction.getPaymentsLeft() != null && mRecurringTransaction.getPaymentsLeft() >= 0) {
// mViewHolder.paymentsLeftEditText.setText(Integer.toString(mRecurringTransaction.getPaymentsLeft()));
// }
}
// Private
private RecurringTransaction getRecurringTransaction() {
return (RecurringTransaction) mCommon.transactionEntity;
}
private void initializeControls() {
// Payment Date
initializePaymentDateSelector();
// Account(s)
mCommon.initAccountSelectors();
// Transaction type
mCommon.initTransactionTypeSelector();
// status
mCommon.initStatusSelector();
// Payee
mCommon.initPayeeControls();
// Category
mCommon.initCategoryControls(SplitRecurringCategory.class.getSimpleName());
// Split Categories
mCommon.initSplitCategories();
// mark checked if there are existing split categories.
boolean hasSplit = mCommon.hasSplitCategories();
mCommon.setSplit(hasSplit);
// Amount and total amount
mCommon.initAmountSelectors();
// transaction number
mCommon.initTransactionNumberControls();
// notes
mCommon.initNotesControls();
// Frequency
Spinner spinFrequencies = (Spinner) findViewById(R.id.spinnerFrequencies);
RecurringTransaction tx = (RecurringTransaction) mCommon.transactionEntity;
Integer recurrence = tx.getRecurrenceInt();
if (recurrence >= 200) {
recurrence = recurrence - 200;
} // set auto execute without user acknowledgement
if (recurrence >= 100) {
recurrence = recurrence - 100;
} // set auto execute on the next occurrence
spinFrequencies.setSelection(recurrence, true);
spinFrequencies.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mCommon.setDirty(true);
getRecurringTransaction().setRecurrence(position);
showPaymentsLeft();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
getRecurringTransaction().setRecurrence(Constants.NOT_SET);
showPaymentsLeft();
}
});
}
private void initializePaymentDateSelector() {
if (mViewHolder.paymentDateTextView == null) return;
final MmxDateTimeUtils dateUtils = new MmxDateTimeUtils();
Date paymentDate = getRecurringTransaction().getPaymentDate();
mViewHolder.paymentDateTextView.setText(dateUtils.format(paymentDate, Constants.LONG_DATE_PATTERN));
// mViewHolder.paymentDateTextView.setTag(paymentDate.toString(Constants.ISO_DATE_FORMAT));
mViewHolder.paymentDateTextView.setOnClickListener(new View.OnClickListener() {
CalendarDatePickerDialogFragment.OnDateSetListener listener = new CalendarDatePickerDialogFragment.OnDateSetListener() {
@Override
public void onDateSet(CalendarDatePickerDialogFragment dialog, int year, int monthOfYear, int dayOfMonth) {
Date dateTime = dateUtils.from(year, monthOfYear, dayOfMonth);
setPaymentDate(dateTime);
}
};
@Override
public void onClick(View v) {
// Show calendar with the current date selected.
//Date dateTime = getPaymentDate();
MmxDate dateTime = new MmxDate(getPaymentDate());
CalendarDatePickerDialogFragment datePicker = new CalendarDatePickerDialogFragment()
.setFirstDayOfWeek(dateUtils.getFirstDayOfWeek())
.setOnDateSetListener(listener)
.setPreselectedDate(dateTime.getYear(), dateTime.getMonth() - 1, dateTime.getDayOfMonth());
if (new UIHelper(RecurringTransactionEditActivity.this).isUsingDarkTheme()) {
datePicker.setThemeDark();
}
datePicker.show(getSupportFragmentManager(), TAG_DATEPICKER);
}
});
mViewHolder.paymentPreviousDayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Date dateTime = new MmxDate(getPaymentDate()).minusDays(1).toDate();
setPaymentDate(dateTime);
}
});
mViewHolder.paymentNextDayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Date dateTime = new MmxDate(getPaymentDate()).plusDays(1).toDate();
setPaymentDate(dateTime);
}
});
}
private RecurringTransaction initializeModel() {
RecurringTransaction tx = RecurringTransaction.createInstance();
Date today = new MmxDate().toDate();
tx.setDueDate(today);
tx.setPaymentDate(today);
return tx;
}
private void initializeViewHolder() {
// Controls need to be at the beginning as they are referenced throughout the code.
mCommon.findControls(this);
mViewHolder = new RecurringTransactionViewHolder();
// Due Date = date
mCommon.initDateSelector();
// Payment Date, next occurrence
mViewHolder.paymentDateTextView = (TextView) findViewById(R.id.paymentDateTextView);
// Previous/Next day adjustment buttons for the Payment Day
mViewHolder.paymentPreviousDayButton = (FontIconView) findViewById(R.id.paymentPreviousDayButton);
mViewHolder.paymentNextDayButton = (FontIconView) findViewById(R.id.paymentNextDayButton);
// Recurrence label
mViewHolder.recurrenceLabel = (TextView) findViewById(R.id.recurrenceLabel);
// Payments Left label
mViewHolder.paymentsLeftTextView = (TextView) findViewById(R.id.textViewTimesRepeated);
// Payments Left text input
mViewHolder.paymentsLeftEditText = (EditText) findViewById(R.id.editTextTimesRepeated);
}
/**
* this method allows you to search the transaction data
* @param recurringTransactionId transaction id
* @return true if data selected, false nothing
*/
private boolean loadRecurringTransaction(int recurringTransactionId) {
RecurringTransactionRepository repo = new RecurringTransactionRepository(this);
mCommon.transactionEntity = repo.load(recurringTransactionId);
if (mCommon.transactionEntity == null) return false;
// Read data.
String transCode = getRecurringTransaction().getTransactionCode();
mCommon.transactionEntity.setTransactionType(TransactionTypes.valueOf(transCode));
// load split transactions only if no category selected.
if (!mCommon.transactionEntity.hasCategory() && mCommon.mSplitTransactions == null) {
RecurringTransactionService recurringTransaction = new RecurringTransactionService(recurringTransactionId, this);
mCommon.mSplitTransactions = recurringTransaction.loadSplitTransactions();
}
AccountRepository accountRepository = new AccountRepository(this);
mCommon.mToAccountName = accountRepository.loadName(mCommon.transactionEntity.getAccountToId());
mCommon.loadPayeeName(mCommon.transactionEntity.getPayeeId());
mCommon.loadCategoryName();
return true;
}
/**
* validate data insert in activity
*
* @return validation result
*/
private boolean validateData() {
if (!mCommon.validateData()) return false;
Core core = new Core(this);
// Due Date is required
if (TextUtils.isEmpty(getRecurringTransaction().getDueDateString())) {
core.alert(R.string.due_date_required);
return false;
}
if (TextUtils.isEmpty(mCommon.viewHolder.dateTextView.getText().toString())) {
core.alert(R.string.error_next_occurrence_not_populate);
return false;
}
// Payments Left must have a value
if (getRecurringTransaction().getPaymentsLeft() == null) {
core.alert(R.string.payments_left_required);
return false;
}
return true;
}
private void collectDataFromUI() {
String value;
// Payment Date
// DateTime dateTime = MmxJodaDateTimeUtils.from(mViewHolder.paymentDateTextView.getTag().toString());
// mRecurringTransaction.setPaymentDate(dateTime);
// Payments Left
value = mViewHolder.paymentsLeftEditText.getText().toString();
if (NumericHelper.isNumeric(value)) {
int paymentsLeft = NumericHelper.toInt(value);
getRecurringTransaction().setPaymentsLeft(paymentsLeft);
} else {
getRecurringTransaction().setPaymentsLeft(Constants.NOT_SET);
}
}
/**
* update data into database
*
* @return true if update data successful
*/
private boolean saveData() {
// get data from input controls
collectDataFromUI();
if (!validateData()) return false;
boolean isTransfer = mCommon.transactionEntity.getTransactionType().equals(TransactionTypes.Transfer);
if (!isTransfer) {
mCommon.resetTransfer();
}
// Transaction. Need the id for split categories.
if (!saveTransaction()) return false;
// Split Categories
if (mCommon.convertOneSplitIntoRegularTransaction()) {
saveTransaction();
}
if(!mCommon.isSplitSelected()) {
// Delete any split categories if split is unchecked.
mCommon.removeAllSplitCategories();
}
if (!saveSplitCategories()) return false;
return true;
}
private void restoreInstanceState(Bundle savedInstanceState) {
// Restore the transaction entity.
mCommon.transactionEntity = Parcels.unwrap(savedInstanceState.getParcelable(KEY_MODEL));
mCommon.mToAccountName = savedInstanceState.getString(KEY_TO_ACCOUNT_NAME);
mCommon.payeeName = savedInstanceState.getString(KEY_PAYEE_NAME);
mCommon.categoryName = savedInstanceState.getString(KEY_CATEGORY_NAME);
mCommon.subCategoryName = savedInstanceState.getString(KEY_SUBCATEGORY_NAME);
mCommon.mSplitTransactions = Parcels.unwrap(savedInstanceState.getParcelable(KEY_SPLIT_TRANSACTION));
mCommon.mSplitTransactionsDeleted = Parcels.unwrap(savedInstanceState.getParcelable(KEY_SPLIT_TRANSACTION_DELETED));
// action
// mIntentAction = savedInstanceState.getString(KEY_ACTION);
}
private boolean saveSplitCategories() {
SplitRecurringCategoriesRepository splitRepo = new SplitRecurringCategoriesRepository(this);
// deleted old split transaction
if (mCommon.getDeletedSplitCategories().size() > 0) {
if (!mCommon.deleteMarkedSplits(splitRepo)) return false;
}
// has split transaction
boolean hasSplitCategories = mCommon.hasSplitCategories();
if (hasSplitCategories) {
for (ISplitTransaction item : mCommon.mSplitTransactions) {
SplitRecurringCategory splitEntity = (SplitRecurringCategory) item;
splitEntity.setTransId(mCommon.transactionEntity.getId());
if (splitEntity.getId() == null || splitEntity.getId() == Constants.NOT_SET) {
// insert data
if (!splitRepo.insert(splitEntity)) {
Toast.makeText(getApplicationContext(), R.string.db_checking_insert_failed, Toast.LENGTH_SHORT).show();
Timber.w("Insert new split transaction failed!");
return false;
}
} else {
// update data
if (!splitRepo.update(splitEntity)) {
Toast.makeText(getApplicationContext(), R.string.db_checking_update_failed, Toast.LENGTH_SHORT).show();
Timber.w("Update split transaction failed!");
return false;
}
}
}
}
return true;
}
private boolean saveTransaction() {
RecurringTransactionRepository repo = new RecurringTransactionRepository(this);
if (!mCommon.transactionEntity.hasId()) {
// insert
mCommon.transactionEntity = repo.insert((RecurringTransaction) mCommon.transactionEntity);
if (mCommon.transactionEntity.getId() == Constants.NOT_SET) {
new Core(this).alert(R.string.db_checking_insert_failed);
Timber.w("Insert new repeating transaction failed!");
return false;
}
} else {
// update
if (!repo.update((RecurringTransaction) mCommon.transactionEntity)) {
new Core(this).alert(R.string.db_checking_update_failed);
Timber.w("Update repeating transaction failed!");
return false;
}
}
return true;
}
private Date getPaymentDate() {
Date dateTime = getRecurringTransaction().getPaymentDate();
if (dateTime == null) {
dateTime = dateUtils.now();
getRecurringTransaction().setPaymentDate(dateTime);
}
return dateTime;
}
private void setPaymentDate(Date dateTime) {
mCommon.setDirty(true);
getRecurringTransaction().setPaymentDate(dateTime);
String display = dateUtils.format(dateTime, Constants.LONG_DATE_PATTERN);
mViewHolder.paymentDateTextView.setText(display);
}
}