I have moved a lot of the data structures around this morning.
Kind of Entity Framework Lego building.
Looks like I forgot to put
[DefaultProperty("Code")]
on my GstCategory class
Now I am puzzling over how to do a data entry screen for a bank statement.
Bank statements are formatted with a debit and credit column.
Cash book programs generally show a debit , credit, and account column.
This is not my data structure , and I want the data structure how it is ( so every amount has a debit and a credit account )
to make the detail list view display like a cash book I am going to add some extra properties
I use the interface here just to put a name to my thinking
public interface ICashbookLine
{
Account Account { get; set; }
Decimal DebitAmount { get; set; }
Decimal CreditAmount { get; set; }
}
Then I implement them inside the Transaction class
[NotMapped]
public Account Account
{
get => Amount > 0 ? DebitAccount : CreditAccount;
set
{
if (Amount > 0)
{
DebitAccount = value ;
}
CreditAccount = value;
HiddenAccount = TranHeader.LinkedAccount;
}
}
[NotMapped]
public Account HiddenAccount // other side of the transaction = header linked account
{
get => Amount > 0 ? CreditAccount : DebitAccount;
set
{
if (Amount > 0)
{
CreditAccount = value;
}
DebitAccount = value;
}
}
[NotMapped]
public decimal CreditAmount {
get => Amount > 0 ? 0 : 0- Amount;
set => Amount = 0 - value;
}
[NotMapped]
public decimal DebitAmount
{
get => Amount > 0 ? Amount : 0;
set => Amount = value;
}
[Browsable(false)]
[RuleFromBoolProperty("AccountOk", DefaultContexts.Save, "Account must not be a header account")]
public bool AccountOk => Account.Header == false;
I did try experimenting with omitting the header records from the pick list by using a DataSourceProperty attribute and a function to create a list of the non header accounts.
However I found that caused the down arrow to behave strangely. Besides it is convenient to see the tree structure.
https://www.devexpress.com/Support/Center/Question/Details/T353187/how-to-make-more-of-a-filter-datasourceproperty
This makes the transaction screen look like a cash book, but what if the header account gets changed?
Well we need to update all the hidden accounts.
I wanted to temporarily see the HiddenAccount in the child listview , so I could be sure it was updating.
After experimenting I found that I needed to call OnPropertyChanged at the end of the setter for Transaction.HiddenAccount and decorate TranHeader.LinkedAccount with [ImmediatePostData]
So In TranHeader.cs I have
private Account _linkedAccount;
[ImmediatePostData]
public virtual Account LinkedAccount {
get => _linkedAccount;
set
{
_linkedAccount = value;
foreach (var tran in Transactions)
{
tran.HiddenAccount= _linkedAccount;
}
}
}
and in Transaction I have
[NotMapped]
public Account HiddenAccount // other side of the transaction = header linked account
{
get => Amount > 0 ? CreditAccount : DebitAccount;
set
{
if (Amount > 0)
{
CreditAccount = value;
}
DebitAccount = value;
OnPropertyChanged();
}
}
Note OnPropertyChanged is required by the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Now experimenting with making the data entry like a bank statement page with an opening and closing balance.
For this I will need to order all the transactions so I can calculate the total of the previous pages.
For the Transaction Header
I want to remember the last date entered in this data entry session.
So I look up the singleton pattern
public override void OnCreated()
{
if ( InstanceWideMemvars.Instance.EntryDate == null);
{
InstanceWideMemvars.Instance.EntryDate = DateTime.Now;
}
Date = InstanceWideMemvars.Instance.EntryDate;
base.OnCreated();
}
How to set the Opening Balance?
I decided this way
private Account _linkedAccount;
[ImmediatePostData]
[XafDisplayName("Account")]
public virtual Account LinkedAccount {
get => _linkedAccount;
set
{
_linkedAccount = value;
foreach (var tran in Transactions)
{
tran.HiddenAccount= _linkedAccount;
}
OpeningBalance = LinkedAccount != null ? HandyFunctions.GetOpeningBalance(this) : 0;
}
}
where
public static decimal GetOpeningBalance(TranHeader header)
{
using (var db = new GLDbContext())
{
try
{
if (header.LinkedAccount == null) return 0;
var credits = db.Transactions
.Where(x => x.CreditAccount.Id == header.LinkedAccount.Id && x.TranHeader.StatementNumber < header.StatementNumber);
var creditTotal = credits.Any() ? credits.Sum(y => y.Amount) :0;
var debits = db.Transactions
.Where(x => x.DebitAccount.Id == header.LinkedAccount.Id && x.TranHeader.StatementNumber < header.StatementNumber);
var debitTotal = debits.Any() ? debits.Sum(y => y.Amount) : 0;
return header.LinkedAccount.OpeningBalance + creditTotal - debitTotal;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
Note if there is an error then XAF simply does not display the record. I put the handler in as a convenient spot to put a break point.
Now to find out how to make the StatementNumber not look like a currency.
[ModelDefault(ModelDefaultConstants.EditMask, ModelDefaultConstants.EditMaskGeneral)]
[ModelDefault(ModelDefaultConstants.DisplayFormat,ModelDefaultConstants.DisplayFormatGeneral)]
public decimal StatementNumber { get; set; }
I have started this class so I don’t need to remember the property names
public static class ModelDefaultConstants
{
public const string AllowEdit = "AllowEdit";
public const string IsFalse = "false";
public const string IsTrue = "true";
public const string EditMask = "EditMask";
public const string DisplayFormat = "DisplayFormat";
public const string EditMaskGeneral = "g";
public const string DisplayFormatGeneral = "{0:g}";
}