exportedFiles = splitQIF(file);
- if (exportedFiles.isEmpty())
+ if (exportedFiles.isEmpty()) {
return Collections.emptyList();
- else if (exportedFiles.size() > 1)
+ } else {
+ // always zip exported file(s)
return zipQifs(exportedFiles);
- else
- return exportedFiles;
+ }
} catch (IOException e) {
throw new ExporterException(mExportParams, e);
}
}
+ /**
+ * Returns the path to the file where the exporter should save the export during generation.
+ * This path is a temporary cache file. For the QIF export, the file extension differs from
+ * the final file name, because multiple QIF files may be written during generation,
+ * but the export result is always a ZIP archive.
+ * This file is deleted every time a new export is started
+ * @return Absolute path to file
+ */
+ @Override
+ protected String getExportCacheFilePath(){
+
+ return getCachePath() + buildExportFileBaseName(mExportParams.getExportFormat(), getBookName()) + ".qif";
+ }
+
@NonNull
private List zipQifs(List exportedFiles) throws IOException {
String zipFileName = getExportCacheFilePath() + ".zip";
@@ -280,7 +297,7 @@ private List zipQifs(List exportedFiles) throws IOException {
*/
private List splitQIF(File file) throws IOException {
// split only at the last dot
- String[] pathParts = file.getPath().split("(?=\\.[^\\.]+$)");
+ String[] pathParts = file.getPath().split("(?=\\.[^.]+$)");
ArrayList splitFiles = new ArrayList<>();
String line;
BufferedReader in = new BufferedReader(new FileReader(file));
diff --git a/app/src/main/java/org/gnucash/android/repository/TransactionRepository.java b/app/src/main/java/org/gnucash/android/repository/TransactionRepository.java
new file mode 100644
index 000000000..878f39a9b
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/repository/TransactionRepository.java
@@ -0,0 +1,41 @@
+package org.gnucash.android.repository;
+
+import android.database.sqlite.SQLiteDatabase;
+
+import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.adapter.AccountsDbAdapter;
+import org.gnucash.android.db.adapter.DatabaseAdapter;
+import org.gnucash.android.db.adapter.SplitsDbAdapter;
+import org.gnucash.android.db.adapter.TransactionsDbAdapter;
+import org.gnucash.android.model.Transaction;
+
+import java.util.List;
+
+public class TransactionRepository {
+
+ final private SQLiteDatabase db;
+
+ public TransactionRepository(SQLiteDatabase db) {
+ this.db = db;
+ }
+
+ /**
+ * Backups of the database, saves opening balances (if necessary)
+ * and deletes all non-template transactions in the database.
+ */
+ public void deleteTransactions(){
+ final List openingBalances;
+ final boolean preserveOpeningBalances = GnuCashApplication.shouldSaveOpeningBalances(false);
+
+ final TransactionsDbAdapter transactionsDbAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db));
+ if (preserveOpeningBalances) {
+ openingBalances = new AccountsDbAdapter(db, transactionsDbAdapter).getAllOpeningBalanceTransactions();
+
+ transactionsDbAdapter.deleteAllNonTemplateTransactions();
+
+ transactionsDbAdapter.bulkAddRecords(openingBalances, DatabaseAdapter.UpdateMethod.insert);
+ } else {
+ transactionsDbAdapter.deleteAllNonTemplateTransactions();
+ }
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index b1a3f7a11..42ae44a25 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -38,15 +38,19 @@
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
import org.gnucash.android.export.ExportAsyncTask;
import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.Exporter;
+import org.gnucash.android.export.ExporterFactory;
import org.gnucash.android.model.Book;
import org.gnucash.android.model.ScheduledAction;
import org.gnucash.android.model.Transaction;
+import org.gnucash.android.repository.TransactionRepository;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.Lock;
/**
* Service for running scheduled events.
@@ -88,9 +92,16 @@ protected void onHandleWork(@NonNull Intent intent) {
scheduledActions.size(), book.getDisplayName()));
processScheduledActions(scheduledActions, db);
+ final Lock readLock = GnuCashApplication.dbLock.readLock();
+ readLock.lock();
+ final SQLiteDatabase activeDb = GnuCashApplication.getActiveDb();
+
//close all databases except the currently active database
- if (!db.getPath().equals(GnuCashApplication.getActiveDb().getPath()))
+ if (!db.getPath().equals(activeDb.getPath())) {
db.close();
+ }
+
+ readLock.unlock();
}
Log.i(LOG_TAG, "Completed service @ " + java.text.DateFormat.getDateTimeInstance().format(new Date()));
@@ -173,8 +184,9 @@ private static int executeBackup(ScheduledAction scheduledAction, SQLiteDatabase
params.setExportStartTime(new Timestamp(scheduledAction.getLastRunTime()));
Boolean result = false;
try {
+ final Exporter exporter = ExporterFactory.getInstance().getExporter(params, db);
//wait for async task to finish before we proceed (we are holding a wake lock)
- result = new ExportAsyncTask(GnuCashApplication.getAppContext(), db).execute(params).get();
+ result = new ExportAsyncTask(GnuCashApplication.getAppContext(), exporter, new TransactionRepository(db)).execute(params).get();
} catch (InterruptedException | ExecutionException e) {
Crashlytics.logException(e);
Log.e(LOG_TAG, e.getMessage());
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index 500c37128..4ca398a75 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
@@ -62,8 +63,10 @@
import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
+import org.gnucash.android.export.ExporterFactory;
import org.gnucash.android.model.BaseModel;
import org.gnucash.android.model.ScheduledAction;
+import org.gnucash.android.repository.TransactionRepository;
import org.gnucash.android.ui.common.UxArgument;
import org.gnucash.android.ui.settings.BackupPreferenceFragment;
import org.gnucash.android.ui.settings.dialog.OwnCloudDialogFragment;
@@ -78,6 +81,7 @@
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.concurrent.locks.Lock;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -324,7 +328,14 @@ private void startExport(){
exportParameters.setCsvSeparator(mExportCsvSeparator);
Log.i(TAG, "Commencing async export of transactions");
- new ExportAsyncTask(getActivity(), GnuCashApplication.getActiveDb()).execute(exportParameters);
+ final Lock readLock = GnuCashApplication.dbLock.readLock();
+ readLock.lock();
+ final SQLiteDatabase db = GnuCashApplication.getActiveDb();
+
+ final Exporter exporter = ExporterFactory.getInstance().getExporter(exportParameters, db);
+ new ExportAsyncTask(getActivity(), exporter, new TransactionRepository(db)).execute(exportParameters);
+
+ readLock.unlock();
if (mRecurrenceRule != null) {
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
index 977b06b57..2e6277b19 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
@@ -21,6 +21,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -40,13 +41,16 @@
import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
+import org.gnucash.android.export.ExporterFactory;
import org.gnucash.android.model.Money;
+import org.gnucash.android.repository.TransactionRepository;
import org.gnucash.android.ui.account.AccountsActivity;
import org.gnucash.android.ui.settings.dialog.DeleteAllAccountsConfirmationDialog;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.Lock;
/**
* Account settings fragment inside the Settings activity
@@ -207,7 +211,13 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
ExportParams exportParams = new ExportParams(ExportFormat.CSVA);
exportParams.setExportTarget(ExportParams.ExportTarget.URI);
exportParams.setExportLocation(data.getData().toString());
- ExportAsyncTask exportTask = new ExportAsyncTask(getActivity(), GnuCashApplication.getActiveDb());
+
+ final Lock readLock = GnuCashApplication.dbLock.readLock();
+ readLock.lock();
+ final SQLiteDatabase db = GnuCashApplication.getActiveDb();
+
+ final Exporter exporter = ExporterFactory.getInstance().getExporter(exportParams, db);
+ final ExportAsyncTask exportTask = new ExportAsyncTask(getActivity(), exporter, new TransactionRepository(db));
try {
exportTask.execute(exportParams).get();
@@ -215,6 +225,8 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
Crashlytics.logException(e);
Toast.makeText(getActivity(), "An error occurred during the Accounts CSV export",
Toast.LENGTH_LONG).show();
+ } finally {
+ readLock.unlock();
}
}
}
diff --git a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
index fd555a7f1..25d270914 100644
--- a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
+++ b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
@@ -30,6 +30,7 @@
import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
+import org.gnucash.android.export.ExporterFactory;
import org.gnucash.android.importer.GncXmlImporter;
import org.gnucash.android.model.Account;
import org.gnucash.android.model.Commodity;
@@ -318,7 +319,9 @@ public void scheduledBackups_shouldRunOnlyOnce(){
backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD);
scheduledBackup.setTag(backupParams.toCsv());
- File backupFolder = new File(Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID()));
+ final Exporter dummyExporter = ExporterFactory.getInstance().getExporter(backupParams, mDb);
+
+ File backupFolder = new File(dummyExporter.getExportFolderPath());
assertThat(backupFolder).exists();
assertThat(backupFolder.listFiles()).isEmpty();
@@ -366,8 +369,9 @@ public void scheduledBackups_shouldNotRunBeforeNextScheduledExecution(){
backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD);
scheduledBackup.setTag(backupParams.toCsv());
- File backupFolder = new File(
- Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID()));
+ final Exporter dummyExporter = ExporterFactory.getInstance().getExporter(backupParams, mDb);
+
+ File backupFolder = new File(dummyExporter.getExportFolderPath());
assertThat(backupFolder).exists();
assertThat(backupFolder.listFiles()).isEmpty();
@@ -413,8 +417,9 @@ public void scheduledBackups_shouldNotIncludeTransactionsPreviousToTheLastRun()
setTransactionInDbModifiedTimestamp(transaction.getUID(),
new Timestamp(LocalDateTime.now().minusDays(9).toDate().getTime()));
- File backupFolder = new File(
- Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID()));
+ final Exporter dummyExporter = ExporterFactory.getInstance().getExporter(backupParams, mDb);
+
+ File backupFolder = new File(dummyExporter.getExportFolderPath());
assertThat(backupFolder).exists();
assertThat(backupFolder.listFiles()).isEmpty();
@@ -469,8 +474,9 @@ public void scheduledBackups_shouldIncludeTransactionsAfterTheLastRun() {
transaction.addSplit(split.createPair(mTransferAccount.getUID()));
mTransactionsDbAdapter.addRecord(transaction);
- File backupFolder = new File(
- Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID()));
+ final Exporter dummyExporter = ExporterFactory.getInstance().getExporter(backupParams, mDb);
+
+ File backupFolder = new File(dummyExporter.getExportFolderPath());
assertThat(backupFolder).exists();
assertThat(backupFolder.listFiles()).isEmpty();