/* * Copyright (c) 2012 - 2014 Ngewi Fet <ngewif@gmail.com> * Copyright (c) 2014 Yongxin Wang <fefe.wyx@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.ui.account; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.Color; import android.os.Bundle; import android.support.design.widget.TextInputLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.Spinner; import org.gnucash.android.R; import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; import org.gnucash.android.db.adapter.DatabaseAdapter; import org.gnucash.android.model.Account; import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Commodity; import org.gnucash.android.model.Money; import org.gnucash.android.ui.colorpicker.ColorPickerDialog; import org.gnucash.android.ui.colorpicker.ColorPickerSwatch; import org.gnucash.android.ui.colorpicker.ColorSquare; import org.gnucash.android.ui.common.UxArgument; import org.gnucash.android.ui.settings.PreferenceActivity; import org.gnucash.android.util.CommoditiesCursorAdapter; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; /** * Fragment used for creating and editing accounts * @author Ngewi Fet <ngewif@gmail.com> * @author Yongxin Wang <fefe.wyx@gmail.com> */ public class AccountFormFragment extends Fragment { /** * Tag for the color picker dialog fragment */ private static final String COLOR_PICKER_DIALOG_TAG = "color_picker_dialog"; /** * EditText for the name of the account to be created/edited */ @BindView(R.id.input_account_name) EditText mNameEditText; @BindView(R.id.name_text_input_layout) TextInputLayout mTextInputLayout; /** * Spinner for selecting the currency of the account * Currencies listed are those specified by ISO 4217 */ @BindView(R.id.input_currency_spinner) Spinner mCurrencySpinner; /** * Accounts database adapter */ private AccountsDbAdapter mAccountsDbAdapter; /** * GUID of the parent account * This value is set to the parent account of the transaction being edited or * the account in which a new sub-account is being created */ private String mParentAccountUID = null; /** * Account ID of the root account */ private long mRootAccountId = -1; /** * Account UID of the root account */ private String mRootAccountUID = null; /** * Reference to account object which will be created at end of dialog */ private Account mAccount = null; /** * Unique ID string of account being edited */ private String mAccountUID = null; /** * Cursor which will hold set of eligible parent accounts */ private Cursor mParentAccountCursor; /** * List of all descendant Account UIDs, if we are modifying an account * null if creating a new account */ private List<String> mDescendantAccountUIDs; /** * SimpleCursorAdapter for the parent account spinner * @see QualifiedAccountNameCursorAdapter */ private SimpleCursorAdapter mParentAccountCursorAdapter; /** * Spinner for parent account list */ @BindView(R.id.input_parent_account) Spinner mParentAccountSpinner; /** * Checkbox which activates the parent account spinner when selected * Leaving this unchecked means it is a top-level root account */ @BindView(R.id.checkbox_parent_account) CheckBox mParentCheckBox; /** * Spinner for the account type * @see org.gnucash.android.model.AccountType */ @BindView(R.id.input_account_type_spinner) Spinner mAccountTypeSpinner; /** * Checkbox for activating the default transfer account spinner */ @BindView(R.id.checkbox_default_transfer_account) CheckBox mDefaultTransferAccountCheckBox; /** * Spinner for selecting the default transfer account */ @BindView(R.id.input_default_transfer_account) Spinner mDefaultTransferAccountSpinner; /** * Account description input text view */ @BindView(R.id.input_account_description) EditText mDescriptionEditText; /** * Checkbox indicating if account is a placeholder account */ @BindView(R.id.checkbox_placeholder_account) CheckBox mPlaceholderCheckBox; /** * Cursor adapter which binds to the spinner for default transfer account */ private SimpleCursorAdapter mDefaultTransferAccountCursorAdapter; /** * Flag indicating if double entry transactions are enabled */ private boolean mUseDoubleEntry; private int mSelectedColor = Account.DEFAULT_COLOR; /** * Trigger for color picker dialog */ @BindView(R.id.input_color_picker) ColorSquare mColorSquare; private final ColorPickerSwatch.OnColorSelectedListener mColorSelectedListener = new ColorPickerSwatch.OnColorSelectedListener() { @Override public void onColorSelected(int color) { mColorSquare.setBackgroundColor(color); mSelectedColor = color; } }; /** * Default constructor * Required, else the app crashes on screen rotation */ public AccountFormFragment() { //nothing to see here, move along } /** * Construct a new instance of the dialog * @return New instance of the dialog fragment */ static public AccountFormFragment newInstance() { AccountFormFragment f = new AccountFormFragment(); f.mAccountsDbAdapter = AccountsDbAdapter.getInstance(); return f; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); mAccountsDbAdapter = AccountsDbAdapter.getInstance(); SharedPreferences sharedPrefs = PreferenceActivity.getActiveBookSharedPreferences(); mUseDoubleEntry = sharedPrefs.getBoolean(getString(R.string.key_use_double_entry), true); } /** * Inflates the dialog view and retrieves references to the dialog elements */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_account_form, container, false); ButterKnife.bind(this, view); mNameEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //nothing to see here, move along } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { //nothing to see here, move along } @Override public void afterTextChanged(Editable s) { if (s.toString().length() > 0) { mTextInputLayout.setErrorEnabled(false); } } }); mAccountTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { loadParentAccountList(getSelectedAccountType()); if (mParentAccountUID != null) setParentAccountSelection(mAccountsDbAdapter.getID(mParentAccountUID)); } @Override public void onNothingSelected(AdapterView<?> adapterView) { //nothing to see here, move along } }); mParentAccountSpinner.setEnabled(false); mParentCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mParentAccountSpinner.setEnabled(isChecked); } }); mDefaultTransferAccountSpinner.setEnabled(false); mDefaultTransferAccountCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { mDefaultTransferAccountSpinner.setEnabled(isChecked); } }); mColorSquare.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showColorPickerDialog(); } }); return view; } /** * Initializes the values of the views in the dialog */ @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); CommoditiesCursorAdapter commoditiesAdapter = new CommoditiesCursorAdapter( getActivity(), android.R.layout.simple_spinner_item); commoditiesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mCurrencySpinner.setAdapter(commoditiesAdapter); mAccountUID = getArguments().getString(UxArgument.SELECTED_ACCOUNT_UID); ActionBar supportActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); if (mAccountUID != null) { mAccount = mAccountsDbAdapter.getRecord(mAccountUID); supportActionBar.setTitle(R.string.title_edit_account); } else { supportActionBar.setTitle(R.string.title_create_account); } mRootAccountUID = mAccountsDbAdapter.getOrCreateGnuCashRootAccountUID(); if (mRootAccountUID != null) mRootAccountId = mAccountsDbAdapter.getID(mRootAccountUID); //need to load the cursor adapters for the spinners before initializing the views loadAccountTypesList(); loadDefaultTransferAccountList(); setDefaultTransferAccountInputsVisible(mUseDoubleEntry); if (mAccount != null){ initializeViewsWithAccount(mAccount); //do not immediately open the keyboard when editing an account getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); } else { initializeViews(); } } /** * Initialize view with the properties of <code>account</code>. * This is applicable when editing an account * @param account Account whose fields are used to populate the form */ private void initializeViewsWithAccount(Account account){ if (account == null) throw new IllegalArgumentException("Account cannot be null"); loadParentAccountList(account.getAccountType()); mParentAccountUID = account.getParentUID(); if (mParentAccountUID == null) { // null parent, set Parent as root mParentAccountUID = mRootAccountUID; } if (mParentAccountUID != null) { setParentAccountSelection(mAccountsDbAdapter.getID(mParentAccountUID)); } String currencyCode = account.getCommodity().getCurrencyCode(); setSelectedCurrency(currencyCode); if (mAccountsDbAdapter.getTransactionMaxSplitNum(mAccount.getUID()) > 1) { //TODO: Allow changing the currency and effecting the change for all transactions without any currency exchange (purely cosmetic change) mCurrencySpinner.setEnabled(false); } mNameEditText.setText(account.getName()); mNameEditText.setSelection(mNameEditText.getText().length()); mDescriptionEditText.setText(account.getDescription()); if (mUseDoubleEntry) { if (account.getDefaultTransferAccountUID() != null) { long doubleDefaultAccountId = mAccountsDbAdapter.getID(account.getDefaultTransferAccountUID()); setDefaultTransferAccountSelection(doubleDefaultAccountId, true); } else { String currentAccountUID = account.getParentUID(); String rootAccountUID = mAccountsDbAdapter.getOrCreateGnuCashRootAccountUID(); while (!currentAccountUID.equals(rootAccountUID)) { long defaultTransferAccountID = mAccountsDbAdapter.getDefaultTransferAccountID(mAccountsDbAdapter.getID(currentAccountUID)); if (defaultTransferAccountID > 0) { setDefaultTransferAccountSelection(defaultTransferAccountID, false); break; //we found a parent with default transfer setting } currentAccountUID = mAccountsDbAdapter.getParentAccountUID(currentAccountUID); } } } mPlaceholderCheckBox.setChecked(account.isPlaceholderAccount()); mSelectedColor = account.getColor(); mColorSquare.setBackgroundColor(account.getColor()); setAccountTypeSelection(account.getAccountType()); } /** * Initialize views with defaults for new account */ private void initializeViews(){ setSelectedCurrency(Money.DEFAULT_CURRENCY_CODE); mColorSquare.setBackgroundColor(Color.LTGRAY); mParentAccountUID = getArguments().getString(UxArgument.PARENT_ACCOUNT_UID); if (mParentAccountUID != null) { AccountType parentAccountType = mAccountsDbAdapter.getAccountType(mParentAccountUID); setAccountTypeSelection(parentAccountType); loadParentAccountList(parentAccountType); setParentAccountSelection(mAccountsDbAdapter.getID(mParentAccountUID)); } } /** * Selects the corresponding account type in the spinner * @param accountType AccountType to be set */ private void setAccountTypeSelection(AccountType accountType){ String[] accountTypeEntries = getResources().getStringArray(R.array.key_account_type_entries); int accountTypeIndex = Arrays.asList(accountTypeEntries).indexOf(accountType.name()); mAccountTypeSpinner.setSelection(accountTypeIndex); } /** * Toggles the visibility of the default transfer account input fields. * This field is irrelevant for users who do not use double accounting */ private void setDefaultTransferAccountInputsVisible(boolean visible) { final int visibility = visible ? View.VISIBLE : View.GONE; final View view = getView(); assert view != null; view.findViewById(R.id.layout_default_transfer_account).setVisibility(visibility); view.findViewById(R.id.label_default_transfer_account).setVisibility(visibility); } /** * Selects the currency with code <code>currencyCode</code> in the spinner * @param currencyCode ISO 4217 currency code to be selected */ private void setSelectedCurrency(String currencyCode){ CommoditiesDbAdapter commodityDbAdapter = CommoditiesDbAdapter.getInstance(); long commodityId = commodityDbAdapter.getID(commodityDbAdapter.getCommodityUID(currencyCode)); int position = 0; for (int i = 0; i < mCurrencySpinner.getCount(); i++) { if (commodityId == mCurrencySpinner.getItemIdAtPosition(i)) { position = i; } } mCurrencySpinner.setSelection(position); } /** * Selects the account with ID <code>parentAccountId</code> in the parent accounts spinner * @param parentAccountId Record ID of parent account to be selected */ private void setParentAccountSelection(long parentAccountId){ if (parentAccountId <= 0 || parentAccountId == mRootAccountId) { return; } for (int pos = 0; pos < mParentAccountCursorAdapter.getCount(); pos++) { if (mParentAccountCursorAdapter.getItemId(pos) == parentAccountId){ mParentCheckBox.setChecked(true); mParentAccountSpinner.setEnabled(true); mParentAccountSpinner.setSelection(pos, true); break; } } } /** * Selects the account with ID <code>parentAccountId</code> in the default transfer account spinner * @param defaultTransferAccountId Record ID of parent account to be selected */ private void setDefaultTransferAccountSelection(long defaultTransferAccountId, boolean enableTransferAccount) { if (defaultTransferAccountId > 0) { mDefaultTransferAccountCheckBox.setChecked(enableTransferAccount); mDefaultTransferAccountSpinner.setEnabled(enableTransferAccount); } else return; for (int pos = 0; pos < mDefaultTransferAccountCursorAdapter.getCount(); pos++) { if (mDefaultTransferAccountCursorAdapter.getItemId(pos) == defaultTransferAccountId) { mDefaultTransferAccountSpinner.setSelection(pos); break; } } } /** * Returns an array of colors used for accounts. * The array returned has the actual color values and not the resource ID. * @return Integer array of colors used for accounts */ private int[] getAccountColorOptions(){ Resources res = getResources(); TypedArray colorTypedArray = res.obtainTypedArray(R.array.account_colors); int[] colorOptions = new int[colorTypedArray.length()]; for (int i = 0; i < colorTypedArray.length(); i++) { int color = colorTypedArray.getColor(i, getResources().getColor(R.color.title_green)); colorOptions[i] = color; } colorTypedArray.recycle(); return colorOptions; } /** * Shows the color picker dialog */ private void showColorPickerDialog(){ FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); int currentColor = Color.LTGRAY; if (mAccount != null){ currentColor = mAccount.getColor(); } ColorPickerDialog colorPickerDialogFragment = ColorPickerDialog.newInstance( R.string.color_picker_default_title, getAccountColorOptions(), currentColor, 4, 12); colorPickerDialogFragment.setOnColorSelectedListener(mColorSelectedListener); colorPickerDialogFragment.show(fragmentManager, COLOR_PICKER_DIALOG_TAG); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.default_save_actions, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_save: saveAccount(); return true; case android.R.id.home: finishFragment(); return true; } return super.onOptionsItemSelected(item); } /** * Initializes the default transfer account spinner with eligible accounts */ private void loadDefaultTransferAccountList(){ String condition = DatabaseSchema.AccountEntry.COLUMN_UID + " != '" + mAccountUID + "' " //when creating a new account mAccountUID is null, so don't use whereArgs + " AND " + DatabaseSchema.AccountEntry.COLUMN_PLACEHOLDER + "=0" + " AND " + DatabaseSchema.AccountEntry.COLUMN_HIDDEN + "=0" + " AND " + DatabaseSchema.AccountEntry.COLUMN_TYPE + " != ?"; Cursor defaultTransferAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition, new String[]{AccountType.ROOT.name()}); if (mDefaultTransferAccountSpinner.getCount() <= 0) { setDefaultTransferAccountInputsVisible(false); } mDefaultTransferAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), defaultTransferAccountCursor); mDefaultTransferAccountSpinner.setAdapter(mDefaultTransferAccountCursorAdapter); } /** * Loads the list of possible accounts which can be set as a parent account and initializes the spinner. * The allowed parent accounts depends on the account type * @param accountType AccountType of account whose allowed parent list is to be loaded */ private void loadParentAccountList(AccountType accountType){ String condition = DatabaseSchema.SplitEntry.COLUMN_TYPE + " IN (" + getAllowedParentAccountTypes(accountType) + ") AND " + DatabaseSchema.AccountEntry.COLUMN_HIDDEN + "!=1 "; if (mAccount != null){ //if editing an account mDescendantAccountUIDs = mAccountsDbAdapter.getDescendantAccountUIDs(mAccount.getUID(), null, null); String rootAccountUID = mAccountsDbAdapter.getOrCreateGnuCashRootAccountUID(); List<String> descendantAccountUIDs = new ArrayList<>(mDescendantAccountUIDs); if (rootAccountUID != null) descendantAccountUIDs.add(rootAccountUID); // limit cyclic account hierarchies. condition += " AND (" + DatabaseSchema.AccountEntry.COLUMN_UID + " NOT IN ( '" + TextUtils.join("','", descendantAccountUIDs) + "','" + mAccountUID + "' ) )"; } //if we are reloading the list, close the previous cursor first if (mParentAccountCursor != null) mParentAccountCursor.close(); mParentAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition, null); final View view = getView(); assert view != null; if (mParentAccountCursor.getCount() <= 0){ mParentCheckBox.setChecked(false); //disable before hiding, else we can still read it when saving view.findViewById(R.id.layout_parent_account).setVisibility(View.GONE); view.findViewById(R.id.label_parent_account).setVisibility(View.GONE); } else { view.findViewById(R.id.layout_parent_account).setVisibility(View.VISIBLE); view.findViewById(R.id.label_parent_account).setVisibility(View.VISIBLE); } mParentAccountCursorAdapter = new QualifiedAccountNameCursorAdapter( getActivity(), mParentAccountCursor); mParentAccountSpinner.setAdapter(mParentAccountCursorAdapter); } /** * Returns a comma separated list of account types which can be parent accounts for the specified <code>type</code>. * The strings in the list are the {@link org.gnucash.android.model.AccountType#name()}s of the different types. * @param type {@link org.gnucash.android.model.AccountType} * @return String comma separated list of account types */ private String getAllowedParentAccountTypes(AccountType type) { switch (type) { case EQUITY: return "'" + AccountType.EQUITY.name() + "'"; case INCOME: case EXPENSE: return "'" + AccountType.EXPENSE.name() + "', '" + AccountType.INCOME.name() + "'"; case CASH: case BANK: case CREDIT: case ASSET: case LIABILITY: case PAYABLE: case RECEIVABLE: case CURRENCY: case STOCK: case MUTUAL: { List<String> accountTypeStrings = getAccountTypeStringList(); accountTypeStrings.remove(AccountType.EQUITY.name()); accountTypeStrings.remove(AccountType.EXPENSE.name()); accountTypeStrings.remove(AccountType.INCOME.name()); accountTypeStrings.remove(AccountType.ROOT.name()); return "'" + TextUtils.join("','", accountTypeStrings) + "'"; } case TRADING: return "'" + AccountType.TRADING.name() + "'"; case ROOT: default: return Arrays.toString(AccountType.values()).replaceAll("\\[|]", ""); } } /** * Returns a list of all the available {@link org.gnucash.android.model.AccountType}s as strings * @return String list of all account types */ private List<String> getAccountTypeStringList(){ String[] accountTypes = Arrays.toString(AccountType.values()).replaceAll("\\[|]", "").split(","); List<String> accountTypesList = new ArrayList<>(); for (String accountType : accountTypes) { accountTypesList.add(accountType.trim()); } return accountTypesList; } /** * Loads the list of account types into the account type selector spinner */ private void loadAccountTypesList(){ String[] accountTypes = getResources().getStringArray(R.array.account_type_entry_values); ArrayAdapter<String> accountTypesAdapter = new ArrayAdapter<>( getActivity(), android.R.layout.simple_list_item_1, accountTypes); accountTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mAccountTypeSpinner.setAdapter(accountTypesAdapter); } /** * Finishes the fragment appropriately. * Depends on how the fragment was loaded, it might have a backstack or not */ private void finishFragment() { InputMethodManager imm = (InputMethodManager) getActivity().getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mNameEditText.getWindowToken(), 0); final String action = getActivity().getIntent().getAction(); if (action != null && action.equals(Intent.ACTION_INSERT_OR_EDIT)){ getActivity().setResult(Activity.RESULT_OK); getActivity().finish(); } else { getActivity().getSupportFragmentManager().popBackStack(); } } @Override public void onDestroy() { super.onDestroy(); if (mParentAccountCursor != null) mParentAccountCursor.close(); if (mDefaultTransferAccountCursorAdapter != null) { mDefaultTransferAccountCursorAdapter.getCursor().close(); } } /** * Reads the fields from the account form and saves as a new account */ private void saveAccount() { Log.i("AccountFormFragment", "Saving account"); if (mAccountsDbAdapter == null) mAccountsDbAdapter = AccountsDbAdapter.getInstance(); // accounts to update, in case we're updating full names of a sub account tree ArrayList<Account> accountsToUpdate = new ArrayList<>(); boolean nameChanged = false; if (mAccount == null){ String name = getEnteredName(); if (name.length() == 0){ mTextInputLayout.setErrorEnabled(true); mTextInputLayout.setError(getString(R.string.toast_no_account_name_entered)); return; } mAccount = new Account(getEnteredName()); mAccountsDbAdapter.addRecord(mAccount, DatabaseAdapter.UpdateMethod.insert); //new account, insert it } else { nameChanged = !mAccount.getName().equals(getEnteredName()); mAccount.setName(getEnteredName()); } long commodityId = mCurrencySpinner.getSelectedItemId(); Commodity commodity = CommoditiesDbAdapter.getInstance().getRecord(commodityId); mAccount.setCommodity(commodity); AccountType selectedAccountType = getSelectedAccountType(); mAccount.setAccountType(selectedAccountType); mAccount.setDescription(mDescriptionEditText.getText().toString()); mAccount.setPlaceHolderFlag(mPlaceholderCheckBox.isChecked()); mAccount.setColor(mSelectedColor); long newParentAccountId; String newParentAccountUID; if (mParentCheckBox.isChecked()) { newParentAccountId = mParentAccountSpinner.getSelectedItemId(); newParentAccountUID = mAccountsDbAdapter.getUID(newParentAccountId); mAccount.setParentUID(newParentAccountUID); } else { //need to do this explicitly in case user removes parent account newParentAccountUID = mRootAccountUID; newParentAccountId = mRootAccountId; } mAccount.setParentUID(newParentAccountUID); if (mDefaultTransferAccountCheckBox.isChecked() && mDefaultTransferAccountSpinner.getSelectedItemId() != Spinner.INVALID_ROW_ID){ long id = mDefaultTransferAccountSpinner.getSelectedItemId(); mAccount.setDefaultTransferAccountUID(mAccountsDbAdapter.getUID(id)); } else { //explicitly set in case of removal of default account mAccount.setDefaultTransferAccountUID(null); } long parentAccountId = mParentAccountUID == null ? -1 : mAccountsDbAdapter.getID(mParentAccountUID); // update full names if (nameChanged || mDescendantAccountUIDs == null || newParentAccountId != parentAccountId) { // current account name changed or new Account or parent account changed String newAccountFullName; if (newParentAccountId == mRootAccountId){ newAccountFullName = mAccount.getName(); } else { newAccountFullName = mAccountsDbAdapter.getAccountFullName(newParentAccountUID) + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + mAccount.getName(); } mAccount.setFullName(newAccountFullName); if (mDescendantAccountUIDs != null) { // modifying existing account, e.t. name changed and/or parent changed if ((nameChanged || parentAccountId != newParentAccountId) && mDescendantAccountUIDs.size() > 0) { // parent change, update all full names of descent accounts accountsToUpdate.addAll(mAccountsDbAdapter.getSimpleAccountList( DatabaseSchema.AccountEntry.COLUMN_UID + " IN ('" + TextUtils.join("','", mDescendantAccountUIDs) + "')", null, null )); } HashMap<String, Account> mapAccount = new HashMap<>(); for (Account acct : accountsToUpdate) mapAccount.put(acct.getUID(), acct); for (String uid: mDescendantAccountUIDs) { // mAccountsDbAdapter.getDescendantAccountUIDs() will ensure a parent-child order Account acct = mapAccount.get(uid); // mAccount cannot be root, so acct here cannot be top level account. if (mAccount.getUID().equals(acct.getParentUID())) { acct.setFullName(mAccount.getFullName() + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + acct.getName()); } else { acct.setFullName( mapAccount.get(acct.getParentUID()).getFullName() + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + acct.getName() ); } } } } accountsToUpdate.add(mAccount); // bulk update, will not update transactions mAccountsDbAdapter.bulkAddRecords(accountsToUpdate, DatabaseAdapter.UpdateMethod.update); finishFragment(); } /** * Returns the currently selected account type in the spinner * @return {@link org.gnucash.android.model.AccountType} currently selected */ private AccountType getSelectedAccountType() { int selectedAccountTypeIndex = mAccountTypeSpinner.getSelectedItemPosition(); String[] accountTypeEntries = getResources().getStringArray(R.array.key_account_type_entries); return AccountType.valueOf(accountTypeEntries[selectedAccountTypeIndex]); } /** * Retrieves the name of the account which has been entered in the EditText * @return Name of the account which has been entered in the EditText */ private String getEnteredName(){ return mNameEditText.getText().toString().trim(); } }