@Test
  public void shouldRecursivelyDeleteAccount() {
    Account account = new Account("Parent");
    Account account2 = new Account("Child");
    account2.setParentUID(account.getUID());

    Transaction transaction = new Transaction("Random");
    account2.addTransaction(transaction);

    Split split = new Split(Money.getZeroInstance(), account.getUID());
    transaction.addSplit(split);
    transaction.addSplit(split.createPair(account2.getUID()));

    mAccountsDbAdapter.addRecord(account);
    mAccountsDbAdapter.addRecord(account2);

    assertThat(mAccountsDbAdapter.getRecordsCount()).isEqualTo(3);
    assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(1);
    assertThat(mSplitsDbAdapter.getRecordsCount()).isEqualTo(2);

    boolean result =
        mAccountsDbAdapter.recursiveDeleteAccount(mAccountsDbAdapter.getID(account.getUID()));
    assertThat(result).isTrue();

    assertThat(mAccountsDbAdapter.getRecordsCount()).isEqualTo(1); // the root account
    assertThat(mTransactionsDbAdapter.getRecordsCount()).isZero();
    assertThat(mSplitsDbAdapter.getRecordsCount()).isZero();
  }
  @Test
  public void bulkAddAccountsShouldNotModifyTransactions() {
    Account account1 = new Account("AlphaAccount");
    Account account2 = new Account("BetaAccount");
    Transaction transaction = new Transaction("MyTransaction");
    Split split = new Split(Money.getZeroInstance(), account1.getUID());
    transaction.addSplit(split);
    transaction.addSplit(split.createPair(account2.getUID()));
    account1.addTransaction(transaction);
    account2.addTransaction(transaction);

    List<Account> accounts = new ArrayList<>();
    accounts.add(account1);
    accounts.add(account2);

    mAccountsDbAdapter.bulkAddRecords(accounts);

    SplitsDbAdapter splitsDbAdapter = SplitsDbAdapter.getInstance();
    assertThat(
            splitsDbAdapter.getSplitsForTransactionInAccount(
                transaction.getUID(), account1.getUID()))
        .hasSize(1);
    assertThat(
            splitsDbAdapter.getSplitsForTransactionInAccount(
                transaction.getUID(), account2.getUID()))
        .hasSize(1);

    assertThat(mAccountsDbAdapter.getRecord(account1.getUID()).getTransactions()).hasSize(1);
  }
  @Test
  public void shouldClearAllTablesWhenDeletingAllAccounts() {
    Account account = new Account("Test");
    Transaction transaction = new Transaction("Test description");
    Split split = new Split(Money.getZeroInstance(), account.getUID());
    transaction.addSplit(split);
    Account account2 = new Account("Transfer account");
    transaction.addSplit(split.createPair(account2.getUID()));

    mAccountsDbAdapter.addRecord(account);
    mAccountsDbAdapter.addRecord(account2);

    ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
    scheduledAction.setActionUID("Test-uid");
    ScheduledActionDbAdapter scheduledActionDbAdapter = ScheduledActionDbAdapter.getInstance();

    scheduledActionDbAdapter.addRecord(scheduledAction);

    mAccountsDbAdapter.deleteAllRecords();

    assertThat(mAccountsDbAdapter.getRecordsCount()).isZero();
    assertThat(mTransactionsDbAdapter.getRecordsCount()).isZero();
    assertThat(mSplitsDbAdapter.getRecordsCount()).isZero();
    assertThat(scheduledActionDbAdapter.getRecordsCount()).isZero();
  }
  @Test
  public void shouldAddAccountsToDatabase() {
    Account account1 = new Account("AlphaAccount");
    Account account2 = new Account("BetaAccount");
    Transaction transaction = new Transaction("MyTransaction");
    Split split = new Split(Money.getZeroInstance(), account1.getUID());
    transaction.addSplit(split);
    transaction.addSplit(split.createPair(account2.getUID()));
    account1.addTransaction(transaction);
    account2.addTransaction(transaction);

    mAccountsDbAdapter.addRecord(account1);
    mAccountsDbAdapter.addRecord(account2);

    Account firstAccount = mAccountsDbAdapter.getRecord(account1.getUID());
    assertThat(firstAccount).isNotNull();
    assertThat(firstAccount.getUID()).isEqualTo(account1.getUID());
    assertThat(firstAccount.getFullName()).isEqualTo(account1.getFullName());

    Account secondAccount = mAccountsDbAdapter.getRecord(account2.getUID());
    assertThat(secondAccount).isNotNull();
    assertThat(secondAccount.getUID()).isEqualTo(account2.getUID());

    assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(1);
  }
 /**
  * Handles the case when we reach the end of the template numeric slot
  *
  * @param characterString Parsed characters containing split amount
  */
 private void handleEndOfTemplateNumericSlot(String characterString, TransactionType splitType) {
   try {
     BigDecimal amountBigD = GncXmlHelper.parseSplitAmount(characterString);
     Money amount = new Money(amountBigD, getCurrencyForAccount(mSplit.getAccountUID()));
     mSplit.setValue(amount.absolute());
     mSplit.setType(splitType);
     mIgnoreTemplateTransaction = false; // we have successfully parsed an amount
   } catch (NumberFormatException | ParseException e) {
     String msg = "Error parsing template credit split amount " + characterString;
     Log.e(LOG_TAG, msg + "\n" + e.getMessage());
     Crashlytics.log(msg);
     Crashlytics.logException(e);
   } finally {
     if (splitType == TransactionType.CREDIT) mInCreditNumericSlot = false;
     else mInDebitNumericSlot = false;
   }
 }
 @Override
 public void startElement(
     String uri, String localName, String qualifiedName, Attributes attributes)
     throws SAXException {
   switch (qualifiedName) {
     case GncXmlHelper.TAG_ACCOUNT:
       mAccount = new Account(""); // dummy name, will be replaced when we find name tag
       mISO4217Currency = false;
       break;
     case GncXmlHelper.TAG_TRANSACTION:
       mTransaction = new Transaction(""); // dummy name will be replaced
       mTransaction.setExported(true); // default to exported when import transactions
       mISO4217Currency = false;
       break;
     case GncXmlHelper.TAG_TRN_SPLIT:
       mSplit = new Split(Money.getZeroInstance(), "");
       break;
     case GncXmlHelper.TAG_DATE_POSTED:
       mIsDatePosted = true;
       break;
     case GncXmlHelper.TAG_DATE_ENTERED:
       mIsDateEntered = true;
       break;
     case GncXmlHelper.TAG_TEMPLATE_TRANSACTIONS:
       mInTemplates = true;
       break;
     case GncXmlHelper.TAG_SCHEDULED_ACTION:
       // default to transaction type, will be changed during parsing
       mScheduledAction = new ScheduledAction(ScheduledAction.ActionType.TRANSACTION);
       break;
     case GncXmlHelper.TAG_SX_START:
       mIsScheduledStart = true;
       break;
     case GncXmlHelper.TAG_SX_END:
       mIsScheduledEnd = true;
       break;
     case GncXmlHelper.TAG_SX_LAST:
       mIsLastRun = true;
       break;
     case GncXmlHelper.TAG_RX_START:
       mIsRecurrenceStart = true;
       break;
     case GncXmlHelper.TAG_PRICE:
       mPrice = new Price();
       break;
     case GncXmlHelper.TAG_PRICE_CURRENCY:
       mPriceCurrency = true;
       mPriceCommodity = false;
       mISO4217Currency = false;
       break;
     case GncXmlHelper.TAG_PRICE_COMMODITY:
       mPriceCurrency = false;
       mPriceCommodity = true;
       mISO4217Currency = false;
       break;
   }
 }
