SBD.GL General Ledger: Part 2

xaf
working-out-loud

#1

Ah, a new day and a new approach now that I have a work around for the last bug.
Lets make that nice and explicit.

Here is my new controller to work on any object that implements IHCategory and IObjectSpaceLink

 public partial class ICategoryController : ViewController
    {
        private NewObjectViewController controller;

        public ICategoryController()
        {
            InitializeComponent();
            TargetObjectType = typeof(IHCategory);
        }

        protected override void OnActivated()
        {
            controller = Frame.GetController<NewObjectViewController>();
            controller.ObjectCreated += controller_ObjectCreated;
            base.OnActivated();
        }

        private void controller_ObjectCreated(object sender, ObjectCreatedEventArgs e)
        {
            SetParent(e, View);
        }

        private static void SetParent(ObjectCreatedEventArgs e, View view)
        {
            var createdObject = e.CreatedObject as IHCategory;

            WorkAroundBug(createdObject);

            var propertyCollectionSource = (view as ListView)?.CollectionSource as PropertyCollectionSource;
            if (!(propertyCollectionSource?.MasterObject is IHCategory master)) return;

            var m = e.ObjectSpace.GetObject(master);
            createdObject.Parent = m;
            m.Children.Add(createdObject);
        }

        private static void WorkAroundBug(IHCategory createdObject)
        {
            // https://www.devexpress.com/Support/Center/Question/Details/T704563/xaf-entity-framework-self-referencing-table-example-with-aggregated-attribute-for
            var c2 = createdObject as IObjectSpaceLink;
            // Do not comment out. This is a bug work around
            var numModifiedObjects = c2.ObjectSpace.ModifiedObjects.Count;
        }

        protected override void OnDeactivated()
        {
            controller.ObjectCreated += controller_ObjectCreated;
            base.OnDeactivated();
        }
    }

#2

Now to fix up the Accounts class

I want Notes to act like a memo so I add the ModelDefault RowCount attribute.

        [ModelDefault("RowCount","5")]
        public string Notes { get; set; }

I want a rule that if the account has a parent, then the category must match the parent category … hmm.

This takes care of the category initialization.

        [Browsable(false)]
        public int Category {
            get => Parent?.Category ?? _category;
            set => _category = value; 
        }

But what if the parent changes?

I can put a validation rule in

        [Browsable(false)]
        [NotMapped]
        [RuleFromBoolProperty("ParentCategoryOk", DefaultContexts.Save, "Parent Category if present must match")]
        public bool ParentCategoryOk
        {
            get
            {
                if (Parent == null) return true;
                return Parent.Category == Category;
            }
        }

But if the parent changes then what?
Well if I put the ImmediatePostData attribute on the parent, then it will update the Category

        [ImmediatePostData]
        public virtual Account Parent { get; set; }

Now , we want some accounts simply to be there for grouping.
These grouping accounts should not have an opening balance ( i.e Opening Balance = zero)

That means if we try to add a child to an account that has an opening balance not equal to zero, that should fail…

I could do this…

        [RuleFromBoolProperty("ValidOpeningBalance", DefaultContexts.Save, "Only Zero Opening balance accounts can have child accounts")]
        public bool ValidOpeningBalance
        {
            get
            {
                if (Children.Count == 0) return true;
                return OpeningBalance == 0;
            }
        }

However since this validation is on a parent it actually does not stop a child being added so I also need

        [RuleFromBoolProperty("ValidParentOpeningBalance", DefaultContexts.Save, "The parent account must have zero opening balance.")]
        public bool ValidParentOpeningBalance
        {
            get
            {
                if (Parent == null) return true;
                return Parent.OpeningBalance == 0;
            }
        }

I am thinking it might be easier to have a Header check box in the Accounts
so as to avoid queries needing to calculate it all the time.

        public bool Header { get; set; }

        [Browsable(false)]
        public decimal OpeningBalance { get; set; }

        [NotMapped]
        public decimal Opening_Balance {
            get => Header ? 0 : OpeningBalance;
            set => OpeningBalance = value;
        }

And yes, adding the Header property then caused the need to create and run a migration… but that is small potatoes.


#3

Now we only want the Category to be enabled if the account has no parent.

I guess there is the XAF conditional appearance options

This looks like what I need

   [Appearance("NotIsRoot", Criteria = "Parent_Id != null", AppearanceItemType = "ViewItem", TargetItems = "GlCategory", Enabled = false, Context = "DetailView")]

put this attribute on the class not the property.
I had no luck with “Parent != null” for the criteria.

Um but it won’t work yet because I did not add the Conditional Apperance Module.
https://documentation.devexpress.com/eXpressAppFramework/113286/Concepts/Extra-Modules/Conditional-Appearance-Module-Overview

I wound up creating a test solution with the Conditional Criteria option checked, then searching for ConditionalCriteria and copying in to my project.


#4

Note putting this in the DbContext initialization is a bad idea as it prevents the Master Detail forms from showing the Details

.Configuration.ProxyCreationEnabled = false;