Wednesday, January 18, 2012

Detail Block Row Model and Post Update Processors

The last big release of Flex, 4.4, concerned itself principally with replacing a UI component we call the detail block, as a necessary step to closing the availability donut hole for kits and packages (which we've now closed). 

The detail block is the component used on quotes and pull sheets to represent line items.  The old detail block was also some of the oldest client side code in Flex.  It was spaghetti.  Obfuscated, brittle and hard to maintain.

It was nice to redesign it.  After living with the limitations of the old version for so long, we had a good idea what sort of things we'd need to change to make our lives easier going forward.   We chucked the separate pull sheet and quote detail blocks in favor of a unified component and gave the new detail block a modular, plugin style architecture for handling things like data loading, drag and drop, and context menus. 

We also abstracted the data model from the view component and built something we call a 'delta processor' to take line item changes from the server and apply them to the user interface.  This was one of the toughest parts of the architecture to get right, but this centralization of data model changes also permitted us to introduce a few new architectural features that I personally think have made things much easier for us day-to-day.  Some of the design changes turned out to be abstraction for the sake of abstraction, but not row model processors and post update processors.  These have saved our bacon on more than one occasion and made life much easier on the dev team.

Here's an MXML snippet from the quote detail block for consideration:

        <model:FinancialDocumentDataProvider>
           <model:rowModelProcessor>
                <coremodel:PluggableGridRowModelProcessor>
                    <coremodel:processors>
                        <mx:ArrayCollection>
                            <model:SubtotalRowProcessor/>
                            <model:WarningRowProcessor/>
                        </mx:ArrayCollection>
                    </coremodel:processors>
                </coremodel:PluggableGridRowModelProcessor>
            </model:rowModelProcessor>
            <model:postUpdateProcessors>
                <mx:ArrayCollection>
                    <project:AvailabilityPostUpdateProcessor batchSize="5"/>
                    <project:SuggestionPostUpdateProcessor/>
                    <coremodel:LineExpandPostUpdateProcessor/>
                    <model:RecalculationPostUpdateProcessor>
                    <project:ResourceAvailabilitySyncPostUpdateProcessor/>
                    </mx:ArrayCollection>
            </model:postUpdateProcessors>
        </model:FinancialDocumentDataProvider>
This code snippet shows a set of row model processors and a set of post update processors.  Their names should give you clues about how we use them in real life.

I mention this today because I had to fix an issue with PDF generation in which users may want a subtotal visible (not muted), but they may want to mute the footer or perhaps mute the discount if the subtotal contains a discount.  This involved going into the detail block and modifying the way subtotal rows get processed when a delta (a batch of data changes) comes back after the server saves the new line or price mute settings.

The tricky thing about subtotals is that the server and Flex's underlying data model only considers them one row, one discreet piece of data.  But to display a subtotal on the screen, you might need to display as many as four rows: a header, a total -- and a discount and total before discount if the subtotal's been discounted.

Rather than hack in some special case for this, we inserted a hook in the architecture -- row model processors -- where incoming row changes can be massaged and manipulated into the form needed in the user interface.  In the case of subtotals, this means one row can become four.  Having that subtotal row processor sitting there made today's line/price mute changes much easier.  And this is just one example of many cases where these processors have helped us take shortcuts without feeling icky afterward.  Row model processors are about to come in handy again as we get ready to add labor planning features to the quote UI.

The row model processors could just as easily been called pre-processors because they act on delta rows before they are passed on to the live data model.  Post update processors are invoked after the update has been applied and we use them to handle situations where a line item change might need to trigger another change elsewhere on the page.

Availability is the most obvious example.  After the line items on a quote load, the availability indicators start filling in 5 at a time.  In the code sample up above you can see the AvailabilityPostUpdateProcessor plugged into the data provider, and with a batch size of 5.  If we changed that number of 10, the availability indicators would start filling in 10 at a time.

Likewise, the SuggestionPostUpdateProcessor examines the delta rows to determine if the suggestion dialog needs to be raised.  The LineExpandPostUpdateProcessor remembers which nodes were expanded or collapsed from last time and sets them to their previous values.  The RecalculationPostUpdateProcessor triggers total recalculations if an edit action would change the total, as in changes to quantity, pricing or pricing models. (In reality, these recalculations happen when the quantity change is made.  The post processor just fetches the recalculated values from the server.)

The newest addition to the post update processor chain is ResourceAvailabilitySyncPostUpdateProcessor.  Our QA team of one, Courtney, noticed a while back that when the same item appeared twice on a quote, if you changed the quantity of one line, it would refresh the availability of the line you changed, but not the other line.  We solved this problem by introducing a new post update processor to scan the line items for duplicates of the item whose availability is changed by an edit, and refresh all the other lines that reference it.  In the 4.3 detail block, this kind of change would have been painful.  With the post update processor architecture, we were able to implement it in about half a day.


As we grow and the demand for FastTrack customizations ramps up, the pressure on the dev team to crank out more features in less time is building.  Arcane architectural flourishes like these may help us do it.

No comments:

Post a Comment