Friday 16 December 2011

Attachments in workflow notifications


Hi,

I am going to explain you about the attachments in workflow notifications. For example, while raising a Purchase Order, user/buyer may attach multiple documents to the approver (for the proof/to review….). In these cases we need to send all the attached documents to the approver in approver’s notification.

In 11.5.10 (Prior to R12), Requisition approval has attachment links. But, PO Approval does not have attachment links in approver’s notification. (Except the reference attachments…explained below).

Oracle has given this option in R12. If the client version is 11i or prior and if the client is asking for the attachment links in PO approval then we need to customize the workflow.

Let us make it as generic rather than module specific. A simple workflow with two types of attachments is shown below:



There are two types of attachments in a notification. One is attachment link and the other is reference attachments (reading/encoding…).


Creating Attachment Links:

Every document which is attached in forms will be saved in fnd_lobs table and with the unique id (document id) in fnd_attached_documents and fnd_documents

Look at the below screenshot to attach any document from forms.




Query to get the attached document:

      SELECT fl.file_name, fl.file_data
  FROM fnd_lobs fl, fnd_attached_documents fad, fnd_documents_tl fdl
  WHERE fad.pk1_value = TO_CHAR (:l_document_id)
   AND fad.pk2_value = TO_CHAR (l_revision_number)
   AND fdl.document_id = fad.document_id
   AND fdl.media_id = fl.file_id
   AND fad.entity_name = :l_entity_name;

 
In above query,
 l_document_id is the primary key of the module attachment. i.e if you are attaching in PO header level then document id will be PO_HEADER_ID value and if the user is attaching documents in line level then document id will be PO_LINE_ID value.

l_entity_name
There are various entity names for each module in header and line level.
Ex:  
REQ_HEADERS                        PA_PROJECTS
REQ_LINES                              PER_ABSENCE_ATTENDANCES                                              
PO_HEADERS                          OIE_HEADER_ATTACHMENTS
PO_HEAD                                OIE_LINE_ATTACHMENTS
PO_LINES                                PA_CONTROL_ITEMS

l_revision_number
Revision number is the attachment revision number, generally starts with ‘0’.

Attachment link looks like below

 

Steps to get the attachment link in notification from fnd_lobs:

  1. Create a attribute in workflow with the type as ‘Document’
Where Value of the attribute hardcode to ‘FND:entity=OIE_LINE_ATTACHMENTS&pk1name=REPORT_LINE_ID&pk1value=556161’

*We can set it in code for dynamic pick.

The syntax should be same as above to get value from fnd_lobs, fnd_attached_documents and fnd_documents using the above mentioned query.

One more example of syntax for PO line attachments:

FND:entity=PO_LINES&pk1name=PO_LINE_ID&pk1value=XXXXX (po_line_id value)

The value for 'entity' can be obtained from DATA_OBJECT_CODE in FND_DOCUMENT_ENTITIES.

The value for 'pk1name' can be obtained from PK1_COLUMN in FND_DOCUMENT_ENTITIES.
The value for 'pk1value' can be obtained from PK1_VALUE in FND_ATTACHED_DOCUMENTS.

and similiarly for pk2, pk3 and so on respectively from the said tables.

Please refer below comments for more info.



  1. Create a message attribute in message in which you want to send the attachment

Message attribute internal name should be ‘#ATTACHMENTS’.
No need to check the attach content check box.
Give default type as ‘Item attribute’ and select the document type item attribute which was just created in step 1.




  1. This is it. You can run the workflow to check the attachment links.
                No limitation for the number of attachments.
                No restriction for the type of the document, you can attach any file.
                Files should present in the tables for each document id.


*********************************************************************************
*********************************************************************************

Other type of attachment is which we can write/read/encode the documents and five it as an attachment.

Same process as above to create attributes, but the syntax will differ and should check the ‘attach content’ check box in message attributes. ‘#ATTACHEMENTS’ is not required.

Separate pl/sql code (with standard parameters) is there to execute the logic.

Steps to create clob/blob attachment:

1)
Create an attribute of type ‘Document’
Drag and drop OR Copy the document and paste it in Message attributes.
 Check the ‘Attach Content’ Check box.

Attribute value is hardcoded in the below screenshot. You can set it in your custom code.



2)
There are 3 various types of syntaxes available to create an attachment.

  1. PLSQL 
  2. PLSQLCLOB
  3. PLSQLBLOB

Syntax: PLSQLCLOB:<Package.Procedure>/<unique_key>
Ex: PLSQLCLOB:xx_attach_package.xx_notif_attach_procedure/234562

PLSQL is for simple plain texts, tables…not for large objects.

If we set the above value to any document type of attribute, whenever the user tries to access the attribute the package.procedure will call automatically. [This is standard functionality given by Oracle – calling dynamic SQL by doing substring from colon (:) to slash (/)].

