Monday, November 30, 2009

Unix/Linux Shell Scripting

This short post is about some tips/tricks in Shell Scripting.

1. Invoke PLSQL/SQL from Shell
Shell START

#!/bin/bash

param_one="$1"
param_two="$2"

if [ ${#1} == 0 ]
then
param_one="null"
fi

if [ ${#2} == 0 ]
then
param_two="null"
fi

echo "one " $param_one
echo "two "$param_two

CHECK_THIS=`sqlplus -s apps/apps@appsDB
<< ! 
set term off 
set head off 
set pages 0 
set feedback off 
BEGIN 
  INSERT_LOG(p_mesg1 => $param_one,p_mesg2=>$param_two);
END;
/
!
`
echo $CHECK_THIS
Shell END

.

2. Print new line in Shell Script
echo $@
.

3. Debug Shell Script
Put below line at the start of the script
#!/bin/bash -x
.

4. Find files, example of search for the string "ERROR" in all xml files in the current directory and all sub-directories
find . -name "*.xml" -exec grep "ERROR" '{}' \; -print
.

5. Find and list files
$find . -name "XXFILENAME" -type f -ls
.

6. More to come..

Extending a VO [View Object] in Oracle Apps

A quick post about how-to extend a view object.

Goal : Add "Created By" and "Creation Date" to HR Salary History Standard Page

1. Find the VO of the table in the page, in this case the VO is SalaryHistoryVO.

2. Get the VO xml and VO class files from application middle tier.
For SalaryHistoryVO, the VO xml is at
$JAVA_TOP/oracle/apps/per/saladmin/server/SalaryHistoryVO.xml
Class files, SalaryHistoryVOImpl.class and SalaryHistoryVORowImpl.class
are at same location as VO.xml

3. Create directory structure under myprojects and myclasses to accommodate this VO
JDEV_HOME\myclasses\oracle\apps\per\saladmin\server 
JDEV_HOME\myprojects\oracle\apps\per\saladmin\server
4. Create a new OA Project in JDEV

5. Create an empty business component package, oracle.apps.per.saladmin.server

6. Add the VO.xml to this package. This is the VO.xml




7. Create a new empty business component package, xxx.oracle.apps.per.saladmin.server which would hold the custom VO.

8. Create a new VO [exSalaryHistoryVO] that extends the SalaryHistoryVO

9. Edit the SQL for this new VO, and include created by and creation date columns in the SQL statement, xml shown below




10. Make sure that attribute mappings remain good after adding new columns.




11. Generate both exSalaryHistoryVOImpl and exSalaryHistoryVORowImpl for this new custom VO

12. Change the exSalaryHistoryVOImpl to import oracle.apps.per.saladmin.server.SalaryHistoryVOImpl and extend from SalaryHistoryVOImpl




13. Compile project.

14. Now build substitution to replace SalaryHistoryVO with exSalaryHistoryVO

Substitution Step 1




Substitution Step 2




15. Save the project, open the project.jpx file and make sure the substitution is valid, jpx view below




16. Now load the new VO and corresponding class files to middle tier at
$JAVA_TOP/xxx/oracle/apps/per/saladmin/server/




17. Load the substitution into database

java oracle.jrad.tools.xml.importer.JPXImporter ExtendVO.jpx  -username apps -password appsDB -dbconnection "(DESCRIPTION= (ADDRESS=(PROTOCOL=tcp)(HOST=db.server.com)(PORT=9999)) (CONNECT_DATA=(SID=SID))) "


18. Once these steps are done, navigate to Salary History Page, and add the new columns at desired places using OA Personalization.


Wednesday, November 18, 2009

OAF [OA Framework Development] Related Stuff

Short note about development related stuff in OA Framework.

1. Find correct JDeveloper Patch for your application
1. Patch 4045639 - 9IJDEVELOPER WITH OA EXTENSION ARU FOR FWK.H
2. Patch 4141787 - 9IJDEVELOPER WITH OA EXTENSION ARU FOR CU1
3. Patch 4573517 - Oracle9i JDeveloper with OA Extension for 11.5.10 CU2
4. Patch 4725670 - 9IJDEVELOPER WITH OA EXTENSION ARU FOR 11i10 RUP3

To determine which patch to use, you can simply check the framework 
version in your instance by using http://host:port/OA_HTML/OAInfo.jsp, 
then choose the matched one. If the version is 11.5.10K, please use 
patch 4045639, 11.5.101CU, then use patch 4141787, 11.5.102CU, then 
it should be patch 4573517, 11.5.103CU should be patch 4725670.

OA FWK Dev Guide
https://metalink.oracle.com/metalink/plsql/docs/269138.1.pdf

Search "JDeveloper" on this blog for more posts.

2. JDeveloper Debug Options
-Djbo.debugoutput=silent    --> No Debug
-Djbo.debugoutput=console   --> Debug to Console
.
3. Importing Certificates into JDev, needed if you are connecting to https URLs
C:\JDEV\jdevbin\jdk1.3.1\jre\bin\keytool -import -v -file ecustom.cer -keystore cacerts
Enter keystore password:  changeit
Owner: CN=certification.owner.com
Issuer: CN=certification.owner.com
Serial number: blahblahblahblahblahblahblahblah
Valid from: Mon Oct 06 15:41:50 PDT 2008 until: Thu Oct 04 15:41:50 PDT 2018
Certificate fingerprints:
MD5:  blahblah
SHA1: blahblah
Trust this certificate? [no]:  yes
Certificate was added to keystore
.
4. Import OAF XML Page
java oracle.jrad.tools.xml.importer.XMLImporter OIEAPPROVERLOV.xml -jdk13 -mmddir "$OA_HTML/jrad" -rootdir . -username apps -password apps -dbconnection "(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=server.db.com)(PORT=9999))(CONNECT_DATA=(SID=SID)))"
.
5. OA Page Export
adjava -mx128m -nojit oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/icx/icatalog/shopping/webui/NonCatalogRequestPG -username apps -password apps -dbconnection "(DESCRIPTION= (ADDRESS=(PROTOCOL=tcp)(HOST=db.server.com)(PORT=9999)) (CONNECT_DATA=(SID=SID))) " -rootdir .
.
6. OA Page Personalization Export
adjava -mx128m -nojit oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/icx/icatalog/shopping/webui/customizations/site/0/NonCatalogRequestPG -username apps -password apps -dbconnection "(DESCRIPTION= (ADDRESS=(PROTOCOL=tcp)(HOST=db.server.com)(PORT=9999)) (CONNECT_DATA=(SID=SID))) " -rootdir .
.
7. List Customizations for OA Page
begin
  jdr_utils.listDocuments('1.xml');
  jdr_utils.LISTCUSTOMIZATIONS('/oracle/apps/icx/icatalog/shopping/webui/ShoppingHomePG');
end;
.
8. OA Page Personalization Import
adjava -mx128m -nojit oracle.jrad.tools.xml.importer.XMLImporter ./oracle/apps/ap/oie/entry/lines/webui/customizations/site/0/DetailsPG.xml -username apps -password apps -dbconnection "(DESCRIPTION= (ADDRESS=(PROTOCOL=tcp)(HOST=db.server.com)(PORT=9999)) (CONNECT_DATA=(SID=SID))) " -rootdir .
.
9. OA Page Debug (Location for SOPLs (System.out.println Statements))
Location
  $ORACLE_HOME/../9iAS/Apache/Jserv/logs/jvm
  Check OACoreGroup.n.stdout [n can be 0,1,2,3 etc]
  Check OACoreGroup.n.stderr [n can be 0,1,2,3 etc]
.
10. Stop/Start Apache Port
1.
cd $COMMON_TOP/admin/scripts
2.
./adapcctl.sh stop
3. Check if http processes still running, if running kill those, then proceed for start
ps -aef | grep -i http | grep -i DBNAME
4. 
./adapcctl.sh start
.
11. Find Java file versions
cd $JAVA_TOP/oracle/apps/icx

1st Way
Execute the following at the unix prompt - from this directory -
 for FILE in `find . -name '*.class' -print`
 do
   echo $FILE
   strings -a $FILE | grep -i header: | cut -c1-78
 done > $HOME/javaver.txt

2nd Way (assuming only one type of files)
XML Versions
 find . -exec grep -i 'Header' {} \; -print > $HOME/xmlver.txt
.

Sunday, November 15, 2009

Re-try workflow

If you ship newer version of a PLSQL code which gets invoked from Workflow, and you forget to bounce Workflow Services, then all workflows using this PLSQL code would result in Invalid Package Error.

You can use following sample script to retry such error items.

BEGIN
   fnd_global.apps_initialize (0,0,0); -- Set correct values here
   FOR get_item_rec IN ( SELECT item_type
                               ,item_key
                           FROM apps.wf_item_activity_statuses wia
                          WHERE item_type          = 'REQAPPRV'
                            AND activity_status    = 'ERROR'
                            AND error_message like '%PACK%INVALID%'
                            AND activity_result_code <> '#EXCEPTION'
                            AND begin_date > SYSDATE-15
                            AND end_date IS NULL
                       )
   LOOP
      wf_engine.handleerror (get_item_rec.item_type
                            ,get_item_rec.item_key
                            ,'PO_REQ_APPROVE_SIMPLE'
                            ,'RETRY');
   END LOOP;
   COMMIT;
END;
/

.

With 11510, Application Manager UI facilitates retrying multiple workflow error items in one shot.

Cheers.

Importing Expense Report Attachments into Payables

Is there any way to get expense report attachments into Payables?

Yes there is. You can use standard Oracle Apps routine to copy attachments from one entity to another. You can copy attachments from ER header to Invoice header, ER line to Invoice Dist or copy all attachments from ER header/lines to AP invoice headers. You can also control which type of attachments should be copied.

Sample code below copies header level expense report attachments to the AP Invoice.

BEGIN
 fnd_attached_documents2_pkg.copy_attachments(
  x_from_entity_name  => 'OIE_HEADER_ATTACHMENTS' -- From Expense Report Header
 ,x_from_pk1_value    => '115000'                 -- From Expense Report Header Id
 ,x_to_entity_name    => 'AP_INVOICES'            -- To AP Invoice Header
 ,x_to_pk1_value      => '1085333'                -- To Invoice Id
 ,x_to_category_id    => 1                        -- Category Id for Attachments
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
 dbms_output.put_line('SQLERR ' || SQLERRM);
 ROLLBACK;
END;

.

Cheers

Who let the dogs out ....when, and for how long..?

.

Okay, thats a catchy title for post, simple SQL to find out who ran the concurrent program and when.
SELECT FCON.concurrent_program_name
      ,FCON.user_concurrent_program_name
      ,FREQ.request_id
      ,FREQ.requested_by
      ,(SELECT FUSE.user_name||' ['||FUSE.description||']'
          FROM fnd_user FUSE 
         WHERE FUSE.user_id = FREQ.requested_by) user_name
      ,TO_CHAR(FREQ.requested_start_date,'DD-MON-RRRR HH24:MI:SS')    req_start_dt
      ,TO_CHAR(FREQ.actual_start_date,'DD-MON-RRRR HH24:MI:SS')       act_start_dt
      ,TO_CHAR(FREQ.actual_completion_date,'DD-MON-RRRR HH24:MI:SS')  act_compl_dt
      ,FREQ.argument_text
  FROM fnd_concurrent_requests FREQ
      ,fnd_concurrent_programs_vl FCON
 WHERE FCON.concurrent_program_id   = FREQ.concurrent_program_id
   AND FCON.concurrent_program_name = 'CONC_PROGRAM_NAME'
 ORDER BY FREQ.request_id DESC

.
Cheers

Find current running SQL for Concurrent Program

This is often times useful, to find out current running SQL of a concurrent program. Helps a lot, if you are analyzing a long running process.

Here is the SQL
SELECT C.sql_text
      ,C.module 
  FROM APPS.fnd_concurrent_requests A
      ,V$SESSION B
      ,V$SQLAREA C 
 WHERE A.oracle_session_id = B.audsid 
   AND B.sql_hash_value    = C.hash_value 
   AND A.request_id        = 6935896

.
Here is another one
SELECT SQLT.hash_value
      ,SQLT.sql_text
      ,VSES.username
      ,VSES.module
      ,VSES.command
  FROM v$sqltext                    SQLT
      ,v$session                    VSES
      ,APPS.fnd_concurrent_requests FCONC
 WHERE SQLT.hash_value         = VSES.sql_hash_value 
   AND FCONC.oracle_session_id = VSES.audsid 
   AND FCONC.request_id        = 6935896
ORDER BY SQLT.piece

.

Cheers

FNDLOAD Post

Ok, I have been avoiding a FNDLOAD only post, but could not resist.

Here are few FNDLOADs that I frequently use.

.
Messages
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afmdmsg.lct igfapmsg.ldt FND_NEW_MESSAGES APPLICATION_SHORT_NAME=IGF message_name='IGF_AP%'

Concurrent Programs
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afcpprog.lct igfcprog.ldt PROGRAM APPLICATION_SHORT_NAME=IGF

Lookups
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/aflvmlu.lct  igflkups.ldt FND_LOOKUP_TYPE APPLICATION_SHORT_NAME=IGF

Menus
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afsload.lct  igsmenus.ldt MENU MENU_NAME=IGS_% APPLICATION_SHORT_NAME=IGS

Descriptive Flex
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afffload.lct igfdflex.ldt DESC_FLEX DESCRIPTIVE_FLEXFIELD_NAME='IGF%'

Request Group
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afcpreqg.lct igfreqgr.ldt REQUEST_GROUP APPLICATION_SHORT_NAME=IGF

Profiles
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afscprof.lct igfprofl.ldt PROFILE APPLICATION_SHORT_NAME=IGF

Attachments
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afattach.lct igsattch.ldt FND_ATTACHMENT_FUNCTIONS APPLICATION_SHORT_NAME=IGS

Responsibility
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afscursp.lct igsresp.ldt  FND_RESPONSIBILITY APPLICATION_SHORT_NAME=IGS

Request Set
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afcprset.lct REQSET.ldt REQ_SET_LINKS APPLICATION_SHORT_NAME=GL REQUEST_SET_NAME=FNDRSSUB963

Forms Personalizations
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/affrmcus.lct FORMS_PERZ.ldt FND_FORM_CUSTOM_RULES function_name=POXPOEPO

Alerts
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $ALR_TOP/patch/115/import/alr.lct XALERTMIG.ldt ALR_ALERTS APPLICATION_SHORT_NAME=SQLAP ALERT_NAME=AlterName%

Printer-Style-Driver
FNDLOAD apps/appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afcppinf.lct PRINTER_INFO.ldt FND_PRINTER_INFORMATION PRINTER_TYPE=EFT_PRINTER PRINTER_STYLE=EFT%

Only value sets
FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afcpprog.lct test_2.ldt VALUE_SET FLEX_VALUE_SET_NAME=IGF_SRS_AWARD_YEAR

FNDLOAD apps/apps@appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afffload.lct igfdfvsv.ldt VALUE_SET FLEX_VALUE_SET_NAME='IGF%' APPLICATION_SHORT_NAME=IGF

Partial UPLOAD Examples

Partial Value sets
FNDLOAD apps/apps@appsDB 0 Y UPLOAD_PARTIAL $FND_TOP/patch/115/import/afcpprog.lct igfcprog.ldt VALUE_SET CUSTOM_MODE=FORCE

Params
FNDLOAD apps/apps@appsDB 0 Y UPLOAD_PARTIAL $FND_TOP/patch/115/import/afcpprog.lct igfcprog.ldt DESC_FLEX CUSTOM_MODE=FORCE

Descriptive Flex - Specific Context Code/Structure
FNDLOAD apps/appsDB 0 Y DOWNLOAD $FND_TOP/patch/115/import/afffload.lct test_flex.ldt DESC_FLEX DESCRIPTIVE_FLEXFIELD_NAME='FND_COMMON_LOOKUPS' APPLICATION_SHORT_NAME='FND' DFF_CONTEXT DESCRIPTIVE_FLEX_CONTEXT_CODE='DFF Context Code%'

.
Cheers

Frequently Used Metalink Notes

.
I would keep this page live.

Note 233205.1 - How To Change The Default Reimbursement Currency For Employee in iExpense
Note 160058.1 - Ext/Pub Customize Pages in iProcurement Creating Descriptive Flexfields (DFF)
Note 286936.1 - POA: DBI Setup Steps for Procurement Intelligence Note 315978.1
Note 316829.1 - No Data Found In Procurement DBI Reports: Missing Security Setup 
Note 286841.1 - POA: Frequently Asked Questions (FAQ) for DBI for Procurement.
Note 286936.1 - POA: DBI Setup Steps for Procurement Intelligence
Note 315978.1 - How To Find Out What DBI and/or OBIS Version Is Applied Into System
Note 316829.1 - No Data Found In Procurement DBI Reports: Missing Security Setup
Note 377320.1 - How to Diagnose a Punchout Issue
Note 555151.1 - How To Diagnose Document Index (Procurement)
Note 290614.1 - How To Get Log Files In iProcurement
Note 343799.1 - Punchout To Oracle Exchange Or Supplier Site Recieve Error Code: 201
Note 356128.1 - How to Run the Punchout TEST Delivered With Oracle Applications?
Note 377320.1 - How to Diagnose a Punchout Issue
Note 249669.1 - How To Collect Apache and Jserv Debugging Details For Applications 11i
Note 293315.1 - 11.5.10 FAQ for Approvals Management (AME) Integration For iProcurement and Purchasing:
Note 402870.1 - How To Diagnose Issues With The Edit Requisition Link In Notifications
Note 429918.1 - How To Remove The "To Supplier" From Requisition Attachment in iProcurement?
Note 1077961.6 - POXPOEPO Fully Receiving And Billing Against The Line Does Not Close PO
Note 105541.1 - Oracle General Ledger 11i Drilldown Features FAQ
Note 108369.1 - GL Drilldown
Note 208756.1 - GL Drilldown
Note 428228.1 - Checks To See If PL/SQL or JRAD Notifications Are Being Used in 11.5.10 Notifications
Note 374339.1 - Notifications Show Error When Clicking Edit Requisition Link
Note 455687.1 - How Do You Set Up A Test Periodic Alert That Sends An Email
Note 406563.1 - Enddated Responsibility Duplicates And Is Still Available
Note 421778.1 - Responsibilities Not Showing Up According to the End Date In List of Available Responsibilities
Note 134168.1 - iProcurement Setup 11i: Oracle Internet Procurement Functional Setup
Note 290614.1 - How To Get Log Files In iProcurement
Note 382592.1 - How To Diagnose A Problem In The Item Catalog
Note 160058.1 - Ext/Pub Customize Pages in iProcurement Creating Descriptive Flexfields (DFF)
Note 339561.1 - In iProcurement When Selecting 'Requisition Status' - 'Notifications' Receive Java Error (Corrupted Personalizations)
Note 340078.1 - How To Call a PL/SQL Stored Procedure from an Application Module and Access the Returned Data from a Java Application
Note 279202.1 - SSLInitFailureException Occurs While Attempting To Download Supplier Punchout Definition
Note 389596.1 - How To Disable SOAP In Applications 11i

Custom Lookups vs Profile Options

Well, this is a no-contest.

Lookups win over Profile Options whichever way you look at. If you want custom decision points in the application, lookups offer much more flexibility than Profile Options.

You can configure any flex-structure around lookups and make it much more versatile. And best part is there is no need of any bounce or cache clearing issue with the lookups.

Of course, when it comes to User controlled options, then there is no alternative to Profiles Options.

Cheers.

Saturday, November 14, 2009

Send workflow notification without statring workflow

You can send workflow notifications without starting the actual workflow. Here is how
.
lv_key := TO_CHAR(SYSDATE,'DDMMRRRRHH24MISS');
ln_notification_id := wf_notification.send (lv_role_name
                                           ,'WF_ITEM_NAME'
                                           ,'WF_NOTIFICATION_NAME'
                                           ,SYSDATE+3
                                           ,'WF_ENGINE.CB'
                                           ,'WF_ITEM_NAME:'||lv_key||':'||TO_CHAR(SYSDATE, 'JSS')
                                           ,NULL
                                           ,NULL );

wf_notification.setattrtext (ln_notification_id,'SUBJECT',lv_subject_txt);
wf_notification.setattrtext (ln_notification_id,'BODY',lv_body_txt);

UPDATE wf_notifications
   SET from_user       = 'System'
      ,from_role       = 'SYSADMIN'
 WHERE notification_id = ln_notification_id;


.
Note the item key synatx.

Cheers.

Workfllow adhoc Role Expiry

I created one adhoc role using wf_directory.createadhocrole and set its expiry to 15 days. Then on 16th day, I tried to create the same role, but the routine failed, cause was "Duplicate Role Name".

I checked WF_ROLES, and I did not find the expired role there. Then I checked WF_LOCAL_ROLES, the role end date was in past, but the role status was still active.

To avoid error, I added following before creating new adhoc role.
 wf_directory.setadhocrolestatus('ADROLE' , 'INACTIVE');
 wf_directory.deleterole('ADROLE' , 'WF_LOCAL_ROLES' , 0);
.

This makes sure the old role is deleted before creating a new one with same name.

Expesne Reports to Invoice - Line Justification

When payables imports expense reports into the invoice, expense report line justification is not carried over. A simple trigger on invoice distribution could solve the problem.

Here is the trigger, note the difference between 1159 and 11510 versions
.
CREATE OR REPLACE TRIGGER copy_ap_expense_rpt_just
--
-- Trigger on Expense Lines table to copy Justification to Item Description 
--
  BEFORE INSERT ON ap_invoice_distributions_all
  REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW
DECLARE
  lv_justification VARCHAR2(240);
BEGIN
    --
    -- For 1159
    --
    IF INSERTING THEN
      SELECT EXPRL.justification
        INTO lv_justification
        FROM ap_expense_report_lines_all   EXPRL
            ,ap_expense_report_headers_all EXPRH
            ,ap_invoices_all               APINV
       WHERE EXPRL.report_header_id    = EXPRH.report_header_id
         AND EXPRH.invoice_num         = APINV.invoice_num
         AND APINV.invoice_id          = :NEW.invoice_id
         AND EXPRL.code_combination_id = :NEW.dist_code_combination_id
         AND EXPRL.item_description    = :NEW.description
         AND EXPRL.start_expense_date  = :NEW.start_expense_date
         AND EXPRL.amount              = :NEW.amount;

         :NEW.description := lv_justification;
    END IF;
    --
    -- For 11510
    --
    IF INSERTING THEN
      SELECT EXPRL.justification
        INTO lv_justification
        FROM ap_expense_report_lines_all   EXPRL
            ,ap_expense_report_headers_all EXPRH
            ,ap_invoices_all               APINV
            ,ap_exp_report_dists_all       EXPDIST
       WHERE EXPRL.report_header_id      = EXPRH.report_header_id
         AND EXPRH.invoice_num           = APINV.invoice_num
         AND EXPRL.report_line_id        = EXPDIST.report_line_id
         AND APINV.invoice_id            = :NEW.invoice_id
         AND EXPDIST.code_combination_id = :NEW.dist_code_combination_id
         AND EXPRL.item_description      = :NEW.description
         AND EXPRL.start_expense_date    = :NEW.start_expense_date
         AND EXPRL.amount                = :NEW.amount;

      :NEW.description := lv_justification;
    END IF;

EXCEPTION
   WHEN NO_DATA_FOUND THEN
     NULL; -- Do nothing as this is not a ER Invoice
   WHEN OTHERS THEN
     fnd_log.string(fnd_log.level_exception
                   ,'copy_ap_expense_rpt_just'
                   ,'Exception ' ||SQLERRM);   
END copy_ap_expense_rpt_just;

Some thoughts for Cloning from Production Enviornment

.
Based on my experience, here is what comes to my mind, a MUST have list for cloning Apps Production to test environment apart from regular data scrambling. Note that this is purely from developer perspective.

1. Wipe out all workflow notifications, if you do not want to do it for some 
   reason, at least set the mail status to SENT for ALL workflow notifications.
2. Set a test address for workflow mailer
3. Disable all ALERTS
4. Disable all FTP programs
5. Disable all 3rd party mailing programs
6. Make sure the workflow FROM e-mail address has some sort of environment 
   identification in it.
7. For alerts, you can add a test disclaimer in alert setup.
8. Disable all scheduled application specific programs
9. Remove all Database Links, or point to corresponding test instance
.
Gee..this is really small list, BUT really important. I would be adding more as I learn more.

XML to CSV

Often there is a need to load XML data into database. With the popularity of the Web Services and Java technology, you get lots of these type of requests. Ideally XML is not really good for high volume data.

I had used in past a DOM parser to parse and load XML data into database. It was awfully slow. First a DOM parser need huge memory for bigger files, second the parsing itself is painfully slower. Third, if you are doing a record-by-record insert from XML into database through Java, the process is as good as dead for any data length over 10000 records.

So, I thought of converting XML into a CSV and load that CSV into database using SQL*Loader. This way the process is 1000x times faster. I put together a small java code that uses SAX parser and produces a csv.

Here is the syntax to invoke Java parser

.
java GenericSaxParseToCsv "Feed.xml" "COLUMN_1,COLUMN_2,COLUMN_3,COLUMN_4,COLUMN_5,COLUMN_6,COLUMN_7,COLUMN_8,COLUMN_8,COLUMN_9,COLUMN_10" "123456" "2" > Feed.csv

1. Feed.xml is in the input file
2. COLUMN_1,COLUMN_2,COLUMN_3,COLUMN_4,COLUMN_5,COLUMN_6,COLUMN_7,COLUMN_8,COLUMN_8,COLUMN_9,COLUMN_10
   are the columns in the XML, they would appear in the same sequence in the 
   outputted csv as passed here
3. 123456 - is request id for the whole set
4. 2 
   is number of levels in XML, basically this would control when you want to 
   start your new record in CSV. 
   Passing 2 would create new record in CSV when XML parser encounters COLUMN_1 or 
   COLUMN_2 is any sequence.

And here is the Java Parser
.

//
//    Generic SAX Parser
//    Source http://appswork.blogspot.com/



import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.File;
import java.io.IOException;

import org.xml.sax.AttributeList;
import org.xml.sax.HandlerBase;
import org.xml.sax.SAXException;

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

import java.util.StringTokenizer;

public class GenericSaxParseToCsv extends HandlerBase
{

    protected static boolean showVal  = false;
    protected static int nCurrCounter = 1;
    protected static int nLineCounter = 0;
    protected static int nRequestId   = 0;
    protected static int nLevel       = 1;
    protected static int nColumnCount = 0;
    protected static int nCurrentColPos = -1;
    protected static String vDetLevel;

    static private Writer wOut;

    protected static String vColumnHeaders;
    protected static String vColNameValP[][];
    protected static StringTokenizer vColNamesToken;
    protected static String vInputFile;

    public static void main (String argv [])
    {
      // Use the default (non-validating) parser

      vInputFile           = argv[0];
      vColumnHeaders       = argv[1];
      
      try
      {
        nRequestId         = Integer.parseInt(argv[2]);
      }
      catch(Exception e)
      {
      }

      try
      {
        nLevel             = Integer.parseInt(argv[3]);
      }
      catch(Exception e)
      {
      }

      vColNamesToken = new StringTokenizer(vColumnHeaders,",");
      vColNameValP   = new String [2] [vColNamesToken.countTokens()];

      int i = 0;
      while(vColNamesToken.hasMoreTokens())
      {
        vColNameValP[0][i] = vColNamesToken.nextToken().trim();
        vColNameValP[1][i] = "";
        i = i + 1;
      }

      nColumnCount = i;

      SAXParserFactory factory = SAXParserFactory.newInstance();
      try {
          // Set up output stream
          wOut = new OutputStreamWriter (System.out, "UTF8");

          // Parse the input
          SAXParser saxParser = factory.newSAXParser();
          saxParser.parse( new File(vInputFile), new GenericSaxParseToCSV() );

        } catch (Throwable t) {
            t.printStackTrace ();
        }
        System.exit (0);
    }


    public void startDocument ()
    throws SAXException
    {
        showData("Request_ID,"+"LineCounter,"+"DetLevel,"+vColumnHeaders);
        newLine();
        showData("~"+nRequestId+"~"+","+"~"+"STARTREQUEST"+"~"+","+"~"+vInputFile+"~");
        newLine();
        showData("~"+nRequestId+"~"+","+"~"+nLineCounter+"~"+","+"~"+"L0"+"~"+",");
    }

    public void endDocument ()
    throws SAXException
    {
        try {
             printColValues();
             newLine();
             showData("~"+nRequestId+"~"+","+"~"+"ENDREQUEST"+"~"+","+"~"+vInputFile+"~");
             newLine();
             wOut.flush ();
        } catch (IOException e) {
            throw new SAXException ("I/O error", e);
        }
    }

    public void startElement (String name, AttributeList attrs)
    throws SAXException
    {
        for (int i =0; i nCurrCounter)
         {
            printColValues();
            newLine();
            showData("~"+nRequestId+"~"+","+"~"+nLineCounter+"~"+","+"~"+vDetLevel+"~"+",");
            nCurrCounter = nLineCounter;
         }

        if (showVal && !((System.getProperty("line.separator")).equals(s)) && !("\t".equals(s)) && !("\n".equals(s)))
        {
          int ascVal  = s.charAt(0);
          if (ascVal != 10)
            if (nCurrentColPos > -1 )
            {
              vColNameValP[1][nCurrentColPos] = vColNameValP[1][nCurrentColPos] + s;
            }
        }
    }

    private void showData (String s)
    throws SAXException
    {
        try {
            wOut.write (s);
            wOut.flush ();
        } catch (IOException e) {
            throw new SAXException ("I/O error", e);
        }
    }

    // Start a new line
    private void newLine ()
    throws SAXException
    {
        String lineEnd =  System.getProperty("line.separator");
        try {
            wOut.write (lineEnd);
        } catch (IOException e) {
            throw new SAXException ("I/O error", e);
        }
    }
 } // class
.

This java parser would work real cool for simple XML formats for any level of nested elements. It would expect, though non-repeating unique element tags in the XML. And it would not work with XML that has id attributes inside elements.

Cheers.
.

ORA-06502: PL/SQL: numeric or value error: host bind array too small

I was running anon PLSQL block on SQL prompt, and it was printing a large varchar2(32767) variable. And then I got this error. The data length in the varchar2 variable was just 1200, and it was throwing this error.

Turned out the issue is the with the DBMS_OUTPUT.PUT_LINE, it wont accept more than 200 chars to print. So if you have a larger data to be printed using DBMS_OUTPUT.PUT_LINE, then break it down and print 200 chars at a time.

You can use following outputter to break down big variables.
PROCEDURE print_big_text(bigTextInput VARCHAR2)
  IS
    ln_maxChar NUMBER := 200;
    ln_schar   NUMBER;
    bigText    VARCHAR2(32767) := bigTextInput;
  BEGIN
    LOOP
      EXIT WHEN bigText IS NULL;
      ln_schar := INSTR(bigText, ' ', ln_maxChar);
      -- Find first space
      IF ln_schar = 0 THEN
        ln_schar := ln_maxChar + 1;
      END IF;
      -- If no space, just print first 200 
      dbms_output.put_line( SUBSTR(bigText, 1, ln_schar - 1) );
      -- Move text by ln_schar ahead
      bigText := SUBSTR(bigText, ln_schar);
    END LOOP;
  END print_big_text;

Thursday, November 12, 2009

Combinations Algo

I wanted to achieve following, produce data under OUT for a given IN.


------------------------------------
IN => OUT
------------------------------------
1  => 1
2  => 1,2, 1+2
3  => 1,2,3, 1+2,1+3,2+3, 1+2+3
------------------------------------
.

This algo has its uses when doing matching sets. For example, you have a set D, and there are three details A, B and C. D could be matched with details in any one way

1. A
2. B
3. C
4. AB
5. BC
6. AC
7. ABC
.
With 10g, achieving this is super-easy. Just one SQL would get you all possible combinations.

SELECT SYS_CONNECT_BY_PATH ( GT.seq_number, ',') col_val
   FROM (SELECT ROWNUM seq_number
           FROM all_objects 
          WHERE ROWNUM <= &p_num_dtls ) GT 
CONNECT BY TO_NUMBER(GT.seq_number) > PRIOR TO_NUMBER(GT.seq_number)
If you need to separate out each element at each level, you can use delimstring_to_table function, search on this blog and you would find it.

Cheers.

Friday, November 6, 2009

XML Publisher Burst - Conditional Emails and Templates

Burst control is much flexible than I thought it was. Here is what I did





There are two templates for same document, and controlled by the filter. Filter specifies two conditions, one if to send email, and second to specify region(or OU); email would be sent only if both conditions are true.

There is one interesting thing here, if I give type as "rtf", the publisher would always pick up the default template. Giving type as "XSL-FO" makes sure you get right template everytime.

Enjoy!