Oracle procedure for send single email from query result - oracle

My Oracle procedures are not working as expected, kindly advise which part needs to update.
Expectation: The query result will send in single emails.
For example, the result from the query has 3 entries.
However, the email sending with 3 separate emails instead of 1 email content of 3 entries.
v_subject := 'Missing info';
for email in
(
select *
from Info WHERE info_status is null
)
loop
v_message := 'Name' || email.name || ' --> '|| email.status || ' '|| email.year || ' ';
SEND_MAIL(
SENDER => v_From,
RECIPIENT => v_TO,
SUBJECT => v_subject,
MESSAGE => v_message);
END LOOP email;

As written, the code inside the loop runs once for each row returned from the query.
Hint: Build the MESSAGE and RECIPIENT values for each row returned, then send only one e-mail at the end.

Related

Validate email that was sent using apex_mail.send and apex_mail_log

I'm using Oracle Apex version 22.2. I have a pl/sql process where I'm sending an email, and another process runs based on the status of that email being sent. I can select the mail_id from apex_mail_log and display it on one of my page items just fine. But when I try to use that mail_id to select the mail_send_error of that email record, I keep getting ORA-01403: no data found (the email sends regardless of this error). I have validated that it's returning the correct mail_id of the record that was just inserted. I have also validated that my select statement runs just find when ran in SQL Workshop. I need to be able to validate this email was actually sent before I can continue with the other processes, any ideas?
declare
v_orgname varchar2(500);
v_mailid number;
v_mailresult number;
begin
-- select our variables to send with our email
select name into v_orgname from organizations where keyid = :P277_ORGKEYID;
v_mailid := apex_mail.send (
p_to => :P277_EMAILTO,
p_from => :P277_EMAILFROM,
p_template_static_id => 'ACTUM_WELCOME_LETTER',
p_placeholders => '{' ||
' "SITENAME":' || apex_json.stringify( v_orgname ) ||
' ,"MY_APPLICATION_LINK":' || apex_json.stringify( apex_mail.get_instance_url || apex_page.get_url( 'test' )) ||
' ,"SUBID":' || apex_json.stringify( :P277_PARENTID ) ||
'}' );
apex_mail.push_queue();
-- Using the mailid, we can select the result of that email
select nvl(mail_send_error,0) into v_mailresult from apex_mail_log where mail_id = v_mailid;
-- This page item is being set so this variable is correct, why is my process saying no data found?
select v_mailid into :P277_MAILID from dual;
-- If the result is a success, we select 1 into our mail sent variable to tell the login process to run
if v_mailresult = 0 then
select 1 into :P277_EMAILSENT from dual;
else
select 0 into :P277_EMAILSENT from dual;
end if;
end;
The issue is that the apex_mail.push_queue is an asynchronous call. Your code to be executed afterwards does not wait for the actual mail sending to finish. You can check this in DBA_SYNONYMS where APEX_MAIL synonym leads to APEX_220200.WWV_FLOW_MAIL_API. Unwrap its code and see that it leads to WWW_FLOW_MAIL.PUSH_QUEUE which leads to PUSH_QUEUE_BACKGROUND whose code is:
SYS.DBMS_SCHEDULER.RUN_JOB( JOB_NAME => 'ORACLE_APEX_MAIL_QUEUE', USE_CURRENT_SESSION => FALSE );
So, what you can do, you can run directly that job but with USE_CURRENT_SESSION => TRUE, if you really want to synchronously wait for the execution result. An other option is to check the result in a separate block of code / functionality, and then take UI rendering decisions based on that result.

Extract information from a string that is not delimited but has break lines PL/SQL

