Skip to content

Commit 6acdf7f

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(oracle-fusion-procurement): align runtime contracts and selector coverage
1 parent a88b07a commit 6acdf7f

16 files changed

Lines changed: 981 additions & 565 deletions

apps/sim/blocks/blocks/oracle_fusion_procurement.ts

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ const NEGOTIATION_KEY_OPERATIONS = [
9999
'oracle_fusion_procurement_update_supplier_negotiation',
100100
'oracle_fusion_procurement_validate_or_publish_supplier_negotiation',
101101
]
102-
const NEGOTIATION_ID_OPERATIONS = [
103-
'oracle_fusion_procurement_list_supplier_negotiation_responses',
104-
]
102+
const NEGOTIATION_ID_OPERATIONS = ['oracle_fusion_procurement_list_supplier_negotiation_responses']
105103
const RESPONSE_KEY_OPERATIONS = ['oracle_fusion_procurement_get_supplier_negotiation_response']
106104
const ASSIGNMENT_ID_OPERATIONS = ['oracle_fusion_procurement_get_procurement_agent']
107105
const SUPPLIER_NAME_OPERATIONS = ['oracle_fusion_procurement_create_supplier']
@@ -113,9 +111,7 @@ const PROCUREMENT_BUID_OPERATIONS = [
113111
'oracle_fusion_procurement_create_supplier_site',
114112
]
115113
const PREPARER_ID_OPERATIONS = ['oracle_fusion_procurement_create_purchase_requisition']
116-
const REQUISITIONING_BUID_OPERATIONS = [
117-
'oracle_fusion_procurement_create_purchase_requisition',
118-
]
114+
const REQUISITIONING_BUID_OPERATIONS = ['oracle_fusion_procurement_create_purchase_requisition']
119115
const BUYER_ID_OPERATIONS = [
120116
'oracle_fusion_procurement_create_draft_purchase_order',
121117
'oracle_fusion_procurement_create_supplier_negotiation',
@@ -132,7 +128,9 @@ const REMOVE_HOLD_REASON_OPERATIONS = ['oracle_fusion_procurement_remove_purchas
132128
const REQUEST_FUNDS_OVERRIDE_FLAG_OPERATIONS = [
133129
'oracle_fusion_procurement_submit_purchase_requisition',
134130
]
135-
const VALIDATE_BEFORE_SUBMIT_FLAG_OPERATIONS = ['oracle_fusion_procurement_submit_draft_purchase_order']
131+
const VALIDATE_BEFORE_SUBMIT_FLAG_OPERATIONS = [
132+
'oracle_fusion_procurement_submit_draft_purchase_order',
133+
]
136134
const IGNORE_WARNINGS_OPERATIONS = [
137135
'oracle_fusion_procurement_validate_or_publish_supplier_negotiation',
138136
]
@@ -689,7 +687,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
689687
type: 'short-input',
690688
canonicalParamId: 'requisitionKey',
691689
mode: 'advanced',
692-
placeholder: 'Opaque purchase-requisition key from the key output; do not substitute RequisitionHeaderId',
690+
placeholder:
691+
'Opaque purchase-requisition key from the key output; do not substitute RequisitionHeaderId',
693692
condition: { field: 'operation', value: REQUISITION_KEY_OPERATIONS },
694693
required: true,
695694
},
@@ -712,7 +711,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
712711
type: 'short-input',
713712
canonicalParamId: 'draftPurchaseOrderKey',
714713
mode: 'advanced',
715-
placeholder: 'Opaque draft purchase-order key from the key output; do not substitute POHeaderId',
714+
placeholder:
715+
'Opaque draft purchase-order key from the key output; do not substitute POHeaderId',
716716
condition: { field: 'operation', value: DRAFT_PURCHASE_ORDER_KEY_OPERATIONS },
717717
required: true,
718718
},
@@ -735,7 +735,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
735735
type: 'short-input',
736736
canonicalParamId: 'purchaseOrderKey',
737737
mode: 'advanced',
738-
placeholder: 'Opaque approved purchase-order key from the key output; do not substitute POHeaderId',
738+
placeholder:
739+
'Opaque approved purchase-order key from the key output; do not substitute POHeaderId',
739740
condition: { field: 'operation', value: PURCHASE_ORDER_KEY_OPERATIONS },
740741
required: true,
741742
},
@@ -758,7 +759,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
758759
type: 'short-input',
759760
canonicalParamId: 'poHeaderId',
760761
mode: 'advanced',
761-
placeholder: 'Numeric POHeaderId from a purchase order, as a decimal string (not its opaque key)',
762+
placeholder:
763+
'Numeric POHeaderId from a purchase order, as a decimal string (not its opaque key)',
762764
condition: { field: 'operation', value: PO_HEADER_ID_OPERATIONS },
763765
required: true,
764766
},
@@ -781,7 +783,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
781783
type: 'short-input',
782784
canonicalParamId: 'receiptKey',
783785
mode: 'advanced',
784-
placeholder: 'Opaque receipt key from List Purchase Order Receipts for this POHeaderId; not ReceiptId',
786+
placeholder:
787+
'Opaque receipt key from List Purchase Order Receipts for this POHeaderId; not ReceiptId',
785788
condition: { field: 'operation', value: RECEIPT_KEY_OPERATIONS },
786789
required: true,
787790
},
@@ -850,7 +853,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
850853
type: 'short-input',
851854
canonicalParamId: 'responseKey',
852855
mode: 'advanced',
853-
placeholder: 'Opaque supplier-negotiation-response key from the key output; not ResponseNumber',
856+
placeholder:
857+
'Opaque supplier-negotiation-response key from the key output; not ResponseNumber',
854858
condition: { field: 'operation', value: RESPONSE_KEY_OPERATIONS },
855859
required: true,
856860
},
@@ -1197,18 +1201,23 @@ export const OracleFusionProcurementBlock: BlockConfig = {
11971201
if (!active) {
11981202
result[field] = undefined
11991203
} else if (field === 'body') {
1200-
// Keep JSON strings intact; the server rejects already-imprecise numeric IDs.
1204+
/** Keep JSON strings intact; the server rejects already-imprecise numeric IDs. */
12011205
result[field] = params[field] === '' ? undefined : params[field]
12021206
} else if (field === 'limit' || field === 'offset') {
12031207
result[field] = parseOptionalNumberInput(params[field], field, {
12041208
integer: true,
12051209
min: field === 'limit' ? 1 : 0,
12061210
max: field === 'limit' ? 100 : 1_000_000,
12071211
})
1208-
} else if ([
1209-
'totalResults', 'requestFundsOverrideFlag', 'validateBeforeSubmitFlag', 'ignoreWarnings',
1210-
].includes(field)) {
1211-
result[field] = parseOptionalBooleanInput(params[field], field)
1212+
} else if (
1213+
[
1214+
'totalResults',
1215+
'requestFundsOverrideFlag',
1216+
'validateBeforeSubmitFlag',
1217+
'ignoreWarnings',
1218+
].includes(field)
1219+
) {
1220+
result[field] = parseOptionalBooleanInput(params[field])
12121221
} else {
12131222
result[field] = optionalString(params[field], field)
12141223
}
@@ -1230,23 +1239,28 @@ export const OracleFusionProcurementBlock: BlockConfig = {
12301239
},
12311240
requisitionKey: {
12321241
type: 'string',
1233-
description: 'Opaque purchase-requisition key from the key output; do not substitute RequisitionHeaderId',
1242+
description:
1243+
'Opaque purchase-requisition key from the key output; do not substitute RequisitionHeaderId',
12341244
},
12351245
draftPurchaseOrderKey: {
12361246
type: 'string',
1237-
description: 'Opaque draft purchase-order key from the key output; do not substitute POHeaderId',
1247+
description:
1248+
'Opaque draft purchase-order key from the key output; do not substitute POHeaderId',
12381249
},
12391250
purchaseOrderKey: {
12401251
type: 'string',
1241-
description: 'Opaque approved purchase-order key from the key output; do not substitute POHeaderId',
1252+
description:
1253+
'Opaque approved purchase-order key from the key output; do not substitute POHeaderId',
12421254
},
12431255
poHeaderId: {
12441256
type: 'string',
1245-
description: 'Numeric POHeaderId from a purchase order, as a decimal string (not its opaque key)',
1257+
description:
1258+
'Numeric POHeaderId from a purchase order, as a decimal string (not its opaque key)',
12461259
},
12471260
receiptKey: {
12481261
type: 'string',
1249-
description: 'Opaque receipt key from List Purchase Order Receipts for this POHeaderId; not ReceiptId',
1262+
description:
1263+
'Opaque receipt key from List Purchase Order Receipts for this POHeaderId; not ReceiptId',
12501264
},
12511265
negotiationKey: {
12521266
type: 'string',
@@ -1258,7 +1272,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
12581272
},
12591273
responseKey: {
12601274
type: 'string',
1261-
description: 'Opaque supplier-negotiation-response key from the key output; not ResponseNumber',
1275+
description:
1276+
'Opaque supplier-negotiation-response key from the key output; not ResponseNumber',
12621277
},
12631278
assignmentId: {
12641279
type: 'string',
@@ -1302,7 +1317,8 @@ export const OracleFusionProcurementBlock: BlockConfig = {
13021317
},
13031318
actionIntent: {
13041319
type: 'string',
1305-
description: 'Explicit action intent: Validate or Publish. Validation does not publish the negotiation',
1320+
description:
1321+
'Explicit action intent: Validate or Publish. Validation does not publish the negotiation',
13061322
},
13071323
holdReason: {
13081324
type: 'string',
@@ -1318,13 +1334,17 @@ export const OracleFusionProcurementBlock: BlockConfig = {
13181334
},
13191335
validateBeforeSubmitFlag: {
13201336
type: 'boolean',
1321-
description: 'Validate the draft purchase order before submission; Oracle defaults to false when omitted',
1337+
description:
1338+
'Validate the draft purchase order before submission; Oracle defaults to false when omitted',
13221339
},
13231340
ignoreWarnings: {
13241341
type: 'boolean',
13251342
description: 'Explicitly ignore negotiation publishing warnings (true maps to Y, false to N)',
13261343
},
1327-
body: { type: 'json', description: 'Documented fields for the selected create or header update' },
1344+
body: {
1345+
type: 'json',
1346+
description: 'Documented fields for the selected create or header update',
1347+
},
13281348
q: { type: 'string', description: 'Oracle resource filter expression' },
13291349
orderBy: { type: 'string', description: 'Comma-separated ordering attributes' },
13301350
limit: { type: 'number', description: 'Page size from 1 through 100' },
@@ -1439,9 +1459,7 @@ export const OracleFusionProcurementBlock: BlockConfig = {
14391459
'Agent assignment (AssignmentId, person AgentId, Agent, ProcurementBUId, Status, procurement permissions)',
14401460
condition: {
14411461
field: 'operation',
1442-
value: [
1443-
'oracle_fusion_procurement_get_procurement_agent',
1444-
],
1462+
value: ['oracle_fusion_procurement_get_procurement_agent'],
14451463
},
14461464
},
14471465
purchaseOrder: {
@@ -1450,9 +1468,7 @@ export const OracleFusionProcurementBlock: BlockConfig = {
14501468
'Purchase order (opaque key, numeric POHeaderId, OrderNumber, Status, SupplierId, SupplierSiteId, Ordered, Total, CurrencyCode)',
14511469
condition: {
14521470
field: 'operation',
1453-
value: [
1454-
'oracle_fusion_procurement_get_purchase_order',
1455-
],
1471+
value: ['oracle_fusion_procurement_get_purchase_order'],
14561472
},
14571473
},
14581474
lifecycleDetails: {
@@ -1461,9 +1477,7 @@ export const OracleFusionProcurementBlock: BlockConfig = {
14611477
'Purchase-order lifecycle summary (POHeaderId, OrderNumber, CurrencyCode, ordered, delivered, receiving, transit, and payment amounts)',
14621478
condition: {
14631479
field: 'operation',
1464-
value: [
1465-
'oracle_fusion_procurement_get_purchase_order_lifecycle_details',
1466-
],
1480+
value: ['oracle_fusion_procurement_get_purchase_order_lifecycle_details'],
14671481
},
14681482
},
14691483
purchaseOrderReceipt: {
@@ -1472,9 +1486,7 @@ export const OracleFusionProcurementBlock: BlockConfig = {
14721486
'Receipt visibility (opaque key, numeric ReceiptId and POHeaderId, Receipt, ReceiptDate, received/delivered/returned quantities)',
14731487
condition: {
14741488
field: 'operation',
1475-
value: [
1476-
'oracle_fusion_procurement_get_purchase_order_receipt',
1477-
],
1489+
value: ['oracle_fusion_procurement_get_purchase_order_receipt'],
14781490
},
14791491
},
14801492
supplierNegotiationResponse: {
@@ -1483,9 +1495,7 @@ export const OracleFusionProcurementBlock: BlockConfig = {
14831495
'Supplier-visible response (opaque key, numeric ResponseNumber and AuctionHeaderId, Supplier, ResponseStatus, string ResponseAmount, ResponseCurrencyCode)',
14841496
condition: {
14851497
field: 'operation',
1486-
value: [
1487-
'oracle_fusion_procurement_get_supplier_negotiation_response',
1488-
],
1498+
value: ['oracle_fusion_procurement_get_supplier_negotiation_response'],
14891499
},
14901500
},
14911501
result: {
@@ -1510,8 +1520,10 @@ export const OracleFusionProcurementBlock: BlockConfig = {
15101520
},
15111521
}
15121522

1513-
// Workflow precedents: Oracle 26C supplier/requisition/draft-order create examples,
1514-
// purchasing hold and lifecycle APIs, and ValidateAndPublishNegotiation examples.
1523+
/**
1524+
* Workflow precedents: Oracle 26C supplier/requisition/draft-order create examples,
1525+
* purchasing hold and lifecycle APIs, and ValidateAndPublishNegotiation examples.
1526+
*/
15151527
export const OracleFusionProcurementBlockMeta = {
15161528
tags: ['automation', 'data-analytics'],
15171529
url: 'https://www.oracle.com/erp/procurement/',

apps/sim/lib/integrations/credential-display.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ const EXPECTED_COVERAGE: Record<string, string[]> = {
6868
// NetSuite remains an API-key catalog integration, like Snowflake, while its
6969
// block uses the shared reusable-credential selector.
7070
'netsuite-service-account': [],
71+
/** Fusion Procurement exposes reusable credentials through its API-key-classified block. */
72+
'oracle-fusion-service-account': [],
7173
'pipedrive-service-account': ['pipedrive'],
7274
'salesforce-service-account': ['salesforce'],
7375
'shopify-service-account': ['shopify'],

apps/sim/lib/internal/oracle-fusion-procurement/execute-tool.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export const executeOracleFusionProcurementTool: InternalToolOperationHandler =
2626
account.providerId !== ORACLE_FUSION_SERVICE_ACCOUNT_PROVIDER_ID
2727
) {
2828
return Response.json(
29-
{ success: false, output: {}, error: 'Oracle Fusion service-account credential is unavailable' },
29+
{
30+
success: false,
31+
output: {},
32+
error: 'Oracle Fusion service-account credential is unavailable',
33+
},
3034
{ status: 403 }
3135
)
3236
}

apps/sim/lib/internal/oracle-fusion-procurement/operations.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import {
1212
validateOracleFusionSelfLink,
1313
} from '@/lib/internal/oracle-fusion/protocol'
1414
import {
15-
parseProcurementBody,
1615
PROCUREMENT_MAX_OFFSET,
17-
procurementIdentifierSchema,
1816
ProcurementInputError,
19-
procurementKeySchema,
20-
procurementPagingSchema,
2117
type ProcurementResource,
2218
ProcurementResponseError,
23-
procurementResourceSchemas,
2419
type ProcurementWriteOperation,
20+
parseProcurementBody,
21+
procurementIdentifierSchema,
22+
procurementKeySchema,
23+
procurementPagingSchema,
24+
procurementResourceSchemas,
2525
procurementWriteSchemas,
2626
} from '@/lib/internal/oracle-fusion-procurement/schema'
2727
import type { ToolResponse } from '@/tools/types'
@@ -448,7 +448,8 @@ function detailAddress(
448448
params: Record<string, unknown>
449449
): OracleFusionResourceAddress {
450450
const definition = resourceDefinition(resource)
451-
if (!definition.keyParam) throw new ProcurementInputError('This resource has no direct detail lookup')
451+
if (!definition.keyParam)
452+
throw new ProcurementInputError('This resource has no direct detail lookup')
452453
const key = definition.opaque
453454
? procurementKeySchema.parse(params[definition.keyParam])
454455
: procurementIdentifierSchema.parse(params[definition.keyParam])
@@ -470,7 +471,7 @@ function projectResource(
470471
collection: OracleFusionResourceAddress
471472
): Record<string, unknown> {
472473
try {
473-
// The raw self link is authoritative, including REST framework v9 @context.links.
474+
/** The raw self link is authoritative, including REST framework v9 @context.links. */
474475
const key = resourceDefinition(resource).opaque
475476
? extractOracleFusionOpaqueKey(raw, credential.instanceUrl, collection)
476477
: undefined
@@ -682,11 +683,13 @@ const actionDefinitions = {
682683

683684
const stringActionSchema = z.object({ result: z.string() })
684685
const withdrawActionSchema = z.object({
685-
result: z.record(z.array(z.record(z.string()))),
686+
result: z.record(z.string(), z.array(z.record(z.string(), z.string()))),
686687
})
687-
const validationActionSchema = z.object({ result: z.array(z.record(z.string())) })
688-
// Oracle's 26C examples explicitly differ from the generic dictionary schema:
689-
// ErrorsListId may be null; Negotiation may be a JSON integer or a string.
688+
const validationActionSchema = z.object({ result: z.array(z.record(z.string(), z.string())) })
689+
/**
690+
* Oracle's 26C examples explicitly differ from the generic dictionary schema:
691+
* ErrorsListId may be null; Negotiation may be a JSON integer or a string.
692+
*/
690693
const negotiationActionSchema = z.object({
691694
result: z.object({
692695
Status: z.string(),

0 commit comments

Comments
 (0)