how boolean function validation works in apex? - database

declare
curr_quantity number;
begin
select quantity
into curr_quantity
from stockrecord
where itemserialnumber = :p28_item_1
And inventoryid = :p28_inv_id;
if curr_quantity < :p28_quantity_1 then
return false;
else
return true;
end if;
end;
STOCKRECORD(itemserialnumber, inventoryid, quantity).
I am trying to validate my quantity at the front end in APEX Oracle with PL/SQL Function Body (Returning Boolean).
Suppose if I have a 500 quantity of an item in table and I set :p28_quantity_1 from 1-250 validation works fine but when i set it to 251 or so on the validation fails. I can't figure out the problem. Any help please.

Related

pl sql validations on apex oracle database application forms

I need validations for three different forms
1- not null validation
2- value entered must be between 1 and 100
3- and selected date can't be before 5/10/2000 dd/mm/yyy
Right-click an item and create validation whose type is "Item is not null". Or, set it to be "required" (in its properties)
Set its "minimum and maximum" allowed values in its properties. If you have to create a validation, then make it a function that returns a Boolean (or Error text), such as
return :P1_ITEM not between 1 and 100;
The same as #2 ...
return :P1_DATE_ITEM >= date '2000-10-05';
Create new Validation with type PL/SQL Function (returning Error Text) with code:
if :P1_ITEM1 is null then return
'P1_ITEM1 is null' end if;
if :P1_ITEM2 not between 1 and 100 then return
'P1_ITEM2 not between' end if;
if to_date(:P1_ITEM3, 'DD/MM/YYYY') >= to_date('05/10/2000') then return
'P1_ITEM3 earlier than 05/10/2000' end if;
return null;
Additionally in Error Message type:
Unknown error.
Regards

What code is needed to conditionally provide a value to a field in a beforeinsert trigger?