I am working on a function which of the following string:
create or replace PROCEDURE get_error_keys(p_error_code IN VARCHAR2) IS
v_error_key varchar2(2000):= '
EF03 There are disabled accounts
EF04 The account is invalid. Check your cross validation rules and segment values
EF05 There is no account with this account combination ID
EF06 The alternate account is invalid
WF01 An alternate account was used instead of the original account
WF02 A suspense account was used instead of the original account';
v_linea VARCHAR2(32767);
--
--
Begin
SELECT
regexp_substr(v_error_key_list, '[^('||chr(13)||chr(10)||')]+',1,level) Linea into v_linea
FROM
dual
CONNECT BY
regexp_substr(v_error_key_list, '[^('||chr(13)||chr(10)||')]+',1,level) IS NOT NULL;
End;
My intention is to filter this query, which receives p_error_code and filters by that parameter, for example:
If p_error_code = 'EF04', then the query returns: EF04 The account is invalid. Check your cross validation rules and segment values
I have modified the query to add a where condition:
select
regexp_substr(v_error_key, '[^('||chr(13)||chr(10)||')]+',1,level) Line into v_line
DESDE
dual
WHERE Line like '%'||p_error_code||'%'
CONNECT BY
regexp_substr(v_error_key, '[^('||chr(13)||chr(10)||')]+',1,level) IS NOT NULL;
But I get the following error:
7/18 PL/SQL: ORA-00904: "Linea": invalid identifier
Thanks,
Cesar.
As I said in the comment, the right way is to put the code/descriptions in a table and then do a simple lookup. But since I'm sitting in a meeting and can play with this...it can be simplified by treating the string of error/descriptions as a delimited string with a chr(10) character as the delimiter. Get the substring that starts with the error code passed in, followed by one or more whitespace characters, then zero or more any characters non-greedy until the delimiter or the end of the line.
SELECT
REGEXP_SUBSTR(v_error_key_list, '('||p_error_code||'\s+.*?)('||CHR(10)||'|$)')
INTO v_linea
But at least do this for easier maintenance if you can't create a proper lookup table for some reason. Use a WITH clause to put your codes/descriptions in a table form. Best practice is to always allow for the exception where your code is not found also.
BEGIN
WITH tbl_err(code, DESCR) AS (
SELECT 'EF03', 'There are disabled accounts' FROM dual UNION ALL
SELECT 'EF04', 'The account is invalid. Check your cross validation rules and segment values' FROM dual UNION ALL
SELECT 'EF05', 'There IS NO ACCOUNT WITH this ACCOUNT combination ID' FROM dual UNION ALL
SELECT 'EF06', 'The alternate account is invalid' FROM dual UNION ALL
SELECT 'WF01', 'An alternate ACCOUNT was used INSTEAD OF THE original ACCOUNT' FROM dual UNION ALL
SELECT 'WF02', 'A suspense account was used instead of the original account' FROM dual
)
SELECT DESCR
INTO v_linea
FROM tbl_err
WHERE code = p_error_code;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_linea := 'Code: ' || p_error_code || ' not found.';
END;
It would really be best to put the error codes and error texts into some sort of lookup table. Below is a function that meets your requirements with a hard-coded string containing multiple error codes.
create or replace FUNCTION get_error_keys(p_error_code IN VARCHAR2)
RETURN VARCHAR2
IS
v_error_key_list varchar2(2000):= '
EF03 There are disabled accounts
EF04 The account is invalid. Check your cross validation rules and segment values
EF05 There is no account with this account combination ID
EF06 The alternate account is invalid
WF01 An alternate account was used instead of the original account
WF02 A suspense account was used instead of the original account';
--
--
Begin
RETURN REGEXP_SUBSTR(v_error_key_list, p_error_code || '\s+[^' || CHR(10) ||'$]+');
End;
Below is code to test the function.
DECLARE
TYPE t_input_value_tbl IS TABLE OF VARCHAR2(4);
l_input_value_tbl t_input_value_tbl;
l_return_value VARCHAR2(32767);
BEGIN
l_input_value_tbl := t_input_value_tbl('EF03','EF04','EF05','EF06','WF01','WF02','ZZ01');
FOR i IN l_input_value_tbl.FIRST..l_input_value_tbl.LAST LOOP
l_return_value := get_error_keys(l_input_value_tbl(i));
dbms_output.put_line('input: "' || l_input_value_tbl(i) || '" l_return_value: "' || l_return_value || '"');
END LOOP;
END;
Results
input: "EF03" l_return_value: "EF03 There are disabled accounts"
input: "EF04" l_return_value: "EF04 The account is invalid. Check your cross validation rules and segment values"
input: "EF05" l_return_value: "EF05 There is no account with this account combination ID"
input: "EF06" l_return_value: "EF06 The alternate account is invalid"
input: "WF01" l_return_value: "WF01 An alternate account was used instead of the original account"
input: "WF02" l_return_value: "WF02 A suspense account was used instead of the original account"
input: "ZZ01" l_return_value: ""

