/* * Copyright (c) 2012 - 2014 Ngewi Fet <ngewif@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gnucash.android.model; import android.graphics.Color; import android.support.annotation.NonNull; import org.gnucash.android.BuildConfig; import org.gnucash.android.export.ofx.OfxHelper; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; /** * An account represents a transaction account in with {@link Transaction}s may be recorded * Accounts have different types as specified by {@link AccountType} and also a currency with * which transactions may be recorded in the account * By default, an account is made an {@link AccountType#CASH} and the default currency is * the currency of the Locale of the device on which the software is running. US Dollars is used * if the platform locale cannot be determined. * * @author Ngewi Fet <ngewif@gmail.com> * @see AccountType */ public class Account extends BaseModel { /** * The MIME type for accounts in GnucashMobile * This is used when sending intents from third-party applications */ public static final String MIME_TYPE = "vnd.android.cursor.item/vnd." + BuildConfig.APPLICATION_ID + ".account"; /** * Default color, if not set explicitly through {@link #setColor(String)}. */ // TODO: get it from a theme value? public static final int DEFAULT_COLOR = Color.LTGRAY; /** * Accounts types which are used by the OFX standard */ public enum OfxAccountType { CHECKING, SAVINGS, MONEYMRKT, CREDITLINE } /** * Name of this account */ private String mName; /** * Fully qualified name of this account including the parent hierarchy. * On instantiation of an account, the full name is set to the name by default */ private String mFullName; /** * Account description */ private String mDescription = ""; /** * Commodity used by this account */ private Commodity mCommodity; /** * Type of account * Defaults to {@link AccountType#CASH} */ private AccountType mAccountType = AccountType.CASH; /** * List of transactions in this account */ private List<Transaction> mTransactionsList = new ArrayList<>(); /** * Account UID of the parent account. Can be null */ private String mParentAccountUID; /** * Save UID of a default account for transfers. * All transactions in this account will by default be transfers to the other account */ private String mDefaultTransferAccountUID; /** * Flag for placeholder accounts. * These accounts cannot have transactions */ private boolean mIsPlaceholderAccount; /** * Account color field in hex format #rrggbb */ private int mColor = DEFAULT_COLOR; /** * Flag which marks this account as a favorite account */ private boolean mIsFavorite; /** * Flag which indicates if this account is a hidden account or not */ private boolean mIsHidden; /** * An extra key for passing the currency code (according ISO 4217) in an intent */ public static final String EXTRA_CURRENCY_CODE = "org.gnucash.android.extra.currency_code"; /** * Extra key for passing the unique ID of the parent account when creating a * new account using Intents */ public static final String EXTRA_PARENT_UID = "org.gnucash.android.extra.parent_uid"; /** * Constructor * Creates a new account with the default currency and a generated unique ID * @param name Name of the account */ public Account(String name) { setName(name); this.mFullName = mName; setCommodity(Commodity.DEFAULT_COMMODITY); } /** * Overloaded constructor * @param name Name of the account * @param commodity {@link Commodity} to be used by transactions in this account */ public Account(String name, @NonNull Commodity commodity) { setName(name); this.mFullName = mName; setCommodity(commodity); } /** * Sets the name of the account * @param name String name of the account */ public void setName(String name) { this.mName = name.trim(); } /** * Returns the name of the account * @return String containing name of the account */ public String getName() { return mName; } /** * Returns the full name of this account. * The full name is the full account hierarchy name * @return Fully qualified name of the account */ public String getFullName() { return mFullName; } /** * Sets the fully qualified name of the account * @param fullName Fully qualified account name */ public void setFullName(String fullName) { this.mFullName = fullName; } /** * Returns the account description * @return String with description */ public String getDescription() { return mDescription; } /** * Sets the account description * @param description Account description */ public void setDescription(@NonNull String description) { this.mDescription = description; } /** * Get the type of account * @return {@link AccountType} type of account */ public AccountType getAccountType() { return mAccountType; } /** * Sets the type of account * @param mAccountType Type of account * @see AccountType */ public void setAccountType(AccountType mAccountType) { this.mAccountType = mAccountType; } /** * Adds a transaction to this account * @param transaction {@link Transaction} to be added to the account */ public void addTransaction(Transaction transaction) { transaction.setCommodity(mCommodity); mTransactionsList.add(transaction); } /** * Sets a list of transactions for this account. * Overrides any previous transactions with those in the list. * The account UID and currency of the transactions will be set to the unique ID * and currency of the account respectively * @param transactionsList List of {@link Transaction}s to be set. */ public void setTransactions(List<Transaction> transactionsList) { this.mTransactionsList = transactionsList; } /** * Returns a list of transactions for this account * @return Array list of transactions for the account */ public List<Transaction> getTransactions() { return mTransactionsList; } /** * Returns the number of transactions in this account * @return Number transactions in account */ public int getTransactionCount() { return mTransactionsList.size(); } /** * Returns the aggregate of all transactions in this account. * It takes into account debit and credit amounts, it does not however consider sub-accounts * @return {@link Money} aggregate amount of all transactions in account. */ public Money getBalance() { Money balance = Money.createZeroInstance(mCommodity.getCurrencyCode()); for (Transaction transaction : mTransactionsList) { balance.add(transaction.getBalance(getUID())); } return balance; } /** * Returns the color of the account. * @return Color of the account as an int as returned by {@link Color}. */ public int getColor() { return mColor; } /** * Sets the color of the account. * @param color Color as an int as returned by {@link Color}. * @throws java.lang.IllegalArgumentException if the color is transparent, * which is not supported. */ public void setColor(int color) { if (Color.alpha(color) < 255) throw new IllegalArgumentException("Transparent colors are not supported: " + color); mColor = color; } /** * Sets the color of the account. * @param colorCode Color code to be set in the format #rrggbb * @throws java.lang.IllegalArgumentException if the color code is not properly formatted or * the color is transparent. */ //TODO: Allow use of #aarrggbb format as well public void setColor(@NonNull String colorCode) { setColor(Color.parseColor(colorCode)); } /** * Tests if this account is a favorite account or not * @return <code>true</code> if account is flagged as favorite, <code>false</code> otherwise */ public boolean isFavorite() { return mIsFavorite; } /** * Toggles the favorite flag on this account on or off * @param isFavorite <code>true</code> if account should be flagged as favorite, <code>false</code> otherwise */ public void setFavorite(boolean isFavorite) { this.mIsFavorite = isFavorite; } /** * Return the commodity for this account */ @NonNull public Commodity getCommodity() { return mCommodity; } /** * Sets the commodity of this account * @param commodity Commodity of the account */ public void setCommodity(@NonNull Commodity commodity) { this.mCommodity = commodity; //todo: should we also change commodity of transactions? Transactions can have splits from different accounts } /** * Sets the Unique Account Identifier of the parent account * @param parentUID String Unique ID of parent account */ public void setParentUID(String parentUID) { mParentAccountUID = parentUID; } /** * Returns the Unique Account Identifier of the parent account * @return String Unique ID of parent account */ public String getParentUID() { return mParentAccountUID; } /** * Returns <code>true</code> if this account is a placeholder account, <code>false</code> otherwise. * @return <code>true</code> if this account is a placeholder account, <code>false</code> otherwise */ public boolean isPlaceholderAccount() { return mIsPlaceholderAccount; } /** * Returns the hidden property of this account. * <p>Hidden accounts are not visible in the UI</p> * @return <code>true</code> if the account is hidden, <code>false</code> otherwise. */ public boolean isHidden() { return mIsHidden; } /** * Toggles the hidden property of the account. * <p>Hidden accounts are not visible in the UI</p> * @param hidden boolean specifying is hidden or not */ public void setHidden(boolean hidden) { this.mIsHidden = hidden; } /** * Sets the placeholder flag for this account. * Placeholder accounts cannot have transactions * @param isPlaceholder Boolean flag indicating if the account is a placeholder account or not */ public void setPlaceHolderFlag(boolean isPlaceholder) { mIsPlaceholderAccount = isPlaceholder; } /** * Return the unique ID of accounts to which to default transfer transactions to * @return Unique ID string of default transfer account */ public String getDefaultTransferAccountUID() { return mDefaultTransferAccountUID; } /** * Set the unique ID of account which is the default transfer target * @param defaultTransferAccountUID Unique ID string of default transfer account */ public void setDefaultTransferAccountUID(String defaultTransferAccountUID) { this.mDefaultTransferAccountUID = defaultTransferAccountUID; } /** * Maps the <code>accountType</code> to the corresponding account type. * <code>accountType</code> have corresponding values to GnuCash desktop * @param accountType {@link AccountType} of an account * @return Corresponding {@link OfxAccountType} for the <code>accountType</code> * @see AccountType * @see OfxAccountType */ public static OfxAccountType convertToOfxAccountType(AccountType accountType) { switch (accountType) { case CREDIT: case LIABILITY: return OfxAccountType.CREDITLINE; case CASH: case INCOME: case EXPENSE: case PAYABLE: case RECEIVABLE: return OfxAccountType.CHECKING; case BANK: case ASSET: return OfxAccountType.SAVINGS; case MUTUAL: case STOCK: case EQUITY: case CURRENCY: return OfxAccountType.MONEYMRKT; default: return OfxAccountType.CHECKING; } } /** * Converts this account's transactions into XML and adds them to the DOM document * @param doc XML DOM document for the OFX data * @param parent Parent node to which to add this account's transactions in XML * @param exportStartTime Time from which to export transactions which are created/modified after */ public void toOfx(Document doc, Element parent, Timestamp exportStartTime) { Element currency = doc.createElement(OfxHelper.TAG_CURRENCY_DEF); currency.appendChild(doc.createTextNode(mCommodity.getCurrencyCode())); //================= BEGIN BANK ACCOUNT INFO (BANKACCTFROM) ================================= Element bankId = doc.createElement(OfxHelper.TAG_BANK_ID); bankId.appendChild(doc.createTextNode(OfxHelper.APP_ID)); Element acctId = doc.createElement(OfxHelper.TAG_ACCOUNT_ID); acctId.appendChild(doc.createTextNode(getUID())); Element accttype = doc.createElement(OfxHelper.TAG_ACCOUNT_TYPE); String ofxAccountType = convertToOfxAccountType(mAccountType).toString(); accttype.appendChild(doc.createTextNode(ofxAccountType)); Element bankFrom = doc.createElement(OfxHelper.TAG_BANK_ACCOUNT_FROM); bankFrom.appendChild(bankId); bankFrom.appendChild(acctId); bankFrom.appendChild(accttype); //================= END BANK ACCOUNT INFO ============================================ //================= BEGIN ACCOUNT BALANCE INFO ================================= String balance = getBalance().toPlainString(); String formattedCurrentTimeString = OfxHelper.getFormattedCurrentTime(); Element balanceAmount = doc.createElement(OfxHelper.TAG_BALANCE_AMOUNT); balanceAmount.appendChild(doc.createTextNode(balance)); Element dtasof = doc.createElement(OfxHelper.TAG_DATE_AS_OF); dtasof.appendChild(doc.createTextNode(formattedCurrentTimeString)); Element ledgerBalance = doc.createElement(OfxHelper.TAG_LEDGER_BALANCE); ledgerBalance.appendChild(balanceAmount); ledgerBalance.appendChild(dtasof); //================= END ACCOUNT BALANCE INFO ================================= //================= BEGIN TIME PERIOD INFO ================================= Element dtstart = doc.createElement(OfxHelper.TAG_DATE_START); dtstart.appendChild(doc.createTextNode(formattedCurrentTimeString)); Element dtend = doc.createElement(OfxHelper.TAG_DATE_END); dtend.appendChild(doc.createTextNode(formattedCurrentTimeString)); //================= END TIME PERIOD INFO ================================= //================= BEGIN TRANSACTIONS LIST ================================= Element bankTransactionsList = doc.createElement(OfxHelper.TAG_BANK_TRANSACTION_LIST); bankTransactionsList.appendChild(dtstart); bankTransactionsList.appendChild(dtend); for (Transaction transaction : mTransactionsList) { if (transaction.getModifiedTimestamp().before(exportStartTime)) continue; bankTransactionsList.appendChild(transaction.toOFX(doc, getUID())); } //================= END TRANSACTIONS LIST ================================= Element statementTransactions = doc.createElement(OfxHelper.TAG_STATEMENT_TRANSACTIONS); statementTransactions.appendChild(currency); statementTransactions.appendChild(bankFrom); statementTransactions.appendChild(bankTransactionsList); statementTransactions.appendChild(ledgerBalance); parent.appendChild(statementTransactions); } }