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 9fa8f0de8..e792fc223 100644 --- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java +++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java @@ -41,6 +41,7 @@ import org.gnucash.android.model.ScheduledAction; import org.gnucash.android.model.Transaction; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -168,6 +169,8 @@ private static int executeBackup(ScheduledAction scheduledAction, SQLiteDatabase return 0; ExportParams params = ExportParams.parseCsv(scheduledAction.getTag()); + // HACK: the tag isn't updated with the new date, so set the correct by hand + params.setExportStartTime(new Timestamp(scheduledAction.getLastRunTime())); try { //wait for async task to finish before we proceed (we are holding a wake lock) new ExportAsyncTask(GnuCashApplication.getAppContext(), db).execute(params).get(); 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 83f57c61a..ac1db752e 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 @@ -15,12 +15,13 @@ */ package org.gnucash.android.test.unit.service; +import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; import org.gnucash.android.BuildConfig; import org.gnucash.android.R; import org.gnucash.android.app.GnuCashApplication; +import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; @@ -39,10 +40,12 @@ import org.gnucash.android.model.ScheduledAction; import org.gnucash.android.model.Split; import org.gnucash.android.model.Transaction; +import org.gnucash.android.model.TransactionType; import org.gnucash.android.service.ScheduledActionService; import org.gnucash.android.test.unit.testutil.GnucashTestRunner; import org.gnucash.android.test.unit.testutil.ShadowCrashlytics; import org.gnucash.android.test.unit.testutil.ShadowUserVoice; +import org.gnucash.android.util.TimestampHelper; import org.joda.time.DateTime; import org.joda.time.LocalDateTime; import org.joda.time.Weeks; @@ -57,6 +60,7 @@ import java.io.File; import java.io.IOException; import java.math.BigDecimal; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -79,6 +83,7 @@ public class ScheduledActionServiceTest { private static Account mTransferAccount = new Account("Transfer Account"); private static Transaction mTemplateTransaction; + private TransactionsDbAdapter mTransactionsDbAdapter; public void createAccounts(){ try { @@ -117,9 +122,8 @@ public void setUp(){ accountsDbAdapter.addRecord(mBaseAccount); accountsDbAdapter.addRecord(mTransferAccount); - TransactionsDbAdapter transactionsDbAdapter = TransactionsDbAdapter.getInstance(); - transactionsDbAdapter.addRecord(mTemplateTransaction, DatabaseAdapter.UpdateMethod.insert); - + mTransactionsDbAdapter = TransactionsDbAdapter.getInstance(); + mTransactionsDbAdapter.addRecord(mTemplateTransaction, DatabaseAdapter.UpdateMethod.insert); } @Test @@ -363,6 +367,103 @@ public void scheduledBackups_shouldNotRunBeforeNextScheduledExecution(){ assertThat(backupFolder.listFiles()).hasSize(0); } + /** + * Tests that an scheduled backup doesn't include transactions added or modified + * previous to the last run. + */ + @Test + public void scheduledBackups_shouldNotIncludeTransactionsPreviousToTheLastRun() { + ScheduledAction scheduledBackup = new ScheduledAction(ScheduledAction.ActionType.BACKUP); + scheduledBackup.setStartTime(LocalDateTime.now().minusDays(15).toDate().getTime()); + scheduledBackup.setLastRun(LocalDateTime.now().minusDays(8).toDate().getTime()); + long previousLastRun = scheduledBackup.getLastRunTime(); + scheduledBackup.setExecutionCount(1); + scheduledBackup.setRecurrence(PeriodType.WEEK, 1); + ExportParams backupParams = new ExportParams(ExportFormat.QIF); + backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD); + backupParams.setExportStartTime(new Timestamp(scheduledBackup.getStartTime())); + scheduledBackup.setTag(backupParams.toCsv()); + + // Create a transaction with a modified date previous to the last run + Transaction transaction = new Transaction("Tandoori express"); + Split split = new Split(new Money("10", Commodity.DEFAULT_COMMODITY.getCurrencyCode()), + mBaseAccount.getUID()); + split.setType(TransactionType.DEBIT); + transaction.addSplit(split); + transaction.addSplit(split.createPair(mTransferAccount.getUID())); + mTransactionsDbAdapter.addRecord(transaction); + // We set the date directly in the database as the corresponding field + // is ignored when the object is stored. It's set through a trigger instead. + setTransactionInDbModifiedTimestamp(transaction.getUID(), + new Timestamp(LocalDateTime.now().minusDays(9).toDate().getTime())); + + File backupFolder = new File( + Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID())); + assertThat(backupFolder).exists(); + assertThat(backupFolder.listFiles()).isEmpty(); + + List actions = new ArrayList<>(); + actions.add(scheduledBackup); + ScheduledActionService.processScheduledActions(actions, mDb); + + assertThat(scheduledBackup.getExecutionCount()).isEqualTo(2); + assertThat(scheduledBackup.getLastRunTime()).isGreaterThan(previousLastRun); + assertThat(backupFolder.listFiles()).hasSize(0); + } + + /** + * Sets the transaction modified timestamp directly in the database. + * + * @param transactionUID UID of the transaction to set the modified timestamp. + * @param timestamp new modified timestamp. + */ + private void setTransactionInDbModifiedTimestamp(String transactionUID, Timestamp timestamp) { + ContentValues values = new ContentValues(); + values.put(DatabaseSchema.TransactionEntry.COLUMN_MODIFIED_AT, + TimestampHelper.getUtcStringFromTimestamp(timestamp)); + mTransactionsDbAdapter.updateTransaction(values, "uid = ?", + new String[]{transactionUID}); + } + + /** + * Tests that an scheduled backup includes transactions added or modified + * after the last run. + */ + @Test + public void scheduledBackups_shouldIncludeTransactionsAfterTheLastRun() { + ScheduledAction scheduledBackup = new ScheduledAction(ScheduledAction.ActionType.BACKUP); + scheduledBackup.setStartTime(LocalDateTime.now().minusDays(15).toDate().getTime()); + scheduledBackup.setLastRun(LocalDateTime.now().minusDays(8).toDate().getTime()); + long previousLastRun = scheduledBackup.getLastRunTime(); + scheduledBackup.setExecutionCount(1); + scheduledBackup.setRecurrence(PeriodType.WEEK, 1); + ExportParams backupParams = new ExportParams(ExportFormat.QIF); + backupParams.setExportTarget(ExportParams.ExportTarget.SD_CARD); + backupParams.setExportStartTime(new Timestamp(scheduledBackup.getStartTime())); + scheduledBackup.setTag(backupParams.toCsv()); + + Transaction transaction = new Transaction("Orient palace"); + Split split = new Split(new Money("10", Commodity.DEFAULT_COMMODITY.getCurrencyCode()), + mBaseAccount.getUID()); + split.setType(TransactionType.DEBIT); + transaction.addSplit(split); + transaction.addSplit(split.createPair(mTransferAccount.getUID())); + mTransactionsDbAdapter.addRecord(transaction); + + File backupFolder = new File( + Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID())); + assertThat(backupFolder).exists(); + assertThat(backupFolder.listFiles()).isEmpty(); + + List actions = new ArrayList<>(); + actions.add(scheduledBackup); + ScheduledActionService.processScheduledActions(actions, mDb); + + assertThat(scheduledBackup.getExecutionCount()).isEqualTo(2); + assertThat(scheduledBackup.getLastRunTime()).isGreaterThan(previousLastRun); + assertThat(backupFolder.listFiles()).hasSize(1); + } + @After public void tearDown(){ TransactionsDbAdapter.getInstance().deleteAllRecords();