I'm working with Oracle Forms. I have a field named SOLD_TO_CUST_PARTY_NAME. I have to execute a process if I detect a change in the field's value. I tried using when_validate, but it's executed even if you just click the field and move to another field (validation ALWAYS occurs whether you change the value or not). Is there anyway I can check :old and :new or something similar to execute a process only if the field is modified?
EDIT:
Can't use personalizations. It has to be done with pl/sql.
There is a property called database value that let you check if the field has been modified and if it has not you just have to exit the validation trigger.
Ex.
BEGIN
IF :BLOCK.ITEM = GET_ITEM_PROPERTY('BLOCK.ITEM', database_value) THEN
RETURN;
END IF;
/* VALIDATION */
END;
Related
Total newbie in AL here; I'm experimenting with automated testing in Dynamics BC for a new deployment. As a very basic test, I'd like to simply create a new Item record in the Cronus test database and validate each field. I'm running into trouble when I try to select the value for an enum field. Here's the code I'm using:
Procedure AddGoodItem()
// [Given] Good item data
var
recItem: Record Item Temporary;
Begin
recItem."Description" := 'zzzz';
recItem.validate("Description");
recItem.Type := recItem.Type::Inventory;
recItem.Validate(Type);
recItem."Base Unit of Measure" := 'EA';
recItem.Validate("Base Unit of Measure");
recItem."Item Category Code" := 'FURNITURE';
recItem.validate("Item Category Code");
End;
When I run this in Cronus, I get the error:
You cannot change the Type field on Item because at least one Item Journal Line exists for this item.
If I comment the Type lines out, the process runs successfully.
Given that this is a temporary record, it shouldn't have any Item Journal Lines, should it? What am I missing?
The code in the OnValidate trigger still runs, even if you have marked the Record variable as temporary. In addition temporary is not inherited to the underlying variables meaning any Record variables used in the OnValidate trigger are not temporary (unless marked as such).
There are two options:
Leave out the line recItem.Validate(Type);, if the code run by the OnValidate trigger is not relevant in this case.
Replace the line recItem.Validate(Type); with your own clone of the code from the OnValidate trigger and then remove the unneeded parts.
There are 2 tables linked by master-detail. When adding a new value to the detail table, the foreign key selected from the master table is not bound.
The M-D connection itself is performed on the form using two Dblookupcombobox and DataSource, ADOQuery for each, respectively.
enter image description here
Using the [ + ] buttons, new values are added that are not present in the combobox. But the problems start at the second [ + ] (aka detail), when creating a new line, you need it to bind the foreign key from the previous LookUpComboBox (Master). Button code of the second button [+]:
begin
Form4.ADOQuery1.SQL.Clear;
Form4.ADOQuery1.SQL.Add('Select City from City WHERE City='+#39+Form5.DBEdit1.Text+#39); //checking for duplicates
Form4.ADOQuery1.Open;
if Form4.ADOQuery1.IsEmpty then
begin
Form4.Query_city.FieldByName('City').AsString := Form5.DBEdit1.Text; //The PROBLEM is SOMEWHERE HERE! It Adds a new value without binding the foreign key
Form4.Query_city.Open;
Form4.Query_city.Post;
MessageBox(Handle, 'New data entered','Adding a new value',MB_ICONINFORMATION);
end
else
begin
Form4.Query_spec.Cancel;
Form4.ADOQuery1.Cancel;
MessageBox(Handle,PChar(''+Form5.DBEdit1.text+' already on the list!'),'Error',MB_ICONWARNING);
end;
end;
The new value is written to DBEdit1. It has a corresponding binding to tables.
So How i can insert field with with the corresponding foreign key?
You are making this unneccessarily difficult because of the way your
code is structured. Try something like this instead:
Open ADOQuery1, SELECTing its entire contents, with e.g.
procedure TForm4.OpenCitiesTable;
begin
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('Select City from City');
ADOQuery1.Open;
end;
and leave it open while the user does whatever operations lead to wanting to add a new city.
Then, when the users wants to add a city, call this procedure, e.g. supplying the City
value from Form5.DBEdit1.Text.
procedure TForm4.AddCity(ACity : String);
begin
ACity := Trim(ACity); // remove any leading or trailing blanks
// Do any formatting checks on ACity here, re.g convert it to Proper case
// check for adding a duplicate
if ADOQuery1.Locate('City', ACity, []) then begin
ShowMessageFmt('%s is already in the City table', [ACity]);
Exit;
end;
try
ADOQuery1.Insert;
ADOQuery1.FieldByName('City').AsString := ACity;
finally
ADOQuery1.Post;
// at this point you might want to refresh the contents of whatever source
// you are populating Form5.DBEdit1.Text from
end;
end;
I assume you can adjust the code relating to TForm4.Query_spec yourself;
Btw, you might want to consider using a TDBLookUpComboBox instead of your DBEdit1.
Every time I try to run my database script I reach the last few lines that have my 2 triggers and my script stops after compiling the first trigger.
These are the 2 triggers I have and it compiles "Player Round Trigger" and then the script stops and doesn't reach my second trigger "Handicap Trigger"
--
-- Player Round Trigger
--
CREATE TRIGGER playerRoundUpdateAudit BEFORE UPDATE ON PlayerRound
FOR EACH row BEGIN
INSERT INTO PlayerRoundAudit(old_data_PlayerID, old_data_RoundID, old_data_Holenumber, old_data_holeScore,
new_data_PlayerID, new_data_RoundID, new_data_Holenumber, new_data_holeScore, tbl_name)
VALUES (OLD.playerID, OLD.roundID, OLD.holeNumber, OLD.holeScore, NEW.playerID, NEW.roundID, NEW.holeNumber, NEW.holeScore, "PlayerRound");
END;
/
--
-- Handicap Trigger
--
CREATE TRIGGER handicapUpdateAudit BEFORE UPDATE ON Handicap
FOR EACH row BEGIN
INSERT INTO HandicapAudit(old_data_handicapID, old_data_playerID, old_data_handicapDate, old_data_handicapScore,
new_data_handicapID, new_data_playerID, new_data_handicapDate, new_data_handicapScore, tbl_name)
VALUES (OLD.handicapID, OLD.playerID, OLD.handicapDate, OLD.handicapScore, NEW.handicapID, NEW.playerID, NEW.handicapDate, NEW.handicapScore, "Handicap");
END;
/
I'm running the script in Oracle SQL Developer version 4.1.2.20 (the newest one atm)
Acturlly the first triggers compiles with errors, and breaks the script.
You can do an experiment - change a header of first trigger into CREATE OR REPLACE TRIGGER ....,
then in SQL Developer click on the first trigger to move cursor into it's code, then press Ctrl-Enter - this executes one statement where the cursor is placed (actually - 'CREATE` statement of the first trigger).
Then examine "Compiler log" window - you will see a message like this:
There are two problems with this trigger:
you are using OLD.column_name and NEW.column_name, what is wrong. You need use :OLD.column_name and :NEW.column_name, using a colon as a prefix
you are using double quotes instead of quotes here: "PlayerRound", and Oracle doesn't interpret this as a string, but as an identifier (of variable, column name etc.). Use 'PlayerRound' within quotes instead.
Change the first trigger like below an it should compile:
set define off
CREATE or replace TRIGGER playerRoundUpdateAudit BEFORE UPDATE ON PlayerRound
FOR EACH row BEGIN
INSERT INTO PlayerRoundAudit(old_data_PlayerID, old_data_RoundID,
old_data_Holenumber, old_data_holeScore,
new_data_PlayerID, new_data_RoundID, new_data_Holenumber,
new_data_holeScore, tbl_name)
VALUES (:OLD.playerID, :OLD.roundID, :OLD.holeNumber, :OLD.holeScore,
:NEW.playerID, :NEW.roundID, :NEW.holeNumber, :NEW.holeScore, 'PlayerRound');
end;
/
You need also correct the second trigger, because it contains similar errors.
Remark : put SET DEFINE OFF into the script to turn off a variable substitution, otherwise SQL-Developer will prompt to enter a value for each :NEW and :OLD
I'm not so familiar with Oracle SQL Developer but is there an option to run the script as "Execute as script"? This feature is available on TOAD...
I found this code to create a calc field to a TADOTable in Delphi somewhere ...
.....
procedure TfrmMain.ABSTable1CalcFields(DataSet: TDataSet);
begin
with ABSTable1 do
FieldByName('cost').AsFloat := FieldByName('price').AsFloat *
FieldByName('quantity').AsInteger;
// add new field cost as Price * quantity !!!!
end;
end.
Inside my app i create a TADOQuery at rum time like
try
Fquery.sql.clear;
Fquery.sql.AddStrings(Amemo.lines);
Fquery.Open;
.....
finally
end;
How to add more calc fields to my Query derived from the first code Fragment ?
I think the only way you can easily do this is by creating a set of persistent TFields in the IDE (or do the equivalent creation of them in code before you open the dataset). Otherwise, when you call Open on the dataset, IIRC that will call BindFields and that - unless the dataset already has a set of TFields - will create a set of dynamic TFields which will last as long as the dataset is open, but will not include any calculated fields.
By the time BindFields has been called, it's too late to add any more, so you have to set them up beforehand or not at all.
I'm only a beginner in learning to use Parameterised Queries as I used to do a lot of concatentating before. I've been trying to get this query below to work. It is a simple 'Book' table, with a field called 'BookTitle'. I have a simple textbox where I invite the user to enter any title...and it should run the query below to find if that book exists. Below is my code that, when run, manages to compile. However, when an entry into the textbox is added and the button to run the query is pressed, a Debugger Exception Notification appears with the following statement.
Debugger Exception Notification: Project Project1.exe raised exception class EOleException with message 'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another'.
I then have the option to press 'Break' or 'Continue'. If I press 'Break', the line:
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
is filled with a purple/red colour (not sure what this means?).
That said, if I press 'Continue', the program will work as expected, and will continue to do so. Here is the code i've been testing.
procedure TForm4.btnRunQueryClick(Sender: TObject);
var BookEntry:string;
begin
BookEntry:=edtBookTitle.Text;
qbook.SQL.Text:='SELECT BookTitle FROM Book WHERE BookTitle = :BookTitle';
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
qbook.Parameters.ParamByName('BookTitle').Value := BookEntry;
qbook.Open;
end;
Further points to note: The components in my Delphi form are as follows
a TADOQuery named 'qbook',
a TDataSource,
a TDBGrid,
aTEdit into which the user enters their desired search criteria and
a TButton that once pressed, initiates the query.
With regards to the database, it is:
a MySQL database (Community Edition)
a table named 'Book', where BookID is the PK and is of INT data type.
a field entitled 'BookTitle' which i've set as VARCHAR(35). It is not part of the key. However, it is in the BookTitle field, that i want to apply my query.
NOTE: This answer was posted based on the original code in the question, which has been edited to match what is in my answer. See the question's revision history for the first version of the question on which my answer was based.
The solution you saw in the other post was correct; it was just for a standard TQuery and not TADOQuery. TADOQuery requires a couple of minor syntax changes:
Use Parameters.ParamByName() instead of Parameters
Set a DataType for each parameter before using it
Use .Value instead of .AsString
Here's a corrected version of your code (which also includes setting a value for BookTitle before using it.
procedure TForm4.btnRunQueryClick(Sender: TObject);
var
BookEntry:string;
begin
BookEntry := 'Some book title'; // or QueryEdit.Text or whatever
qbook.SQL.Text:='SELECT BookTitle FROM Book WHERE BookTitle = :BookTitle';
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
qbook.Parameters.ParamByName('BookTitle').Value := BookEntry;
qbook.Open;
end;
I have never known a string type query parameter need the datatype or whatever set, I would simply remove any reference to the datatype.
After all, if it hurts when you bang your head on a wall, just stop banging it.