PDA

View Full Version : Canceling methodolgy for sets


ABC123

danh
04-22-2008, 01:35 PM
I am trying to understand the proper methodology of undoing (canceling) changes to records in a form base on a set, including canceling new records. When I say records, I am referring to the complete form, including all the data from all the tables. If I want to give the user the option of saving or canceling before moving to the next record or closing the form, I can do it just fine in a form based on a single table by using the following code in the 'CanSave' and 'CanExit' events.



dim vMode as C
vMode = parentform.Mode_Get()
if vMode <> "VIEW" then
dim vAns as N
vAns = msgbox("Save?","You have made changes, do you want to save them before leaving?",UI_ATTENTION_SYMBOL+UI_FIRST_BUTTON_DEFAULT+UI_YES_NO_CANCEL)
if vAns = UI_YES_SELECTED then
'Yes selected,
'do nothing, record saved automatically
elseif vAns = UI_NO_SELECTED then
'No selected
topparent.cancel(.t.)
elseif vAns = UI_CANCEL_SELECTED then
'Cancel selected
cancel()
end if
end if
end if

Alpha's help file says:

In almost every case, Alpha Five treats sets exactly like tables. You can create layouts and perform and save operations for a set. When entering data into a form designed for a set, the information is automatically placed into the correct table along with the necessary linking information.

But this code does not work for a form base on a set. Alpha saves (commits) the changes to the table for each table affected as you move around on the form. So, what is the proper method of canceling...

I'll use Alpha Sports for an example...

Open up Alpha Sports and open the invoice form,

on any existing invoice; add/change a credit card number in the credit card field,
then move down and add a line item to the invoice,
then move up and edit the price on an existing line item...

... now cancel all of the above... how do I do it?

Thanks, Dan

Tom Cone Jr
04-22-2008, 02:38 PM
Dan, on your Invoice form example I'll wager the parent table record is saved automatically when focus shifts to a row in the items browse. Right? This is Alpha's way of assuring no loss in data. The same is true if you start a change in a row in the browse and then shift focus to a field on the form from the parent table. No data loss because the change is commited automatically.

Because the data is stored in separate tables I think Alpha's design choice is a good one. You wouldn't want the user entering child table records and then have the parent table record get cancelled, leaving orphans behind, for example. In many cases, too, the key field in the primary table is based on an autoincrement field rule. This value is not "fixed" until the primary table record is saved. What you see on screen is only a tentative or preliminary key field value and it will change if another user on the network saves their new primary table record before you.

So, you're asking a hard question. Basically you're saying you want to abandon the built in safeguards of the table by table commits and roll your own data entry sequences. This can be done of course, but it's a lot of work. Here are some ideas. Perhaps others will have better suggestions for you, too.

a) Do the data entry using temporary tables, providing the user with a button to push that will create (copy) records from the set's temporary tables to the real ones. I don't see a way this will work with an autoinc field rule in your real tables, but then you may want to roll your own autoinc sequences, too <grin>.

b) Use xdialogs and data entry fields that are bound to variables. Again, you probably will hve to roll your own autoinc sequence.

c) Force the user to push a button to begin edits. The button script would store the current records (primary and linked child) in arrays. Put a button on the form to cancel all edits. Have that button overwrite the edited records with original field values.

If you head down this path you will need to plan for undoing single record enters, edits, and deletes, as well as undoing composite record enters, edits, or deletes. Not an easy task.

In my own work I shift the responsibility to the user where possible. Before permitting focus to shift to an embedded browse I check the mode of the form and if it's not VIEW I ask the user to cancel or save edits before proceedings. Ditto when the user wants to shift focus away from an embedded browse. This prevents the "accidental" save that's built in by default.

-- tom

danh
04-22-2008, 06:15 PM
Why would the data be lost when it is still on the screen? Wouldn't there be just as much chance of data lose on a form based on a single table? It's not committed until you go to the next record or close the form. That is all I want with a set. Why do I need to explain to my user (just for forms based on sets) that certain data is coming from multiple tables and therefore needs to be save individually? How does QuickBooks do it. I can change any part of an invoice, PO, Bill, etc, (existing or new) all day long, and turn around and cancel the whole thing. This was a problem in Access and I was hoping Alpha would be different. It creates a lot of work for the programmer to have to figure out a way around this, unless someone has a simple solution. I wonder what the Alpha programmers would say.

