Skip to content

Commit 450fd35

Browse files
committed
Merge branch 'develop' into hotfix/patches
2 parents 52f7297 + f3f2977 commit 450fd35

11 files changed

Lines changed: 480 additions & 34 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Accounts | Transactions | Reports
1111
:-------------------------:|:-------------------------:|:-------------------------:
1212
![Accounts List](docs/images/v2.0.0_home.png) | ![Transactions List](docs/images/v2.0.0_transactions_list.png) | ![Reports](docs/images/v2.0.0_reports.png)
1313

14-
The application supports Android 4.4 KitKat (API level 10) and above.
14+
The application supports Android 4.4 KitKat (API level 19) and above.
1515

1616
Features include:
1717

app/src/main/java/org/gnucash/android/db/adapter/DatabaseAdapter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,11 @@ private void createTempView() {
160160
// ( CASE WHEN IFNULL ( splits_memo , '' ) == '' THEN 'a' ELSE 'b' END ) || accounts_uid
161161
// ) ,
162162
// 2
163-
// ) as trans_acct_a_uid ,
164-
// TOTAL ( CASE WHEN splits_type = 'DEBIT' THEN splits_amount ELSE - splits_amount END ) AS trans_acct_balance,
165-
// COUNT ( DISTINCT accounts_currency ) as trans_currency_count
163+
// ) AS trans_acct_a_uid ,
164+
// TOTAL ( CASE WHEN splits_type = 'DEBIT' THEN splits_value_num
165+
// ELSE - splits_value_num END ) * 1.0 / splits_value_denom AS trans_acct_balance ,
166+
// COUNT ( DISTINCT accounts_currency_code ) AS trans_currency_count ,
167+
// COUNT (*) AS trans_split_count
166168
// FROM trans_split_acct GROUP BY transactions_uid
167169
//
168170
// This temporary view would pick one Account_UID for each

app/src/main/java/org/gnucash/android/export/qif/QifExporter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
import java.io.OutputStreamWriter;
3939
import java.math.BigDecimal;
4040
import java.util.ArrayList;
41-
import java.util.Currency;
4241
import java.util.List;
42+
import java.util.Locale;
4343

4444
import static org.gnucash.android.db.DatabaseSchema.AccountEntry;
4545
import static org.gnucash.android.db.DatabaseSchema.SplitEntry;
@@ -210,9 +210,10 @@ public List<String> generateExport() throws ExporterException {
210210
if (quantity_denom != 0) {
211211
quantity = quantity_num / quantity_denom;
212212
}
213+
final Locale noLocale = null;
213214
writer.append(QifHelper.SPLIT_AMOUNT_PREFIX)
214215
.append(splitType.equals("DEBIT") ? "-" : "")
215-
.append(String.format("%." + precision + "f", quantity))
216+
.append(String.format(noLocale, "%." + precision + "f", quantity))
216217
.append(newLine);
217218
}
218219
if (!currentTransactionUID.equals("")) {

app/src/main/java/org/gnucash/android/importer/GncXmlHandler.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
import java.sql.Timestamp;
6161
import java.text.ParseException;
6262
import java.util.ArrayList;
63+
import java.util.Calendar;
64+
import java.util.Collections;
65+
import java.util.Date;
6366
import java.util.HashMap;
6467
import java.util.List;
6568
import java.util.Map;
@@ -791,6 +794,10 @@ public void endElement(String uri, String localName, String qualifiedName) throw
791794

792795
case GncXmlHelper.TAG_SCHEDULED_ACTION:
793796
if (mScheduledAction.getActionUID() != null && !mIgnoreScheduledAction) {
797+
if (mScheduledAction.getRecurrence().getPeriodType() == PeriodType.WEEK) {
798+
// TODO: implement parsing of by days for scheduled actions
799+
setMinimalScheduledActionByDays();
800+
}
794801
mScheduledActionsList.add(mScheduledAction);
795802
int count = generateMissedScheduledTransactions(mScheduledAction);
796803
Log.i(LOG_TAG, String.format("Generated %d transactions from scheduled action", count));
@@ -1115,4 +1122,17 @@ private int generateMissedScheduledTransactions(ScheduledAction scheduledAction)
11151122
scheduledAction.setLastRun(lastRuntime);
11161123
return generatedTransactionCount;
11171124
}
1125+
1126+
/**
1127+
* Sets the by days of the scheduled action to the day of the week of the start time.
1128+
*
1129+
* <p>Until we implement parsing of days of the week for scheduled actions,
1130+
* this ensures they are executed at least once per week.</p>
1131+
*/
1132+
private void setMinimalScheduledActionByDays() {
1133+
Calendar calendar = Calendar.getInstance();
1134+
calendar.setTime(new Date(mScheduledAction.getStartTime()));
1135+
mScheduledAction.getRecurrence().setByDays(
1136+
Collections.singletonList(calendar.get(Calendar.DAY_OF_WEEK)));
1137+
}
11181138
}

app/src/main/java/org/gnucash/android/model/ScheduledAction.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -241,43 +241,43 @@ private long computeNextScheduledExecutionTimeStartingAt(long startTime) {
241241
* Computes the next time that this weekly scheduled action is supposed to be
242242
* executed starting at startTime.
243243
*
244-
* If no weekdays have been set (GnuCash desktop allows it), it will return a
244+
* If no days of the week have been set (GnuCash desktop allows it), it will return a
245245
* date in the future to ensure ScheduledActionService doesn't execute it.
246246
*
247247
* @param startTime LocalDateTime to use as start to compute the next schedule.
248248
*
249-
* @return Next run time as a LocalDateTime. A date in the future, if no weekdays
249+
* @return Next run time as a LocalDateTime. A date in the future, if no days of the week
250250
* were set in the Recurrence.
251251
*/
252252
@NonNull
253253
private LocalDateTime computeNextWeeklyExecutionStartingAt(LocalDateTime startTime) {
254254
if (mRecurrence.getByDays().isEmpty())
255255
return LocalDateTime.now().plusDays(1); // Just a date in the future
256256

257-
// Look into the week of startTime for another scheduled weekday
258-
for (int weekDay : mRecurrence.getByDays() ) {
259-
int jodaWeekDay = convertCalendarWeekdayToJoda(weekDay);
260-
LocalDateTime candidateNextDueTime = startTime.withDayOfWeek(jodaWeekDay);
257+
// Look into the week of startTime for another scheduled day of the week
258+
for (int dayOfWeek : mRecurrence.getByDays() ) {
259+
int jodaDayOfWeek = convertCalendarDayOfWeekToJoda(dayOfWeek);
260+
LocalDateTime candidateNextDueTime = startTime.withDayOfWeek(jodaDayOfWeek);
261261
if (candidateNextDueTime.isAfter(startTime))
262262
return candidateNextDueTime;
263263
}
264264

265-
// Return the first scheduled weekday from the next due week
266-
int firstScheduledWeekday = convertCalendarWeekdayToJoda(mRecurrence.getByDays().get(0));
265+
// Return the first scheduled day of the week from the next due week
266+
int firstScheduledDayOfWeek = convertCalendarDayOfWeekToJoda(mRecurrence.getByDays().get(0));
267267
return startTime.plusWeeks(mRecurrence.getMultiplier())
268-
.withDayOfWeek(firstScheduledWeekday);
268+
.withDayOfWeek(firstScheduledDayOfWeek);
269269
}
270270

271271
/**
272-
* Converts a java.util.Calendar weekday constant to the
272+
* Converts a java.util.Calendar day of the week constant to the
273273
* org.joda.time.DateTimeConstants equivalent.
274274
*
275-
* @param calendarWeekday weekday constant from java.util.Calendar
276-
* @return weekday constant equivalent from org.joda.time.DateTimeConstants
275+
* @param calendarDayOfWeek day of the week constant from java.util.Calendar
276+
* @return day of the week constant equivalent from org.joda.time.DateTimeConstants
277277
*/
278-
private int convertCalendarWeekdayToJoda(int calendarWeekday) {
278+
private int convertCalendarDayOfWeekToJoda(int calendarDayOfWeek) {
279279
Calendar cal = Calendar.getInstance();
280-
cal.set(Calendar.DAY_OF_WEEK, calendarWeekday);
280+
cal.set(Calendar.DAY_OF_WEEK, calendarDayOfWeek);
281281
return LocalDateTime.fromCalendarFields(cal).getDayOfWeek();
282282
}
283283

app/src/main/java/org/gnucash/android/model/Transaction.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ public Split createAutoBalanceSplit(){
178178
if (!imbalance.isAmountZero()){
179179
// yes, this is on purpose the account UID is set to the currency.
180180
// This should be overridden before saving to db
181-
Split split = new Split(imbalance.negate(), mCommodity.getCurrencyCode());
181+
Split split = new Split(imbalance.abs(), mCommodity.getCurrencyCode());
182+
split.setType(imbalance.isNegative() ? TransactionType.CREDIT : TransactionType.DEBIT);
182183
addSplit(split);
183184
return split;
184185
}
@@ -261,7 +262,7 @@ public Money getBalance(String accountUID){
261262
* <p><b>Note:</b> If this is a multi-currency transaction, an imbalance of zero will be returned</p>
262263
* @return Money imbalance of the transaction or zero if it is a multi-currency transaction
263264
*/
264-
public Money getImbalance(){
265+
private Money getImbalance(){
265266
Money imbalance = Money.createZeroInstance(mCommodity.getCurrencyCode());
266267
for (Split split : mSplitList) {
267268
if (!split.getQuantity().getCommodity().equals(mCommodity)) {

app/src/test/java/org/gnucash/android/test/unit/export/QifExporterTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.gnucash.android.db.adapter.BooksDbAdapter;
2626
import org.gnucash.android.export.ExportFormat;
2727
import org.gnucash.android.export.ExportParams;
28-
import org.gnucash.android.export.ofx.OfxExporter;
2928
import org.gnucash.android.export.qif.QifExporter;
3029
import org.gnucash.android.model.Account;
3130
import org.gnucash.android.model.Book;
@@ -99,8 +98,8 @@ public void testGenerateQIFExport(){
9998
exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD);
10099
exportParameters.setDeleteTransactionsAfterExport(false);
101100

102-
OfxExporter ofxExporter = new OfxExporter(exportParameters, mDb);
103-
List<String> exportedFiles = ofxExporter.generateExport();
101+
QifExporter qifExporter = new QifExporter(exportParameters, mDb);
102+
List<String> exportedFiles = qifExporter.generateExport();
104103

105104
assertThat(exportedFiles).hasSize(1);
106105
File file = new File(exportedFiles.get(0));
@@ -136,8 +135,8 @@ public void multiCurrencyTransactions_shouldResultInMultipleQifFiles(){
136135
exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD);
137136
exportParameters.setDeleteTransactionsAfterExport(false);
138137

139-
OfxExporter ofxExporter = new OfxExporter(exportParameters, mDb);
140-
List<String> exportedFiles = ofxExporter.generateExport();
138+
QifExporter qifExporter = new QifExporter(exportParameters, mDb);
139+
List<String> exportedFiles = qifExporter.generateExport();
141140

142141
assertThat(exportedFiles).hasSize(2);
143142
File file = new File(exportedFiles.get(0));

app/src/test/java/org/gnucash/android/test/unit/importer/GncXmlHandlerTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@
2222
import org.gnucash.android.db.DatabaseHelper;
2323
import org.gnucash.android.db.adapter.AccountsDbAdapter;
2424
import org.gnucash.android.db.adapter.BooksDbAdapter;
25+
import org.gnucash.android.db.adapter.RecurrenceDbAdapter;
26+
import org.gnucash.android.db.adapter.ScheduledActionDbAdapter;
2527
import org.gnucash.android.db.adapter.SplitsDbAdapter;
2628
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
2729
import org.gnucash.android.export.xml.GncXmlHelper;
2830
import org.gnucash.android.importer.GncXmlHandler;
2931
import org.gnucash.android.model.Account;
3032
import org.gnucash.android.model.AccountType;
3133
import org.gnucash.android.model.Money;
34+
import org.gnucash.android.model.ScheduledAction;
3235
import org.gnucash.android.model.Split;
3336
import org.gnucash.android.model.Transaction;
3437
import org.gnucash.android.model.TransactionType;
@@ -47,6 +50,8 @@
4750
import java.io.IOException;
4851
import java.io.InputStream;
4952
import java.text.ParseException;
53+
import java.util.Calendar;
54+
import java.util.Date;
5055

5156
import javax.xml.parsers.ParserConfigurationException;
5257
import javax.xml.parsers.SAXParser;
@@ -64,6 +69,7 @@ public class GncXmlHandlerTest {
6469
private BooksDbAdapter mBooksDbAdapter;
6570
private TransactionsDbAdapter mTransactionsDbAdapter;
6671
private AccountsDbAdapter mAccountsDbAdapter;
72+
private ScheduledActionDbAdapter mScheduledActionDbAdapter;
6773

6874
@Before
6975
public void setUp() throws Exception {
@@ -95,6 +101,8 @@ private void setUpDbAdapters(String bookUID) {
95101
SQLiteDatabase mainDb = databaseHelper.getReadableDatabase();
96102
mTransactionsDbAdapter = new TransactionsDbAdapter(mainDb, new SplitsDbAdapter(mainDb));
97103
mAccountsDbAdapter = new AccountsDbAdapter(mainDb, mTransactionsDbAdapter);
104+
RecurrenceDbAdapter recurrenceDbAdapter = new RecurrenceDbAdapter(mainDb);
105+
mScheduledActionDbAdapter = new ScheduledActionDbAdapter(mainDb, recurrenceDbAdapter);
98106
}
99107

100108
/**
@@ -334,6 +342,31 @@ public void simpleScheduledTransactionImport() throws ParseException {
334342
assertThat(split2.isPairOf(split1)).isTrue();
335343
}
336344

345+
/**
346+
* Tests that importing a weekly scheduled action sets the days of the
347+
* week of the recursion.
348+
*/
349+
@Test
350+
public void importingScheduledAction_shouldSetByDays() throws ParseException {
351+
String bookUID = importGnuCashXml("importingScheduledAction_shouldSetByDays.xml");
352+
setUpDbAdapters(bookUID);
353+
354+
ScheduledAction scheduledTransaction =
355+
mScheduledActionDbAdapter.getRecord("b5a13acb5a9459ebed10d06b75bbad10");
356+
357+
// There are 3 byDays but, for now, getting one is enough to ensure it is executed
358+
assertThat(scheduledTransaction.getRecurrence().getByDays().size()).isGreaterThanOrEqualTo(1);
359+
360+
// Until we implement parsing of days of the week for scheduled actions,
361+
// we'll just use the day of the week of the start time.
362+
int dayOfWeekFromByDays = scheduledTransaction.getRecurrence().getByDays().get(0);
363+
Date startTime = new Date(scheduledTransaction.getStartTime());
364+
Calendar calendar = Calendar.getInstance();
365+
calendar.setTime(startTime);
366+
int dayOfWeekFromStartTime = calendar.get(Calendar.DAY_OF_WEEK);
367+
assertThat(dayOfWeekFromByDays).isEqualTo(dayOfWeekFromStartTime);
368+
}
369+
337370
/**
338371
* Checks for bug 562 - Scheduled transaction imported with imbalanced splits.
339372
*

app/src/test/java/org/gnucash/android/test/unit/model/ScheduledActionTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ public void testComputingTimeOfLastSchedule(){
136136
}
137137

138138
/**
139-
* Weekly actions scheduled to run on multiple weekdays should be due
139+
* Weekly actions scheduled to run on multiple days of the week should be due
140140
* in each of them in the same week.
141141
*
142142
* For an action scheduled on Mondays and Thursdays, we test that, if
143143
* the last run was on Monday, the next should be due on the Thursday
144144
* of the same week instead of the following week.
145145
*/
146146
@Test
147-
public void multiWeekdayWeeklyActions_shouldBeDueOnEachWeekdaySet() {
147+
public void multiDayOfWeekWeeklyActions_shouldBeDueOnEachDayOfWeekSet() {
148148
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
149149
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
150150
recurrence.setByDays(Arrays.asList(Calendar.MONDAY, Calendar.THURSDAY));
@@ -159,10 +159,10 @@ public void multiWeekdayWeeklyActions_shouldBeDueOnEachWeekdaySet() {
159159

160160
/**
161161
* Weekly actions scheduled with multiplier should skip intermediate
162-
* weeks and be due in the specified weekday.
162+
* weeks and be due in the specified day of the week.
163163
*/
164164
@Test
165-
public void weeklyActionsWithMultiplier_shouldBeDueOnTheWeekdaySet() {
165+
public void weeklyActionsWithMultiplier_shouldBeDueOnTheDayOfWeekSet() {
166166
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
167167
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
168168
recurrence.setMultiplier(2);
@@ -179,12 +179,12 @@ public void weeklyActionsWithMultiplier_shouldBeDueOnTheWeekdaySet() {
179179

180180
/**
181181
* Weekly actions should return a date in the future when no
182-
* weekdays have been set in the recurrence.
182+
* days of the week have been set in the recurrence.
183183
*
184184
* See ScheduledAction.computeNextTimeBasedScheduledExecutionTime()
185185
*/
186186
@Test
187-
public void weeklyActionsWithoutWeekdaySet_shouldReturnDateInTheFuture() {
187+
public void weeklyActionsWithoutDayOfWeekSet_shouldReturnDateInTheFuture() {
188188
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
189189
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
190190
recurrence.setByDays(Collections.<Integer>emptyList());

app/src/test/java/org/gnucash/android/test/unit/model/TransactionTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
import org.gnucash.android.model.Money;
66
import org.gnucash.android.model.Split;
77
import org.gnucash.android.model.Transaction;
8+
import org.gnucash.android.model.TransactionType;
89
import org.gnucash.android.test.unit.testutil.GnucashTestRunner;
910
import org.gnucash.android.test.unit.testutil.ShadowCrashlytics;
1011
import org.gnucash.android.test.unit.testutil.ShadowUserVoice;
11-
import org.gnucash.android.ui.transaction.SplitEditorFragment;
1212
import org.junit.Test;
1313
import org.junit.runner.RunWith;
1414
import org.robolectric.annotation.Config;
@@ -76,4 +76,32 @@ public void settingUID_shouldSetTransactionUidOfSplits(){
7676
.doesNotContain("non-existent")
7777
.doesNotContain("pre-existent");
7878
}
79+
80+
@Test
81+
public void testCreateAutoBalanceSplit() {
82+
Transaction transactionCredit = new Transaction("Transaction with more credit");
83+
Split creditSplit = new Split(new Money("1", "EUR"), "test-account");
84+
creditSplit.setType(TransactionType.CREDIT);
85+
transactionCredit.addSplit(creditSplit);
86+
Split debitBalanceSplit = transactionCredit.createAutoBalanceSplit();
87+
88+
assertThat(creditSplit.getValue().isNegative()).isFalse();
89+
assertThat(debitBalanceSplit.getValue()).isEqualTo(creditSplit.getValue());
90+
91+
assertThat(creditSplit.getQuantity().isNegative()).isFalse();
92+
assertThat(debitBalanceSplit.getQuantity()).isEqualTo(creditSplit.getQuantity());
93+
94+
95+
Transaction transactionDebit = new Transaction("Transaction with more debit");
96+
Split debitSplit = new Split(new Money("1", "EUR"), "test-account");
97+
debitSplit.setType(TransactionType.DEBIT);
98+
transactionDebit.addSplit(debitSplit);
99+
Split creditBalanceSplit = transactionDebit.createAutoBalanceSplit();
100+
101+
assertThat(debitSplit.getValue().isNegative()).isFalse();
102+
assertThat(creditBalanceSplit.getValue()).isEqualTo(debitSplit.getValue());
103+
104+
assertThat(debitSplit.getQuantity().isNegative()).isFalse();
105+
assertThat(creditBalanceSplit.getQuantity()).isEqualTo(debitSplit.getQuantity());
106+
}
79107
}

0 commit comments

Comments
 (0)