Sending email based on field name UTL_MAIL.send

How can I send an email based on what is entered into a apex Text field. eg If I enter me#test.com in :p6_supervisor, I would like the email to be sent to that person.
At present I have a preset UTL_MAIL.send which is working.
begin
UTL_MAIL.send(sender => 'test#test.com',
recipients => 'test1#test.com',
subject => 'Test,
message => 'Please Note this is a test' );
end;
But of course its for another purpose, which is sending email to one recipient from a trigger.
Below is the Cursor example
create or replace function "email"
( name_in IN varchar2 )
RETURN number
IS
supervisoremail varchar2(30);
CURSOR c1
IS
select
supervisoremail
from
EMPLOYEE,supervisors
where TO_DATE(contract_start_period,'DD-MM-YYYY') < TO_DATE (SYSDATE,'DD-MM-YYYY') - 275
and (supervisors.supervisorname = employee.supervisorname1
or supervisors.supervisorname = employee.supervisorname2)
and employee_name ='test'
;
BEGIN
OPEN c1;
FETCH c1 INTO supervisoremail;
CLOSE c1;
RETURN supervisoremail;
END;
It'll work, no problem. A simple way is to
create a text item (P6_SUPERVISOR)
create a button which submits the page; you need it so that P6_SUPERVISOR's value is set into the session state
create a process which calls UTL_MAIL; P6_SUPERVISOR item's contents is used as a source for the RECIPIENTS parameter. For example:
UTL_MAIL.send (sender => 'test#test.com',
recipients => :P6_SUPERVISOR,
subject => 'Test message - subject',
MESSAGE => 'Test message - message body');
[EDIT: how to send mails in a loop?]
I'm not sure what you meant by creating a function; it returns just one e-mail address that, probably, belongs to name passed through the NAME_IN parameter (but you used the 'test' name in cursor query).
However, you specified that the function returns a NUMBER, while variable you're selecting the result into is a VARCHAR2. So, which one of these is true?
If you insist on a function, don't enclose its name into double quotes as you'll always have to reference it that way (double quotes, correct upper/lower/mixed case).
Furthermore, what is contract_start_period column's datatype? If it is DATE, don't TO_DATE it. The same goes for SYSDATE - it is a function that returns DATE datatype anyway, so it is wrong to convert it to date once again.
Here's an example which uses a cursor FOR loop (as it is easier to maintain) and sends mail to all supervisoremail addresses returned by that SELECT statement.
begin
for cur_r in (select supervisoremail
from employee e join supervisors s on s.supervisorname = e.supervisorname1
or s.supervisorname = e.supervisorname2
where contract_start_period < sysdate - 275
and e.employee_name = 'test'
)
loop
utl_mail.send(sender => 'test#test.com',
recipients => cur_r.supervisoremail,
subject => 'Test message - subject',
message => 'Test message - message body');
end loop;
end;

Procedure for Sending Mails to Customers from the DB