Dan

Tom Cone Jr
04-22-2008, 06:22 PM
I'm sure the folks at Intuit worked very very hard to make things seem seamless. Perhaps others will have better suggestions for you. My guess is that you'd be happier with a form upon which all the objects were bound to variables, using a single event or button click to save or cancel all edits. This runs counter to the flow of typical Alpha Five forms. The design choice was made long ago to bind the form and its objects to the underlying data structures automatically, by default. This has many benefits. Unwinding a set based transaction is not one of them. -- tom

DaveM
04-22-2008, 06:38 PM
edit: sorry Tom, I was slow typing with both fingers.


Dan,

You have the option as a programmer to make your own screen with all variables on it. copy the data from a file and then modify all you want. Once modified, you can the put the changes back in the table(s) or cancel and nothing is changed.
It is a lot of work, but you can do this.

If you work with alpha as it is with the drawn forms, you are kinda doing the same thing, but as soon as you do something(knowingly or not) to save it, it cannot revert back.

Now think of this. If you redo your sets to cascade/changes and deletes? for the cihild tables? It may help you. You can also setup to copy the previous record to a new one(button) and change as needed then copy changes back. You can have 2 sets with identical(but not the same tables) to post back and forth.

I am just giving methods here: I just delete what I don't want, but the delete button just marks the record so i don't see it again. Later, i may really delete it.

Another note:
If you had the same problem in access and alpha, it would be to presume that you will with anything else and since the programs are not going to change, the solution is..............?

I do have 2 out of a bunch of forms that have nothing on them but variables. There are reasons for it, but most of the time it is not as good.

EDIT: One more way, you can always copy all the fields into vars or an array and if you want to revert back, set it to a button to reput the data.

Al Buchholz
04-22-2008, 06:58 PM
Dan

What you are looking for is a 'two-phase commit' model.

The default Access or Alpha model doesn't use that technique.

It usually consists of a statement that starts a transaction, does the steps of the transaction, then finishes with a transaction end statement. Then and only then is the data viewable by any other users. That includes the current user opening the same data in another session.

I haven't tried that model with Alpha 5 on a database that supports the 'two-phase commit' and whether you can start a transaction and later finish it or abandon it. Perhaps someone else has and can comment.

So you can try that or try one of these other options.
1. variables - as listed previously
2. assign a transaction id to each of the components of the transaction and build your own two-phase commit. Either save the components or roll back through the tables and remove the components with the given transaction id.

Personally I'd try the database that supports it first.

MikeC
04-22-2008, 08:20 PM
Hi,
am wondering why a blob variable hasn't been considered using <tbl>.record_data_get() and <tbl>.record_data_set(). I have used this very effectively in "cancelling" actions after committing. Set the records to the blob prior to making any changes, then if a user wants to cancel changes made and committed, then set the record values back to the blob value.

I have a couple of forms in which I do this---mainly when the parent record has to be committed prior to creating the child. If a user gets to this point the child can still be simply cancelled, and then the parent's value gets set back to its initial value that the blob variable holds.

Is there something that is incorrect in this procedure ?? Sure seems to work and work easily.

DaveM
04-22-2008, 08:33 PM
Mike,

Maybe wrong, but I think it is similar to an array. or copying to seperate variables. and changing it back if nec.

I do have one form that calls an identicla form that rolls payments(all variables in second form). If the change is liked, the changes are made to the first form.

MikeC
04-22-2008, 08:41 PM
Hi Dave,
My point I guess is using <tbl>.record_data_get() and....set() can be as simple as getting the data of the parent into a blob from the button that opens up a child form using <tbl>.record_data_get(). If once on the child form and the user wants to cancel then a simple cancel does it for the child usually (if the record is not saved of course). Then if the parent also is wanted to be cancelled, then simply have the <tbl>.record_data_set() on a cancel button for the parent.

Two steps and, depending on how the forms are set up, possibly only two lines of script. Sure sounds like less overhead/maintenance than the other ways.

Tom Cone Jr
04-22-2008, 08:45 PM
MIkeC, I'd be interested in seeing a working example of this. Always ready to learn something new! Thanks. -- tom

MikeC
04-22-2008, 10:10 PM
Sure thing Tom--I made it slightly more involved than what I mentioned in the post but still a very easy way to "cancel" commited changes. A text box on the form should explain things. I used mostly Action Script where I could easily so that most here should be able to use this.

