I'm trying to simulate an user, selecting a single record on a list page (i.e. Item List). This is for testing a procedure, which opens said list page, waiting for user input and, if the user presses Ok, adds the selected lines from the list page as lines to a document (i.e. Purchase Line/Purchase Header)
[Test]
[HandlerFunctions('ItemListOkModalPageHandler')]
procedure TestAddItemLineItemList()
var
LRec_PurchaseHeader: Record "Purchase Header";
LRec_PurchaseLine: Record "Purchase Line";
LC_PageHelper: Codeunit "Page Helper";
begin
//[GIVEN] given
CreatePurchaseOrder(LRec_PurchaseHeader);
LRec_PurchaseLine.SetRange("Document Type", LRec_PurchaseHeader."Document Type");
LRec_PurchaseLine.SetRange("Document No.", LRec_PurchaseHeader."No.");
LRec_PurchaseLine.SetRange(Type, "Purchase Line Type"::Item);
GC_LibraryAssert.RecordIsEmpty(LRec_PurchaseLine);
//[WHEN] when
LC_PageHelper.AddMultiplePurchaseLines(LRec_PurchaseHeader);
//[THEN] then
GC_LibraryAssert.RecordCount(LRec_PurchaseLine, 1); // error happens here
end;
[ModalPageHandler]
procedure ItemListOkModalPageHandler(var PTP_ItemList: TestPage "Item List");
begin
PTP_ItemList.First();
PTP_ItemList.OK().Invoke();
end;
When trying to do it this way, I get the following error message:
Error:
Assert.RecordCount failed. Expected number of Purchase Line entries: 1. Actual: 0.
Any suggestions, what am I doing wrong?
Edit: Code of Procedure AddMultiplePurchaseLines
procedure AddMultiplePurchaseLines(PRec_PurchaseHeader: Record "Purchase Header")
var
LRec_Item: Record Item;
LP_ItemList: Page "Item List";
begin
PRec_PurchaseHeader.TestField(Status, "Purchase Document Status"::Open);
if GuiAllowed then begin
LP_ItemList.LookupMode(true);
if LP_ItemList.RunModal() = Action::LookupOK then begin
LRec_Item.SetFilter("No.", LP_ItemList.GetSelectionFilter());
AddPurchaseOrderItemLines(PRec_PurchaseHeader, LRec_Item);
end;
end;
end;
The first issue you need to resolve is that activating LookupMode on a Page changes the returned value of RunModal.
This means that in AddMultiplePurchaseLines you must change it to:
if LP_ItemList.RunModal() = Action::LookupOK then begin
Secondly the call to Expand in ItemListOkModalPageHandler does not do anything. All it does is expand the tree structure (if it exists).
Related
I have an Access database table named Receipts with the following field names: receipt number item name, buying price, selling price. I am using an Adoquery and datasource to connect to the access database. The following is the code I am using to print a report.
procedure TReceiptsform.BitBtn1Click(Sender: TObject);
var
qry :string;
begin
with ADOQuery1 do
begin
if ADOQuery1.Locate('receipt number',Edit1.Text,[]) then
open;
SQL.Clear;
qry:= 'SELECT*from Receipts WHERE (((Receipts.[receipt number])='+ edit1.Text+'))order by Receipts.[Item name]';
SQL.Add(qry);
Active:= True;
ReceiptForm.QuickRep1.Preview;
end;
end;
However when I run the program then I click BitBtn1Click at runtime, I get this error.
ProjectSales.exe raised exception class EOleException with message 'Extra ) in query expression "(((Receipt.[item name])=))". Process stopped. Use Step or Run to continue.
Which exception handling code can I use to prevent this error or is there a problem with the Query?
You problem is, that if Edit1 is Null, no value is given to filter on because the use of + eliminates the quotes:
'SELECT * from Receipts WHERE (((Receipts.[receipt number])='+ edit1.Text+'))order by Receipts.[Item name]'
=>
'SELECT * from Receipts WHERE (((Receipts.[receipt number])=)) order by Receipts.[Item name]'
So, use ampersand and some spaces:
'SELECT * from Receipts WHERE (((Receipts.[receipt number])=' & edit1.Text &')) order by Receipts.[Item name]'
I have an Access database table named ReceiptTable with the following field names: item name, buying price, selling price, goods total, cash, change. I am using an Adoquery and datasource to connect to the access database. When I want to update records to receiptTable, I use the following code to locate an item name from the database then update all the records with similar item name in the database with the values from edit box field values:
procedure TReceiptForm.BitBtn1Click(Sender: TObject);
begin
with ADOQuery1 do
ADOQuery1.Open;
ADOQuery1.Locate('item name',Edit1.Text,[]) ;
ADOQuery1.edit;
ADOQuery1.FieldValues['goods total']:=edit3.Text;
ADOQuery1.FieldValues['cash']:=edit4.Text;
ADOQuery1.FieldValues['change']:=edit5.Text;
ADOQuery1.Post;
end;
The problem I have is that only one row with the item name is updated but the other rows with similar item name are not updated. What code should I add above so that all the rows which have similar item names are updated with values from edit boxes?
This simple code answers your question:
procedure TReceiptForm.BitBtn1Click(Sender: TObject);
var
itemname, goodstotal, cash, change: string;
begin
// Execute query
try
ADOQuery1.Open;
except
on E: Exception do begin
ShowMessage(E.Message);
Exit;
end{on};
end{try};
// Values
itemname := Edit1.Text;
goodstotal := Edit3.Text;
cash := Edit4.Text;
change := Edit5.Text;
// Find first matching record, then go to the end of resultset.
try
ADOQuery1.DisableControls;
if ADOQuery1.Locate('item name', itemname, []) then begin
while not ADOQuery1.Eof do begin
if ADOQuery1.FieldByName('item name').AsString = itemname then begin
ADOQuery1.Edit;
ADOQuery1.FieldValues['goods total'] := goodstotal;
ADOQuery1.FieldValues['cash'] := cash;
ADOQuery1.FieldValues['change'] := change;
ADOQuery1.Post;
end{if};
ADOQuery1.Next;
end{while};
end{if};
finally
ADOQuery1.EnableControls;
end{try};
end;
This will work, but you can consider using one SQL statement for updating the table, or
if it is possible order your query by 'item name' and use this:
...
// Find first matching record, then update while next record matches too.
if ADOQuery1.Locate('item name', itemname, []) then begin
while (not ADOQuery1.Eof) and
(ADOQuery1.FieldByName('item name').AsString = itemname) do begin
ADOQuery1.Edit;
ADOQuery1.FieldValues['goods total'] := goodstotal;
ADOQuery1.FieldValues['cash'] := cash;
ADOQuery1.FieldValues['change'] := change;
ADOQuery1.Post;
ADOQuery1.Next;
end{while};
end{if};
...
I have created a query that displays highest returned items from a table. My query it works perfectly with no errors! However, I want an efficient way of converting the query to PL/SQL block. The purpose of conversion is to handle errors.
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (
SELECT MAX(ITEM_NUM) FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
Here's a way we do some exception handling in packages. I altered it to use your code as an example. Maybe you can use some ideas from it.
At the top, set a CONSTANT to the name of the procedure, and some variables to catch Oracle SQL error number and message in. Then in the body of the procedure, there is an anonymous block containing the select. First we set a variable to indicate where we are (err_loc) in case there are multiple locations where an error could be caught in one block. Then the select is issued. If an error occurs, it's caught by the EXCEPTION clause. Error info from Oracle is caught in the err* variables, the err_string is built and then emailed via the UTL_MAIL package. RAISE raises the error so the program halts. It's set up like this to be generic as possible, we can drop in the template, change the MBR_NAME, SQL, err_loc and that's it.
PROCEDURE TEST_PROC AS
MBR_NAME CONSTANT VARCHAR2(100) := 'TEST_PROC'; -- For use in error handling. Package member name.
err_nbr NUMBER; -- Holds a SQL error number if an exception occurs.
err_msg VARCHAR2(1000); -- Holds a SQL error message if an exception occurs.
err_string VARCHAR2(2000);
BEGIN
BEGIN
err_loc := 'Selecting max quantity'; -- Email subject
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (SELECT MAX(ITEM_NUM)
FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
EXCEPTION
WHEN OTHERS THEN
err_nbr := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 1000);
err_string := 'ERROR: ' || err_nbr || ' occurred: ' || err_msg;
-- PKG_NAME and err_email_recip set in the body.
UTL_MAIL.send(sender => PKG_NAME||'.'||MBR_NAME||'#yourcompany.com',
recipients => err_email_recip,
subject => 'ERROR '|| err_loc,
message => CHR(13))||err_string);
RAISE;
END;
END TEST_PROC;
Have a look at cursors and records.
That way you have fetch data from the query and process the line if needed.
I don't have a database by hand to test my code, but this might give you an idea how a cursor and record work.
In order to capture a EXCEPTION you could add an exception handler and let it log the record you where busy with when the exception occured.
DECLARE
CURSOR CursorName IS
SELECT ColumnOne
FROM TableA
WHERE Name = 'Me';
RecordNumber CursorName%ROWTYPE;
BEGIN
-- Fetch the records from the cursor.
OPEN CursorName;
LOOP
FETCH CursorName INTO RecordNumber;
-- Do something with the record.
EXIT WHEN CursorName %NOTFOUND;
END LOOP;
CLOSE CursorName;
END;
/
Adding a error handeling would be done right above END:
EXCEPTION
WHEN OTHERS THEN
-- Log error message.
END;
/
Link: Error handeling
Does that answer you question a bit?
I'm trying to write a Stored Procedure which takes input of an ID then searches 3 different tables for that item using different criteria. One solution I have is to perform the select statement on each table one by one, and catching the NO_DATA_FOUND exception if nothing is found via the select.
In pseudocode:
Select item from from first table
If no data found, throw exception.
Handle exception by selecting data from second table
If no data found, throw another exception.
Handle exception by selecting data from third table (if the data is not present in any row it should return 0 rows)
Here's what I have:
OPEN REQUEST FOR
SELECT REQ_TYPE, REQ_TYPE_STATUS FROM TABLEONE
WHERE TABLEONE.REQ_ID = REQUESTID
AND (REQ_STATUS = 'D' OR REQ_STATUS = 'A');
EXCEPTION WHEN NO_DATA_FOUND THEN
BEGIN
OPEN REQUEST FOR
SELECT REQ_TYPE, '-' AS REQ_TYPE_STATUS FROM TABLETWO
WHERE TABLETWO.REQ_ID = REQUESTID;
EXCEPTION WHEN NO_DATA_FOUND THEN
begin
OPEN REQUEST FOR
SELECT REQ_TYPE, '-' as REQ_TYPE_STATUS FROM THIRDTABLE
WHERE THIRDTABLE.REQ_ID = REQUESTID;
end;
END;
If an item is found in TABLEONE, it successfully returns the data for it. However, the SELECT operations that are performed inside the caught exceptions don't seem to run as the the stored procedure doesn't return any rows.
I have separately verified that the data I'm searching for definitely exists in TABLETWO AND/OR TABLETHREE.
The syntax is valid as it compiles, it's just that it doesn't return any rows if the item doesn't exist in TABLEONE (but exists in either TABLETWO or TABLETHREE).
Any ideas?
Credit to #Franek for this.
The referenced linked article explains the following:
Cursor FOR loop is smart. It won't raise NO-DATA-FOUND as it is; if
there's nothing to be fetched, it will exit the loop and terminate
execution successfully. Therefore, if you meant to handle it as an
EXCEPTION - you can not.
What I was attempting to do is apparently not possible. Therefore, I have implemented it this way instead:
Pseudocode:
Search for the item in the first table
If item was found
select the details of it from the first table
If item was not found
search for the item in the second table
If Item was found
Select the details of it from the second table
If item was not found
Select the item from the third table (returning blank if it's not found here neither)
PL/SQL Implementation:
-- Search for the request ID in the first table
SELECT COUNT(REQ_ID) INTO requestFound FROM FIRSTTABLE
WHERE FIRSTTABLE.REQ_ID = REQUESTID
AND (REQ_STATUS = 'D' OR REQ_STATUS = 'A');
IF(REQUESTFOUND > 0) THEN
-- Select the request details
OPEN REQUEST FOR
SELECT REQ_ID, REQ_TYPE_STATUS FROM FIRSTTABLE
WHERE FIRSTTABLE.REQ_ID = REQUESTID
AND (REQ_STATUS = 'D' OR REQ_STATUS = 'A');
ELSE
-- Search for the request from the second table
SELECT COUNT(REQ_ID) INTO REQUESTFOUND FROM SECONDTABLE
WHERE SECONDTABLE.REQ_ID = REQUESTID;
IF(REQUESTFOUND > 0) THEN
-- Select the request details from second table
OPEN REQUEST FOR
SELECT REQ_TYPE, '-' AS REQ_TYPE_STATUS FROM SECONDTABLE
WHERE SECONDTABLE.REQ_ID = REQUESTID;
ELSE
-- Get the request from third table (will return as blank if nothing found)
OPEN REQUEST FOR
SELECT REQ_TYPE, '-' AS REQ_TYPE_STATUS FROM THIRDTABLE
WHERE THIRDTABLE.REQ_ID = REQUESTID;
END IF;
END IF;
Q :
Display all doctors that their charge per
appointment is lower than the minimum.
If exists, display a message to ask these doctors to
increase their charge per appointment by 30%.
Otherwise, provide a message in exception
handlers to notify the user of such result.
Allow the user to enter the minimum charge per
appointment.
the exception doesnt work ??
ACCEPT minchg PROMPT 'Enter the minimum charge per appointment: RM '
DECLARE
ex_min := chgperappt > &minchg EXCEPTION;
v_id doctor.doc_id%TYPE;
v_name doctor.doc_name%TYPE;
v_chg doctor.chgperappt%TYPE;
CURSOR doc_chg IS
SELECT doc_id, doc_name, chgperappt
FROM doctor
WHERE chgperappt < &minchg;
BEGIN
OPEN doc_chg;
LOOP
FETCH doc_chg INTO v_id, v_name, v_chg;
EXIT WHEN doc_chg%NOTFOUND;
DBMS_OUTPUT.PUT_LINE ('Dr. '||v_name||' ('||v_id||') is charging RM '|| v_chg);
DBMS_OUTPUT.PUT_LINE ('Please increase the charge per appointment by 30% --> RM '||v_chg*1.3);
EXCEPTION
WHEN ex_min THEN`enter code here`
DBMS_OUTPUT.PUT_LINE ('All charge per appointment met the minimum criteria.') ;
END LOOP;
CLOSE doc_chg;
END;
/
can you help ?
The basic template for creating a user-defined exception, and handling it, is something like this:
(not your complete code example, but a snippet)
DECLARE
ex_min EXCEPTION;
BEGIN
// open cursor, etc.
IF chgperappt > &minchg THEN
RAISE ex_min;
END IF;
// other code to execute if no exception is raised.
EXCEPTION
WHEN ex_min THEN
// add you code here for exception handling case
END;
Here is a link to a tutorial that goes through the process step-by-step. Hope that helps.