I have a list of customers who owns three bankaccount each (credit, check, retirement) Each bankaccount is a seperate class, with a seperate withdraw method inside. I choose a person in a list (Visual studio) and can by clicking on them see their bankaccount in another list. Now i want to choose one of their account and deposit Money into it, and it is now i get stuck. The money are just beeing depostit to the first bankaccount in the list(index 0)...
I guess i have to compare the account that the customer want to deposit to with the right objects namn in my list (the right account), but cant figure out how i should write this!
the account could also be in different order so i cant just say saving = [0], check [1], retiremet [2],
This is how i call the method
validCustomer = (Customer)lstBankKunder.SelectedItem;
if (radioButtonSaving.IsChecked == true)
{
savingAccount = new SavingsAccount();
savingAccount.DepositMoney(299, valdCustomer);
lstKonton.ItemsSource = null;
lstKonton.ItemsSource = valdCustomer.myBankAccount;
}
And the SavingsAccount class
public override void DepositMoney(int money, Customer validCustomer)
{
savingsaccount = new SavingsAccount();
var item = validCustomer.myBankAccount[savingsaccount.Balance];
item.Balance += money;
validCustomer.myBankAccount[savingsaccount.Balance] = item;
}
I think the error is in your DepositMoney method. You are creating a new savings account, every time the method is called and I guess, that the initial balance is 0. But then you are using this balance to select a bank account:
valdCustomer.myBankAccount[savingsaccount.Balance]
With this logic this would always be the first account in the list.
There are more than one issue with your code. You are creating unnecessary objects of SavingsAccount class at various places.
You haven't shared the code of Customer class so I am not sure what is the type of myBankAccount property.
You need a good way to select a proper account from the customer's multiple account based on the accounttype and then call deposit or withdraw method on that account.
You can use enum for this. Let say you have an enum AccountType as following which identifies the type of account.
public enum AccountType
{
Credit,
Check,
Retirement
}
And then have a property of AccountType in the Account.
public class BankAccount
{
protected double balance;
protected int accountNumber;
protected AccountType accountType;
public BankAccount(int accountNumber, AccountType accountType)
{
this.accountNumber = accountNumber;
this.accountType = accountType;
}
public virtual void DepositMoney(double amount)
{
this.balance += amount;
}
public virtual void WithdrawMoney(double amount)
{
this.balance -= amount;
}
public double Balance
{
get
{
return this.balance;
}
}
public AccountType AccountType
{
get
{
return this.accountType;
}
}
public override string ToString()
{
var output = new StringBuilder();
output.Append(string.Format("Account Number : {0}{1}", this.accountNumber, Environment.NewLine));
output.Append(string.Format("Account Type : {0}{1}", this.accountType, Environment.NewLine));
output.Append(string.Format("Account Balance : {0}{1}", this.balance, Environment.NewLine));
return output.ToString();
}
}
And in Customer class you can have List of accounts to represent the accounts belonging to that customer.
public class Customer
{
private List<BankAccount> accounts;
public Customer()
{
this.accounts = new List<BankAccount>();
}
public string Name {get;set;}
public List<BankAccount> Accounts
{
get
{
return this.accounts;
}
}
public override string ToString()
{
var output = new StringBuilder();
output.Append(string.Format("Customer Name : {0}{1}", this.Name, Environment.NewLine));
output.Append(string.Format("Accounts details {0}", Environment.NewLine));
foreach(var account in this.accounts)
{
output.Append(account.ToString());
}
return output.ToString();
}
}
And then you can locate specific type of account of customer and perform deposit or withdraw operation on it as following.
var checkAccount = customer.Accounts.FirstOrDefault(acct => acct.AccountType == AccountType.Check);
if(checkAccount != null)
{
checkAccount.DepositMoney(3000);
}
FirstOrDefault is an extension method which is part of LINQ. You need to add using System.Linq; in the using directives of the code file whereever you are using it.
Note : The names of class, methods and properties are different in my answer then your class names.
I hope this helps you resolve your issue.
EDIT : Edited the answer to have List of accounts in Customer class instead of Dictionary.
Related
I made a custom selector that only displays the customers of the current user but when I select a customer I get the error: 'Customer' Cannot be found in the system.
The code for the Custom selector and how I implemented it on the DAC:
[PXNonInstantiatedExtension]
public class SO_SOOrder_ExistingColumn : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region CustomerID
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXForeignReference(typeof(Field<SOOrder.customerID>.IsRelatedTo<BAccount.bAccountID>))]
[SalesRepCustomer]
public int? CustomerID { get; set; }
#endregion
}
public class SalesRepCustomer : PXCustomSelectorAttribute
{
public SalesRepCustomer() : base(typeof(Customer.acctCD))
{
this.DescriptionField = typeof(Customer.acctCD);
}
protected virtual IEnumerable GetRecords()
{
foreach (Customer pc in PXSelect<Customer>.Select(this._Graph))
{
//Getting Current UserID
var cache1 = _Graph.Caches[BqlCommand.GetItemType(typeof(AccessInfo.userName))];
AccessInfo currentCacheObjecta = (AccessInfo)cache1.Current;
var userName = currentCacheObjecta.UserName;
SalesPerson person = PXSelect<SalesPerson, Where<SalesPerson.descr, Equal<Required<AccessInfo.userName>>>>.Select(_Graph, userName);
if (person != null)
{
CustSalesPeople custSalesPeople = PXSelect<CustSalesPeople, Where<CustSalesPeople.salesPersonID, Equal<Required<SalesPerson.salesPersonID>>, And<CustSalesPeople.bAccountID, Equal<Required<CustSalesPeople.bAccountID>>>>>.Select(_Graph, person.SalesPersonID, pc.BAccountID);
//return all customers related to this SalesPersonID in the CustSalesPeople table
if (!(custSalesPeople is null))
{
yield return pc;
}
}
else
{
//current user is not a sales person
//return all of the customers
yield return pc;
}
}
}
}
Screenshot of the selector on the Sales Order Screen:
Any help on this will be appreciated
You should have the constructor look something like
public SalesRepCustomer() : base(typeof(Customer.bAccountID))
{
this.DescriptionField = typeof(Customer.acctName);
this.SubstituteKey = typeof(Customer.acctCD);
}
The first type that you pass to the base constructor is the type of the value that will be used for the field.
In this case you want the customer id(an int) and you are currently using the AcctCD(a string) field. The description field would typically be the name of the customer account and the substitute key will make it so that the users see the AcctCD instead of the the customer ID(the actual value) which is just an integer.
I'm having some trouble in allowing a Staff or a Person to "own" an account as per details below.
In the snippet below, on the Account class, I only accept Person as the owner. I kind of need to accept either a Staff or a Person
My main issue is that, later in the method applyFee(), I need to reach out the the owner object and if the owner has a feeDiscount property, I will need to use to calculate.
My issue is that since in the Account class the type is Person owner I am not getting the feeDiscount as it is null.
class Person
{
public string name;
public Person(string newName)
{
name = newName;
}
}
class Staff : Person
{
public decimal feeDiscount;
public override Staff(string newName)
{
name = newName;
feeDiscount = 0.5;
}
}
class Account
{
private decimal balance = 1000;
private Person owner;
public Account(Person newOwner)
{
owner = newOwner;
}
public void applyFee() {
decimal fee = 100;
if (owner != null)
{
if (owner.feeDiscount) {
balance = balance - (fee * owner.feeDiscount);
} else {
balance = balance - fee;
}
}
}
}
class Program
{
static void Main(string[] args)
{
Person person1 = new Person("Bob");
Staff staff1 = new Staff("Alice");
Account account1 = new Account(person1);
Account account2 = new Account(staff1);
account1.applyFee();
account2.applyFee();
}
}
If you want Person to remain as generic as possible, then you could make another class called customer who has a feeDiscount of 0.
So anyone who has any business spending money at a store, has some feeDiscount. This way, you can applyFee to a Customer or a Staff but not a Person
The problem I am having is displaying items from my list in ascending order. I understand the problem that is creating this issue which is I am adding caccounts to the List and then adding saccounts to the list but i am not sure of any other way I can do this. The outcome should be to display the accounts in whatever order they were created which is dependent on the user. So as you see by the image below it is storing and displaying all created checking accounts first then savings accounts which is incorrect. It should show the in the order they were created. As you see from the image below the items are out of order. It should show Savings account 1 first then Checking account 2 and so on.
This is the code that is adding the items to this list and is creating the problem
List<Account> accounts = new List<Account>();
accounts.AddRange(caccounts);
accounts.AddRange(saccounts);
foreach (Account account in accounts)
{
List<Transaction> transactions = account.closeMonth();
allTransactions.AddRange(transactions);
}
This code shows the list I am adding saccounts and caccounts to
List<SavingsAccount> saccounts = new List<SavingsAccount>();
List<CheckingAccount> caccounts = new List<CheckingAccount>();
List<Transaction> allTransactions = new List<Transaction>();
This is code that I have in my checking class and savings class that override close month in the abstract account class
public override List<Transaction> closeMonth()
{
var transactions = new List<Transaction>();
var endString = new Transaction();
string reportString = ("Checking account: " + AccountID.ToString() +
" has a balance of $" + endingBalance.ToString());
endString.EndOfMonth = reportString;
transactions.Add(endString);
return transactions;
}
This is the property for the AccountID and i have this in the checking and savings class
class SavingsAccount : Account
{
public override int AccountID { get; set; }
}
When the account is originally created this is the code that assigns the AccountID
if (checkingRadioButton1.Checked == true)
{
_nextIndex++;
transactionLabel5.Text = "Checking Account: #" + _nextIndex +
" created with a starting balance of $" + balance;
accountTextBox1.Text = "" + _nextIndex;
caccounts.Add(new CheckingAccount(balance)
{
AccountID = _nextIndex,
Student = isStudent
});
}
else if (savingsRadioButton2.Checked == true)
{
_nextIndex++;
transactionLabel5.Text = "Savings Account: #" + _nextIndex +
" created with a starting balance of $" + balance;
accountTextBox1.Text = "" + _nextIndex;
saccounts.Add(new SavingsAccount(balance)
{
AccountID = _nextIndex,
Senior = isSenior
});
}
You could use OrderBy on the collection.
var orderedTransactions = allTransactions.OrderBy(x=>x.AccountId).ToList();
Of course you would need to have that CreateDate or whatever property you want ordered by in your object.
Instead of using this code to merge accounts into one list
List<Account> accounts = new List<Account>();
accounts.AddRange(caccounts);
accounts.AddRange(saccounts);
foreach (Account account in accounts)
{
List<Transaction> transactions = account.closeMonth();
allTransactions.AddRange(transactions);
}
you can use this code
List<Account> accounts = new List<Account>();
int saccountsAdded = 0;
int caccountsAdded = 0;
for (int i = 0; i < saccounts.Count + caccounts.Count; i++)
{
if(saccountsAdded == saccounts.Count)
{
accounts[i] = caccounts[caccountsAdded++];
continue;
}
if (caccountsAdded == caccounts.Count)
{
accounts[i] = saccounts[saccountsAdded++];
continue;
}
if (saccounts[saccountsAdded].AccountID > caccounts[caccountsAdded].AccountID)
{
accounts[i] = caccounts[caccountsAdded ++];
}
else
{
accounts[i] = saccounts[saccountsAdded ++];
}
}
In my case, you are sorting accounts when you are combining them and then your accounts list will be ordered.
You can also use the solution from NoSaidTheCompiler, the only difference is that I am sorting and populating the list at the same time and he firstly populates the list and sorts after the list has been populated
The short answer is that you can order your Transactions by the AccountID using the System.Linq extension method OrderBy (along with a .SelectMany method call to flatten each account's transaction list) before you output them:
List<Transaction> transactions = accounts
.OrderBy(a => a.AccountID)
.SelectMany(a => a.closeMonth())
.ToList();
Other thoughts
As I was answering your question, I couldn't figure out why Account was abstract, or why there was a separate class for CheckingAccount and SavingsAccount, because I couldn't see a difference between the two and it felt so redundant to override all these abstract properties and methods with the exact same code in two different classes.
With that in mind, I felt compelled to share some thoughts which may or may not apply to your specific situation. Note that I have never written a banking app before, so there are probably many things I'm not thinkning of - just wanted to share this in case it helps.
Accounts
To me, an account is an account. There may be different rules associated with one type or another, but those can be defined by the Bank that creates the accounts, and the class could have a List<Rule> AccountRules that would be set based on that particular bank's policies (i.e. perhaps there is a minimum balance for a Checking account, but not a Savings account, or they have different interest rate schedules, or something else, but nothing that I can think of that is inherently different).
But in the end, an account is a repository for a decimal amount which can be changed by a Withdrawl or a Deposit (or an Interest accruement).
Transactions
Also, the closeMonth() method seemed really strange to me. It returns a List<Transaction> that contains a single item. Instead, I think an Account should have a List<Transaction> that gets populated from every public method - even balance inquiries. Then this list can be queried to get the activities within a particular time frame, such as a month or a year (by filtering on the Transaction.Timestamp property). Or it could be queried to get different types of transactions (by filtering on the Transaction.TransactionType property).
To simplify adding a new transaction to the list, I added a private AddTransaction method that takes in a Transaction.Type and a description of the transaction. It then creates the Transaction and adds it to the list. Also notice that the Transaction class automatically adds the current DateTime as a Timestamp, so there's no need to worry about that on the client side.
Code
To prevent race conditions where Deposit and Withdrawl are called at the same time from different threads, I added some lock() blocks around any code that accessed Balance.
I also added an override to the ToString method on the Transaction class so that it was easier to output the details of each transaction.
Another item of interest is that you can format a decimal as currency by using the C format string (i.e. if you have decimal balance = 5M; you can do balance.ToString("C") or "{balance:C}" and the string result will be "$5.00" in the U.S. or "£5.00" in England).
So the classes might then look something like the following:
public class Transaction
{
public enum Type { Open, Close, Deposit, Withdrawl, Inquiry, Other }
public Type TransactionType { get; }
public string Description { get; }
public DateTime Timestamp { get; }
public Transaction(Type transactionType, string description)
{
TransactionType = transactionType;
Description = description;
Timestamp = DateTime.Now;
}
public override string ToString()
{
return $"{Timestamp}: {TransactionType} - {Description}";
}
}
public class Account
{
public enum Type { Checking, Savings }
public Type AccountType { get; }
public int Id { get; }
public List<Transaction> Transactions { get; private set; }
private static readonly object IdLock = new object();
private static int _lastAccountId;
private readonly object balanceLock = new object();
private decimal balance;
public Account(decimal initialDeposit, Type accountType)
{
if (initialDeposit < 0)
{
throw new ArgumentOutOfRangeException("initialDeposit cannot be negative");
}
AccountType = accountType;
balance = initialDeposit;
lock (IdLock)
{
Id = ++_lastAccountId;
}
AddTransaction(Transaction.Type.Open,
$"{AccountType} account Id {Id} created with a deposit of: {balance:C}.");
}
public void Deposit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException("Deposit amount cannot be negative");
}
lock (balanceLock)
{
balance += amount;
AddTransaction(Transaction.Type.Deposit,
$"{amount} was deposited. New balance: {balance:C}.");
}
}
public void Withdraw(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException("Withdrawl amount cannot be negative");
}
if (amount > balance)
{
throw new ArgumentOutOfRangeException(
"Cannot withdraw more money than the account contains");
}
lock (balanceLock)
{
balance -= amount;
AddTransaction(Transaction.Type.Withdrawl,
$"{amount} was withdrawn. New balance: {balance:C}.");
}
}
public decimal GetBalance()
{
lock (balanceLock)
{
AddTransaction(Transaction.Type.Inquiry,
$"Balance inquiry made. Balance: {balance:C}.");
return balance;
}
}
private void AddTransaction(Transaction.Type transactionType, string description)
{
if (Transactions == null) Transactions = new List<Transaction>();
Transactions.Add(new Transaction(transactionType, description));
}
}
So, with all that, here's an example that populates a list of accounts in the same order that you have shown in your form above, with an example of how you can output all the transactions for the accounts ordered by the account id:
static void Main(string[] args)
{
// Adding accounts in the same order as they appear on your form
var accounts = new List<Account>
{
new Account(293.05M, Account.Type.Checking),
new Account(293.05M, Account.Type.Checking),
new Account(300M, Account.Type.Savings),
new Account(300M, Account.Type.Savings),
};
// Sort transactions by account Id
List<Transaction> transactions = accounts
.OrderBy(a => a.Id)
.SelectMany(a => a.Transactions)
.ToList();
// Write results to Console
transactions.ForEach(Console.WriteLine);
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
I don't know if I have chosen a correct headline, but I do hope I have.
I am currently trying to get a better understand of methods in C#, and in doing so I thought that I'd make a simple BankAccount example.
So, what I have is this:
I have three methods:
a method to "deposit" money.
a method to "withdraw" money.
a method to print everything (name, balance).
class BankAccount
{
public string Name
{
get { return _Name; }
set { _Name = value; }
}
private string _Name;
public int Balance
{
get { return _Balance; }
set { _Balance = value; }
}
private int _Balance;
public BankAccount(string name)
{
Name = name;
Balance = 1000;
}
// deposit money
public int Deposit(int balance)
{
Balance += balance;
return Balance;
}
// withdraw money
public int WithDraw(int balance)
{
Balance -= balance;
return Balance;
}
// print to console
public void Print()
{
Console.WriteLine("Owner: " + Name
+ "\nYour current balance is: $" + Balance);
}
}
What I want to do is this:
if I call "deposit" in Main and pass it a value, I want the print method to show me the amount (same goes for "withdraw").
How do I achieve this? I have tried some controle statements, but I don't know how to do this correctly with methods that have parameters?
I hope that someone can shed some light on this issue of mine.
What you can do is overload the method to do more than one thing, for example you can create an overload that takes an int (the balance being subtracted or added) and a string saying which action happening, then you can have this method in the code along with the already existing one
public void Print(int balance, string action)
{
Console.WriteLine("Owner: " + Name
+ "\nYour current balance is: $" + Balance
+ "and you " + action + ": $" + balance);
}
This could be used by passing the string action as "withdrew" or "deposited" depending on which method calls it.
Using this overload allows you to both output the original Print method, if they want to know their balance but never withdrew or deposited, and the new version all depending on which parameters you pass
For more information on overloading see this MSDN page
Example Usage:
public int Deposit(int balance)
{
Balance += balance;
Print(balance, "deposited"); //prints current balance AND action that was completed
return Balance;
}
public void ShowBalance()
{
Print(); //Just prints current balance
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodyGarrettEX3
{
public class Account
{
private long acctNumber;
protected double balance;
public Savings SavingsAccount;
public Checking CheckingAccount;
public Account()
{
this.acctNumber = 1234;
Savings SavingsAccount = new Savings(acctNumber);
Checking CheckingAccount = new Checking(acctNumber);
}
public Account(long newAcctNo)
{
this.acctNumber = newAcctNo;
Savings SavingsAccount = new Savings(newAcctNo);
Checking CheckingAccount = new Checking(newAcctNo);
}
//Members
public long AcctNo
{
get { return AcctNo; }
set { AcctNo = value; }
}
public double Balance
{
get { return balance; }
set { balance = value; }
}
public virtual double Withdrawal(double amount)
{
if ((balance - amount) < 0)
{
PolicyException insufficientFundsException = new PolicyException("Insufficient Funds To Complete This Transaction");
throw insufficientFundsException;
}
return balance;
}
public double Deposit(double amount)
{
balance = balance + amount;
return balance;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodyGarrettEX3
{
public class Savings: Account
{
private double interestRate;
private double minBalance;
public Savings(long newAcctNumber):base(newAcctNumber)
{
balance = 0;
interestRate = 0;
minBalance = 0;
}
//Properties
public double InterestRate //This is a correctional to ensure that the interest rate is always stored as a decimal for math calculations
{
get { return interestRate; }
set
{
if (value > 0)
{
value = value / 100;
}
interestRate = value;
}
}
public double MinBalance
{
get { return minBalance; }
set { minBalance = value; }
}
public override double Withdrawal(double amount)
{
if (minBalance > 0)
{
if (amount > (balance - minBalance))
{
PolicyException minBalanceException = new PolicyException("Insufficient Funds to Complete This Transaction, Exceeds Account Balance and Minimum Balance of Account");
throw minBalanceException;
}
}
else
{
if(amount > (balance - amount))
{
PolicyException insufficientFundsException = new PolicyException("Insufficient Funds to Complete This Transaction, Exceeds Account Balance");
throw insufficientFundsException;
}
}
return balance;
}
}
}
The main issue as it would seem is that upon compilation of the class that manipulates my parent Which is the Account Class is that I get a really odd loop that just goes from the Account Constructor to the Savings Constructor rapidly and then causes a stack overflow.
This happens after the execution of "Account BankAccount = new Account(9999);" in my program testing class.
Thank you so much for your help everyone! I'm really struggling to figure out what is the cause here.
Also, side note I have the constructor built that way due to a requirement in an assignment that mandates we have to have the acctNumber variable pass into the object before it can be created. Even if the object doesn't use the value. However, if this is not possible I am open to anything really.
The loop isn't so odd.
In Savings, you are inheriting from Account. The constructor of Savings also calls the constructor of Account.
The constructor of Account creates a new "Savings" object. The constructor of this new savings object wants to pass data to the constructor of Accounts, which wants to create a new Savings object. Rince and repeat. This will go on and on and on.
It looks like a code smell that the base class (Account) creates (and knows about) the inheriting classes (like Savings).
Edit:
I don't know the details of your requirements, but as I'm looking at this the design is not appropriate for your problem.
You've got your bank account, and this bank account can be a Savings-type account.
class Account
{
protected int _accNr;
public Account()
{
_accnr = 1234;
}
public Account(int accNr)
{
_accNr = accNr;
// only do basic 'account' stuff
}
}
class Savings : Account
{
public Savings() : base()
{
// do other stuff
}
public Savings(int accNr) : base(accNr)
{
// do other stuff
}
}
Now, I've only set up the constructors and nothing else. Point is that now you can either create a Savings class with, or, without an account nr by calling:
Account a = new Savings();
or
Account b = new Savings(9999);
Now, responsabilities are split between account- and savings-classes. The Account class doesn't want to know about inheriting classes and their responsibilities. Through polymorphism we can create Accounts of type 'Savings', but you could also ignore that and do Savings a = new Savings(9999).