Example #7
0
  @Test
  public void testAddingSplitToTransaction() {
    Split split = new Split(Money.getZeroInstance(), "Test");
    assertThat(split.getTransactionUID()).isEmpty();

    Transaction transaction = new Transaction("Random");
    transaction.addSplit(split);

    assertThat(transaction.getUID()).isEqualTo(split.getTransactionUID());
  }
  @Test
  public void deletingTransactionsShouldDeleteSplits() {
    Transaction transaction = new Transaction("");
    Split split = new Split(Money.getZeroInstance(), alphaAccount.getUID());
    transaction.addSplit(split);
    mTransactionsDbAdapter.addRecord(transaction);

    assertThat(mSplitsDbAdapter.getSplitsForTransaction(transaction.getUID())).hasSize(1);

    mTransactionsDbAdapter.deleteRecord(transaction.getUID());
    assertThat(mSplitsDbAdapter.getSplitsForTransaction(transaction.getUID())).hasSize(0);
  }
  @Test
  public void simpleAccountListShouldNotContainTransactions() {
    Account account = new Account("Test");
    Transaction transaction = new Transaction("Test description");
    Split split = new Split(Money.getZeroInstance(), account.getUID());
    transaction.addSplit(split);
    Account account1 = new Account("Transfer");
    transaction.addSplit(split.createPair(account1.getUID()));

    mAccountsDbAdapter.addRecord(account);
    mAccountsDbAdapter.addRecord(account1);

    List<Account> accounts = mAccountsDbAdapter.getSimpleAccountList();
    for (Account testAcct : accounts) {
      assertThat(testAcct.getTransactionCount()).isZero();
    }
  }
  @Test
  public void testComputeBalance() {
    Transaction transaction = new Transaction("Compute");
    Money firstSplitAmount = new Money("4.99", DEFAULT_CURRENCY.getCurrencyCode());
    Split split = new Split(firstSplitAmount, alphaAccount.getUID());
    transaction.addSplit(split);
    Money secondSplitAmount = new Money("3.50", DEFAULT_CURRENCY.getCurrencyCode());
    split = new Split(secondSplitAmount, bravoAccount.getUID());
    transaction.addSplit(split);

    mTransactionsDbAdapter.addRecord(transaction);

    // balance is negated because the CASH account has inverse normal balance
    transaction = mTransactionsDbAdapter.getRecord(transaction.getUID());
    Money savedBalance = transaction.getBalance(alphaAccount.getUID());
    assertThat(savedBalance).isEqualTo(firstSplitAmount.negate());

    savedBalance = transaction.getBalance(bravoAccount.getUID());
    assertThat(savedBalance.getNumerator()).isEqualTo(secondSplitAmount.negate().getNumerator());
    assertThat(savedBalance.getCurrency()).isEqualTo(secondSplitAmount.getCurrency());
  }