17077

CALocklin
04-22-2008, 10:14 PM
Hi Dave,
My point I guess is using <tbl>.record_data_get() and....set() can be as simple as getting the data of the parent into a blob from the button that opens up a child form using <tbl>.record_data_get(). If once on the child form and the user wants to cancel then a simple cancel does it for the child usually (if the record is not saved of course). Then if the parent also is wanted to be cancelled, then simply have the <tbl>.record_data_set() on a cancel button for the parent.

Two steps and, depending on how the forms are set up, possibly only two lines of script. Sure sounds like less overhead/maintenance than the other ways.Sounds to me like this would work BUT what if the parent record did not exist and then you wanted to cancel it after starting the first child record? In this case, I think the only option would be to delete the parent record. (i.e., IF blob is empty and record canceled, THEN delete the record rather than resetting to the original blob values.)

As for autoincrementing if using temporary tables or variables, I think that could be handled fairly easily. First, use any linking value in the temp tables. Then, since the results would have to be added to the "real" tables via xbasic, first save the parent table and get the autoincremented value after saving the record, then just add that autoincremented value into the appropriate field of the child tables as each child record is created.

MikeC
04-22-2008, 10:28 PM
Cal,
In the forms that I use this method, I have a few error traps that go along with what you are saying as well as a CanSave script that prevents the saving of a "blank" record--so with this in place (or a delete script as you mentioned and I have a couple of those as well in other places), even if the blob="" and this resulted in basically a blank record it simply would not be saved.

You do bring up some very valid points---as with most solutions/ways of doing things, there are going to be various error traps that will be necessary especially if there will be more than one user.

CALocklin
04-22-2008, 10:59 PM
Cal,
In the forms that I use this method, I have a few error traps that go along with what you are saying as well as a CanSave script that prevents the saving of a "blank" record--so with this in place (or a delete script as you mentioned and I have a couple of those as well in other places), even if the blob="" and this resulted in basically a blank record it simply would not be saved.
Think "Enter totally new record - including parent" vs. "Change existing record." Unless I'm misunderstanding something, I believe you would have to save the parent record initially in order to start the child record. If the user then decided to cancel the whole thing, the parent record would have to be deleted because it didn't even exist initially. I doubt it would be acceptable to just leave it blank.

MikeC
04-22-2008, 11:30 PM
Yep Cal,

That scenario is exactly where I have a delete operation in place.

The best way the Blob works is when a parent record is edited and commited and then a child record is brought up and the user wants to cancel everything. The child changes usually do not have to be saved and so these changes can just be cancelled. Once back on the parent form the blob can be used to cancel the changes made.

I find that the latter is most prevalent and the blob deals with it easily.

The other case in which a new parent is created as you referred to needs to have a delete operation--again fairly easy to incorporate.

I think here is another aspect of Alpha that should be placed in a FAQ or Newbie forum as am sure it is a question that will keep coming up time and again.

DaveM
04-22-2008, 11:49 PM
I use a utility(my word) script on close of the database that checks the tables for blanks and deletes all blank table fields. This applied to the tables I have found with that problem.

danh
04-23-2008, 02:55 PM
MikeC,

If I am understanding your solution correctly, you are talking about opening up the child record(s) in a separate form and canceling or saving each child record individually. What about a form with an embedded browse where the user can change the parent record and/or one or more child records directly from the open form, as in Alpha Sorts invoice form? Any ideas??

Dan

MoGrace
04-23-2008, 03:47 PM
Just a note about Quickbooks. I am guessing that there is a flag set when a new record is entered even though the record is saved during data entry (it can be printed eg.); the flag is not "committed" until the user either moves off the record or uses one of the save buttons. Such a flag would help in determining whether posts occur or not. Since QB seems to make deletion/ clearing of records while still on the record, pretty effortless. The setting of such a flag dependent upon mode would not be that difficult. QB is probably trapping all the navigation keys to test the setting of this flag.

Just my thoughts.

MikeC
04-23-2008, 09:14 PM
Dan,

