TOPIC: Saving each row in a BrowseControl
#154
Saving each row in a BrowseControl 4 Years, 4 Months ago
It appears to me that BrowseControl doesn't save a record if I modify or add a row. It saves the records only if I do a 'save all'. It is a problem for my app, because in this way the trigger that I have to execute after each row added, it is not executed... Yes, the triggers are all executed when I do a 'save all', but then is useless for me, because for each row added I have to do some checks that depend from what I did in the previous row. But if the previous row is not saved, the checks are wrong.
There is a way to save a record after a row in a BrowseControl is modified?
 
 
Mauro
 
#155
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
I'm afraid there is no way to save records individually in a BrowseControl at the moment. There are reasons for this, and one is to handle issues that arise when dealing with master-detail relationships. If you do not have a master-detail relationship (ie. you're not using the linkField option in the BrowseControl), you could use ExplorerListViewControl, since it does save records individually when not using the linkField option.

Post edited by: jferguson, at: 2006/04/06 14:42
 
 
Jeff Ferguson
Suneido Software
 
#156
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
Yes, it's a master-detail relationship (I use the linkField option)... :(
Example: I have to generate an invoice for some products. The products are in a table with a stock field (it's to indicate the remaining pieces of that product). After I insert a row in the browseControl for the invoice (the detail rows of the invoice), a trigger should update the stock field of the products table (in this way I could do the appropriate validation controls to check that the quantity specified in each row has not exceeded the stock number for that product). But unfortunately the trigger is not working because the records aren't saved individually in the BrowseControl.
Actually, I think this can be a big limitation for Suneido because these validation checks are used very often in the master-detail forms of management applications (and other database IDE that I have used in the past have this feature).
Do you think that it is not possible to create an alternative BrowseControl that can save the rows individually?
 
 
Mauro
 
#157
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
Hi Mauro. Another solution may be to create a rule that would calculate the quantity based on the stock field and the amounts from the line items and then use that to do the validation. Then use it to update the stock field when the record is saved. Then you would not have to worry about saving each line item when it is entered.
 
 
#158
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
Hi, jennebelle. Thanks for your suggestion, but I think there would be a problem: if I use the validation rule to do the checks and to update the stock field, then what would happen if I execute a sequence like:

1) In the BrowseControl I add/edit a record => the row is not saved yet, but the validation rule will update the stock field and then the following checks will do as expected. It is not an elegant solution as the triggers because more business logic is moved to the user interface code, instead of the database. However I think it should work.

2) In the BrowseControl I delete the record that I have added before => now my database is in an inconsistence state, because in (1) the rule has updated the stock field that now it should be restored (but I think the validation rule is not executed if I delete a record in the BrowseControl). Neither can I execute the trigger because the BrowseControl doesn't do the row updates individually.

Anyway, I have managed to do an individual row save in the BrowseControl with the following change in this method:

Code:

List_AfterEdit(col, row, data, valid?)
{
.Save() //*** ADDED
record = .list.GetRow(row);
.Send('Browse_AfterEdit', col, row, data, valid?, record)

if (not valid? and not .editFieldValid)
.list.AddInvalidCell(col, row)
else
.list.RemoveInvalidCell(col, row)
.check_valid_field(record);
.editFieldValid = false
}


In this way, the BrowseControl saves the rows individually as I add/update them and the triggers are executed as I expected, with validation checks now working ok for the rows that I add/edit next. Unfortunately it remains the delete problem... If I delete a row, it appears an 'X' symbol on its left, but the trigger is not executed until I do a 'save all'.

P.S.: I don't know if the upper mod could do some mess with the normal functioning of BrowseControl. For now it appears to me that is working well (delete problem apart).

Post edited by: Mauro, at: 2006/04/06 16:38
 
 
Mauro
 
#159
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
There is another way that should work, but I have not tested it:


  • Continue to update the stock amounts from the detail records trigger.

  • Do the validation from the master (header) record. To do this you use AccessControl's validField option (a validation rule).

  • From this rule you can access the browse data by accessing the member as specified in the BrowseControl's dataMember option. You will have to look at the listrow_deleted member of each browse record so you can ignore the deleted records (these will be the ones marked with a red 'X').

  • As you read through the browse records you can total up the amounts for each item and determine if saving the line items will exceed the stock amount for each item. Returning an empty string from the rule means the record is valid, otherwise the string message will be displayed to the user and will stop the user from saving the record (including the detail records).