There are some standard parameters to be used whenever we use the procedure in between : and /.
Those parameters are

(document_id   IN VARCHAR2,
 display_type  IN VARCHAR2,
 document      IN OUT NOCOPY BLOB,
 document_type IN OUT NOCOPY VARCHAR2)

Where document id would be unique id like po_header_id,po_requisition_header_id, project_id …etc…

Display_types would be
Ex: display_type := 'text/html';  --OR ‘text/plain’;

Document could be CLOB/BLOB
The datatype of the file_data column in fnd_lobs is BLOB. So if you are using PLSQLCLOB then convert/encode the document using WF_MAIL_UTIL.encodeblob package.


Document type would be mime type. This could be pdf/excel/html/msword…..etc…
Syntax for pdf:
document_type :='application/pdf;name='||<file_name>;


3)
For custom workflows  generally we set the document attribute after creating the process i.e before starting the process.

 Launch Process = Create Process + Start Process (In wf_engine API)

              
PSEUDO CODE:

-- This is the pseudo code. Modify this according to your requirement

CREATE OR REPLACE PACKAGE BODY xx_attach_package
AS
   PROCEDURE xx_notif_attach_procedure (
      document_id     IN       VARCHAR2,
      display_type    IN       VARCHAR2,
      document        IN OUT   BLOB,
      document_type   IN OUT   VARCHAR2
   )
   IS
      l_docid               PLS_INTEGER;
      l_temp_blob           BLOB;
--l_file_path VARCHAR2(100) := '/u02/DEV/apps/apps_st/appl/soncus/12.0.0/bin';
      l_file_name           VARCHAR2 (100);   -- := 'Son_attachment_doc.doc';
      l_file_on_os          BFILE;
      l_dest_offset         NUMBER         := 1;
      l_src_offset          NUMBER         := 1;
      l_file_content_type   VARCHAR2 (256);
   BEGIN
      l_docid := TO_NUMBER (document_id);
/*Here we are reading  from a file*/
      DBMS_LOB.createtemporary (l_temp_blob, CACHE => FALSE);

      SELECT file_name, file_content_type, file_data
        INTO l_file_name, l_file_content_type, l_temp_blob
        FROM fnd_lobs
       WHERE file_id = 234562; -- Can get it dynamically using document id