I think Robin is Correct in how it would be set up. There is no easy way to set a variable to more than just the one record that is changed...but if this was a case in which it HAD to be done, I think with a lot of script it could be accomplished. I really don't think it would be worth the effort. Stick to being able to cancel only one child and one parent record as it is not that hard to do this with the method I use or the others that have been suggested. In the child embedded browse you refer to you would have to make it so that if a record was edited or changed then the only way to cancel it would be prior to moving off the record--I experimented around with the sample I gave in attempting this and am close but when allowed to move off the changed record it gets a bit complicated (for me!! :) ).

Again, I would keep to one record (of parent and one of child) to cancel.

danh
04-24-2008, 12:45 PM
Maybe I am being difficult, but I'm really not satisfied with limiting myself to one parent and one child. I'll go back to my previous post; Why do I need to explain to my user (just for forms based on sets) that certain data is coming from multiple tables and therefore needs to be save individually? The user sees the whole form and all the data in it and doesn't know (or need to, in my mind) that it is from different tables and needs to be handled differently. I really want to come up with a way to cancel any changes to any data in the set.

The problem seems to be with the child records. Could you call multiples of the same variable for the child records from the CanRowChange (or similar) event of the embedded browse?

Or would I be better off from a secure point of view to copy all data to a different table/set and edit from there and then when saved, overwrite the data in the first table/set?

Also, just ran across something in the help file I thought was interesting. Look in the 'how to...' for "Tracking Changes to Table Fields". Could I use a form of that?

Dan

MoGrace
04-24-2008, 01:09 PM
Dan,
If you are trying to implement the QB model for canceling a new invoice then add referential integrity to the set and do a delete on the parent record of the form. Because this is what QB does when you cancel - you lose the whole invoice. QB prompts the user to save and if No is selected then the record(s) are removed. All your button needs to do is pass a Ctrl-D.

But if you want to cancel changes to an already existing record then you will have to do something more involved - no way around that. The ESC key will cancel any changes that have not been saved - in the region in which it is used.

Perhaps there is something else that should be addressed as to why your users make entries they must cancel?

Doug Page
04-24-2008, 02:44 PM
Just so it is not misunderstood, I am not trying to be offensive with this post.

I do not know the QB program or how it does things. I can understand what you are asking for in your request. QB don't make the millions of dollars that they do just by rolling code from STANDARD scripts. I am sure that they are writing a lot of very complicated code to do the things that they do to make things easier for the customer. Unfortunately, it is a fact of life with programming that if you want to make things easier for the user, it almost always equates into more work for the programmer. What you are asking for can be done in Alpha with a lot of coding. You would need to dig into the Xbasic and XDialog and get VERY familiar with each. eg) Roll your own dialog that will enter all data into variables, perform data checks as necessary along the way, save data when you want, clear child information upon saving the record and set the cursor to the starting object for the child data to continue.

I must agree with the other posters here that the way that Alpha deals with a basic form with child tables is the very best way that it should be done. It caused me untold grief while I have been learning A5 (it is a never ending avocation) as I too wanted more polished looking apps. That is until I appreciated the fact that there is more power behind this beast than any single person will ever be able to fully grasp.

My suggestion, whenever you hear yourself saying to yourself something like:



Why do I need to explain to my user (just for forms based on sets) that certain data is coming from multiple tables and therefore needs to be save individually?


Quickly slap yourself in the face and say something like:



If I new all there was to know about A5, what would be the best way to achieve what I want so that I can give my users an easier to use app.


Look up other possibilities in the docs and if you're not able to find an answer, come to the board and ask something like: "I have been creating my forms like this in the past and have found these limitations. This, however, is what I am trying to achieve. Is there a way that someone could point me in the direction of to accomplish my goal." Then I'll bet you'll see some pretty creative answers. Right now the way I am reading it, you are telling everybody that "I am doing it this way, this is the way I want to do it cause this is what I know, and I want it changed so that it works the way I want - why can't this be done!" The complexities of forms are fantastic but there needs to be logical rules behind them. As you can tell from the discussion here, most people believe the rules are correct for the vast majority.

You have had some very good creative thinkers on this board chime in here to give you some suggestions. I think it is time to go back and look over them and weed out the advice they have given. In the end, it doesn't hurt them if you don't heed the advice but they might not be as ready to continue the thread!

danh
04-24-2008, 05:25 PM
"I am doing it this way, this is the way I want to do it cause this is what I know, and I want it changed so that it works the way I want - why can't this be done!"
I am sorry if this is the way I was coming across. Sometimes it's easy to write something and not realize how it may sound to someone else!

