Interesting implementation question

Interesting implementation question

Old forum URL: forums.lhotka.net/forums/t/5365.aspx


white911 posted on Tuesday, September 09, 2008

Hi,

I am creating a receive payment screen, where the user can enter a payment amount and apply it to open invoices.
The screen has input boxes for the payment info and a grid with a list of open invoices. The grid needs to be editable since we need to add a check box to select/deselect an invoice line. Also we may change the amount applied to an invoice.

My Question:

How do I go about with objects. What I did is I loaded a readonly list and then converted it to a dataset using the mapper and added a column for select/unselect.
In the front end I handled the Select/Unselect actions to apply an amount to an invoice or zero out.

However, I think I am doing it wrong, since I am using a dataset and I am putting alot of business logic in the front end code. Does it make sense to use a regular business base list for such a scenario and handle the business logic in the object?

Thanks in advance

Peter

shane.sanders replied on Tuesday, September 09, 2008

Normally you just set the grid's data-source to the business object.

gvGridView.DataSource = myObject;


Add an extra column in.  Iterate through the DataColumns and Cells determine which has been edited and handle it.  DataKey's help here if I remember correctly.

There are infinite many ways to do something.  No real wrong or right way.
Everything has drawbacks.





JoeFallon1 replied on Tuesday, September 09, 2008

I use editable grids all over the place in my web app.

I also have a checkbox column labeled "Include?" which is used to include that row or not in the Save. I wanted to leave the rows on screen when Include is unchecked, so the user could re-check it if they wanted to. I did not want to remove the row just because it was unchecked.

I use ECCs, Editable Child Collections, as the data source to the grid.

The user can edit all rows in the grid at once - they are all text boxes and dropdowns. So they can add rows, uncheck rows, fill in rows, whatever. I do not have a Read Only grid and force the user to Edit a single row at a time.

My BOs handle as much logic as possible. The screen logic only takes care of displaying the BO data.

HTH

Joe

 

SonOfPirate replied on Wednesday, September 10, 2008

I guess a lot of it depends on where you want to put your processing.  If the idea is that the user clicks an "Apply" button (or something similar) and the call is routed to the Save or Apply method on your business object, then having a manual column in your grid doesn't work because your business object shouldn't know anything about the grid and would, therefore, have no way of finding out what items were selected.  On the other hand, you could have the logic for applying the value in the button's Click event-handler, which goes against the idea of encapsulating the logic in the BO.

I prefer the first approach and the way I've handled this situation is by implementing a set of business objects specifically for the use-case with the necessary properties, etc. to satisfy the requirement.  These would include:

Your form should be bound to the PaymentInfo object which will have a PaymentAmount property and the Save method called when the Apply or Save button is clicked.  The grid is bound to the OpenInvoices property of the PaymentInfo object.

The OpenInvoice class would include an editable Selected (bool) property that can be bound to a checkbox column in your grid (along with any other read-only properties for an invoice to display).

Finally, a helper method, GetSelectedItems(), on the OpenInvoiceList class can be used within the PaymentInfo object's Save method to perform the actual task.

Another consideration would be adding logic in the OpenInvoiceList that maintains an aggregate sum of the selected invoices.  This could be handled in the OnListChanged method with the sum increased or decreased when ListChangedType == ItemChanged and the property is the Selected property.  This would allow you to add a validation rule to your PaymentInfo object that ensures the total of all selected invoices is greater than or equal to the payment amount before you allow the user to save the payment.

Not only does this satisfy your use case but keeps your UI code clean and minimal.  Plus it makes it easy to implement additional features such as validation.

HTH

 

white911 replied on Thursday, September 11, 2008

Thanks SonOfPirate. Sounds great.

1 question only

Open Invoice is a child of what??? if it's a child from OpenInvoiceList, I would not be able to select or unselect the line because it's read only, and the grid is bound to the readonly list.

Please elaborate Thanks


SonOfPirate replied on Friday, September 12, 2008

Remember that a read-only list means that you can't make changes to the LIST - not the items within the list.  In other words, you can't add or remove items.  You will need to make sure that AllowEdit is true, of course.  This will allow users to make changes to the child items even though the list itself is read-only.

In this case, the OpenInvoice class (or whatever you name it) would have only one editable field, Selected.  This field would be bound to a checkbox column in the grid, and because it is editable, the grid will allow users to check or uncheck the field to select or unselect the item.  All other properties exposed by the OpenInvoice class will be read-only and for displaying in the grid only.

This works because all you care about is whether an item is selected or not.  So, in the Save method of your PaymentInfo class, you can query the OpenInvoiceList for all of the items that are selected and do whatever you need to with them.

Make sense?

 

pegesaka replied on Wednesday, November 23, 2011

That has helped me with object design heaps. But there is still a design problem when it comes to selected items in a visual control and mapping them to the underlying BO. In circumstances where requiring the user to select each row in a grid via a checkbox is too imposing it is easier to just have the grid hold the selected items. There is also the problem of implementing selecting the items between the last selected item, and the current when the shift key is pressed, especially when the visual control already does it.

 

When using the checkbox method of selecting (as described above), I use a SelectedItems property, which returns a collection/array of child objects that have the Selected property true. If the checkbox method is not used, then transferring the selected state of a row in a visual control to the underlying child BO as the user clicks the row, seems pointless (and because of the Shift key problem, too difficult). So returning an array of collected items then needs to be done by the form, which iterates through its Rows collection and extracts the child BO if the DataGridViewRow.Selected (for example) is true.

 

But this doesnt feel very OO. Am doing the processing of the list in the UI. Can anyone give me some advice on this?

 

Thanks

asinc replied on Thursday, November 24, 2011

The list is just a UI element - why not display the grid also textbox and ignore the check box stuff where the user enters the 'amount to apply' to the open invoices? 

Then when the user chooses save I would suggest that you:

Add child 'PaymentDetail' records as a child collection of the Payment Header object. Ie Loop through the elements of the grid and add a detail payment record that includes amount applied and invoice being paid etc. this can be tricky if you need to display a large list of open invoices with sorting and paging etc. but concept is the same.

In the save I would then call a command object that does multiple things in a transaction:

1- create/save the payment info (header and detail) . Can validate rules here on applied amount matching to payment amount etc as well as allowing discounts or write offs to be coded or authorized by role etc. All normal CSLA BO stuff.

2- update each of the related invoices with payment details ( if your use case/ design requires this) ie update open balance and last payment dates ...

3- if an integrated application may also create an accounting journal entry object and either post/save that or submit to an unposted batch

This flow would mirror many AR/GL type scenarios because the payment detail is distinct from the invoice and the system automatically creates the accounting entry as a separate object and validates that object etc.

You also have the ability with this design to write custom validation either forcing a balance of payment amount to applied cash or you can define logic allowing a difference that would automatically create an unapplied record in the customer / AR ledger keeping everything in balance.

Just another opinion.

honus replied on Thursday, September 11, 2008

Just a thought, but you could add properties to your object that would correspond to a checkbox in the grid, as well as your apply to invoice amount.  Then, you could simply have a method in the object to do the processing.

So, think of a boolean property that would reflect whether or not the item is selected, and a field for the amount they want to apply to invoice.

Without knowing more details of your problem, hopefully this suggestion might help you move more of the logic to the object(s).

Copyright (c) Marimer LLC