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
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.
A quick question on OOP. I am using a list together with a class and class constructor. So I use the class constructor to define the data set and then add each record to my list as the user creates them.
My questions is once the data is in the list and say I want to alter something is it good practice to find the record, create an instance using that record and then use my class methods to do whatever needs doing - and then put it back in the list?
For example below I have my class with constructor. Lets say I only want the system to release strCode if the Privacy field is set to public. Now just using Instances I would use for example Console.WriteLine(whateverproduct.ProductCode) but if the record is already in a list do i take it out of the list - create an instance and then use this method?
class Product
{
private String strCode;
private Double dblCost;
private Double dblNet;
private String strPrivacy;
public Product(String _strCode, Double _dblCost, Double _dblNet, String _strPrivacy)
{
strCode = _strCode;
dblCost = _dblCost;
dblNet = _dblNet;
strPrivacy = _strPrivacy;
}
public string ProductCode
{
get
{
if (strPrivacy == "Public")
{
return strCode;
}
else
{
return "Product Private Can't release code";
}
}
}
Lets say we have the following:
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
private string _test = "Some constant value at this point";
public string GetTest()
{
return _test;
}
public void SetTest()
{
//Nothing happens, you aren't allow to alter it.
//_test = "some constant 2";
}
}
public class Program
{
public static void Main(string[] args)
{
List<Test> listOfTest = new List<Test>()
{
new Test() {Id = 0, Name = "NumberOne", Amount = 1.0M},
new Test() {Id = 1, Name = "NumberTwo", Amount = 2.0M}
};
Test target = listOfTest.First(x => x.Id == 0);
Console.WriteLine(target.Name);
target.Name = "NumberOneUpdated";
Console.WriteLine(listOfTest.First(x => x.Id == 0).Name);
Console.WriteLine(listOfTest.First(x => x.Id == 0).GetTest());//This will alsways be "Some constant value at this point";
Console.ReadLine();
}
}
Technically you could do away with the SetTest method entirely. However, I included it to demonstrate, what it would look like, if you wanted to alter _test.
You don't want to ever create a new instance of a class, you already have an instance of. you can just alter the class where it is allowed by the author of the class, where you need to. And keep that class reference for as long as you need it. Once you are done, the reference will be garbage collected, once the program finds no active reference to your object(instance).
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.
I am trying to output from Array list to a Listbox. My problem is I think is I do not know how to connect the Class to the Generic array list a made? The end result should look like this:
And the information should be then sorted like so: all the information enters the first list box, and then the above 18 goes to adults, and the below 18 to kids. My class looks like this:
namespace Patients
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public String Password { get; set; }
public Person() //Constructor
{
Age = 0;
Password = "";
}
public Person (string name, int age, string password) //Parameters
{
this.Name = name;
this.Age = age;
this.Password = password;
}
public override string ToString() //
{
return Name + Age.ToString() + Password; //outputs as a string
// return Name + " (" + Age + " years) " + Password ;
}
}
}
namespace Patients
{
public partial class WebForm1 : System.Web.UI.Page
{
public static void Page_Load(object sender, EventArgs e)
{
}
public void ButtonAdd_Click(object sender, EventArgs e)
{
Person p = new Person();
List<string> People = new List<string>();
People.Add(TextBoxName.Text);
People.Add(TextBoxAge.Text);
People.Add(TextBoxPassword.Text);
foreach (object Person in People)
{
ListBoxAll.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
if (p.Age > 18)
{
ListBoxAdults.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
else
{
ListBoxKids.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
}
}
}
I think your problem is, that you don't set the Properties. In Fact you don't need a List at all, but you can use a List to keep hold of your patients. It's still not necessary though:
namespace Patients
{
public partial class WebForm1 : System.Web.UI.Page
{
// Define Property and initialize List
public List<Person> patients{ get; } = new List<Person>();
public static void Page_Load(object sender, EventArgs e)
{
}
public void ButtonAdd_Click(object sender, EventArgs e)
{
// Use the Constructor with Parameters
Person p = new Person(TextBoxName.Text, TextBoxAge.Text, TextBoxPassword.Text);
// Add your patient to your List
patients.Add(p);
// Use the ToString() of your Person
ListBoxAll.Items.Add(p.ToString());
if (p.Age > 18)
{
ListBoxAdults.Items.Add(p.ToString());
}
else
{
ListBoxKids.Items.Add(p.ToString());
}
}
}
}
Looks like you are mixing and matching a bit.
Try something like this.
Person p = new Person();
p.Name = TextBoxName.Text;
p.Age= TextBoxAge.Text;
p.Password= TextBoxPassword.Text;
ListBoxAll.Items.Add(p);
A few tricks that are nice to us, first off you can declare defaults for properties like so:
public string Name { get; set; } = "Steve";
public int Age { get; set; } = 1;
public String Password { get; set; } = "password";
However, it should also be noted that "" is the default for strings already and 0 is the default for non-nullable int, so you don't even need to worrying about those default values.
Declaring Age = 0; in the constructor is basically a waste of time in this case. (If it was a nullable int however the default is null)
Next up, since you are okay with defaults, you don't need to declare properties in the constructor like you are.
You can completely remove the constructor and just do the following:
var myPerson = new Person { Name = "Steve", Age = 18, Password = "Foo" };
Next up, you are losing all your existing people as soon as you exit the scope of the button click.
Instead you'll want to declare two lists of people outside the scope of the click method (that way they persist), something like "Adults" and "Children"
Then perhaps make a method called "PopulateLists" that would do the following:
Clear all list boxes
Add to each box the list of each groups names that apply (you can make an IQueryable by using Linq and Select statements on your list)
When you click the button, you should make a new person, assign it to the right list, then call PopulateLists()
Here's the info you need to get started:
Linq selection to get list of properties (in this case Im going to turn a List of People into a List of Ages, you can do the same with names though)
var ages = People.Select(p => p.Age);
The .Items property of a ListBox works the same as a list, it just visually shows itself. It's a list of strings specifically.
So for example you can do things like...
MyListBox.Items.Clear();
MyListBox.Items.Add(...);
MyListBox.Items.AddRange(...);
etc etc.
That should get you started!
I have an object which has some properties and a few of those properties are Lists. Each list contains instances of other classes. What i want to do is take the first item from a list and overwrite those property values.
Here's a pseudo example of what i have:
public class User
{
public List<Address> Addresses = new List<Address>();
public User ( )
{
Addresses = fill with data;
}
}
public class TestUser
{
public User user; // Is filled somewhere in this class
public void TestUpdateList ( Address addr )
{
// The param "addr" contains new values
// These values must ALWAYS be placed in the first item
// of the "Addresses" list.
// Get the first Address object and overwrite that with
// the new "addr" object
user.Addresses[0] = addr; // <-- doesn't work, but should give you an idea
}
}
I hope this example shed some light on what i want to do.
So i am basically looking for a way to "update" an existing item in a list, which is in this case an object.
It is not entirely clear what you are trying to accomplish, however, see the following code -- there is an Address, a User, and an utility called FeatureX that replaces the first Address of a User with a given value.
class Address {
public string Street { get; set; }
}
class User {
public List<Address> Addresses = new List<Address>();
}
class FeatureX {
public void UpdateUserWithAddress(User user, Address address) {
if (user.Addresses.Count > 0) {
user.Addresses[0] = address;
} else {
user.Addresses.Add(address);
}
}
}
The following usage outputs 'Xyz' two times:
User o = new User();
Address a = new Address() { Street = "Xyz" };
new FeatureX().UpdateUserWithAddress(o, a);
Console.WriteLine(o.Addresses[0].Street);
o = new User();
o.Addresses.Add(new Address { Street = "jjj" });
new FeatureX().UpdateUserWithAddress(o, a);
Console.WriteLine(o.Addresses[0].Street);
Be aware that public fields may cause a lot of trouble if you share your DLL with a third party.
Your example doesn't compile because you're accessing the Addresses property via class name. That is only possible if it is static. So you need an instance of a user first, to update his addresses:
User u = new User(userID); // assuming that theres a constructor that takes an identifier
u.Addresses[0] = addr;
C# Language Specification: 10.2.5 Static and instance members
I think the problem is that Addresses is a private field.
This works:
[TestFixture]
public class ListTest
{
[Test]
public void UpdateTest()
{
var user = new User();
user.Addresses.Add(new Address{Name = "Johan"});
user.Addresses[0] = new Address { Name = "w00" };
}
}
public class User
{
public List<Address> Addresses { get;private set; }
public User()
{
Addresses= new List<Address>();
}
}
public class Address
{
public string Name { get; set; }
}
public void TestUpdateList ( User user, Address addr )
{
// The param "addr" contains new values
// These values must ALWAYS be placed in the first item
// of the "Addresses" list.
// Get the first Address object and overwrite that with
// the new "addr" object
user.Addresses[0] = addr; // <-- doesn't work, but should give you an idea
}