I need to store a string value in a field in a table, specifically in its Subcategory VarChar(50) column.
The value of Subcategory prior to this post processing is either 0 or 1; I need to change that to a more human-friendly value.
I haven't created a database trigger in decades and need some help with the code. This is my pseudo SQL (a hodgepodge of SQL and VB):
CREATE OR REPLACE TRIGGER tr_CustomerCategoryLog_BeforeInsert
BEFORE INSERT ON CustomerCategoryLog FOR EACH ROW
DECLARE _Category = :new.Category;
DECLARE _Subcategory = :new.Subcategory;
BEGIN
If _Category = "New"
If _Subcategory = 0
:new.Subcategory := 'New';
End If
Else If _Subcategory = 1
:new.Subcategory := 'Assumed';
End If
End If
If _Category = "Existing"
If _Subcategory = 0
:new.Subcategory := 'Existing';
End If
Else If _Subcategory = 1
:new.Subcategory := 'Organic'
End If
End If
Return "Unknown"
End Function
END;
If the logic isn't clear, in semi-plain English it is:
If the value of the Category field is "New", set the Subcategory field value also to "New" if the value of Subcategory is currently 0; otherwise, set it to "Assumed"
If the value of the Category field is "Existing", set the Subcategory field value also to "Existing" if the value of Subcategory is currently 0; otherwise, set it to "Organic"
Maybe I need to give Steely Dan's album "Trigger Logic" a listen.
UPDATE
I think the answer will work, but it's not complete enough for me.
Since I apparently have Oracle code mixed up in the pseudoSQL above, what would the complete code need to look like (to create a BeforeInsert trigger on the CustomerCategoryLog table)?
Is this more like it:
CREATE TRIGGER tr_CustomerCategoryLog_BeforeInsert
ON CustomerCategoryLog
INSTEAD OF INSERT
AS
BEGIN
SELECT
CASE
WHEN #Category = 'New'
THEN CHOOSE(#Subcategory + 1, 'New', 'Assumed')
WHEN #Category = 'Existing'
THEN CHOOSE(#Subcategory + 1, 'Existing', 'Organic')
ELSE 'Unknown'
END
END
?
I tend to avoid triggers (perhaps a character flaw... I also don't like mashed potatoes), but the following illustration could simplify your logic
Declare #Category varchar(50) = 'Existing'
Declare #Subcategory int = 1 -- works if BIT
Select case when #Category = 'New' then choose(#Subcategory+1,'New','Assumed')
when #Category = 'Existing' then choose(#Subcategory+1,'Existing','Organic')
else 'Unknown' end
Returns
Organic

Oracle Issue with retrieving single row when multiple rows returned

I have a problem that i've spent about 3 days on.
I have a table(CDKEY) with 6 columns: CDKEYSEQ, Userseq,Banned, Communityseq, cdkey, Email.
Banned is always 0 (at this point), Userseq is NULL unless someone logged on/registered with the cdkey and email is NULL until the cdkey is registered.
Basically Userseq doesn't get filled in until a user logs in. So there will always be an email value before a user sequence value.
NOW The issue:
I'm trying to create a stored procedure that gets called when someone wants a cdkey (which they provide an email for).
The procedure first checks a table called community to make sure the Community exists.
Then if the Community exists The procedure is supposed to check the CDKEY Table for a key that has the correct community sequence AND AlSO has a NULL Value for both USERSEQ and EMAIL.
Obviously using just a select query doesnt work because there are multiple rows that are returned that match those conditions.
I tried using cursors, which got me a little further.
The problem with the cursors is that when I had two conditions after the WHERE clause, it didnt return anything.
Here is my current Procedure Code:
create or replace PROCEDURE KEYREGISTRATION(
PRODUCT_IN IN VARCHAR2 ,
in_CPUID IN LONG ,
in_MACID IN LONG ,
in_MACID2 IN LONG ,
in_HDID IN LONG ,
in_PCCores IN LONG ,
in_PCName IN VARCHAR2 ,
in_Email IN VARCHAR2 ,
out_cdkey OUT VARCHAR2 ,
returncode OUT NUMBER )
AS
CodeSuccess CONSTANT NUMBER := 0;
CoreError CONSTANT NUMBER := 2;
CodeAlreadyExists CONSTANT NUMBER := 3;
CodeBadProduct CONSTANT NUMBER := 4;
new_cdkey VARCHAR2(50);
old_cdkey VARCHAR2(50);
acommunitySeq NUMBER;
BEGIN
acommunitySeq := 0;
new_cdkey := '';
old_cdkey := '';
SELECT COMMUNITYSEQ INTO acommunityseq FROM COMMUNITY WHERE NAME = PRODUCT_IN;
returncode := CodeSuccess;
/*EXCEPTION
WHEN NO_DATA_FOUND THEN
returncode := CodeBadProduct; */
IF returncode = CodeSuccess THEN
BEGIN
SELECT CDKEY INTO old_cdkey FROM CDKEY WHERE EMAIL = in_email;
returncode := CodeBadProduct;
out_cdkey := old_cdkey;
RETURN;
EXCEPTION
WHEN NO_DATA_FOUND THEN
returncode := CodeSuccess;
END;
END IF;
IF returncode = CodeSuccess THEN
/*SELECT CDKEY into new_cdkey FROM CDKEY WHERE EMAIL = NULL AND COMMUNITYSEQ = acommunityseq; */
DECLARE
CURSOR c1
IS
SELECT CDKEY FROM CDKEY WHERE COMMUNITYSEQ = acommunityseq AND EMAIL = NULL;
BEGIN
OPEN c1;
FETCH c1 INTO new_cdkey;
IF ( c1%notfound ) THEN
returncode := CoreError;
END IF;
UPDATE cdkey SET EMAIL = in_email WHERE CDKEY = new_cdkey;
INSERT INTO user_hw VALUES( EMAIL = in_email, CPUID = in_cpuid,
MACID = in_macid, MACID2 = in_macid2, CPUCORES = in_pccores, PCNAME = in_pcname;
out_cdkey := new_cdkey;
returncode := CodeSuccess;
COMMIT;
END;
ELSE
returncode := CoreError;
ROLLBACK;
END IF;
END KEYREGISTRATION;
You think a query will not work - and your reasoning is "because a query will return too many rows." That is incorrect. Add a WHERE clause (or add to the filters you already have), with the condition ROWNUM = 1 - this will return the first row that meets all the other conditions, the processing will end, and you will get just this row and nothing else.

Oracle APEX: Show value of binding variable

Currently, I am a newbier of Apex. I create a page with 2 text box (YEAR and MONTH) and 1 button to submit value of 2 text box into PL/SQL script
DECLARE
count_files NUMBER;
BEGIN
select count(*) into count_files
from FAC_FILE_MANAGEMENT_VIEW
where FAC_MONTH = :P6_MONTH and FAC_YEAR = :P6_YEAR;
IF count_files = 0 THEN
-- insert something
END IF;
END;
The issue is the PL/SQl code does not work and I don't know how to print the value of 2 binding text box (:P6_MONTH and :P6_YEAR) to debug
Example:
htp.p('<script language="javascript">');
htp.p('alert("'||:P6_MONTH||'");');
htp.p('</script>');

How do I require a field to be mandatory on a NAV page?

It seems that the underlying nature of NAV is to resist requiring the populating of a field to be mandatory. In the case of our business logic, certain fields must be populated in order for the data to be valid. For example, a customer record must have at least a name and phone number. I've searched a number of places but have not found a suitable solution. So how can this be accomplished?
After struggling to find a succinct way to require certain fields on a card to be populated, I have come up with the following and it (so far) is working for me. I started to sense that NAV was not meant to have mandatory fields, but I need them for our business logic. Anyways, here we go...
Step One:
- We have a codeunit for various validation logic, in which I've added the function to read a custom table listing the tables and their fields that are mandatory. This function takes the table number, key field, and a "create mode". It returns a text "completion status" value. I find the table for the record I am validating. I loop through the mandatory fields, if the field is not populated, I add it to a list of incomplete fields. If the list of incomplete fields is empty, the completion status is "done". If the list of incomplete fields is populated, a message is displayed indicating the missing fields and allows the user an option to cancel the create of a new record or to stay on the (new or existing) record and enter the missing data, and the completion status is set to "delete" to cancel a create, or "return" to stay on the record. Logic follows:
CheckMadatoryFields(TableNumber : Integer;KeyField : Code[10];CreateMode : Boolean) Completion Status : Text[30]
// Read the 'LockoutFields' table to find the manditory fields for the table passed in.
LockoutFields.RESET;
LockoutFields.SETRANGE("Table No.", TableNumber);
LockoutFields.SETFILTER("Filter ID", 'MANDITORY_FIELD');
// Get a record reference for the table passed in
RecRef.OPEN(TableNumber);
RecRef.SETVIEW('WHERE("No." = FILTER(' + KeyField + '))');
// Set this to done, i.e. data is complete (don't delete by mistake).
CompletionStatus := 'done';
IF RecRef.FINDFIRST THEN BEGIN
// Check the record's manditory field(s) listed in the 'LockoutFields' table to see if they're blank.
IF LockoutFields.FINDSET THEN BEGIN
REPEAT
FldRef := RecRef.FIELD(LockoutFields."Field No.");
IF FORMAT(FldRef.VALUE) = '' THEN
FldList := FldList + ' - ' + FldRef.CAPTION + '\';
UNTIL LockoutFields.NEXT = 0;
END;
IF FldList <> '' THEN BEGIN
// If creating the record, add the 'Cancel Create' message, otherwise don't.
IF CreateMode THEN
DeleteRecord := CONFIRM(Text_ManditoryField + '\' + FldList + '\' + Text_CancelCreate, FALSE)
ELSE BEGIN
DeleteRecord := FALSE;
MESSAGE(Text_ManditoryField + '\' + FldList, FALSE);
END;
// Return a 'delete' status when deleting, or a 'return' status to stay on the record.
IF DeleteRecord THEN
CompletionStatus := 'delete'
ELSE
CompletionStatus := 'return';
END;
END;
RecRef.CLOSE;`
Step 2:
- On the card you want to check for mandatory fields, in my case the Customer card, I added a function to call the validation function in the codeunit described above. I also added logic to the
OnQueryClosePage trigger to call my local function. This all worked fine, as the user could not close the Customer card without completing the mandatory fields or cancelling the create of the customer, except if the user were to use Ctrl+PgUp or Ctrl+PgDn, which took them off the record. The trick was putting the the correct logic in the OnNextRecord trigger so that the validation was executed and the Ctrl+PgUp or Ctrl+PgDn still functioned (note: I found this bit somewhere on mibuso, many thanks!). Logic follows:
OnNextRecord(...)
IF CheckManditoryFields = TRUE THEN BEGIN
Customer := Rec;
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps <> 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END;
OnQueryClosePage(...)
EXIT(CheckManditoryFields);
CheckMandatoryFields() ExitValue : Boolean
// Check for manditory fields on this table. If there are missing manditory
// fields, the user can cancel this create, in which case, a 'TRUE' value is
// returned and we'll delete this record.
ExitValue := TRUE;
IF Rec."No." <> '' THEN BEGIN // This is blank if user quits immediately after page starts.
CompletionStatus := HHValidation.CheckManditoryFields(18,Rec."No.",CreateMode);
IF (CompletionStatus = 'delete')
AND (CreateMode = TRUE) THEN // User cancelled create (not edit), delete the record and exit.
Rec.DELETE(TRUE)
ELSE
IF CompletionStatus = 'done' THEN // User completed manditory fields, OK to exit.
ExitValue := TRUE
ELSE
ExitValue := FALSE; //User did not complete manditory fields and wants to return and add them.
END;
I think that's about it. The details of the custom table are really up to how you want to code it. The validation code unit can be what you want it to be. Using a table allows the mandatory fields to added or removed without changing any logic, and this "generic" validation logic could be put on any page. The key is the two triggers on the card and having a common validation routine to call.
I used this code on a form (classic client) but there is one error in the OnNextRecord code.
OnNextRecord(...)
IF CheckManditoryFields = TRUE THEN BEGIN
Customer := Rec;
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps <> 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END;
You should also cover the situation where CheckMandatoryFields returns FALSE. Otherwise when I request the next record it shows me an empty record.
It should be like this:
OnNextRecord(...)
IF CheckManditoryFields = TRUE THEN BEGIN
Customer := Rec;
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps <> 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END ELSE
EXIT(Steps);
There is another error in the OnNextRecord function:
OnNextRecord(...)
IF CheckManditoryFields = TRUE THEN BEGIN
Customer := Rec;
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END ELSE
EXIT(Steps);
The filters set on the sourcetable of the page (or form) are not copied to the record on which the steps are taken. So you can navigate to a record that is not in your filterset.
Instead assigning the Rec to the Customer, you should copy it:
OnNextRecord(...)
IF CheckManditoryFields THEN BEGIN
Customer.COPY(Rec);
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END ELSE
EXIT(Steps);
Try using this code from the page trigger
OnQueryClosePage(CloseAction : Action None) : Boolean
Var
Begin
Rec.TESTFIELD(FieldName);
End;

Resources