I have a procedure for sending emails to customers once the data loaded by them has been processed using the inbuilt procedure for sending mails. It takes the recipient of the mail,the subject and the message as input parameters.
I have to create another procedure for taking the input parameters from 2 different tables. I want to do something like the below mentioned :
To: Email of user loaded the data thru portal.
Subject : Your application “&APP_Name” has been Published.
Message : Your application “&APP_Name” has been Published. Please visit the link : XXXXXXXXXX
I have to get the recipient and app_name from two different tables? What is the best way of doing that?
Is this what you are looking for? Just select the values you need.
DECLARE
v_r VARCHAR2(100);
v_app VARCHAR2(100);
PROCEDURE email(p_receipent IN VARCHAR2, p_app_name IN VARCHAR2) IS
BEGIN
dbms_output.put_line('send to:' || p_receipent || ' subject:' || p_app_name || ' message: ' || p_app_name);
END;
BEGIN
SELECT 'demo#mydemo.so' INTO v_r FROM dual; --table1
SELECT 'app1' INTO v_app FROM dual; --table2;
email(v_r, v_app);
END;

Items assigned using API does not appear in Oracle EBS application windows

I used the following API to assign multiple items (one by one) from one organization to another.I had created test items through the inventory responsibility using master item window and then assign them to another organization using the following code:
BEGIN
DECLARE
l_api_version NUMBER := 1.0;
l_init_msg_list VARCHAR2(2) := FND_API.G_TRUE;
l_commit VARCHAR2(2) := FND_API.G_FALSE;
x_message_list error_handler.error_tbl_type;
itemid mtl_system_items_b.inventory_item_id %TYPE;
segment1 mtl_system_items_b.segment1 %TYPE;
primary_uom_code mtl_system_items_b.primary_uom_code %TYPE;
x_return_status VARCHAR2(2);
x_msg_count NUMBER := 0;
BEGIN
SELECT inventory_item_id INTO itemid FROM mtl_system_items_b WHERE inventory_item_id=2447106 and organization_id=116;
SELECT segment1
INTO segment1 FROM mtl_system_items_b WHERE inventory_item_id=2447106 and organization_id=116;
SELECT primary_uom_code INTO primary_uom_code FROM mtl_system_items_b WHERE inventory_item_id=2447106 and organization_id=116;
EGO_ITEM_PUB.ASSIGN_ITEM_TO_ORG(
P_API_VERSION => l_api_version
, P_INIT_MSG_LIST => l_INIT_MSG_LIST
, P_COMMIT => l_COMMIT
, P_INVENTORY_ITEM_ID => itemid --(item id from the above Query)
, P_ITEM_NUMBER => segment1 --(Item Code from the above Query)
, P_ORGANIZATION_ID => 117 --(Organization Id for assingment)
, P_ORGANIZATION_CODE => 'D12'--v_organization_code
, P_PRIMARY_UOM_CODE =>primary_uom_code --(UOM from the above Query)
, X_RETURN_STATUS => X_RETURN_STATUS
, X_MSG_COUNT => X_MSG_COUNT
);
DBMS_OUTPUT.PUT_LINE('Status: '||x_return_status);
IF (x_return_status <> FND_API.G_RET_STS_SUCCESS) THEN
DBMS_OUTPUT.PUT_LINE('Error Messages :');
Error_Handler.GET_MESSAGE_LIST(x_message_list=> x_message_list);
FOR j IN 1..x_message_list.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(x_message_list(j).message_tex t);
END LOOP;
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception Occured :');
DBMS_OUTPUT.PUT_LINE(SQLCODE ||':'||SQLERRM);
END;
END;
After completion, I checked into the database and the items were assigned to the organization i wanted to. However, these items were not appearing in any of the windows when i searched through GUI (Item information window or item search window) under the new organization that i assigned them too.
I tried changing few values that appeared to be abnormal (E.g: last update time was '-1' probably because i used API and not GUI using my user id and password).
Why are not the items appearing in GUI?Is there a step other than executing the above code correctly that I am missing? Please help.
You are not committing the transaction. You declare:
l_commit VARCHAR2(2) := FND_API.G_FALSE;
Which you then pass into the API. If you then query the tables using the same transaction, you will see the modified data. But the application forms are using another session.
You need to add a commit at the end. As to whether to pass in G_TRUE, you need to read the API documentation.

Resources