This blog has moved.
In light of Capital IT finishing up, and no more BAS to do, I've moved this blog to:
ThePeopleSoftExperience.wordpress.com
Looks nicer and the search seems to work better too. This blog will stay around indefinitely but it won't be getting any new posts.
Component Field Find & Replace
Posted by
Michael Nitschke
on Thursday, 4 December 2014
/
Comments: (0)
Sometimes you just want to find a field and replace its value no matter where it is in the Component.
class General;
method ComponentFieldFindReplace(&rs As Rowset, &fieldName As string, &find As string, &replace As string)
end-class;
/* Searches *entire* Component Buffer for a given Field Name, and optional value,
and replaces with the given value.
&rs: The RowSet that the search will begin at, and cascade down from. Generally use GetLevel0().
&fieldName: The name of the Field whose value you want to replace.
&find: (optional) The value to match
&replace: The value to update the Field.Value with.
Example: &clsGPUtilities.ComponentFieldFindReplace(GetLevel0(), "YOUR_FIELD", "", "X");
*/
method ComponentFieldFindReplace
/+ &rs as Rowset, +/
/+ &fieldName as String, +/
/+ &find as String, +/
/+ &replace as String +/
Local integer &i, &r, &f, &c;
For &i = 1 To &rs.RowCount
For &r = 1 To &rs(&i).RecordCount
For &f = 1 To &rs(&i).GetRecord(&r).FieldCount
If &rs(&i).GetRecord(&r).GetField(&f).Name = &fieldName Then
If &find = "" Or
&find = &rs(&i).GetRecord(&r).GetField(&f).Value Then
&rs(&i).GetRecord(&r).GetField(&f).Value = &replace;
End-If;
End-If;
End-For;
End-For;
/* Do same for each child RowSet of this Row. */
For &c = 1 To &rs(&i).ChildCount
%This.ComponentFieldFindReplace(&rs(&i).GetRowset(&c), &fieldName, &find, &replace);
End-For;
End-For;
end-method;
Tetris SQL
Posted by
Michael Nitschke
on Thursday, 14 August 2014
/
Comments: (0)
This is a DB2 solution.
Oracle will be different for the 'daterange' WITH clause (subquery factoring)
-- Two step process:
-- Delete rows in Target that don't exist in Results,
-- Insert rows from Result that don't exist in Target.
-- Tricky bit:
-- Don't overwrite any manually added rows in the Target.
-- Instead block *around* them; Tetris.
-- DELETE
delete from TARGET_TBL d
where d.source = 'ee'
and
(processing_range.start_dt between d.start_dt and d.end_dt
or processing_range.end_dt between d.start_dt and d.end_dt
or d.start_dt between processing_range.start_dt and processing_range.end_dt)
and not exists
(select 1
from RESULTS_TBL a
where a.pin_num = d.pin_num
and a.start_dt = d.start_Dt
and a.end_dt = d.end_dt)
-- INSERT
with daterange(level,dt) as (
select 1, current date - 1 year
from sysibm.sysdummy1
union all select level + 1, dt + 1 day
from daterange
where level < 1000 and dt < current date + 1 year
)
select '1. Result' as thing, pin_num, start_dt, end_dt, 'ee' as source from RESULTS_TBL
union
select '2. Existing' as thing, pin_num, start_dt, end_dt, source from TARGET_TBL
union
select
'3. Answer' as thing
, a.pin_num
-- For PI/EA
, greatest(a.start_dt,
(select coalesce(max(s.end_dt) + 1 day, a.start_dt)
from TARGET_TBL s
where s.pin_num = a.pin_num
and s.end_dt between a.start_dt and d.dt)) as start_dt
-- For PI/EA
, least(a.end_dt,
(select coalesce(min(s.start_dt) - 1 day, a.end_dt)
from TARGET_TBL s
where s.pin_num = a.pin_num
and s.start_dt between d.dt and s.end_dt)) as end_dt
, 'ee' as source
from
RESULTS_TBL a
, daterange d
where d.dt between a.start_dt and a.end_dt
and not exists
(select 1
from TARGET_TBL x
where x.pin_num = a.pin_num
and d.dt between x.start_dt and x.end_dt)
Application Engine Log File
Posted by
Michael Nitschke
on Wednesday, 7 May 2014
/
Comments: (0)
Handy little library to open Application Engine log files, based on whether running on client while developing, or on server.
class FuncLib
method isTestingMode(&iProcessInst As integer) Returns boolean;
method GetFile(&bTesting As boolean, &sFilename As string, &sMode As string) Returns File;
end-class;
/**********************************************************************
* Returns whether app engine is in test mode (running locally). *
* -------------------------------------------------------------------*
* Parameters : &numProcessInst (process instance) *
* Returns : True if running locally, otherwise false. *
**********************************************************************/
method isTestingMode
/+ &iProcessInst as Integer +/
/+ Returns Boolean +/
Local boolean &bExists, &bTestMode;
/* Check if process instance exists in a process scheduler table */
SQLExec("SELECT 1 FROM PSPRCSRQST WHERE PRCSINSTANCE = :1", &iProcessInst, &bExists);
If Not &bExists Then
/* If not, then its running in test mode. */
&bTestMode = True;
End-If;
Return &bTestMode;
end-method;
/**********************************************************************
* This method creates/opens a log file when running an app engine *
* in either 2 (testing) or n tier mode. *
* -------------------------------------------------------------------*
* Parameters *
* Parm 1: Boolean - Testing mode? *
* Parm 2: File name *
* Parm 3: Mode: "W" will create a new file for writing, *
* "A" will append to an existing file. *
* Returns : File *
**********************************************************************/
method GetFile
/+ &bTesting as Boolean, +/
/+ &sFilename as String, +/
/+ &sMode as String +/
/+ Returns File +/
Local string &sPath;
Local File &fFile;
If &bTesting Then
&sPath = "C:\TEMP\";
&fFile = GetFile(&sPath | &sFilename, &sMode, %FilePath_Absolute);
Else
&sPath = "";
&fFile = GetFile(&sFilename, &sMode);
End-If;
If &sMode = "W" Then
MessageBox(0, "", 0, 0, "Creating file: %1", &sPath | &sFilename);
End-If;
Return &fFile
end-method;
And to use:
import YOUR_PACKAGE:Utilities:FuncLib:*;
Component File &logFile;
&cbTestingMode = (create YOUR_PACKAGE:Utilities:FuncLib()).isTestingMode(YOUR_STATE_RECORD_AET.PROCESS_INSTANCE);
&logFile = (create YOUR_PACKAGE:Utilities:FuncLib()).GetFile(&cbTestingMode, "log.txt", "W");
PeopleCode to get a Sibling RowSet
Posted by
Michael Nitschke
on Monday, 28 April 2014
/
Comments: (0)
For some reason this just took me far too long to figure out. Must be getting old. Writing down for reference.
Let's say you have a page with a Record structure like this:
Parent
Sibling 1
Sibling 2
And you want some code/event on Sibling 1 to change the data in Sibling 2.
The somewhat unusual scenario I had is that Sibling 2 is a kind of audit record for Sibling 1, so that every time data changed on Sibling 1 a new row was inserted into Sibling 2 with Oprid and DateTime etc.
Normally you could just have Sibling 2 as a child of Sibling 1 (and technically it is) but in my scenario Sibling 1 was already at Level 3. The data was to be displayed. I had no room to move.
Place the following in something like SIBLING_1.Field1.SaveEdit()
You'll now have the sibling Record as a RowSet.
.ParentRow is the property of the RowSet you are looking for.
Let's say you have a page with a Record structure like this:
Parent
Sibling 1
Sibling 2
And you want some code/event on Sibling 1 to change the data in Sibling 2.
The somewhat unusual scenario I had is that Sibling 2 is a kind of audit record for Sibling 1, so that every time data changed on Sibling 1 a new row was inserted into Sibling 2 with Oprid and DateTime etc.
Normally you could just have Sibling 2 as a child of Sibling 1 (and technically it is) but in my scenario Sibling 1 was already at Level 3. The data was to be displayed. I had no room to move.
Place the following in something like SIBLING_1.Field1.SaveEdit()
Local Rowset &rsSIBLING_2 = GetRowset().ParentRow.GetRowset(Scroll.SIBLING_2);
You'll now have the sibling Record as a RowSet.
.ParentRow is the property of the RowSet you are looking for.
Make a Field Required using PeopleCode
Posted by
Michael Nitschke
on Wednesday, 2 April 2014
/
Comments: (0)
Simple example of controlling whether a Field is required using PeopleCode instead of the Record definition properties.
Place this in the Record Field's SaveEdit PeopleCode.
Place this in the Record Field's SaveEdit PeopleCode.
If None(GetField().Value) Then
GetField().SetCursorPos(%Page);
GetField().Style = "PSERROR";
Error MsgGet(15, 30, "Highlighted fields are required.");
Else
GetField().Style = "PSEDITBOX";
End-If;
Randomly Generated Data - Oracle SQL
Posted by
Michael Nitschke
on Friday, 13 December 2013
/
Comments: (0)
select
trunc(dbms_random.value(1, 11)) as seconds
from dual
connect by level < 11
(You could join the results to a table using rownum I guess)
SQL to Group or Chunk Date Time Fields by n Minutes
Posted by
Michael Nitschke
on Tuesday, 26 November 2013
/
Comments: (0)
Say you want to chunk some statistics data into 10 minute intervals. as of Oracle 11g you can use the extract() function that will pull out hours, minutes and the like from a DateTime column.
Note the +0.1 bit that handles the range of results, up to that point spanning from 0 to 6. We can't have nn:60 as a vaild DateTime. This is also why we have the -10 part.
Also note that depending on the platform that is actually executing the SQL you may have to fiddle around with the DateTime formats. 'dd-mon-yy hh24:mi' might not cut it for you. PeopleSoft will require 'yyyy-mm-dd hh24:mi' for example.
A breakdown of the entire line:
The final results from the first query:
select
to_date(trunc(dttm_stamp) || ' ' || extract(hour from dttm_stamp) || ':' || lpad(ceil(extract(minute from dttm_stamp)/10 + 0.1) * 10 - 10, 2, '0'), 'dd-mon-yy hh24:mi') as dttm_stamp
, count(some_field) "count"
from
ps_statistics_table
group by
trunc(dttm_stamp) || ' ' || extract(hour from dttm_stamp) || ':' || lpad(ceil(extract(minute from dttm_stamp)/10 + 0.1) * 10 - 10, 2, '0')
;
Note the +0.1 bit that handles the range of results, up to that point spanning from 0 to 6. We can't have nn:60 as a vaild DateTime. This is also why we have the -10 part.
Also note that depending on the platform that is actually executing the SQL you may have to fiddle around with the DateTime formats. 'dd-mon-yy hh24:mi' might not cut it for you. PeopleSoft will require 'yyyy-mm-dd hh24:mi' for example.
A breakdown of the entire line:
select
dttm_stamp as a
, extract(minute from dttm_stamp) as b
, ceil(extract(minute from dttm_stamp)/10 + 0.1) as c
, ceil(extract(minute from dttm_stamp)/10 + 0.1) * 10 - 10 as d
, lpad(ceil(extract(minute from dttm_stamp)/10 + 0.1) * 10 - 10, 2, '0') as e
, trunc(dttm_stamp) || ' ' || extract(hour from dttm_stamp) || ':' || lpad(ceil(extract(minute from dttm_stamp)/10 + 0.1) * 10 - 10, 2, '0') as dttm_stamp
from ps_statistics_table
;
The final results from the first query:
17/11/2013 10:00:00 AM 99
17/11/2013 10:10:00 AM 120
17/11/2013 10:20:00 AM 50
17/11/2013 10:30:00 AM 10
17/11/2013 10:40:00 AM 65
17/11/2013 10:50:00 AM 77
17/11/2013 11:00:00 AM 189
17/11/2013 11:10:00 AM 201
17/11/2013 11:20:00 AM 235
17/11/2013 11:30:00 AM 188
17/11/2013 11:40:00 AM 105
17/11/2013 11:50:00 AM 77
Merging PDFs Produces Blank Pages
Posted by
Michael Nitschke
on Wednesday, 14 August 2013
/
Comments: (2)
So you're creating a number of individual PDFs, doing something with them, and then you want to merge them all into one, nice, long PDF at the end.
Something like this:
You can see this in Communication Generation for example.
But when you open the PDF only the first page has data, the rest are blank. What's going on?
I finally reduced this down to being something "wrong" with the individual PDFs that were being created, before they were merged. It wasn't the templates. It was something specific to the PDF files themselves.
Comparing them to an environment where (luckily) the merge did work showed that the PDFs that did merge had no security, while the ones that didn't merge did have security. File > Properties > Security.
xdo.cfg was the same in both environments. So where was this coming from?
I noticed that, in 8.53 at least, there are some new parameters for BI Publisher Report Definitions, namely PDF Security. The default for pdf-security was True.
I changed this to False and the merge worked. Finally.
This is overriding the global value at the report level. The global value is set at:
Reporting Tools > BI Publisher > Setup > Global Properties
tl;dr solution:
update PSXPGLBPROP
set propvalue = 'False'
where proplname = 'pdf-security';
Something like this:
/* Merge! */
Local boolean &booMergeSuccess = &objMergeTool.mergePDFs(&arrFiles, &sNSMergedFileDestination | &sDirSep | "merged.pdf", &sError);
You can see this in Communication Generation for example.
But when you open the PDF only the first page has data, the rest are blank. What's going on?
I finally reduced this down to being something "wrong" with the individual PDFs that were being created, before they were merged. It wasn't the templates. It was something specific to the PDF files themselves.
Comparing them to an environment where (luckily) the merge did work showed that the PDFs that did merge had no security, while the ones that didn't merge did have security. File > Properties > Security.
xdo.cfg was the same in both environments. So where was this coming from?
I noticed that, in 8.53 at least, there are some new parameters for BI Publisher Report Definitions, namely PDF Security. The default for pdf-security was True.
I changed this to False and the merge worked. Finally.
This is overriding the global value at the report level. The global value is set at:
Reporting Tools > BI Publisher > Setup > Global Properties
tl;dr solution:
update PSXPGLBPROP
set propvalue = 'False'
where proplname = 'pdf-security';
Order SQL Using A Given, Distinct List
Posted by
Michael Nitschke
on Thursday, 18 July 2013
/
Comments: (0)
Say you have a requirement of updating the preferred phone number using a given, ad hoc, ordered list, where #1 in the list was the preferred number, if it exists, followed by #2 in the list and so on. Basically order by X, Y, Z.
This SQL will fit the bill: It's much easier to read the SELECT version first, as we can use the WITH clause:
This SQL will fit the bill: It's much easier to read the SELECT version first, as we can use the WITH clause:
with x_order as
( select 1 as ord, 'HOME' as phone_type from dual
union all select 2 as ord, 'MOB' as phone_type from dual
union all select 3 as ord, 'WORK' as phone_type from dual
union all select 4 as ord, 'SWK' as phone_type from dual
union all select 5 as ord, 'OTR' as phone_type from dual
union all select 6 as ord, 'FAX' as phone_type from dual)
select * from ps_personal_phone ph
where emplid = '12345678'
and ph.phone_type =
(select a.phone_type
from
ps_personal_phone a
, x_order b
where a.emplid = ph.emplid
and b.phone_type = a.phone_type
and b.ord =
(select min(b2.ord)
from
ps_personal_phone a2
, x_order b2
where a2.emplid = a.emplid
and b2.phone_type = a2.phone_type))
;
And the UPDATE version:
update ps_personal_phone ph
set ph.pref_phone_flag = 'Y'
where emplid = '12345678'
and ph.phone_type =
(select a.phone_type
from
ps_personal_phone a
,
( select 1 as ord, 'HOME' as phone_type from dual
union all select 2 as ord, 'MOB' as phone_type from dual
union all select 3 as ord, 'WORK' as phone_type from dual
union all select 4 as ord, 'SWK' as phone_type from dual
union all select 5 as ord, 'OTR' as phone_type from dual
union all select 6 as ord, 'FAX' as phone_type from dual) b
where a.emplid = ph.emplid
and b.phone_type = a.phone_type
and b.ord =
(select min(b2.ord)
from
ps_personal_phone a2
,
( select 1 as ord, 'HOME' as phone_type from dual
union all select 2 as ord, 'MOB' as phone_type from dual
union all select 3 as ord, 'WORK' as phone_type from dual
union all select 4 as ord, 'SWK' as phone_type from dual
union all select 5 as ord, 'OTR' as phone_type from dual
union all select 6 as ord, 'FAX' as phone_type from dual) b2
where a2.emplid = a.emplid
and b2.phone_type = a2.phone_type))
;
Table Lock
Posted by
Michael Nitschke
on Monday, 3 June 2013
/
Comments: (0)
Some useful tables when something somewhere has a table locked:
SELECT * FROM V$LOCK;
SELECT * FROM dba_blockers;
SELECT * FROM dba_locks;
SELECT * FROM dba_waiters;
SELECT * FROM V$SQLTEXT WHERE ADDRESS IN (SELECT SQL_ADDRESS FROM V$SESSION WHERE SID IN (SELECT HOLDING_SESSION FROM dba_blockers));
SELECT * FROM V$SESSION WHERE SID IN (SELECT HOLDING_SESSION FROM dba_blockers);
SELECT * FROM V$SQLTEXT WHERE SQL_ID = '21hy5qgd56a6g';
SELECT * FROM V$SESSION WHERE SID = 152;
Move a File Using PeopleCode
Posted by
Michael Nitschke
on Wednesday, 17 April 2013
/
Comments: (0)
There's no generic function to move files in the PeopleCode universe, only to open them. Here's an elegant solution using the Java io package.
Local JavaObject &fromFile = CreateJavaObject("java.io.File", &strFromPath);
Local JavaObject &toFile = CreateJavaObject("java.io.File", &strToPath);
&fromFile.renameTo(&toFile);
Tracing Application Engines
Posted by
Michael Nitschke
on Tuesday, 26 March 2013
/
Comments: (0)
Tracing particular Process Definitions
This SQL script can make things a little clearer.
Tracing on the Server:
./psft/pt/8.52/appserv/*/psappsrv.cfg is where you can set tracing for Batch server instances.
This SQL script can make things a little clearer.
select '-TRACE ' || (
1 + -- Initiate the Application Engine Step trace
2 + -- Initiate the Application Engine SQL trace
4 + -- Trace dedicated Temp Table Allocation to AET file
128 + -- Initiate the Statement Timings trace to file, which is similar to the COBOL timings trace to file
256 + -- Initiate the PeopleCode Detail to the file for the Timings trace
1024 + -- Initiate the Statement Timings trace, but, instead of writing to the trace file, this trace stores the results in the following tables: PS_BAT_TIMINGS_LOG and PS_BAT_TIMINGS_DTL
2048 + -- Adding this value requests a database optimizer trace file
4096 + -- Request a database optimizer to be inserted in the Explain Plan Table of the current database
8192 -- This value sets a trace for Integration Broker transform programs
) as TRACE from dual;
select '-TOOLSTRACESQL ' || (
1 + -- Trace SQL statements
2 + -- Trace SQL statement variables
4 + -- Trace SQL connect, disconnect, commit and rollback
8 + -- Show fetched rows (indicates that it occurred, not data)
16 + -- Show all other API calls except ssb
32 + -- Set Select Buffers (identifies the attributes of columns to be selected).
64 + -- Show database API specific calls
128 + -- Show COBOL statement timings
256 + -- Show Sybase bind information
512 + -- Show Sybase fetch information
4096 + -- Show manager information
8192 -- Show Mapcore information
) as TOOLSTRACESQL from dual;
select '-TOOLSTRACEPC ' || (
1 + -- Trace instructions
2 + -- List the program
4 + -- Show assignments to variables
8 + -- Show fetched values
16 + -- Show stack
64 + -- Trace start of programs
128 + -- Trace external function calls
256 + -- Trace internal function calls
512 + -- Show parameter values
1024 + -- Show function return values
2048 -- Trace each statement in program
) as TOOLSTRACEPC from dual;
Tracing on the Server:
./psft/pt/8.52/appserv/*/psappsrv.cfg is where you can set tracing for Batch server instances.
[Trace]
;=========================================================================
; Trace settings
;=========================================================================
; SQL Tracing Bitfield
;
; Bit Type of tracing
; --- ---------------
; 1 - SQL statements
; 2 - SQL statement variables
; 4 - SQL connect, disconnect, commit and rollback
; 8 - Row Fetch (indicates that it occurred, not data)
; 16 - All other API calls except ssb
; 32 - Set Select Buffers (identifies the attributes of columns
; to be selected).
; 64 - Database API specific calls
; 128 - COBOL statement timings
; 256 - Sybase Bind information
; 512 - Sybase Fetch information
; 1024 - SQL Informational Trace
; Dynamic change allowed for TraceSql and TraceSqlMask
TraceSQL=0
;-------------------------------------------------------------------------
; PeopleCode Tracing Bitfield
;
; Bit Type of tracing
; --- ---------------
; 1 - Trace Evaluator instructions (not recommended)
; 2 - List Evaluator program (not recommended)
; 4 - Show assignments to variables
; 8 - Show fetched values
; 16 - Show stack
; 64 - Trace start of programs
; 128 - Trace external function calls
; 256 - Trace internal function calls
; 512 - Show parameter values
; 1024 - Show function return value
; 2048 - Trace each statement in program (recommended)
; Dynamic change allowed for TracePC
TracePC=0
;-------------------------------------------------------------------------
; AE Tracing Bitfield
;
; Bit Type of tracing
; --- ---------------
; 1 - Trace STEP execution sequence to AET file
; 2 - Trace Application SQL statements to AET file
; 4 - Trace Dedicated Temp Table Allocation to AET file
; 8 - not yet allocated
; 16 - not yet allocated
; 32 - not yet allocated
; 64 - not yet allocated
; 128 - Timings Report to AET file
; 256 - Method/BuiltIn detail instead of summary in AET Timings Report
; 512 - not yet allocated
; 1024 - Timings Report to tables
; 2048 - DB optimizer trace to file
; 4096 - DB optimizer trace to tables
TraceAE=0
;------------------------------------------------------------------------
; Performance Monitor Tracing Bitfield
;
; Bit Type of tracing
; --- ---------------
; 1 - Trace All performance monitor agent activity
TracePPM=0
Portal Navigation Path - Oracle PL/SQL Function
Posted by
Michael Nitschke
on Tuesday, 19 March 2013
/
Comments: (0)
Thanks to Jake Gowens a.k.a. Zapp Brannigan
Example:
Select navString(a.portal_name, a.portal_objname)
From psprsmdefn a
Where portal_uri_seg2 = 'JOB_DATA';
Returns:
Workforce Administration > Job Information > Job Data
FUNCTION navString (prtlName IN CHAR, prtlObjName IN CHAR) RETURN VARCHAR2 IS
prtlLabel VARCHAR2(30);
prtlParent VARCHAR2(30);
result VARCHAR2(255);
BEGIN
prtlLabel := '';
SELECT MAX(PORTAL_LABEL), MAX(PORTAL_PRNTOBJNAME)
INTO prtlLabel, prtlParent
FROM PSPRSMDEFN
WHERE PORTAL_NAME = prtlName
AND PORTAL_OBJNAME = prtlObjName
AND PORTAL_REFTYPE = 'C';
if prtlLabel IS NOT NULL then
if prtlParent = 'PORTAL_ROOT_OBJECT' then
result := prtlLabel;
else
result := navStringF(prtlName, prtlParent) || ' > ' || prtlLabel;
end if;
end if;
return result;
END navString;
Example:
Select navString(a.portal_name, a.portal_objname)
From psprsmdefn a
Where portal_uri_seg2 = 'JOB_DATA';
Returns:
Workforce Administration > Job Information > Job Data
Automatically Generate Upgrade Scripts
Posted by
Michael Nitschke
on Thursday, 28 February 2013
/
Comments: (0)
Snippet of PeopleCode that takes a Project and creates an upgrade script for all the Records in that Project. Does 90%+ of the work for you.
Requires a Prompt to select the Project, a button to put this code under, and a HTML Area to output the results to.
Requires a Prompt to select the Project, a button to put this code under, and a HTML Area to output the results to.
If None(NS_MN_TEST.PROJECTNAME.Value) Then
MessageBox(0, "", 0, 0, "Enter a Project Name");
Else
Local string &title, &result, &fields;
Local integer &f;
Local Record &recPSPROJECTDEFN = CreateRecord(Record.PSPROJECTDEFN);
&recPSPROJECTDEFN.PROJECTNAME.Value = NS_MN_TEST.PROJECTNAME.Value;
&recPSPROJECTDEFN.SelectByKey();
Local Record &recPSOPRDEFN = CreateRecord(Record.PSOPRDEFN);
&recPSOPRDEFN.OPRID.Value = &recPSPROJECTDEFN.LASTUPDOPRID.Value;
&recPSOPRDEFN.SelectByKey();
&title = &title | "/*" | Char(13);
&title = &title | " WORK UNIT: " | &recPSPROJECTDEFN.PROJECTNAME.Value | Char(13);
&title = &title | " DEVELOPER: " | &recPSOPRDEFN.OPRDEFNDESC.Value | Char(13);
&title = &title | " DATE: " | String(%Date) | Char(13);
&title = &title | " DESCRIPTION: " | &recPSPROJECTDEFN.PROJECTDESCR.Value | Char(13);
&title = &title | " INSTRUCTIONS: EXECUTE IN THE TARGET" | Char(13);
&title = &title | " DEPENDENCIES: " | Char(13);
&title = &title | "*/" | Char(13) | Char(13);
&title = &title | "-- List of Records that have data converted by this script:" | Char(13);
Local SQL &sql = CreateSQL("SELECT OBJECTVALUE1 FROM PSPROJECTITEM A, PSRECDEFN B WHERE A.PROJECTNAME = :1 AND A.OBJECTTYPE = 0 AND A.OBJECTID2 = 0 AND B.RECNAME = A.OBJECTVALUE1 AND B.RECTYPE = 0", NS_MN_TEST.PROJECTNAME.Value);
Local string &recName;
While &sql.Fetch(&recName)
If Left(&recName, 3) = "NS_" Then
&title = &title | "-- " | &recName | Char(13);
&result = &result | Char(13) | "-- " | &recName | Char(13);
&result = &result | "Delete From ps_" | &recName | ";" | Char(13);
Local Record &rec = CreateRecord(@("record." | &recName));
&result = &result | "Insert Into ps_" | &recName | "(" | Char(13);
For &f = 1 To &rec.FieldCount
If &f = 1 Then
&fields = " " | &rec.GetField(&f).Name | Char(13);
Else
&fields = &fields | ", " | &rec.GetField(&f).Name | Char(13);
End-If;
End-For;
&result = &result | &fields | ")" | Char(13);
&result = &result | "Select " | Char(13) | &fields | "From ps_" | &recName | "@nsconv.unsw.edu.au;" | Char(13);
End-If;
End-While;
&title = &title | Char(13) | "SPOOL ON;" | Char(13);
&title = &title | "SET ECHO ON;" | Char(13);
&title = &title | "SPOOL 'c:\temp\" | Substring(&recPSPROJECTDEFN.PROJECTNAME.Value, 4, 7) | "_data_conversion.log' REPLACE;" | Char(13);
NS_MN_TEST.HTMLAREA1.Value = &title | Char(13) | Char(13) | &result | Char(13) | Char(13) | "commit;" | Char(13) | Char(13) | "SPOOL OFF;";
End-If;
Paste your text here.
Searching Through Views
Posted by
Michael Nitschke
on Wednesday, 27 February 2013
/
Comments: (0)
If you make a change on a Record or View you should check its impact. App Designer doesn't search through SQL definitions but you can do that yourself in SQL:
select SQLID from PSSQLTEXTDEFN
where sqltext like '%SOME_TABLE%'
select SQLID from PSSQLTEXTDEFN
where sqltext like '%SOME_TABLE%'
Posting CommGen to Report Manager
Posted by
Michael Nitschke
on Saturday, 23 February 2013
/
Comments: (1)
As delivered Communication Generation (CommGen) produces the final, merged output of letters on the Server. There is no way to configure it to send to Report Manager or Process Monitor.
However, there is an easy fix.
Each Process Instance has its own directory made at run-time, and all files in that directory are published to Process Monitor and Report Manager. We just need to make a simple tweak to create the Merged File Output in the process's Process Instance directory instead of the file destination directory as defined on the Run Control (by the way it would pay to modify the run control accordingly as well; hide the now unused File Output field etc.).
Application Package SCC_COMMGEN:Model:CommGen
Just change the one line, as above. This will have the merged file go to Report Manager and Process Monitor instead of a directory on the server. I suspect this issue may be a left over from version 7, where users could run on the client and have it appear on their local drive.
However, there is an easy fix.
Each Process Instance has its own directory made at run-time, and all files in that directory are published to Process Monitor and Report Manager. We just need to make a simple tweak to create the Merged File Output in the process's Process Instance directory instead of the file destination directory as defined on the Run Control (by the way it would pay to modify the run control accordingly as well; hide the now unused File Output field etc.).
Application Package SCC_COMMGEN:Model:CommGen
/* This method merges all the communications that have been generated so far */
method MergeCommunications
...
rem &sFileDestination = &rsRunCtlParms(1).SCC_CG_RUNCTL.FILE_PATH.Value;
&sFileDestination = "log_output/AE_SCC_COMMGEN_" | &ProcessInstance;
Just change the one line, as above. This will have the merged file go to Report Manager and Process Monitor instead of a directory on the server. I suspect this issue may be a left over from version 7, where users could run on the client and have it appear on their local drive.
Rowset to HTML Table
Posted by
Michael Nitschke
on Tuesday, 13 November 2012
/
Comments: (2)
Another possibly useless tool:
<*--------------------------------------------------------------------------
UNSW WUHR005 Michael Nitschke 12/11/2012
Utility to take a rowset and return all its values.
At time of writing only handles a single record, flat rowset.
Example:
import NS_NEXTGEN:Utilities:RowsetToHTML;
/* Create and fill a rowset. */
Local Rowset &rowset = CreateRowset(Record.SOME_RECORD);
&rowset.Fill();
/* Put the rowset's data into an HTML Area on a Page. */
Local NS_NEXTGEN:Utilities:RowsetToHTML &rs2HTML = create NS_NEXTGEN:Utilities:RowsetToHTML(&rowset);
NS_DERIVED.HTML_AREA_01.Value = &rs2HTML.RowsetAsHTML;
The above will populate a HTML Area on a page with the values of the rowset.
This utility could be extended to include heirarchical data.
----------------------------------------------------------------------------*>
class RowsetToHTML
method RowsetToHTML(&rs As Rowset);
property string RowsetAsHTML;
end-class;
method RowsetToHTML
/+ &rs as Rowset +/
Local string &style;
&style = &style | "<style type=""text/css"">";
&style = &style | "table.rowset {";
&style = &style | " border-width: 3px;";
&style = &style | " border-spacing: 0px;";
&style = &style | " border-style: solid;";
&style = &style | " border-color: black;";
&style = &style | " border-collapse: collapse;";
&style = &style | " background-color: white;";
&style = &style | "}";
&style = &style | "table.rowset th {";
&style = &style | " border-width: 1px;";
&style = &style | " padding: 4px;";
&style = &style | " border-style: dotted;";
&style = &style | " border-color: gray;";
&style = &style | " background-color: rgb(200, 200, 200);";
&style = &style | " -moz-border-radius: ;";
&style = &style | " font-size: small ;";
&style = &style | "}";
&style = &style | "table.rowset th.recname {";
&style = &style | " background-color: rgb(0, 0, 0);";
&style = &style | " color: white;";
&style = &style | " font-size: large;";
&style = &style | " font-weight: normal;";
&style = &style | " text-align: left;";
&style = &style | "}";
&style = &style | "table.rowset tr.r0 td {";
&style = &style | " background-color: rgb(240, 240, 240);";
&style = &style | "}";
&style = &style | "table.rowset tr.r1 td {";
&style = &style | " background-color: rgb(250, 250, 250);";
&style = &style | "}";
&style = &style | "table.rowset td {";
&style = &style | " border-width: 1px;";
&style = &style | " padding: 4px;";
&style = &style | " border-style: dotted;";
&style = &style | " border-color: gray;";
&style = &style | " background-color: white;";
&style = &style | " -moz-border-radius: ;";
&style = &style | " font-size: small ;";
&style = &style | "}";
&style = &style | "</style>";
Local string &html;
&html = &html | "<table class=""rowset"">";
&html = &html | "<tr>";
&html = &html | "<th class=""recname"" colspan=""" | (&rs(1).GetRecord(1).FieldCount + 1) | """>" | &rs(1).GetRecord(1).Name | "</th>";
&html = &html | "</tr>";
&html = &html | "<tr>";
Local integer &f;
&html = &html | "<th>row</th>";
For &f = 1 To &rs(1).GetRecord(1).FieldCount
&html = &html | "<th>" | &rs(1).GetRecord(1).GetField(&f).Name | "</th>";
End-For;
&html = &html | "</tr>";
Local string &alternate = "1";
Local integer &i;
For &i = 1 To &rs.RowCount
If &alternate = "1" Then
&alternate = "0";
Else
&alternate = "1";
End-If;
&html = &html | "<tr class=""r" | &alternate | """>";
&html = &html | "<td>" | &i | "</td>";
For &f = 1 To &rs(&i).GetRecord(1).FieldCount
&html = &html | "<td>" | &rs(&i).GetRecord(1).GetField(&f).Value | "</td>";
End-For;
&html = &html | "</tr>";
End-For;
&html = &html | "</table>";
%This.RowsetAsHTML = &style | &html;
end-method;