Example #11
0
  @Test
  public void shouldAddTransactionsAndSplitsWhenAddingAccounts() {
    Account account = new Account("Test");
    mAccountsDbAdapter.addRecord(account);

    Transaction transaction = new Transaction("Test description");
    Split split = new Split(Money.getZeroInstance(), account.getUID());
    transaction.addSplit(split);
    Account account1 = new Account("Transfer account");
    transaction.addSplit(split.createPair(account1.getUID()));
    account1.addTransaction(transaction);

    mAccountsDbAdapter.addRecord(account1);

    assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(1);
    assertThat(mSplitsDbAdapter.getRecordsCount()).isEqualTo(2);
    assertThat(mAccountsDbAdapter.getRecordsCount())
        .isEqualTo(3); // ROOT account automatically added
  }
  @Test
  public void testTransactionsAreTimeSorted() {
    Transaction t1 = new Transaction("T800");
    t1.setTime(System.currentTimeMillis() - 10000);
    Split split = new Split(Money.getZeroInstance(), alphaAccount.getUID());
    t1.addSplit(split);
    t1.addSplit(split.createPair(bravoAccount.getUID()));

    Transaction t2 = new Transaction("T1000");
    t2.setTime(System.currentTimeMillis());
    Split split2 = new Split(new Money("23.50"), bravoAccount.getUID());
    t2.addSplit(split2);
    t2.addSplit(split2.createPair(alphaAccount.getUID()));

    mTransactionsDbAdapter.addRecord(t1);
    mTransactionsDbAdapter.addRecord(t2);

    List<Transaction> transactionsList =
        mTransactionsDbAdapter.getAllTransactionsForAccount(alphaAccount.getUID());
    assertThat(transactionsList).contains(t2, Index.atIndex(0));
    assertThat(transactionsList).contains(t1, Index.atIndex(1));
  }
