Upload Builder Copy link
Plugin: Blitz Report Development (
upload-builder)Works with: Claude Code
Build Blitz Upload XML definitions and PL/SQL processing packages for Oracle EBS.
When to use it
- Use when creating a new upload or significantly modifying an existing one.
Claude Code
- Add the Enginatics marketplace in Claude Code.
/plugin marketplace add enginatics/ai-skills - Install the plugin.
/plugin install blitz-report-ai-skills@enginatics - Run the environment setup once; it describes your EBS development instance (never production).
/blitz-report-ai-skills:blitz-environment-setup - Run the skill, or just describe the task and Claude picks it up.
/blitz-report-ai-skills:upload-builder - Keep it current: enable auto-update for the enginatics marketplace, or update manually.
/plugin marketplace update enginatics
Requirements: Blitz Report installed on your Oracle EBS development instance, and database access from Claude Code to that instance (Oracle SQLcl MCP server, SQLcl or SQL*Plus). Never point the skills at production.
Builds the XML definition and PL/SQL processing package for a Blitz Upload based on requirements or a Form Analysis Document from the oracle-forms-analyzer.
When to Use
- Creating a new Blitz Upload from scratch
- Significantly modifying an existing upload (adding columns, changing processing logic)
- Converting a Form Analysis Document into a working upload
Arguments
$ARGUMENTS should contain the upload name, target module, and optionally a reference to the Form Analysis Document.
Inputs
- Form Analysis Document (from oracle-forms-analyzer) — field inventory, LOVs, defaults, API details
- Existing similar uploads — find in
content/r12/uploads/for reference patterns coding-standardsskill — always loaded, must be followed strictly
Phase 1: Design
1.1 Determine Processing Approach
| Approach | When to Use | UPLOAD_TYPE |
|---|---|---|
| API | Oracle provides a public API (e.g., hr_employee_api, ar_invoice_api_pub) | A |
| Interface Table | Oracle uses interface + concurrent processor (e.g., FA Mass Additions, PO PDOI) | A |
| Direct DML | No API available, or API doesn't work on Vision (rare, last resort) | A |
1.2 Design Row Structure
Determine how entities map to Excel rows. For single-entity uploads (one table, one row per record), this is straightforward — skip to 1.3. For multi-entity uploads (parent/child, multiple detail types under the same parent), design the row layout before the column list.
Questions to answer:
How many entity types does the upload cover? List them (e.g., Job Header, Operation, Resource, Component). Identify the hierarchy — which are parents, which are children, and which are siblings.
Do sibling entities share rows or get their own rows?
- Shared rows (flat/cartesian) — appropriate when entities are 1:1 or naturally paired (e.g., invoice header + one line per row). Also works when detail entity types can be freely combined on a single row and the PL/SQL procedure determines which entities to process based on which columns are populated (e.g., QP Price List Upload, QP Modifier Upload).
- Separate rows with Row Type — appropriate when multiple independent child entity types exist under the same parent and combining them on one row would create artificial duplication or ambiguity (e.g., N resources x M components = NxM rows instead of N+M). Each row represents one detail entity.
How does parent data relate to detail rows?
- Parent columns come first in the column layout, before the Row Type and detail columns
- Parent data repeats across all detail rows — users mass-paste data, so every row carries the full parent context
- There is no separate "Header" row type — the parent is processed on the first row encountered and skipped on subsequent rows, regardless of detail content
- The PL/SQL procedure detects a new parent by comparing the current parent key against the previous one
What is the expected row ordering?
- Rows are grouped by parent key, with detail rows underneath
- Within details, the ordering should follow the natural hierarchy (e.g., operations before their child resources/components)
- The Order By SQL must enforce this grouping (e.g.,
upload_row, job_name, row_type_sort, operation_seq_num)
Row Type column — exposed or hidden? This is upload-specific:
- Hidden (download-only) — when the row type is purely for structuring downloaded data. During upload, users can populate columns from different entity types on the same row and the PL/SQL procedure determines which entities to process based on which columns are populated. Detail LOVs are not restricted by row type. Examples: QP Price List Upload, QP Modifier Upload.
- Visible (user-selected) — when entity types are independent siblings and mixing them on one row would cause duplication or ambiguity. The row type drives which columns are relevant, which LOVs are active, and which validation rules apply. Detail LOVs filter by context columns or by the row type value itself.
Column layout pattern:
| Position | Columns | Notes |
|---|---|---|
| First | Framework columns (action, status, message_, modifiedcolumns) | Always first |
| Next | Parent/header columns (e.g., Job Name, Assembly, Class, Status, Dates) | Repeat on every row |
| Next | Row Type | Hidden or visible depending on upload design |
| Next | Detail columns shared across types (e.g., Operation Seq) | Linking column for child rows |
| Last | Detail columns specific to each type (e.g., Resource, Component, Qty/Assy) | Blank when not applicable |
Main SQL structure for downloads — UNION ALL branches, one per detail entity type:
select
null action_,
null status_,
null message_,
null modified_columns_,
x.*
from
(
-- Branch 1: entity type A
select parent_cols, 'Type A' row_type, shared_detail_cols, type_a_cols,
null type_b_col1, null type_b_col2 -- null columns for other types
from parent_table, type_a_table where ...
union all
-- Branch 2: entity type B
select parent_cols, 'Type B' row_type, shared_detail_cols,
null type_a_col1, null type_a_col2, -- null columns for other types
type_b_cols
from parent_table, type_b_table where ...
) x1.2b Design Error and Success SQL
Before designing the column list, determine how errors and successes will be detected and reported. These decisions directly affect which columns are needed (hidden rowid columns, requestid, etc.).
Questions to answer:
Error SQL Design
Can the auto-generated Error SQL be used?
- Yes — when the upload procedure directly creates/updates records via API calls and sets
status_/message_per row. The defaultnvl(xu.status_code_,xxen_upload.status_error)<>xxen_upload.status_successis sufficient. - No — when the upload inserts into interface tables and a separate concurrent program does the actual processing. The procedure sets all rows to success after inserting into the interface, but the concurrent program may reject some rows. A custom Error SQL is needed to detect these post-processing failures.
- Yes — when the upload procedure directly creates/updates records via API calls and sets
For interface-based uploads, how do we detect errors?
- Check the interface table's
process_statuscolumn (e.g.,process_status=3for error in WIP/PO interfaces), NOT the existence of error message rows. The process status is the authoritative error indicator. - Link upload rows to interface rows via hidden rowid columns (see below).
- The WHERE clause checks:
nvl(xu.status_code_,xxen_upload.status_error)<>xxen_upload.status_successORexists (interface row with error status).
- Check the interface table's
How do we retrieve error messages?
- Join from the interface rowid to the interface error table in the SELECT (not the WHERE).
- Use
coalesceto try detail-level errors first, then header-level errors, then the frameworkxu.message_, then a catchall message (e.g.,'No error message returned. Check the <Program> concurrent request.'). - The status column should show
xxen_upload.status_meaning(xxen_upload.status_error)— notxu.status_which reflects the pre-processing status (success after interface insert, before concurrent program ran).
Interface rowid columns — add hidden
in out varchar2columns for each interface table the upload inserts into. These serve dual purpose:- Error detection: join upload rows to interface records to check
process_status - Cleanup on re-upload: when a rowid is populated from a previous failed upload, delete the stale interface error and interface record before re-processing
Implementation:
- Use
RETURNING rowidtochar(rowid) INTO p_row_idon each interface table insert - Position these columns in the outer query after
modified_columns_(standard position for in/out framework columns) - In the PL/SQL procedure, at the start: if any rowid parameter is populated, delete the corresponding error rows first, then the interface record, then clear the parameter before re-inserting
- Error detection: join upload rows to interface records to check
Always list individual columns in Error SQL — never use
xu.*. The column count must match the main SQL exactly.Always use the actual upload view name (e.g.,
xxen_module_upload_NNNN_u) — never the&upload_viewlexical.
Success SQL Design
What defines success?
- For API-based uploads:
xu.status_code_=xxen_upload.status_successis sufficient. - For interface-based uploads: success means
xu.status_code_=xxen_upload.status_successAND the interface records were processed without error. Addnot existschecks against interface tables with errorprocess_status.
- For API-based uploads:
How to link upload rows to the created/updated entities?
- Preferred: hidden ID columns returned as
in outparameters from the API (e.g.,p_wip_entity_id in out). The Success SQL joins on these IDs. - Interface-based fallback: when the API inserts into interface tables and a concurrent program creates the actual entities, the IDs aren't available at upload time. Join on logical business keys instead (e.g.,
xu.job_name=we.wip_entity_name). Use outer joins for detail entities (operations, resources, components) since not every upload row has all detail types.
- Preferred: hidden ID columns returned as
Post procedure
request_id_pattern — for interface-based uploads:- Add
request_id_ out numberto the post procedure signature - After submitting the concurrent program, set
request_id_:=l_request_id - Add a corresponding
UPLOAD_PARAMETERS_ROWwithSOURCE=P,IN_OUT=out,COLUMN_NAME=REQUEST_ID_ - The Success SQL can then use
xu.request_id_to correlate with the concurrent request
- Add
Success message showing actions taken — use the post procedure's
request_id_to determine what happened:- Query
fnd_concurrent_requests.actual_start_datefor the processing request - Compare each entity's
creation_dateandlast_update_dateagainst this timestamp creation_date >= actual_start_date→ "created"last_update_date >= actual_start_date→ "updated"- Concatenate across entities to build a descriptive message (e.g., "Job created, Operation 10 created, Resource 10 created")
- Query
Impact on Column List
These decisions feed directly into the column list (Phase 1.3):
- Hidden rowid columns — one per interface table inserted into, positioned after
modified_columns_in the outer query request_id_— already a standard framework column, but ensure it's included when the post procedure returns a concurrent request ID- Hidden ID columns — for linking Success SQL to actual entities (e.g.,
wip_entity_id) - Business key columns — must be sufficient to join back to the entity tables in the Success SQL
1.3 Design Column List
From the Form Analysis Document, map form fields to upload columns:
Framework columns (always first, always these 4):
null action_, null status_, null message_, null modified_columns_,Data columns in form visual order (CRITICAL):
- Use the FAD section 4.2 (Visual Field Order) as the definitive column sequence
- This is extracted from .fmb canvas coordinates (YPosition → XPosition) and reflects the actual form layout
- Do NOT use generic grouping rules — follow the form's exact visual order
- Walk through every tab and window in the form systematically — main tab, scheduling tab, bill tab, routing tab, and any detail windows (operations, components, resources). Missing a tab means missing fields.
- Check standalone edit forms — detail entities (operations, resources, components) may have separate standalone edit forms with fields not visible from the parent form's inquiry view. For example, the WIP Discrete Jobs form opens operations in inquiry mode, but the standalone Operations form (
WIPOPMDF) has the full editable field set. Check both forms for the complete field inventory. - Include description columns for key LOV columns (e.g., Assembly Description, Component Description) — these are display-only columns that default from the parent LOV column via
$column$reference - Exception: DFF fields (Attribute Category + Attribute1..15) always go last in each entity group, regardless of form position
- Exception: KFF groups stay together (alias → concatenated → segments) even if form shows them differently
Fallback (only if FAD section 4.2 is unavailable):
- Key/identifier fields first (Book, Asset Number, etc.)
- Classification fields (Category, Type, etc.)
- Amount fields (Cost, Salvage Value, etc.)
- Date fields
- Method/rule fields
- KFF groups: alias → concatenated → individual segments
- Assignment fields (Employee, Location)
- DFF fields last
Each column needs:
- SQL alias (becomes Excel header) — must match the form Prompt attribute from FAD section 4.1, NOT the internal DB column name
- Source expression in main SQL (table.column or null for new)
- DATA_TYPE: varchar2 for LOV columns (always), number/date for non-LOV
- LOV definition (if applicable)
- DEFAULT_VALUE (if applicable)
- REQUIRED flag
- CRITICAL:
$column$references always pass display values — when a column has a LOV withid/value, the$column$reference in DEFAULT_VALUE or other LOV queries passes the displayvalue, NOT theid. Any DEFAULT_VALUE or LOV_QUERY_DSP that needs to compare against a numeric ID column (e.g.,inventory_item_id,project_id) must resolve via a subquery:(select msiv.inventory_item_id from mtl_system_items_vl msiv where msiv.concatenated_segments=:$column$.assembly). Direct comparison likemsiv.inventory_item_id=:$column$.assemblycausesORA-01722: invalid number.
DFF columns — always include by default:
- Check the FAD for DFF registrations on the main entity table(s) (identified by the
DFitem orFND_FLEX.EVENTtriggers in the form) - For each registered DFF, include: context column (
<prefix>_attribute_category) + attribute columns (<prefix>_attribute1..15) - Use entity prefix per rule E5a (e.g.,
job_attribute1for WIP jobs,loc_attribute1for locations) - Position DFF columns last in the header column group, before the Row Type and detail columns
- SQL: use
xxen_util.display_flexfield_context()andxxen_util.display_flexfield_value()— never rawtable.attribute1 - LOVs: context column queries
fnd_descr_flex_contexts_vl; attribute columns usexxen_util.dff_attribute_lov()with$column$references to context and all siblings - COMMENTS: each attribute column has a SQL query showing the DFF context usage per attribute
- All attribute columns have
VALIDATE_FROM_LIST_DSP=Y - Add a dynamic column translation rule in
content/r12/dynamic_column_translation_rules.xmlwith the upload's GUID, using the entity prefix pattern ('PREFIX_'||x.application_column_name) - PL/SQL: call
xxen_fnd_upload.validate_dff_attributes()withp_values_or_ids_in=>'V', p_values_or_ids_out=>'I' - See
upload-developmentskill section E for the full checklist
- Check the FAD for DFF registrations on the main entity table(s) (identified by the
1.4 Design PL/SQL Package
- Package name:
XXEN_<MODULE>_UPLOAD(e.g.,XXEN_FA_UPLOAD,XXEN_AP_PAYMENT_UPLOAD) - Validate function:
validate_<entity>— returns varchar2 (null=valid, non-null=error) - Processing procedure:
create_update_<entity>— status and message out params - Post procedure (if interface-based):
post_<entity>— submits concurrent processor
Phase 2: Build the PL/SQL Package
2.1 Validation Function Pattern
function validate_asset(
p_field1 in varchar2,
p_field2 in number
) return varchar2 is
l_message varchar2(4000);
procedure message(p_message in varchar2) is
begin
l_message:=l_message||case when l_message is not null then chr(10) end||p_message;
end message;
begin
-- Required field checks
if p_field1 is null then
message('Field1 is required.');
end if;
-- Cross-field validations
if p_field2 is not null and p_field2<=0 then
message('Field2 must be positive.');
end if;
-- Foreign key existence checks (use not exists pattern)
for c in (
select null from dual where not exists (
select null from some_table st where st.code=p_field1
)
) loop
message('Invalid Field1: '||p_field1||'.');
end loop;
return l_message;
end validate_asset;Key rules:
- Use nested
message()procedure to concatenate ALL errors (never return on first error) - Don't validate fields already covered by LOV with VALIDATE_FROM_LIST=Y
- Use
not existspattern instead ofl_foundflag variables - Follow
coding-standardsskill (no spaces around operators, lowercase, etc.)
2.2 Logging Procedures (Standard Pattern)
Every upload package must include these two procedures:
procedure write_exception(p_text in varchar2) is
begin
xxen_report.write_log(replace(p_text,chr(0),' ')||chr(10)||dbms_utility.format_error_stack||dbms_utility.format_error_backtrace);
end write_exception;
procedure write_log(p_text in varchar2) is
begin
if fnd_profile.value('XXEN_REPORT_DEBUG')='Y' then
xxen_report.write_log(p_text);
end if;
end write_log;write_log— normal debug logging, gated by profile. Call directly as a procedure:write_log('message');write_exception— always logs, includes full error stack. Use in unexpected exception handlers.- Never use a function returning varchar2 (
l_dummy:=write_log(...)) — always use procedure calls.
2.3 Processing Procedure Pattern
procedure create_update_entity(
p_action in varchar2 default null,
status_ out varchar2,
message_ out varchar2,
p_key_id in number default null,
p_key_field in varchar2,
p_data_field in varchar2 default null
) is
l_rec target_table%rowtype;
l_action varchar2(10);
begin
write_log('create_update_entity entry');
write_log('p_key_field: '||p_key_field);
status_:=xxen_upload.g_status_success;
-- Auto-detect with full old value fetch using %rowtype
for c in (
select t.*
from target_table t
where p_action=xxen_upload.g_action_update and p_key_id is not null and t.id=p_key_id
or not(p_action=xxen_upload.g_action_update and p_key_id is not null) and t.key=p_key_field
) loop
l_rec:=c;
end loop;
l_action:=case when l_rec.id is null then xxen_upload.g_action_create else xxen_upload.g_action_update end;
-- Apply new_value for all updatable fields
l_rec.data_field:=xxen_upload.new_value('p_data_field',p_data_field,l_rec.data_field,l_action);
if l_action=xxen_upload.g_action_create then
-- Create: call API or insert into interface
null;
else
-- Update: call API or insert into interface
null;
end if;
message_:='Record processed.';
exception
when le_validation_error then
status_:=xxen_upload.g_status_error;
when others then
write_exception('create_update_entity error for '||p_key_field);
raise;
end create_update_entity;Key rules:
status_andmessage_are mandatory OUT params- Auto-detect using ALL unique key columns, fetch old values into
%rowtyperecord - Apply
xxen_upload.new_valueto every updatable field at every entity level — MANDATORY for updates - For API-based uploads: use local
new_valuewrappers withg_entity_changedto gate update calls - For interface-table uploads: call
xxen_upload.new_valuedirectly — no wrappers needed when sentinels match le_validation_errorfor business errors,when otherswithwrite_exception+raisefor unexpected errors- No unused parameters — every param must have UPLOAD_PARAMETERS_ROW in XML
2.3 KFF Segment Resolution (Dynamic Pattern)
When the upload accepts individual segment values and needs to resolve to a CCID:
-- Dynamic concatenation (no hardcoded delimiters or segment counts)
l_concat_segs:=null;
for c in (
select fifst.concatenated_segment_delimiter d, fifsg.application_column_name
from fnd_id_flex_structures fifst,
fnd_id_flex_segments fifsg
where
fifst.application_id=:app_id and
fifst.id_flex_code=:flex_code and
fifst.id_flex_num=:flex_num and
fifsg.application_id=fifst.application_id and
fifsg.id_flex_code=fifst.id_flex_code and
fifsg.id_flex_num=fifst.id_flex_num and
fifsg.enabled_flag='Y'
order by
fifsg.segment_num
) loop
l_concat_segs:=l_concat_segs||case when l_concat_segs is not null then c.d end||
case c.application_column_name
when 'SEGMENT1' then p_segment1
when 'SEGMENT2' then p_segment2
-- ... up to max segments for this flex
end;
end loop;
-- Look up existing
for c in (select kfv.code_combination_id from kfv_table kfv where kfv.concatenated_segments=l_concat_segs) loop
l_ccid:=c.code_combination_id;
end loop;
-- Create new (if allowed)
if l_ccid is null then
l_ccid:=fnd_flex_ext.get_ccid(:app_short, :flex_code, :flex_num, to_char(sysdate,'YYYY/MM/DD HH24:MI:SS'), l_concat_segs);
if l_ccid=0 then
status_:=xxen_upload.status_error;
message_:='Invalid combination: '||l_concat_segs||'. '||fnd_flex_ext.get_message;
return;
end if;
end if;2.4 Post Procedure (Interface-Based Uploads)
procedure post_entity(
p_org_code in varchar2,
status_ out varchar2,
message_ out varchar2
) is
l_request_id number;
begin
l_request_id:=fnd_request.submit_request(:app, :program, null, null, false, :arg1, :arg2);
commit;
if l_request_id=0 then
status_:=xxen_upload.status_error;
message_:=fnd_message.get;
return;
end if;
-- Wait for completion, check interface error table,
-- update xxen_upload_data for failed rows
end post_entity;Phase 3: Build the XML Definition
3.1 XML Skeleton
<ROOT>
<APPS_INITIALIZE_DATA>
<USER_NAME>ENGINATICS</USER_NAME>
<RESPONSIBILITY_KEY>SYSTEM_ADMINISTRATOR</RESPONSIBILITY_KEY>
<APPLICATION_SHORT_NAME>SYSADMIN</APPLICATION_SHORT_NAME>
</APPS_INITIALIZE_DATA>
<CATEGORIES>
<CATEGORIES_ROW>
<BASE_CATEGORY>Module Name</BASE_CATEGORY>
<CATEGORY_TRANSLATIONS>
<CATEGORY_TRANSLATIONS_ROW>
<LANGUAGE>US</LANGUAGE>
<CATEGORY>Module Name</CATEGORY>
</CATEGORY_TRANSLATIONS_ROW>
</CATEGORY_TRANSLATIONS>
</CATEGORIES_ROW>
</CATEGORIES>
<REPORTS>
<REPORTS_ROW>
<GUID>generate-unique-guid</GUID>
<SQL_TEXT>...</SQL_TEXT>
<VERSION_COMMENTS>Initial version</VERSION_COMMENTS>
<ENABLED>Y</ENABLED>
<TYPE>U</TYPE>
<UPLOAD_TYPE>A</UPLOAD_TYPE>
<UPLOAD_OBJECT>xxen_module_upload.create_update_entity</UPLOAD_OBJECT>
<UPLOAD_EXCEL_VALIDATION>xxen_module_upload.validate_entity(:$column$.field1, :$column$.field2)</UPLOAD_EXCEL_VALIDATION>
...
</REPORTS_ROW>
</REPORTS>
</ROOT>3.2 Main SQL Rules
- No ORDER BY (use UPLOAD_SQLS Order By instead)
- Traditional Oracle join syntax (not ANSI)
- Follow
coding-standardsskill strictly - Lowercase everything, no spaces around operators
3.3 Parameters
Always include Upload Mode — standard Blitz Upload Mode LOV:
<PARAMETERS_ROW>
<SORT_ORDER>1</SORT_ORDER>
<DISPLAY_SEQUENCE>10</DISPLAY_SEQUENCE>
<ANCHOR>1=1</ANCHOR>
<SQL_TEXT>:p_upload_mode like '%' || xxen_upload.action_update</SQL_TEXT>
<PARAMETER_TYPE_DSP>LOV</PARAMETER_TYPE_DSP>
<LOV_NAME>Blitz Upload Mode</LOV_NAME>
<LOV_GUID>E811F942597A2075E0530100007FF4F2</LOV_GUID>
<VALIDATE_FROM_LIST_DSP>Y</VALIDATE_FROM_LIST_DSP>
<LOV_QUERY_DSP>select xxen_upload.action_create id, xxen_upload.action_meaning(xxen_upload.action_create) value, null description from dual union all
select xxen_upload.action_create || ',' || xxen_upload.action_update id, xxen_upload.action_meaning(xxen_upload.action_create)||', '||xxen_upload.action_meaning(xxen_upload.action_update) value, null description from dual</LOV_QUERY_DSP>
<REQUIRED>Y</REQUIRED>
</PARAMETERS_ROW>Rules:
- All filter parameters use
1=1anchor (never create 2=2, 3=3 unless genuinely different SQL positions) - Reuse existing LOVs: HR Operating Unit (
8E2FF36EDEB979D2E0530100007F1FF2), GL Ledger (8E2FF36EDEB879D2E0530100007F1FF2) - Use
LOV customonly for upload columns, not parameters
3.3b LOV Performance and Data Entry Workflow (CRITICAL)
When building LOV queries for upload columns, match the Oracle form's data entry workflow from FAD section 4.2b:
Rule: If the form enforces entering Field A before Field B's LOV is available, the upload LOV for B must use a REQUIRED join on A — no is null or pattern.
-- BAD (prevents index usage — full table scan):
select rcta.trx_number value from ra_customer_trx_all rcta, hz_cust_accounts hca
where rcta.bill_to_customer_id=hca.cust_account_id and
(:$column$.customer_number is null or hca.account_number=:$column$.customer_number)
-- GOOD (index-friendly — parent field required, matching form behavior):
select rcta.trx_number value from ra_customer_trx_all rcta, hz_cust_accounts hca
where rcta.bill_to_customer_id=hca.cust_account_id and
hca.account_number=:$column$.customer_numberWhy this matters:
is null or column=:bindforces the optimizer to plan for both null and non-null cases → typically a full table scan- When the form requires the parent field first, the
is nullbranch can never happen in practice - On large tables (invoices, receipts, journal lines), this causes seconds-long LOV load times
When is null or IS acceptable:
- The form allows opening the child LOV without the parent field populated
- The child table is small (< 10K rows) and a full scan is acceptable
3.4 Column DATA_TYPE Rules
varchar2for ALL LOV columns (even when LOV has numericid)numberfor numeric non-LOV columnsdatefor date columns
3.5 KFF Column Groups
For each KFF, add columns in this order:
- Alias — LOV custom, VALIDATE_FROM_LIST=Y, queries
fnd_shorthand_flex_aliases - Concatenated — LOV custom, queries
_kfvview, DEFAULT_VALUE from alias - Segment1..N — LOV custom, queries
fnd_flex_values_vl, DEFAULT_VALUE from concatenated
DEFAULT_VALUE flows ONE direction only: alias → concatenated → segments. PL/SQL handles the reverse: segments → concatenated → CCID.
3.6 Upload Parameters (PL/SQL Mapping)
<UPLOAD_PARAMETERS_ROW>
<DISPLAY_SEQUENCE>10</DISPLAY_SEQUENCE>
<PARAMETER_NAME>p_key_field</PARAMETER_NAME>
<DATA_TYPE_DSP>Char</DATA_TYPE_DSP>
<SOURCE>U</SOURCE>
<COLUMN_NAME>KEY_FIELD</COLUMN_NAME>
</UPLOAD_PARAMETERS_ROW>- SOURCE=U for upload column values, SOURCE=P for parameter values
- DATA_TYPE_DSP: Number for ID params from LOV
id, Char for strings - DISPLAY_SEQUENCE must be unique
3.7 Success SQL
select
'Success' status_,
t.key_field,
...
from
xxen_module_upload_NNN_u xu,
target_table t
where
xu.status_code_='S' and
xu.key_field=t.key_fieldRules:
- Upload view
xuMUST be in FROM clause (not auto-injected) - Column count must match main SQL exactly
- No ORDER BY
- After first import, find actual view name:
select view_name from all_views where view_name like 'XXEN_%_U'
3.8 Error SQL
Always use framework default — include the row with NO <SQL_TEXT> element:
<UPLOAD_SQLS_ROW>
<SQL_TYPE>Error SQL</SQL_TYPE>
</UPLOAD_SQLS_ROW>When sql_text is NULL, the framework auto-generates the Error SQL via xxen_upload.default_sql() and the "Default" checkbox is checked in the UI. This means the Error SQL auto-updates whenever main SQL columns change — no manual maintenance needed.
Rules:
- Never write explicit Error SQL text when the default pattern (
nvl(xu.status_code_,xxen_upload.status_error)<>xxen_upload.status_success) is sufficient - Only use custom Error SQL (with explicit
<SQL_TEXT>) if non-standard error detection logic is required - Never directly update DB tables to set flags — always go through XML import
3.9 UPLOAD_SQLS Order By
<UPLOAD_SQLS>
<UPLOAD_SQLS_ROW>
<SQL_TYPE>Error SQL</SQL_TYPE>
</UPLOAD_SQLS_ROW>
<UPLOAD_SQLS_ROW>
<SQL_TYPE>Order By</SQL_TYPE>
<SQL_TEXT>key_field, description</SQL_TEXT>
</UPLOAD_SQLS_ROW>
</UPLOAD_SQLS>Phase 4: Deploy
Deploy to the development instance (see blitz-environment.md), the package before the XML.
4.1 Deploy PL/SQL
Follow /deploy-plsql: compile sql/extensions/XXEN_MODULE_UPLOAD.pks then .pkb on the development instance (sqlplus apps/$APPS_PWD, set define off), and confirm the package is VALID with no show errors output.
4.2 Import XML
Lint the XML with ${CLAUDE_PLUGIN_ROOT}/scripts/lint_upload_xml.py, import it with the installed $XXEN_TOP/bin/import_xml $APPS_PWD <file> on the apps tier of the development instance (or xxen_api.import_xml(<clob>)), then check every LOV parses — the deploy procedure in blitz-environment.md. After it runs, verify the Function assignment (see Access Assignments).
4.3 Dependencies
Do NOT put an UPLOAD_DEPENDENCIES section in the XML. import_xml derives the column and parameter dependencies from the :$column$/:$flex$ references automatically.
4.4 Keep the authored XML
The file in content/r12/uploads/ remains the deliverable. Never overwrite it with an xxen_api.export_report export, which omits <ASSIGNMENTS> and other languages (report-development skill).
Quality Checklist
Before delivering, verify:
- [ ] Column order matches FAD section 4.2 visual field order (not generic grouping)
- [ ] Column aliases match FAD form Prompts (not internal DB column names)
- [ ] No
is null orin LOVs where form enforces parent-first entry (FAD section 4.2b) - [ ] Every FAD mandatory field is in UPLOAD_COLUMNS or handled in PL/SQL
- [ ] Every FAD LOV has a corresponding LOV_QUERY_DSP on the upload column
- [ ] Every cross-field rule from the FAD has an UPLOAD_DEPENDENCY
- [ ] Every PL/SQL parameter has a matching UPLOAD_PARAMETERS_ROW in XML
- [ ] LOV columns use DATA_TYPE
varchar2(even when LOV has numericid) - [ ] Validate function uses nested
message()— never returns on first error - [ ] Processing procedure sets
status_/message_in every code path - [ ] KFF resolution is dynamic (queries flex structure, no hardcoded delimiters/segment counts)
- [ ] No ORDER BY in main SQL or success SQL (use UPLOAD_SQLS Order By)
- [ ] Error SQL present as default (no SQL_TEXT — framework auto-generates)
- [ ] Upload Mode uses standard Blitz Upload Mode LOV (not custom)
- [ ] All filter parameters share
1=1anchor (no unnecessary 2=2, 3=3) - [ ] XML special chars escaped:
&'<> - [ ] GUID is a new UUID (not copied from another upload)
- [ ] Report name follows convention:
<MODULE> <Entity> Upload(e.g.AR Customer Upload), filecontent/r12/uploads/<Name_With_Underscores>.xml - [ ] VERSION_COMMENTS present after SQL_TEXT
- [ ] Templates per
upload-developmentskill I11: globalDefault(main columns, no flexfields) plus one per business scenario - [ ] No UPLOAD_DEPENDENCIES section in the XML (derived automatically on import)
- [ ] PL/SQL follows
coding-standardsskill (lowercase, no spaces around operators, traditional joins)
Handoff
The Builder produces:
- PL/SQL package (spec + body) in
sql/extensions/ - Upload XML in
content/r12/uploads/ - Both deployed to the development instance and ready for testing
These are consumed by upload-validator for review and E2E testing.
Access Assignments (MANDATORY — Function level only)
Every upload MUST ship with a top-level <ASSIGNMENTS> block (sibling of <REPORTS>, not nested) so responsibilities can run it. Uploads modify data, so the rule is strict (see upload-development skill I7 and report-development skill "Upload assignments (#6)"):
Functionlevel ONLY — never Form, Application, or Request Group (a Form can be granted query-only, and App/Request-Group would expose the data-modifying upload too broadly).- Update-enabled functions ONLY — resolve the object's data-entry form, list its
fnd_form_functions, and assign the entry/update function(s); EXCLUDE any whoseparameterscontainQUERY_ONLYor whose name/user-name says View/Inquiry/Read-only/Close. Examples: AP Invoice Upload→AP_APXINWKB, INV Item Upload→INV_INVIDITM, FND User Upload→FND_FNDSCAUS, WIP Job Upload→WIP_WIPDJMDF_DEFINE. - Function must be
type='FORM'— OAF/JSP functions are invalid (the Blitz zoom only fires from an Oracle Form). If the object is OAF-only in R12 (Suppliers, Bank Accounts, EAM Failure Codes/Sets), the upload gets no Function assignment. Verify:select type from fnd_form_functions where function_name='<X>'must returnFORM. LEVEL_VALUE= internalfunction_name; noAPPLICATION_SHORT_NAME; emptyFORM_PARAMETERS.- Verify after import:
select assignment_level,level_value from xxen_report_assignments_v where report_name='<Upload>'shows the Function row(s); and the function is update-capable (not QUERY_ONLY).