Essentially, what I'm doing is this:
On insert, delete, or update on the SalesLine table, I'm checking the ProductCode of that line and determining whether or not a discount or miscellaneous charge applies
I have lookup tables for the charges and discounts
When any changes are made, I have to effectively remove the discounts/charges, because the newly modified SalesLine might not qualify for the charges/discounts
I have code that is doing what is described above -- but with the following side-effect:
If I highlight several lines in the grid on the SalesTable Form, and click the toolbar's delete button, I get errors.
The error(s) seem to be caused by my code deleting some of the items, and then the form is also trying to delete the lines after they've already been deleted by the code.
What would be the best way to erase the added-by-code discounts or charges when the parent line is deleted, that won't cause the form to blow up if a user selects those same lines for deletion?
Thanks in advance, this thing is driving me crazy!
EDIT: The specific error that I receive when the cached Form records try to get deleted is this:
Cannot edit a record in Order lines (SalesLine).An update conflict occurred due to another user process deleting the record or changing one or more fields in the record.
The most obvious way to delete the added-by-code discounts or charges would be to add your table in the delete actions of the salesLine table with option Cascade. Also make a table relation on your table to the SalesLine talbe. In this way your records are deleted automatically. I would not do this if the discounts/charges are stored as sales lines though.
The error message make me guess, that you delete other sales lines, when deleting a sales line:
When deleting a "real" sales line you delete discount or charge lines.
When doing multi-line delete the #2 line has been deleted by the #1 line delete, but the form buffer has not been updated and the delete fails. This is effectively what the error messages tells you, a line has been updated since the record was read by the form.
Update for the delete problem:
Remove your code from the SalesLine delete methods.
Create a set of recId to be deleted later, populate in the salesLine datasource delete method.
Delete the lines later (maybe from the active method), if the set is not null.
Related
I am creating an Oracle Form. This form has a field by the name FILE_NUM. There is one more field by the name CLIENT_ID in the form. I have to generate unique FILE_NUM. The process is:
If the CLIENT_ID already exists in the table, get the FILE_NUM and assign it to the new record
ELSE, take the maximum of FILE_NUM, add 1 and assign it to the new record.
This should be taken care when multiple users are working on the form. Hence I did the following:
In Key-Commit trigger, I check if there is a lock on the table.
If the table is locked, I make form wait for 3 second and check again.
If the table is not locked, I am locking the table and inserting the records with the above check.
My query is: is this the right way to do? Is there any other way to generate the FILE_NUM (maybe via trigger?). The problem with key-commit trigger is that if the form closes forcefully, the lock is not removed. This will cause more issues, hence I want to remove the lock feature.
Please advice.
This is the correct way, but indeed the lock can stay in some cases.
If the number doesn't have to follow each other you can use a sequence instead.
This will give you a number when needed and it will be unique.
is there a way to rollback a specific change. I have a button that creates a row in two different tables. I want the changes in one of the tables to be rolled back before the committing so that only the second table is committed. How would this be done? Running normal rollback rollbacks both table iterators.
Ive been trying different methods and nothing is working, Please help before I go insane.
find out the corresponding 'VO' row which you want to rollback .
#1. row.setNewRowState(Row.STATUS_INITIALIZED) ;
or
#2. row.revertRow() ;
or
#3. row.remove() ;
The closest thing to rolling back a row would probably be calling refresh on the view row with the appropriate parameters, something like this:
//Obtain app module
DCBindingContainer dcb = (DCBindingContainer) BindingContext.getCurrent().getBindingsEntry();
ApplicationModuleImpl am = (ApplicationModuleImpl) dcb.getDataControl().getDataProvider();
//Get your view
ViewObjectImpl vo = am.getMyView1;
ViewRowImpl row = vo.getCurrentRow(); //alternately use vo.findByKey to lookup a row or simply iterate through every row if dataset is sufficiently small
//rollback the row
row.refresh(Row.REFRESH_REMOVE_NEW_ROWS | Row.REFRESH_UNDO_CHANGES | Row.REFRESH_CONTAINEES);//review modes for ideal combination for use case
Note that rather than a refresh, a row.remove() would probably be sufficient for what it sounds like you are trying to do. In any case, you will need to keep track of the rows you do not want to commit.
While this solution would work, it does not sound ideal for your use case. If you never commit data in the other table, there is no reason to link it to a database table. I would probably do something like one of the following instead:
If the fields are the same in both tables (or similar) and it will be an all-at-once action, create a transient attribute on the ADF view object to denote whether or not the row is approved. Use view criteria on different instances of the view object (add to your application module twice) to display/process the rows you want. Remove not approved rows prior to committing.
If the fields needed are too different or you want to be able to handle one row at a time, make you history view object a programmatic view object with transient attributes, rather than basing it on the table/Entity. When a row is approved remove it from your history view and add it to your approved view.
There is custom field "Lock Flag" in Account BC, namely in S_ORG_EXT_X table. This field is made available in Opportunity BC using join to above table. The join specification is as follows: Opportunity.Account Id = Account.Id. Account Id is always populated when creating new opportunity. The requirement is that for newly created records in Opportunity BC if "Lock Flag" is equal to 'Y', then we should not allow to create the record and we should show custom error message.
My initial proposal was to use a Runtime Event that is calling Data Validation Manager business service where validation rule is evaluated and error message shown. Assuming that we have to decide whether to write record or not, the logic should be placed in PreWriteRecord event handler as long as WriteRecord have row already commited to database.
The main problem was how to determine if it is new record or updated one. We have WriteRecordNew and WriteRecordUpdated runtime events but they are fired after record is actually written so it doesn't prevent user from saving record. My next approach was to use eScript: write custom code in BusComp_PreWriteRecord server script and call BC's method IsNewRecordPending to determine if it is new record, then check the flag and show error message if needed.
But unfortunately I am faced with another problem. That joined field "Lock Flag" is not populated for newly created opportunity records. Remember we are talking about BC Opportunity and field is placed in S_ORG_EXT_X table. When we create new opportunity we pick account that it belongs to. So it reproduceable: OpportunityBC.GetFieldValue("Lock Flag") returns null for newly created record and returns correct value for the records that was saved previously. For newly created opportunities we have to re-query BC to see "Lock Flag" populated. I have found several documents including Oracle's recomendation to use PreDefaultValue property if we want to display joined field value immediately after record creation. The most suitable expression that I've found was Parent: BCName.FieldName but it is not the case, because active BO is Opportunity and Opportunity BC is the primary one.
Thanks for your patience if you read up to here and finally come my questions:
Is there any way to handle PreWrite event and determine if it is new record or not, without using eScript and BC.IsNewRecordPending method?
How to get value of joined field for newly created record especially in PreWriteRecord event handler?
It is Siebel 8.1
UPDATE: I have found an answer for the first part of my question. Now it seems so simple to me that I am wondering how I haven't done it initially. Here is the solution.
Create Runtime Event triggered on PreWriteRecord. Specify call to Data Validation Manager business service.
In DVM create a ruleset and a rule where condition is
NOT(BCHasRows("Opportunity", "Opportunity", "[Id]='"+[Id]+"'", "AllView"))
That's it. We are searching for record wth the same Row Id. If it is new record there should't be anything in database yet (remember that we are in PreWriteRecord handler) and function returns FALSE. If we are updating some row then we get TRUE. Reversing result with NOT we make DVM raise an error for new records.
As for second part of my question credits goes to #RanjithR who proposed to use PickMap to populate joined field (see below). I have checked that method and it works fine at least when you have appropriate PickMap.
We Siebel developers have used scripting to correctly determine if record is new. One non scripting way you could try is to use RuntimeEvents to set a profileattribute during the BusComp NewRecord event, then check that in the PreWrite event to see if the record is new. However, there is always a chance that user might undo a record, those scenarios are tricky.
Another option, try invokine the BC Method:IsNewRecordPending from RunTime event. I havent tried this.
For the second part of the query, I think you could easily solve your problem using a PickMap.
On Opportunity BC, when your pick Account, just add one more pickmap to pick the Locked flag from Account and set it to the corresponding field on Opportunity BC. When the user picks the Account, he will also pick the lock flag, and your script will work in PreWriteRecord.
May I suggest another solution, again, I haven't tried it.
When new records are created, the field ModificationNumber will be set to 0. Every time you modify it, the ModificationNumber will increment by 1.
Set a DataValidationManager ruleset, trigger it from PreSetFieldValue event of Account field on Opportunity BC. Check for the LockFlag = Y AND (ModificationNumber IS NULL OR ModificationNumber = 0)) and throw error. DVM should throw error when new records are created.
Again, best practices say don't use the ModNumbers. You could set a ProfileAttribute to signal NewRecord, then use that attribute in the DVM. But please remember to clear the value of ProfileAttribute in WriteRecord and UndoRecord.
Let us know how it went !
It's apparently the week for unusual AX problems. I have custom, company-independent tables in AX that on first view always shows one particular record. This occurs if you access them from a form, view the tables directly, etc.
Open a form using one of the malfunctioning tables as a datasource and you only see one record.
Open a table directly (CTRL-O), and you only see that one record. However, if you hit the green arrow to re-execute the SQL query, the rest of the records appear.
I've tried dropping and reimporting the tables, adjusting the indices, deleting a variety of record combinations, but nothing has any effect. If I delete the one row that appears, another one replaces it as the one appearing record. Add that row back in (new RecId, same data) and that row resumes its original position.
I exported these tables from another system where they were working perfectly. I also imported the exact same structure to another instance on the same Dynamics server, and everything works perfectly.
Any suggestions?
Are you absolutely sure you're running the client on the same version as your application? Check to see if your Kernel version matches the Application.
Regarding the SubmitChanges order (Insert, Update, Delete), is there a way to change that order? I need to have the Deletes executed first, any updates, then any new inserts. I have a datagrid where the user can do all add, changes and updates and submit. Since each grid row must have a unique item chosen in it (via a dropdown), it's possible for the the user to delete the row, then try to utilize the deleted dropdown item in a new row, then try to update all changes and have the update fail (since the item the user wants to delete actually still exists in the database because the submit is doing the inserts first). Is there a setting where I can control the automatic update order or do I have to manually do the updates myself?
I have not tried this, but you could consider the following. First, get the ChangeSet using DataContext.ChangeSet. Then, run through the ChangeSet.Deletes calling Table<T>.DeleteOnSubmit on a new instance of your DataContext. Rinse repeat for the ChangeSet.Updates and the ChangeSet.Inserts.
Good luck.
I don't believe it's possible to do this. You would have to process the changes in the order you want, and call SubmitChanges() after each insert, update or delete. If you want the whole thing within the scope of a transaction, use the TransactionScope object.