I am not trying to knocking Alpha's design, it is a very powerful program and I like a lot about it (this feature is not one of them, yet) :), and I was just using QB as an example. I was hoping I missed something when I read in the help file that sets are just like tables and then found out I can't cancel the same way. I was also hoping someone from Alpha would chime in and show what they had in mind for canceling changes to sets.


Perhaps there is something else that should be addressed as to why your users make entries they must cancel?

One example would be if a customer ordered several items and then called back to see about adding to or changing the order. The user could simply pull up the order, make the desired changes and give them a verbal answer and then cancel or save depending on the customers decision.

Anyway, back to the issue; looks like we have about 3 choices;

1) Use <tbl>.record_data_get() and....set(), simple, but with limits of one parent and one child, unless there is a way to repeat it for each child record.

2) Use variables on the form instead of fields attached to tables. Haven't worked with this type of form, so would need some help with this one.

3) Copy data to temporary tables, make edits in temporary tables and then write back to the primary tables. Would this be more secure? I guess the same as #2. If the user left the form open on their computer while editing and the electric blinked, changes would be lost, but the original record(s) would be left intact, right?

Am I missing any?
Any comments on these different (or others) approaches?

Dan

MikeC
04-25-2008, 11:34 AM
Dan,
First, I think that unless a user is extremely new to any type of database, or application for that matter, most would not expect to be able to cancel something after moving on to a completely different record...I mean, if this were the expectation where would you stop?? After two records are created...10 records??....or maybe a week later??? (being a bit obnoxious now! :) ). There has to be a limit really. But if your users want something like this that is a different story and am sure it could be coded via any of the 3 ways presented to you.



1) Use <tbl>.record_data_get() and....set(), simple, but with limits of one parent and one child, unless there is a way to repeat it for each child record.

This could be repeated by having different Blob variables being assigned whenever a record changed and then either an all-out cancel performed or with maybe xdialog a list of choices could be made for the user to cancel only certain ones....this procedure would be the most difficult I am thinking to incorporate.


2) Use variables on the form instead of fields attached to tables. Haven't worked with this type of form, so would need some help with this one.
This is similar to the prior method that uses a Blob variable except that your fields now become variables and as the table fields are not set to these variables until you decide to, if the user decides to cancel there is nothing to do---that is you simply do not set the actual table value to the variable that was changed. To do this with multiple child records.....am not sure if this can be done easily either (if at all). To do this you would have to have some sort of method to change the field variable names so that they would be different for each different record.


3) Copy data to temporary tables, make edits in temporary tables and then write back to the primary tables. Would this be more secure? I guess the same as #2. If the user left the form open on their computer while editing and the electric blinked, changes would be lost, but the original record(s) would be left intact, right?
This is most likely the route to take as it gives you the opportunity to cancel a multitude of changes made, but am thinking this would be an all or nothing proposition. It may be possible to tag each record that is changed and then present the user with a list of the records and cancelling only the ones wanted...I believe it is doable but not for the really inexperienced person--

More secure?? I'm not understanding this I guess...you can prevent a user from doing any or all of any of the above procedures.

And yes, original records are not touched until they are....so if nothing has been written back from the temporary table then the original record has not been changed. I guess this is what you asked...?

I guess now it is up to you to decide on which method you want to pursue. Then when you are to a point where you get lost, come back to the forum and post your problem (in a different thread I would think).

MoGrace
04-25-2008, 12:38 PM
I'll try to stick to the QB theme here ;). Your app ought to follow standard accounting principles - eg. when you write a check and need to change it, you must void it and write another. The same principle should follow for any records that are printed as verification to a customer. Now there is nothing that says you have to do things this way, but some decisions ought to belong to management and not the data entry person. You do not want your users deciding which orders and invoices they will delete if those records have already been sent to your customer, because they provide them proof and give you some control over where the money goes. Which is the beauty and potential harm of a computer system.

With manual records all papers are accounted for and any missing ones would be suspect of potential foul play. It happened to me once with a girl in the office who had written herself some checks and forged the signature. Had she not removed the cancelled checks from the bank reconciliation, I would probably not have discovered what she had done.

Obviously there is more to consider than just the ease of use of your program.

danh
04-25-2008, 02:38 PM
OK, I will do some experimenting, and see what I can figure out from here.

Thanks to all who contributed.

Dan