Add DFF Handling to an Upload Copy link
Plugin: Blitz Report Development (
add-dff-to-upload)Works with: Claude Code
Add descriptive flexfield (DFF) handling to a Blitz Upload - context and attribute columns with context-dependent lists of values, segment prompts as column headers, Success SQL, and the PL/SQL that merges untouched segments, validates the DFF and converts display values to ids before the API call.
When to use it
- Use when asked to add, validate or fix DFF / attribute columns of an upload, e.g.
- "add validation for the attribute5 flexfield segment of my upload".
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:add-dff-to-upload - 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.
Adds one descriptive flexfield to an existing upload the standard Blitz Report way. Users see and enter display values in Excel; the download converts stored values to display values, and the upload procedure validates the whole DFF with the Oracle flexfield engine and converts it back to stored ids. A generator script writes every artefact from the flexfield's facts; the steps below say where each one goes. The upload-development rules, section E, are the checklist behind it.
Inputs
- The upload: its XML (exported from the Blitz Report setup form, Tools > Export) and the PL/SQL package of its upload procedure.
- The flexfield, by name or by the table that carries it, and a short column prefix for the entity (
subinv,loc,line). - A development instance to deploy and test on, as described in
blitz-environment.md.
Step 0 - Check the Blitz Report build
Run the last query of references/flexfield_facts.sql. It must return P_COLUMN_PREFIX. Otherwise the build predates 18 September 2026, lacks xxen_upload.parameter_exists or p_column_prefix, and validates DFFs with contexts incorrectly. Stop and ask for an upgrade.
Step 1 - Collect the flexfield facts
Run queries 1 to 3 of references/flexfield_facts.sql:
| Result | Generator argument / use |
|---|---|
application_id, application_short_name, descriptive_flexfield_name | --app-id, --app-short-name, --flexfield. Several DFFs can sit on one table: take the one the Oracle form uses. |
context_column_name | --context-column (not always ATTRIBUTE_CATEGORY; GL_CODE_COMBINATIONS uses CONTEXT) |
| attribute column family and count (Query 3) | --family (default ATTRIBUTE; HR uses PEI_INFORMATION, ORG_INFORMATION, ...), --count = number of attribute columns of the table, whether used or not. Query 3 returns one row per schema with a table of that name: take the product schema's. |
max_length (Query 3) | over 150: the procedure's record holds 150 bytes per attribute, values above that raise ORA-06502; tell the user |
display_flag N (Query 2) | --hidden with those columns |
context_user_override_flag N with a default_context_field_name | the context follows a reference field: see Special cases |
Step 2 - Generate the artefacts
python ${CLAUDE_PLUGIN_ROOT}/skills/add-dff-to-upload/scripts/gen_dff.py \
--app-id 401 --app-short-name INV --flexfield MTL_SECONDARY_INVENTORIES \
--prefix subinv --count 15 --alias msi \
--guid <upload GUID> --rule-name "XX Subinventory DFF Attributes" \
--first-column <COLUMN_NUMBER of UPLOAD_ROW> --first-sequence <last upload parameter's DISPLAY_SEQUENCE + 1> \
--template-keys ORGANIZATION_CODE,SUBINVENTORY \
--package <upload package> --procedure <upload procedure> \
--key-params p_organization_code,p_subinventory \
--cursor-from "mtl_parameters mp,mtl_secondary_inventories msi" \
--cursor-where "mp.organization_code=p_organization_code and mp.organization_id=msi.organization_id and msi.secondary_inventory_name=p_subinventory" \
--out <folder>--first-column: the DFF columns go beforeUPLOAD_ROW, or before the column you name instead.--first-sequence: the procedure's new parameters follow its existing ones.--template-keys: the upload column names that identify the record, as in the upload XML.--aliasmust be the alias of the flexfield's own table (Query 1application_table_name) in the main SQL, not of a view: its rowid resolves dependent value sets.--param-prefix/--context-paramname the procedure parameters (defaultp_attribute,p_attribute_category); give each DFF its own when an upload has several.--dry-runwrites a test build that reports the values the API would receive instead of calling it. Run the tests with it first.- Aliases must stay within 30 bytes for databases before 12.2; the script warns.
Output: main_sql_columns.sql, success_sql_columns.sql, upload_columns.xml, upload_parameters.xml, translation_rule.xml, template.xml, <package>.pks, <package>.pkb.
Step 3 - Upload definition
- Main SQL: insert
main_sql_columns.sqlafter the entity's columns, beforeupload_row. - Upload columns: insert the
upload_columns.xmlrows and renumber theCOLUMN_NUMBERof the columns after them, which must follow the select-list order.- Context column:
LOV customoverfnd_descr_flex_contexts_vl, returning context names. Keep it when the DFF has only global segments: its list stays empty until a context is defined, as in the shipped uploads. - Attribute columns:
LOV customoverxxen_util.dff_attribute_lov, with all siblings passed as:$column$, thevalue~descriptionsplit, Validate From ListY, and aselectcomment listing the segment per context in the shipped format(Context) Attribute1: (Global) <prompt>, (<context>) <prompt>. - Keep the generated LOV text as it is (named arguments, lower case, literals at the start): the framework reads those literals from the first 1,000 characters to give Excel per-context date and number checks.
- Context column:
- Upload parameters: insert the
upload_parameters.xmlrows in the procedure's parameter order and renumberDISPLAY_SEQUENCEafter them. - Success SQL: add
success_sql_columns.sqlat the same position as in the main SQL. The tables sit in an inline viewxthat selectsrowid row_id, because pre-12c databases allow an outer-joined table to reference only one other table:from <data view> xu, ( select mp.organization_code, msi.rowid row_id, msi.* from mtl_parameters mp, mtl_secondary_inventories msi where mp.organization_id=msi.organization_id ) x where xu.organization_code=x.organization_code(+) and xu.subinventory=x.secondary_inventory_name(+) and xu.status_code_=xxen_upload.status_successThe data view name is shown on the setup form's Upload Results tab.
- Templates:
- Keep the DFF columns out of the default template. An upload without one shows every column: add a default template with the existing columns.
- Insert the
template.xmlrow into the upload XML's<TEMPLATES>. It holds the key columns and all DFF columns, as the shipped uploads do, Upload Mode Update, and Site sharing.
- Translation rule: import
translation_rule.xmlafter the upload, as its own file. A rule whose upload does not exist yet is skipped, and rules import before reports in a combined file. It renames the attribute headers to the segment prompts. Developer access can also maintain it under Tools > Dynamic Column Translations.
Import the XML through the setup form (Tools > Import) or bin/import_xml. Editing an upload needs System access.
Step 4 - Upload procedure
Add the generated parameters (default null) to the procedure in spec and body. Then merge the generated declarations, helper functions and DFF block into it:
- Helper functions:
modified(p):parameter_existsplusvalue_modified. A hand-built csv/xlsx counts every parameter as modified, also those its template omits. Test the entity's other columns with it too:xxen_upload.new_valuealone clears a column the csv template omits.dff_modified: true on any DFF change.segment_enabled: whether an enabled segment uses a column.merged: builds the value of each cell:- a changed cell takes the uploaded value;
- an untouched cell takes the stored value in display form;
- a column without an enabled segment keeps its stored value unconverted;
- after a context change, the old context's untouched segments are cleared, as in the Oracle form.
- Read the stored row with its
rowidintol_stored, and initialisel_dff:=l_stored. The generated code treats a key without a stored row as a create; in an update-only upload, keep the procedure's own not-found error ahead of the DFF block. - Merge and validate only on create or when
dff_modified: merge, thenvalidate_dff_attributesfrom'V'to'I'. On error, setmessage_and raise. - Detect a change by comparing the converted ids with
l_stored, and report "No change." when nothing changed. - Rename consistently: every parameter name literal (
modified('p_attribute5'), the first argument of eachmergedcall, the context parameter) must match the declared parameters, or edits are dropped silently. - API call: replace
xx_my_api.create_or_updatewith the entity's API and check its return status. A cleared cell is null in the record. How to pass it:
| API family | Null means | Pass |
|---|---|---|
| Table handlers, APIs writing every column | clear | the record as is |
HR (hr_*_api) | clear; omitted = hr_api.g_varchar2 = keep | the record as is, never hr_api.g_varchar2 for a cleared cell |
TCA (hz_*_v2pub) | keep | on update fnd_api.g_miss_char for a null attribute, on create null |
| Interface tables | per import program | its clear marker (WIP Mass Load fnd_api.g_miss_*, item revisions '!') |
Never convert the stored record with validate_dff_attributes('I','V'): the conversion is all or nothing, so one invalid stored segment blocks every row.
Special cases
- Reference-field context (override
N,default_context_field_nameset): give the context column aDEFAULT_VALUEthat derives the context name from the reference column, as the FND Lookup Upload does forFND_COMMON_LOOKUPS. - Context set by the Oracle form itself, with neither override nor reference field (
MTL_EAM_ASSET_ATTR_VALUES): in'V'mode only global segments are validated, and context segments come back as typed. Convert each segment withxxen_util.dff_attribute_value_to_idand validate in'I'mode. That function converts only segments with a list; convert dates and numbers withfnd_date/fnd_numberyourself. - Non-displayed segments (
--hidden):'V'mode returns them empty, so the generated code copies their stored values back. - Value sets joining
fnd_sessions(common in HR): callhr_integration_utils.add_or_update_session(<effective date>)at the start of the procedure. - Date segments:
- DFF dates travel as text in the Blitz Report XLSX Date Format profile, or else the ICX date mask, but validation reads them in the ICX mask. Keep the two equal.
- Set Blitz Report XLSX Use Long Date Format = Yes when a date can fall outside 1950-2049; otherwise even untouched dates change century.
- Several DFFs in one upload: one prefix, parameter set and translation rule per DFF. Generate each with its own
--prefix,--param-prefixand--context-param. Merge the helper functions under per-DFF names, or parametrise them with flexfield, record, rowid and context flag.
Step 5 - Deploy and test
- Compile the package with
--dry-runcode, import the upload XML, then the rule. Check that the attribute headers show the prompts. - Download with the Descriptive Flexfield template, edit the rows, upload, and check the result workbook. Excel accepts only list values and valid dates, so write the error rows into the file outside Excel (cells as shared strings, Modified Columns listing the changed column positions). A csv maps by position: Action, Status, Message, then the template's columns in template order.
| Scenario | Expected |
|---|---|
| Change one segment | Success; the other segments keep their stored values |
| Clear a segment | Success; segment cleared (Excel also empties the row's other filled attribute cells: re-enter those that should stay) |
| No DFF change | "No change." |
| Value not in the list / invalid context | <Column> is invalid from the list check, naming the column (Subinv Attribute5), not the segment prompt; the procedure is not called |
| Invalid date in a free-text date segment | message from validate_dff_attributes |
| Context change without a required segment of the new context | "The following required field does not have a value: ..." |
| Context change with values / context emptied | Success; old context segments cleared |
| Upload with the Default template, and a csv without DFF columns uploaded with it | "No change." |
- Replace the dry-run block with the API call, then repeat the tests and check the table.
Troubleshooting
| Symptom | Cause |
|---|---|
List shows value~description, downloaded values rejected | LOV returns column_value without the split |
| Headers still generic | rule not enabled or not assigned to the upload, prefix differs from the column names, or no segment uses the column |
| Edits report "No change." | a parameter name literal does not match the declared parameter |
| Untouched segments rejected | the stored value is no longer valid (disabled, expired, newly required): correct it in the same row |
| Stored DFF cleared by an upload | parameters passed instead of the merged record, or a csv/xlsx uploaded without a template |
Every download fails with ORA-00923 | a plain-text column comment starts with the word "select" |
| Works on 19c, fails on 11.2/12.1 | identifier over 30 bytes, or an outer join to two tables in the Success SQL (ORA-01417) |
Reference
xxen_util.display_flexfield_context(app_id, flexfield, context_code): returns the context name.xxen_util.display_flexfield_value(app_id, flexfield, context_code, column_name, rowid, value): returns the display value.xxen_util.dff_attribute_lov(p_application_id, p_descriptive_flexfield_name, p_context_name, p_column_name, p_lov_delimter default '~', p_attribute1..30): pipelined, onevalue~descriptionrow per value. Call it with named notation.xxen_fnd_upload.validate_dff_attributes(p_appl_sn, p_flexfield_name, p_values_or_ids_in, p_values_or_ids_out, p_column_prefix, px_attribute_rec, x_error_message): on error the record is unchanged and the message is set.xxen_upload.value_modified(p)/xxen_upload.parameter_exists(p): parameter names in lower case, as declared.- Shipped uploads to compare: INV Manufacturer Upload (one DFF), AR Customer Upload (five DFFs, one rule with a
caseon the flexfield name), FND Lookup Upload (reference-field context).