accountUIDList, long startTimestamp, long endTimestamp) {
String currencyCode = GnuCashApplication.getDefaultCurrencyCode();
Money balance = Money.createZeroInstance(currencyCode);
+
+ if (accountUIDList.isEmpty())
+ return balance;
+
boolean hasDebitNormalBalance = getAccountType(accountUIDList.get(0)).hasDebitNormalBalance();
SplitsDbAdapter splitsDbAdapter = SplitsDbAdapter.getInstance();
diff --git a/app/src/main/java/org/gnucash/android/db/DatabaseAdapter.java b/app/src/main/java/org/gnucash/android/db/DatabaseAdapter.java
index 5281404ff..2e810b569 100644
--- a/app/src/main/java/org/gnucash/android/db/DatabaseAdapter.java
+++ b/app/src/main/java/org/gnucash/android/db/DatabaseAdapter.java
@@ -423,7 +423,7 @@ public String getUID(long id){
if (cursor.moveToFirst()) {
uid = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.CommonColumns.COLUMN_UID));
} else {
- throw new IllegalArgumentException("Account record ID " + id + " does not exist in the db");
+ throw new IllegalArgumentException(mTableName + " Record ID " + id + " does not exist in the db");
}
} finally {
cursor.close();
@@ -620,6 +620,20 @@ public void setTransactionSuccessful() {
mDb.setTransactionSuccessful();
}
+ /// Foreign key constraits should be enabled in general.
+ /// But if it affects speed (check constraints takes time)
+ /// and the constrained can be assured by the program,
+ /// or if some SQL exec will cause deletion of records
+ /// (like use replace in accounts update will delete all transactions)
+ /// that need not be deleted, then it can be disabled temporarily
+ public void enableForeignKey(boolean enable) {
+ if (enable){
+ mDb.execSQL("PRAGMA foreign_keys=ON");
+ } else {
+ mDb.execSQL("PRAGMA foreign_keys=OFF");
+ }
+ }
+
/**
* Expose mDb.endTransaction()
*/
diff --git a/app/src/main/java/org/gnucash/android/db/TransactionsDbAdapter.java b/app/src/main/java/org/gnucash/android/db/TransactionsDbAdapter.java
index b522ea7f7..0f9b4bfa6 100644
--- a/app/src/main/java/org/gnucash/android/db/TransactionsDbAdapter.java
+++ b/app/src/main/java/org/gnucash/android/db/TransactionsDbAdapter.java
@@ -37,6 +37,7 @@
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
@@ -633,6 +634,26 @@ public long getTimestampOfLatestTransaction(AccountType type, String currencyCod
return getTimestamp("MAX", type, currencyCode);
}
+ /**
+ * Returns the most recent `modified_at` timestamp of non-template transactions in the database
+ * @return Last moodified time in milliseconds or current time if there is none in the database
+ */
+ public Timestamp getTimestampOfLastModification(){
+ Cursor cursor = mDb.query(TransactionEntry.TABLE_NAME,
+ new String[]{"MAX(" + TransactionEntry.COLUMN_MODIFIED_AT + ")"},
+ null, null, null, null, null);
+
+ Timestamp timestamp = new Timestamp(System.currentTimeMillis());
+ if (cursor.moveToFirst()){
+ String timeString = cursor.getString(0);
+ if (timeString != null){ //in case there were no transactions in the XML file (account structure only)
+ timestamp = Timestamp.valueOf(timeString);
+ }
+ }
+ cursor.close();
+ return timestamp;
+ }
+
/**
* Returns the earliest or latest timestamp of transactions for a specific account type and currency
* @param mod Mode (either MAX or MIN)
diff --git a/app/src/main/java/org/gnucash/android/export/Exporter.java b/app/src/main/java/org/gnucash/android/export/Exporter.java
index 9727f1013..7e2878a8d 100644
--- a/app/src/main/java/org/gnucash/android/export/Exporter.java
+++ b/app/src/main/java/org/gnucash/android/export/Exporter.java
@@ -201,11 +201,11 @@ public String getExportMimeType(){
public static class ExporterException extends RuntimeException{
public ExporterException(ExportParams params){
- super("Failed to generate " + params.getExportFormat().toString());
+ super("Failed to generate export with parameters: " + params.toString());
}
public ExporterException(@NonNull ExportParams params, @NonNull String msg) {
- super("Failed to generate " + params.getExportFormat().toString() + "-" + msg);
+ super("Failed to generate export with parameters: " + params.toString() + " - " + msg);
}
public ExporterException(ExportParams params, Throwable throwable){
diff --git a/app/src/main/java/org/gnucash/android/importer/GncXmlImporter.java b/app/src/main/java/org/gnucash/android/importer/GncXmlImporter.java
index 43ea7c614..eb9eea9a4 100644
--- a/app/src/main/java/org/gnucash/android/importer/GncXmlImporter.java
+++ b/app/src/main/java/org/gnucash/android/importer/GncXmlImporter.java
@@ -21,6 +21,7 @@
import android.util.Log;
import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.TransactionsDbAdapter;
import org.gnucash.android.export.Exporter;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -93,8 +94,11 @@ public static void parse(InputStream gncXmlInputStream) throws ParserConfigurati
xr.parse(new InputSource(bos));
long endTime = System.nanoTime();
- String timeStamp = new Timestamp(System.currentTimeMillis()).toString();
- PreferenceManager.getDefaultSharedPreferences(GnuCashApplication.getAppContext()).edit().putString(Exporter.PREF_LAST_EXPORT_TIME, timeStamp).apply();
+ Timestamp timeStamp = TransactionsDbAdapter.getInstance().getTimestampOfLastModification();
+ PreferenceManager.getDefaultSharedPreferences(GnuCashApplication.getAppContext())
+ .edit()
+ .putString(Exporter.PREF_LAST_EXPORT_TIME, timeStamp.toString())
+ .apply();
Log.d(GncXmlImporter.class.getSimpleName(), String.format("%d ns spent on importing the file", endTime-startTime));
}
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java
index b83b0288f..0a62283f6 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java
@@ -846,7 +846,9 @@ private void saveAccount() {
if (mAccountsDbAdapter == null)
mAccountsDbAdapter = AccountsDbAdapter.getInstance();
// bulk update, will not update transactions
+ mAccountsDbAdapter.enableForeignKey(false);
mAccountsDbAdapter.bulkAddRecords(accountsToUpdate);
+ mAccountsDbAdapter.enableForeignKey(true);
finishFragment();
}
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
index 494d817ba..34e9e3c8f 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
@@ -525,10 +525,11 @@ public static void startXmlFileChooser(Activity activity) {
* This method is usually called in response to {@link AccountsActivity#startXmlFileChooser(Activity)}
* @param context Activity context
* @param data Intent data containing the XML uri
+ * @param onFinishTask Task to be executed when import is complete
*/
- public static void importXmlFileFromIntent(Activity context, Intent data) {
+ public static void importXmlFileFromIntent(Activity context, Intent data, TaskDelegate onFinishTask) {
GncXmlExporter.createBackup();
- new ImportAsyncTask(context).execute(data.getData());
+ new ImportAsyncTask(context, onFinishTask).execute(data.getData());
}
/**
diff --git a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java
index 6d12529f2..c0b3460bd 100644
--- a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java
@@ -193,7 +193,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case AccountsActivity.REQUEST_PICK_ACCOUNTS_FILE:
- AccountsActivity.importXmlFileFromIntent(this, data);
+ AccountsActivity.importXmlFileFromIntent(this, data, null);
break;
}
}
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/SettingsActivity.java b/app/src/main/java/org/gnucash/android/ui/settings/SettingsActivity.java
index 250a799c0..6cd389c43 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/SettingsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/SettingsActivity.java
@@ -578,7 +578,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case AccountsActivity.REQUEST_PICK_ACCOUNTS_FILE:
if (resultCode == Activity.RESULT_OK && data != null) {
- AccountsActivity.importXmlFileFromIntent(this, data);
+ AccountsActivity.importXmlFileFromIntent(this, data, null);
}
break;
case GeneralPreferenceFragment.PASSCODE_REQUEST_CODE:
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
index 2358ddbf1..5d60d8b32 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
@@ -95,7 +95,7 @@ public class ScheduledActionsListFragment extends ListFragment implements
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.transactions_context_menu, menu);
+ inflater.inflate(R.menu.schedxactions_context_menu, menu);
return true;
}
@@ -115,16 +115,24 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.context_menu_delete:
for (long id : getListView().getCheckedItemIds()) {
- Log.i(TAG, "Cancelling scheduled transaction(s)");
- String trnUID = mTransactionsDbAdapter.getUID(id);
- ScheduledActionDbAdapter scheduledActionDbAdapter = GnuCashApplication.getScheduledEventDbAdapter();
- List actions = scheduledActionDbAdapter.getScheduledActionsWithUID(trnUID);
-
- if (mTransactionsDbAdapter.deleteRecord(id)){
- Toast.makeText(getActivity(), R.string.toast_recurring_transaction_deleted, Toast.LENGTH_SHORT).show();
- for (ScheduledAction action : actions) {
- scheduledActionDbAdapter.deleteRecord(action.getUID());
+
+ if (mActionType == ScheduledAction.ActionType.TRANSACTION) {
+ Log.i(TAG, "Cancelling scheduled transaction(s)");
+ String trnUID = mTransactionsDbAdapter.getUID(id);
+ ScheduledActionDbAdapter scheduledActionDbAdapter = GnuCashApplication.getScheduledEventDbAdapter();
+ List actions = scheduledActionDbAdapter.getScheduledActionsWithUID(trnUID);
+
+ if (mTransactionsDbAdapter.deleteRecord(id)) {
+ Toast.makeText(getActivity(),
+ R.string.toast_recurring_transaction_deleted,
+ Toast.LENGTH_SHORT).show();
+ for (ScheduledAction action : actions) {
+ scheduledActionDbAdapter.deleteRecord(action.getUID());
+ }
}
+ } else if (mActionType == ScheduledAction.ActionType.BACKUP){
+ Log.i(TAG, "Removing scheduled exports");
+ ScheduledActionDbAdapter.getInstance().deleteRecord(id);
}
}
mode.finish();
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java
index 49042df76..48db0b815 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java
@@ -785,8 +785,8 @@ private void saveNewTransaction() {
mTransaction.addSplit(split);
String transferAcctUID;
- if (mUseDoubleEntry) {
- long transferAcctId = mTransferAccountSpinner.getSelectedItemId();
+ long transferAcctId = mTransferAccountSpinner.getSelectedItemId();
+ if (mUseDoubleEntry || transferAcctId < 0) {
transferAcctUID = mAccountsDbAdapter.getUID(transferAcctId);
} else {
transferAcctUID = mAccountsDbAdapter.getOrCreateImbalanceAccountUID(currency);
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java
index 350f0a503..b2beebf76 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java
@@ -24,6 +24,7 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
@@ -32,6 +33,7 @@
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
@@ -60,8 +62,12 @@
import org.gnucash.android.ui.util.OnTransactionClickedListener;
import org.gnucash.android.ui.util.Refreshable;
import org.gnucash.android.util.QualifiedAccountNameCursorAdapter;
+import org.joda.time.LocalDate;
import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import butterknife.Bind;
import butterknife.ButterKnife;
@@ -92,6 +98,7 @@ public class TransactionsActivity extends BaseDrawerActivity implements
* Number of pages to show
*/
private static final int DEFAULT_NUM_PAGES = 2;
+ private static SimpleDateFormat mDayMonthDateFormat = new SimpleDateFormat("EEE, d MMM");
/**
* GUID of {@link Account} whose transactions are displayed
@@ -153,7 +160,6 @@ public void onNothingSelected(AdapterView> parent) {
private PagerAdapter mPagerAdapter;
-
/**
* Adapter for managing the sub-account and transaction fragment pages in the accounts view
*/
@@ -486,6 +492,29 @@ public static void displayBalance(TextView balanceTextView, Money balance){
balanceTextView.setTextColor(fontColor);
}
+ /**
+ * Formats the date to show the the day of the week if the {@code dateMillis} is within 7 days
+ * of today. Else it shows the actual date formatted as short string.
+ * It also shows "today", "yesterday" or "tomorrow" if the date is on any of those days
+ * @param dateMillis
+ * @return
+ */
+ @NonNull
+ public static String getPrettyDateFormat(Context context, long dateMillis) {
+ LocalDate transactionTime = new LocalDate(dateMillis);
+ LocalDate today = new LocalDate();
+ String prettyDateText = null;
+ if (transactionTime.compareTo(today.minusDays(1)) >= 0 && transactionTime.compareTo(today.plusDays(1)) <= 0){
+ prettyDateText = DateUtils.getRelativeTimeSpanString(dateMillis, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS).toString();
+ } else if (transactionTime.getYear() == today.getYear()){
+ prettyDateText = mDayMonthDateFormat.format(new Date(dateMillis));
+ } else {
+ prettyDateText = DateUtils.formatDateTime(context, dateMillis, DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_YEAR);
+ }
+
+ return prettyDateText;
+ }
+
@Override
public void createNewTransaction(String accountUID) {
Intent createTransactionIntent = new Intent(this.getApplicationContext(), FormActivity.class);
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
index e59155ea3..9b0a8c416 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
@@ -21,6 +21,7 @@
import android.content.res.Configuration;
import android.database.Cursor;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
@@ -42,6 +43,7 @@
import android.widget.TextView;
import org.gnucash.android.R;
+import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.AccountsDbAdapter;
import org.gnucash.android.db.DatabaseCursorLoader;
import org.gnucash.android.db.DatabaseSchema;
@@ -57,8 +59,10 @@
import org.gnucash.android.ui.util.CursorRecyclerAdapter;
import org.gnucash.android.ui.util.Refreshable;
import org.gnucash.android.ui.util.widget.EmptyRecyclerView;
+import org.joda.time.LocalDate;
import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@@ -196,7 +200,6 @@ public void onLoaderReset(Loader loader) {
mTransactionRecyclerAdapter.swapCursor(null);
}
-
/**
* {@link DatabaseCursorLoader} for loading transactions asynchronously from the database
* @author Ngewi Fet
@@ -221,8 +224,6 @@ public Cursor loadInBackground() {
public class TransactionRecyclerAdapter extends CursorRecyclerAdapter{
- DateFormat simpleDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
-
public TransactionRecyclerAdapter(Cursor cursor) {
super(cursor);
}
@@ -264,7 +265,8 @@ public void onBindViewHolderCursor(ViewHolder holder, Cursor cursor) {
holder.transactionNote.setText(text);
long dateMillis = cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseSchema.TransactionEntry.COLUMN_TIMESTAMP));
- String dateText = DateUtils.getRelativeTimeSpanString(dateMillis, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS).toString();
+ String dateText = TransactionsActivity.getPrettyDateFormat(getActivity(), dateMillis);
+
holder.transactionDate.setText(dateText);
final long id = holder.transactionId;
@@ -288,7 +290,6 @@ public void onClick(View v) {
}
-
public class ViewHolder extends RecyclerView.ViewHolder implements PopupMenu.OnMenuItemClickListener{
@Bind(R.id.primary_text) public TextView transactionDescription;
@Bind(R.id.secondary_text) public TextView transactionNote;
diff --git a/app/src/main/java/org/gnucash/android/ui/wizard/FirstRunWizardActivity.java b/app/src/main/java/org/gnucash/android/ui/wizard/FirstRunWizardActivity.java
index 850d80329..717328493 100644
--- a/app/src/main/java/org/gnucash/android/ui/wizard/FirstRunWizardActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/wizard/FirstRunWizardActivity.java
@@ -52,6 +52,7 @@
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.importer.ImportAsyncTask;
import org.gnucash.android.ui.account.AccountsActivity;
+import org.gnucash.android.ui.util.TaskDelegate;
import java.io.FileNotFoundException;
import java.io.InputStream;
@@ -212,6 +213,8 @@ private void createAccountsAndFinish() {
finish();
} else if (mAccountOptions.equals(getString(R.string.wizard_option_import_my_accounts))){
AccountsActivity.startXmlFileChooser(this);
+ } else {
+ finish();
}
}
@@ -252,9 +255,13 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case AccountsActivity.REQUEST_PICK_ACCOUNTS_FILE:
if (resultCode == Activity.RESULT_OK && data != null) {
- AccountsActivity.importXmlFileFromIntent(this, data);
+ AccountsActivity.importXmlFileFromIntent(this, data, new TaskDelegate() {
+ @Override
+ public void onTaskComplete() {
+ finish();
+ }
+ });
}
- finish();
break;
}
}
diff --git a/app/src/main/res/menu/schedxactions_context_menu.xml b/app/src/main/res/menu/schedxactions_context_menu.xml
new file mode 100644
index 000000000..74106be52
--- /dev/null
+++ b/app/src/main/res/menu/schedxactions_context_menu.xml
@@ -0,0 +1,26 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/transactions_context_menu.xml b/app/src/main/res/menu/transactions_context_menu.xml
index cd4f77178..14cb5e34e 100644
--- a/app/src/main/res/menu/transactions_context_menu.xml
+++ b/app/src/main/res/menu/transactions_context_menu.xml
@@ -1,6 +1,6 @@
- By default, only new transactions since last export will be exported. Check this option to export all transactions