--EXECUTE IMMEDIATE('CREATE OR REPLACE DIRECTORY SON_ATTACHMENTS AS '''||l_file_path||'''');
--l_file_on_os:=bfilename('',l_file_name);

      --dbms_lob.fileopen(l_file_on_os,dbms_lob.file_readonly);

      -- DBMS_LOB.LOADBLOBFROMFILE (dest_lob => l_temp_blob,
--                            src_bfile=>l_file_on_os,
--                            amount=>dbms_lob.getlength(l_file_on_os)
--                            ,dest_offset=>l_dest_offset  --in out parameter
--                            ,src_offset =>l_src_offset);

      --dbms_lob.fileclose(l_file_on_os);
      DBMS_LOB.createtemporary (document, CACHE => FALSE);
      DBMS_LOB.COPY (document, l_temp_blob, DBMS_LOB.getlength (l_temp_blob));
/*We have to determine document_type which is nothing but the mime type
document_type := 'image/jpg; name=filename.jpg';
Depending on the extension of the document the MIME type is determined. For simplicity
we are hard coding here*/
      document_type := 'application/pdf;name=' || l_file_name;
--commit;
   EXCEPTION
      WHEN OTHERS
      THEN
         wf_core.CONTEXT ('XX_ATTACH_PACKAGE',
                          'XX_NOTIF_ATTACH_PROCEDURE',
                          document_id,
                          display_type
                         );
         RAISE;
   END xx_notif_attach_procedure;

/****************************************************************/
   PROCEDURE son_attachment_wf
   IS
      l_itemtype      VARCHAR2 (30)  := 'SONCUSAT';
      l_itemkey       VARCHAR2 (300);
      -- l_file_name VARCHAR2(100) := '24Mar2010.xls';
      l_document_id   NUMBER         := 234562;
   -- You can get document id from the standard workflows (ex: po_header_id, expense_report_header_id...) while customizing standard workflows
   BEGIN
      l_itemkey := 'SONCUSAT' || TO_CHAR (SYSDATE, 'dd/mm/yyhh:mm:ss');
      wf_engine.createprocess (l_itemtype,
                               l_itemkey,
                               'SON_CUS_ATTACH_PROCESS'
                              );
      wf_engine.setitemattrdocument
         (itemtype        => l_itemtype,
          itemkey         => l_itemkey,
          aname           => 'SON_ATTACHMENT_ATTRIBUTE',
          documentid      =>    'PLSQLBLOB:xx_attach_package.xx_notif_attach_procedure/'
                             || l_document_id
         );
      wf_engine.startprocess (l_itemtype, l_itemkey);
   END;
/****************************************************************/
END xx_attach_package;
/



[*A PL/SQL CLOB document that you include as an attachment to a notification can contain a PDF or RTF document or, if your database version is Oracle9i Database or higher, other binary data that is encoded to base64. You should first store the document in the database as a binary large object (BLOB) and then convert the document into a CLOB as part of the PL/SQL procedure that generates the CLOB. You can use the UTL_RAW.Cast_To_VARCHAR2 function to convert the data from the BLOB into VARCHAR2 data that you write to a CLOB. If your database version is Oracle9i Database or higher, you can optionally use the WF_MAIL_UTIL.EncodeBLOB procedure to encode the binary data to base64.

Note: You can call WF_NOTIFICATION.WriteToClob() to help build a CLOB by appending a string of character data to it.]

You can write using HTML tags to create tables/column header/data in notification using PLSQL:<package.procedure>/<document_id>


Compared to the above attachment process, attachment links are simple to create.

Whenever the user wants to give the data (whatever he/she has entered) as an attachment (which is not yet saved in fnd_lobs/fnd_documents) then we can use the above syntax to write the data/creating the document/send as an attachment.


 Your suggestions are most welcome.



Thanks,
Rajesh

28 comments:

  1. Hi Rajesh.

    Thanks for the information. I have a problem with Req workflow. In the Requisition seeded wf we changed the notification message from framework region to document. But after the changes #ATTACHMENTS is not working for mail. Though we can see the attachment in application notification but its not coming in mail. Any help or suggestion please.

    ReplyDelete
  2. Hi,
    Is this issue only with few users or all users? Change the user preference to MAILATTH for a single user/for all users. You can see my post on 'Notifications are not getting through email' on 22nd Nov,2011.

    ReplyDelete
  3. Rajesh, Thanks for the info. I am creating .ICS file during the apporval process and able to add it to the notification with out saving it in FND_DOCUMENTS table. How ever, I am able to see the .ICS file only from email notification but not from worklist. Do you have any suggestions?

    regards,
    Ramana

    ReplyDelete
    Replies
    1. Hi Ramana,
      Which user preference is set to the user? Change the user preference and try. It should work.

      Regards,
      Rajesh

      Delete
  4. Hello.

    I need to get the attachments dynamically.

    FND:entity=PO_LINES&pk1name=PO_LINE_ID&pk1value=XXXXX (po_line_id value)

    The pk1value should refer to a workflow attribute.
    Could you tell me how to proceed please.

    Thank you.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Hi Rajesh,

      Thanks for article.

      pk1name and pk1value is confusing for me too. Are pk1name and pk1value attributes that i have to create or are already present in the FND standard WF attributes and we are just assigning them values ?

      so FND:entity=PO_LINES&pk1name=PO_LINE_ID&pk1value=XXXXX (po_line_id value)

      if &pk1name is attribute and it is being set in the function why do you need to do &pk1name=PO_LINE_ID and similarly for &pk1value?


      For example i am doing the same for attachments in the GL JE header, is this correct ?
      FND:entity=GL_JE_HEADERS&BATCH_ID&JE_HEADER_ID

      My objective is to dynamically attach multiple attachments. Thanks in advance.

      Delete
  5. Thanks got my questions answered. For details see an explanation of key value pairs https://community.oracle.com/thread/464924

    ReplyDelete
  6. /***
    **Following attribute is set to show multiple attachments in the WF notification
    using the SPECIAL seeded #ATTACHMENTS attribute in the custom message

    The value for 'entity' can be obtained from DATA_OBJECT_CODE in FND_DOCUMENT_ENTITIES.
    The value for 'pk1name' can be obtained from PK1_COLUMN in FND_DOCUMENT_ENTITIES.
    The value for 'pk1value' can be obtained from PK1_VALUE in FND_ATTACHED_DOCUMENTS.
    and similiarly for pk2, pk3 and so on respectively from the said tables.

    For example, FND:entity=PO_HEADERS&pk1name=PO_HEADER_ID&pk1value=9395
    -pk1name and pk1value are key value pairs and must be entered in ascending order
    e.g. you can't skip to pk2name and pk2value if you don't wish to use the first pair
    - &pk1name=je_batch_id is case-sensitive
    -values of attribute for example je_batch_id and je_header_id can't be accessed in WF builder using &JE_BATCH_ID even if the attribute is
    defined and included in the message attributes so it has be to be set from the procedure in the following
    way.
    ***/

    wf_engine.setitemattrtext(itemtype => itemtype,
    itemkey => itemkey,
    aname => 'SET_GL_JE_ATTACHMENTS',
    avalue => 'FND:entity=GL_JE_HEADERS&pk1name=je_batch_id&pk1value=' ||
    l_je_batch_id ||
    '&pk2name=je_header_id&pk2value=' ||
    l_je_header_id);

    Rest the GLOBAL attribute of Type Document and MESSAGE attribute of type #ATTACHMENTS is to be created in the same way mentioned in the screenshots above. I am sharing this info in hope that someone else would be saved from the WF builder harassment in future ;)

    This is much cleaner way to show multiple attachments in the WF notification.

    Thanks! hope it helps

    ReplyDelete
    Replies
    1. Thank you for posting this here Sarfraz. It is helpful for all those who have confusions on pk1 and pk2 values. I did use this long back, I overlooked and replied to set attribute. Thanks for reminding this. I really appreciate your help and sharing knowledge with all. :)

      Delete
    2. This worked, really a hero

      Delete
  7. hi Rajesh
    Using the first approach we see the attachments through a link. However our requirement is to have the attachment inline in the workflow email notification. For this we need to go by your second approach. However question now is how can multiple attachments be implemented using second approach?

    Can you share some tips here. Thx!

    ReplyDelete
    Replies
    1. Hi Melvin and Rajesh,

      Did you got it resolved for multiple attachments. Thanks.

      Warm regards,
      Saravana Mahesh
      saravanamahesh.thangavelu@gmail.com

      Delete
    2. Hi,

      Were you able to send multiple attachments using second approach?

      Delete
  8. Hi Rajesh,

    how and where to check if user don't receiving attachment. when I check in fnd_lobs , fnd_attached_documents , fnd_documents_tl attachment information is there.

    Would you share some tip please. Thanks A lot.

    ReplyDelete
  9. hi,,, i need query for employee wise available leave .
    please help me

    ReplyDelete
  10. Hi,

    If I use the second approach to attach the content of the attachment it is working correctly. I am getting the attachment. But when I try to approve through email it does not get approved. Any idea?

    Thanks,
    Viji

    ReplyDelete
  11. Hi,
    Very Good Document, I have one issue. I already create document type attribute and code as HTML TAG, can i create url to call in HTML TAG

    ReplyDelete
  12. Help! I am getting 'wrong number of arguments' for dbms_lob.copy even though I used exact code above. Ideas?

    ReplyDelete
  13. Hi
    I try to do as same as your step above with PO attachment in R12.2.5.But ít does not work. could you please send or instruct me how to attachment via mailer?

    ReplyDelete

  14. I set new attribute with Internal name #ATTACHMENTS then set contants: FND:entity=PO_HEADERS&pk1name=PO_HEADER_ID&pk1value=101896. It can attachment whole attachments by Buyer. However, It is statis PO_HEADER_ID.
    Then I was changed the FND:entity=PO_HEADERS&pk1name=PO_HEADER_ID&pk1value=&DOCUMENT_ID . But It's not included Attachment. Please help to instruct/advise me how to use dynamic PO_HEADER_ID/DOCUMENT_ID?

    ReplyDelete
  15. Hi,
    can you please suggest how can i get po lines attachments along with po header attachments in workflow notification mail.
    i am using below code for po headers and po lines attachments we are getting either header or po line at once we the requirement is we need both po header and po lines attachemnt.
    PO_WF_UTIL_PKG.SetItemAttrDocument (
    itemtype => itemtype,
    itemkey => itemkey,
    aname => '',
    documentid => 'FND:entity=PO_HEADERS'
    || '&'
    || 'pk1name=PO_HEADER_ID'
    || '&'
    || 'pk1value='
    || po_header_id);

    please suggest

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. hi,

    We are facing an issue with above approach (first approach).

    When we attach File type attachment at PO Header level, in addition to that file we get some txt file having some text in a different language (proabably dutch) in the actual email but not in the status monitor notification.

    Same is the case with Long text attachment, we get one file with the text we entered in long text attachment and another file with some other content (with same file name). With Long text attachment there is another issue too, some random 'pdf' file is being shown in the actual email the source of which we are not sure. Again, these extra attachments do not appear in status monitor, only in the actual email.

    Please help.

    Srini

    ReplyDelete
  18. Hi Rajesh,

    Using first approach (FND:entity), i am to bring the Journal Header attachments(file, long text, short text, web page) in the worklist notification.But i unable to bring these attachments in email notification. we're on 12.2.6 and workflow is Journal Batch. Is there any different setup required for email. Pl suggest. Thanks a lot !

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete
  20. Hi Rajesh, the approval notification is showing following errror -> 'wrong Number Of Primary Key-value Pairs Supplied For Attachment Context'. what could be the cause ? pls give some insights.

    ReplyDelete
  21. Hi Rajesh,
    I need to get the attachments from multiple invoices dynamically for a payment approval process. Is there a way to get dynamically all attachments from all invoices related to the payment. Multiple attachments where the no of attachments count is not known before.

    TIA.

    ReplyDelete