Example #13
0
  /** Tests the foreign key constraint "ON DELETE CASCADE" between accounts and splits */
  @Test
  public void shouldDeleteSplitsWhenAccountDeleted() {
    Account first = new Account(ALPHA_ACCOUNT_NAME);
    first.setUID(ALPHA_ACCOUNT_NAME);
    Account second = new Account(BRAVO_ACCOUNT_NAME);
    second.setUID(BRAVO_ACCOUNT_NAME);

    mAccountsDbAdapter.addRecord(second);
    mAccountsDbAdapter.addRecord(first);

    Transaction transaction = new Transaction("TestTrn");
    Split split = new Split(Money.getZeroInstance(), ALPHA_ACCOUNT_NAME);
    transaction.addSplit(split);
    transaction.addSplit(split.createPair(BRAVO_ACCOUNT_NAME));

    mTransactionsDbAdapter.addRecord(transaction);

    mAccountsDbAdapter.deleteRecord(ALPHA_ACCOUNT_NAME);

    Transaction trxn = mTransactionsDbAdapter.getRecord(transaction.getUID());
    assertThat(trxn.getSplits().size()).isEqualTo(1);
    assertThat(trxn.getSplits().get(0).getAccountUID()).isEqualTo(BRAVO_ACCOUNT_NAME);
  }
  /**
   * Serializes transactions from the database to XML
   *
   * @param xmlSerializer XML serializer
   * @param exportTemplates Flag whether to export templates or normal transactions
   * @throws IOException if the XML serializer cannot be written to
   */
  private void exportTransactions(XmlSerializer xmlSerializer, boolean exportTemplates)
      throws IOException {
    String where = TransactionEntry.TABLE_NAME + "." + TransactionEntry.COLUMN_TEMPLATE + "=0";
    if (exportTemplates) {
      where = TransactionEntry.TABLE_NAME + "." + TransactionEntry.COLUMN_TEMPLATE + "=1";
    }
    Cursor cursor =
        mTransactionsDbAdapter.fetchTransactionsWithSplits(
            new String[] {
              TransactionEntry.TABLE_NAME + "." + TransactionEntry.COLUMN_UID + " AS trans_uid",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_DESCRIPTION
                  + " AS trans_desc",
              TransactionEntry.TABLE_NAME + "." + TransactionEntry.COLUMN_NOTES + " AS trans_notes",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_TIMESTAMP
                  + " AS trans_time",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_EXPORTED
                  + " AS trans_exported",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_CURRENCY
                  + " AS trans_currency",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_CREATED_AT
                  + " AS trans_date_posted",
              TransactionEntry.TABLE_NAME
                  + "."
                  + TransactionEntry.COLUMN_SCHEDX_ACTION_UID
                  + " AS trans_from_sched_action",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_UID + " AS split_uid",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_MEMO + " AS split_memo",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_TYPE + " AS split_type",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_VALUE_NUM + " AS split_value_num",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_VALUE_DENOM + " AS split_value_denom",
              SplitEntry.TABLE_NAME
                  + "."
                  + SplitEntry.COLUMN_QUANTITY_NUM
                  + " AS split_quantity_num",
              SplitEntry.TABLE_NAME
                  + "."
                  + SplitEntry.COLUMN_QUANTITY_DENOM
                  + " AS split_quantity_denom",
              SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_ACCOUNT_UID + " AS split_acct_uid"
            },
            where,
            null,
            TransactionEntry.TABLE_NAME
                + "."
                + TransactionEntry.COLUMN_TIMESTAMP
                + " ASC , "
                + TransactionEntry.TABLE_NAME
                + "."
                + TransactionEntry.COLUMN_UID
                + " ASC ");
    String lastTrxUID = "";
    Commodity trnCommodity = null;
    String denomString = "100";

    if (exportTemplates) {
      mRootTemplateAccount = new Account("Template Root");
      mRootTemplateAccount.setAccountType(AccountType.ROOT);
      mTransactionToTemplateAccountMap.put(" ", mRootTemplateAccount);
      while (cursor.moveToNext()) {
        Account account = new Account(BaseModel.generateUID());
        account.setAccountType(AccountType.BANK);
        String trnUID = cursor.getString(cursor.getColumnIndexOrThrow("trans_uid"));
        mTransactionToTemplateAccountMap.put(trnUID, account);
      }

      exportTemplateAccounts(xmlSerializer, mTransactionToTemplateAccountMap.values());
      // push cursor back to before the beginning
      cursor.moveToFirst();
      cursor.moveToPrevious();
    }

    while (cursor.moveToNext()) {
      String curTrxUID = cursor.getString(cursor.getColumnIndexOrThrow("trans_uid"));
      if (!lastTrxUID.equals(curTrxUID)) { // new transaction starts
        if (!lastTrxUID.equals("")) { // there's an old transaction, close it
          xmlSerializer.endTag(null, GncXmlHelper.TAG_TRN_SPLITS);
          xmlSerializer.endTag(null, GncXmlHelper.TAG_TRANSACTION);
        }
        // new transaction
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRANSACTION);
        xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_VERSION, GncXmlHelper.BOOK_VERSION);
        // transaction id
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRX_ID);
        xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_TYPE, GncXmlHelper.ATTR_VALUE_GUID);
        xmlSerializer.text(curTrxUID);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TRX_ID);
        // currency
        String currencyCode = cursor.getString(cursor.getColumnIndexOrThrow("trans_currency"));
        trnCommodity =
            CommoditiesDbAdapter.getInstance()
                .getCommodity(currencyCode); // Currency.getInstance(currencyCode);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRX_CURRENCY);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_COMMODITY_SPACE);
        xmlSerializer.text("ISO4217");
        xmlSerializer.endTag(null, GncXmlHelper.TAG_COMMODITY_SPACE);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_COMMODITY_ID);
        xmlSerializer.text(currencyCode);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_COMMODITY_ID);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TRX_CURRENCY);
        // date posted, time which user put on the transaction
        String strDate =
            GncXmlHelper.formatDate(cursor.getLong(cursor.getColumnIndexOrThrow("trans_time")));
        xmlSerializer.startTag(null, GncXmlHelper.TAG_DATE_POSTED);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TS_DATE);
        xmlSerializer.text(strDate);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TS_DATE);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_DATE_POSTED);

        // date entered, time when the transaction was actually created
        Timestamp timeEntered =
            Timestamp.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("trans_date_posted")));
        String dateEntered = GncXmlHelper.formatDate(timeEntered.getTime());
        xmlSerializer.startTag(null, GncXmlHelper.TAG_DATE_ENTERED);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TS_DATE);
        xmlSerializer.text(dateEntered);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TS_DATE);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_DATE_ENTERED);

        // description
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRN_DESCRIPTION);
        xmlSerializer.text(cursor.getString(cursor.getColumnIndexOrThrow("trans_desc")));
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TRN_DESCRIPTION);
        lastTrxUID = curTrxUID;
        // slots
        ArrayList<String> slotKey = new ArrayList<>();
        ArrayList<String> slotType = new ArrayList<>();
        ArrayList<String> slotValue = new ArrayList<>();

        String notes = cursor.getString(cursor.getColumnIndexOrThrow("trans_notes"));
        boolean exported = cursor.getInt(cursor.getColumnIndexOrThrow("trans_exported")) == 1;
        if (notes != null && notes.length() > 0) {
          slotKey.add(GncXmlHelper.KEY_NOTES);
          slotType.add(GncXmlHelper.ATTR_VALUE_STRING);
          slotValue.add(notes);
        }
        if (!exported) {
          slotKey.add(GncXmlHelper.KEY_EXPORTED);
          slotType.add(GncXmlHelper.ATTR_VALUE_STRING);
          slotValue.add("false");
        }

        String scheduledActionUID =
            cursor.getString(cursor.getColumnIndexOrThrow("trans_from_sched_action"));
        if (scheduledActionUID != null && !scheduledActionUID.isEmpty()) {
          slotKey.add(GncXmlHelper.KEY_FROM_SCHED_ACTION);
          slotType.add(GncXmlHelper.ATTR_VALUE_GUID);
          slotValue.add(scheduledActionUID);
        }
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRN_SLOTS);
        exportSlots(xmlSerializer, slotKey, slotType, slotValue);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_TRN_SLOTS);

        // splits start
        xmlSerializer.startTag(null, GncXmlHelper.TAG_TRN_SPLITS);
      }
      xmlSerializer.startTag(null, GncXmlHelper.TAG_TRN_SPLIT);
      // split id
      xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_ID);
      xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_TYPE, GncXmlHelper.ATTR_VALUE_GUID);
      xmlSerializer.text(cursor.getString(cursor.getColumnIndexOrThrow("split_uid")));
      xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_ID);
      // memo
      String memo = cursor.getString(cursor.getColumnIndexOrThrow("split_memo"));
      if (memo != null && memo.length() > 0) {
        xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_MEMO);
        xmlSerializer.text(memo);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_MEMO);
      }
      // reconciled
      xmlSerializer.startTag(null, GncXmlHelper.TAG_RECONCILED_STATE);
      xmlSerializer.text("n");
      xmlSerializer.endTag(null, GncXmlHelper.TAG_RECONCILED_STATE);
      // value, in the transaction's currency
      String trxType = cursor.getString(cursor.getColumnIndexOrThrow("split_type"));
      int splitValueNum = cursor.getInt(cursor.getColumnIndexOrThrow("split_value_num"));
      int splitValueDenom = cursor.getInt(cursor.getColumnIndexOrThrow("split_value_denom"));
      BigDecimal splitAmount = Money.getBigDecimal(splitValueNum, splitValueDenom);
      String strValue = "0/" + denomString;
      if (!exportTemplates) { // when doing normal transaction export
        strValue = (trxType.equals("CREDIT") ? "-" : "") + splitValueNum + "/" + splitValueDenom;
      }
      xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_VALUE);
      xmlSerializer.text(strValue);
      xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_VALUE);
      // quantity, in the split account's currency
      String splitQuantityNum =
          cursor.getString(cursor.getColumnIndexOrThrow("split_quantity_num"));
      String splitQuantityDenom =
          cursor.getString(cursor.getColumnIndexOrThrow("split_quantity_denom"));
      if (!exportTemplates) {
        strValue =
            (trxType.equals("CREDIT") ? "-" : "") + splitQuantityNum + "/" + splitQuantityDenom;
      }
      xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_QUANTITY);
      xmlSerializer.text(strValue);
      xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_QUANTITY);
      // account guid
      xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_ACCOUNT);
      xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_TYPE, GncXmlHelper.ATTR_VALUE_GUID);
      String splitAccountUID = null;
      if (exportTemplates) {
        // get the UID of the template account
        splitAccountUID = mTransactionToTemplateAccountMap.get(curTrxUID).getUID();
      } else {
        splitAccountUID = cursor.getString(cursor.getColumnIndexOrThrow("split_acct_uid"));
      }
      xmlSerializer.text(splitAccountUID);
      xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_ACCOUNT);

      // if we are exporting a template transaction, then we need to add some extra slots
      if (exportTemplates) {
        xmlSerializer.startTag(null, GncXmlHelper.TAG_SPLIT_SLOTS);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_SLOT);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_SLOT_KEY);
        xmlSerializer.text(
            GncXmlHelper.KEY_SCHEDX_ACTION); // FIXME: not all templates may be scheduled actions
        xmlSerializer.endTag(null, GncXmlHelper.TAG_SLOT_KEY);
        xmlSerializer.startTag(null, GncXmlHelper.TAG_SLOT_VALUE);
        xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_TYPE, "frame");

        List<String> slotKeys = new ArrayList<>();
        List<String> slotTypes = new ArrayList<>();
        List<String> slotValues = new ArrayList<>();
        slotKeys.add(GncXmlHelper.KEY_SPLIT_ACCOUNT_SLOT);
        slotTypes.add(GncXmlHelper.ATTR_VALUE_GUID);
        slotValues.add(cursor.getString(cursor.getColumnIndexOrThrow("split_acct_uid")));
        TransactionType type = TransactionType.valueOf(trxType);
        if (type == TransactionType.CREDIT) {
          slotKeys.add(GncXmlHelper.KEY_CREDIT_FORMULA);
          slotTypes.add(GncXmlHelper.ATTR_VALUE_STRING);
          slotValues.add(GncXmlHelper.formatTemplateSplitAmount(splitAmount));
          slotKeys.add(GncXmlHelper.KEY_CREDIT_NUMERIC);
          slotTypes.add(GncXmlHelper.ATTR_VALUE_NUMERIC);
          slotValues.add(GncXmlHelper.formatSplitAmount(splitAmount, trnCommodity));
        } else {
          slotKeys.add(GncXmlHelper.KEY_DEBIT_FORMULA);
          slotTypes.add(GncXmlHelper.ATTR_VALUE_STRING);
          slotValues.add(GncXmlHelper.formatTemplateSplitAmount(splitAmount));
          slotKeys.add(GncXmlHelper.KEY_DEBIT_NUMERIC);
          slotTypes.add(GncXmlHelper.ATTR_VALUE_NUMERIC);
          slotValues.add(GncXmlHelper.formatSplitAmount(splitAmount, trnCommodity));
        }

        exportSlots(xmlSerializer, slotKeys, slotTypes, slotValues);

        xmlSerializer.endTag(null, GncXmlHelper.TAG_SLOT_VALUE);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_SLOT);
        xmlSerializer.endTag(null, GncXmlHelper.TAG_SPLIT_SLOTS);
      }

      xmlSerializer.endTag(null, GncXmlHelper.TAG_TRN_SPLIT);
    }
    if (!lastTrxUID.equals("")) { // there's an unfinished transaction, close it
      xmlSerializer.endTag(null, GncXmlHelper.TAG_TRN_SPLITS);
      xmlSerializer.endTag(null, GncXmlHelper.TAG_TRANSACTION);
    }
    cursor.close();
  }