I hope that helps. We have validated several cases in our applications this way and it seems to work well.
 
 
Jeff Ferguson
Suneido Software
 
#160
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
Hi, Jeff. Thank you for your suggestion too.
In your approach you would use a validation rule from the header record in the AccessControl... It's interesting, but it seems to me that in this way the checks aren't done on a per row basis, because the validation rule of the AccessControl would be executed only if I loose focus from the BrowseControl, right?
I would like that the checks on the stock amounts would be done every time I add/update a record. In this way, if there is a problem, the app could display an alert window to inform the user that the quantity he has inserted in that row of the invoice is wrong because it exceed the actual stock amount and he should have to correct it.

Anyway, I think I have resolved the delete problem too.
I define a Controller like this:

Code:

Controller
{
New()
{
.browse = .Access.Vert.Scroll.Border.Data.Vert.Vert.cod
}
Controls:
#(Access masterTable
title: "some title"
(Vert
field1
field2
field3
...
...
(Browse 'detailTable'
linkField: 'linkFieldInDetailTable' name: 'associatedFieldInMasterTable'
validField: VALIDATE_detailTable //this rule does the necessary checks
)
)
)

Browse_AfterEdit(col, row, data, valid?, record)
{
.browse.Save() //to save individually each added/updated row
}

Browse_AfterDelete(record)
{
try .browse.Save() //to delete immediately a row marked as deleted (so that the trigger is executed)
}
}


In this way it is not more necessary to modify the List_AfterEdit method of the BrowseControll class (that therefore can be used as usual, without change its standard behaviour). Actually I have had to add only this code to the BrowseControll class:

Code:

List_DeleteRecord(record)
{
if (.allowNextDelete or
AllowDeleteRecord?(record, .protectField, record.Member?('Browse_NewRecord')))
{
...
...
...
.Send("BrowseData_Changed", data, row, false, .list.Get()[row]);
.Send("Browse_AfterDelete", record) //***ADDEDD
return record.Member?('Browse_NewRecord') ? true : false
}
return false
}

It's completely harmless and it is necessary to send the 'Browse_AfterDelete' message that I catch in my controller to manage the problem of the deleted rows.

It seems to work. :)
 
 
Mauro
 
#161
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
I'm glad to hear you found another solution. Is it possible to use the exising message "Browse_DeleteRecord" from BrowseControl instead of adding another one?
 
 
Jeff Ferguson
Suneido Software
 
#162
Re:Saving each row in a BrowseControl 4 Years, 4 Months ago
Hi Jeff, I had already tried to use the Browse_DeleteRecord message but then, the .browse.Save() in my controller generated a strange error. For this I added the Browse_AfterDelete message.

Later, I realized that the error was caused by the line position of the .Send("Browse_DeleteRecord", record) in the List_DeleteRecord definition. It has to be under the .Send("BrowseData_Changed",...) line, because the .Save() method that I call in the Browse_DeleteRecord definition does some changes to the .list member (that it is passed to the BrowseData_Changed message).
My Browse_AfterDelete worked well because I had inserted its send after the .Send("BrowseData_Changed",...), but I didn't figure out this.

Finally, answering to your question: yes, it can be used the existing "Browse_DeleteRecord" message, but you have to change the line order of its send in this way:
Code:


List_DeleteRecord(record)
{
if (.allowNextDelete or
AllowDeleteRecord?(record, .protectField, record.Member?('Browse_NewRecord')))
{
...
...
...
.list.RepaintRow(row)
//.Send("Browse_DeleteRecord", record)
data = .GetBrowseData()
.Send("SetField", .dataMember, data);
.Send("SetField", 'all_browse_data', .list.Get())
.Send("InvalidateFields", Object(.dataMember, 'all_browse_data'));
.Send("BrowseData_Changed", data, row, false, .list.Get()[row]);
.Send("Browse_DeleteRecord", record) //***MOVED HERE
return record.Member?('Browse_NewRecord') ? true : false
}
return false
}


I think that the move of the .Send("Browse_DeleteRecord",record) line shouldn't mess up anything in the BrowseControl.
 
 
Mauro