#
tokens: 48207/50000 10/825 files (page 22/43)
lines: off (toggle) GitHub
raw markdown copy
This is page 22 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .DS_Store
├── .github
│   ├── dependabot.yml
│   ├── instructions
│   │   ├── mcp-node-tests.instructions.md
│   │   └── mcp-yml-tests.instructions.md
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bug_fix.md
│   │   ├── documentation.md
│   │   └── new_tool.md
│   ├── pull_request_template.md
│   └── workflows
│       ├── ci.yml
│       ├── deploy-pages.yml
│       ├── publish.yml
│       └── update-docs.yml
├── .gitignore
├── .husky
│   └── pre-commit
├── aegis.config.docs-only.json
├── aegis.config.json
├── aegis.config.with-dw.json
├── AGENTS.md
├── ai-instructions
│   ├── claude-desktop
│   │   └── claude_custom_instructions.md
│   ├── cursor
│   │   └── .cursor
│   │       └── rules
│   │           ├── debugging-workflows.mdc
│   │           ├── hooks-development.mdc
│   │           ├── isml-templates.mdc
│   │           ├── job-framework.mdc
│   │           ├── performance-optimization.mdc
│   │           ├── scapi-endpoints.mdc
│   │           ├── security-patterns.mdc
│   │           ├── sfcc-development.mdc
│   │           ├── sfra-controllers.mdc
│   │           ├── sfra-models.mdc
│   │           ├── system-objects.mdc
│   │           └── testing-patterns.mdc
│   └── github-copilot
│       └── copilot-instructions.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── docs
│   ├── best-practices
│   │   ├── cartridge_creation.md
│   │   ├── isml_templates.md
│   │   ├── job_framework.md
│   │   ├── localserviceregistry.md
│   │   ├── ocapi_hooks.md
│   │   ├── performance.md
│   │   ├── scapi_custom_endpoint.md
│   │   ├── scapi_hooks.md
│   │   ├── security.md
│   │   ├── sfra_client_side_js.md
│   │   ├── sfra_controllers.md
│   │   ├── sfra_models.md
│   │   └── sfra_scss.md
│   ├── dw_campaign
│   │   ├── ABTest.md
│   │   ├── ABTestMgr.md
│   │   ├── ABTestSegment.md
│   │   ├── AmountDiscount.md
│   │   ├── ApproachingDiscount.md
│   │   ├── BonusChoiceDiscount.md
│   │   ├── BonusDiscount.md
│   │   ├── Campaign.md
│   │   ├── CampaignMgr.md
│   │   ├── CampaignStatusCodes.md
│   │   ├── Coupon.md
│   │   ├── CouponMgr.md
│   │   ├── CouponRedemption.md
│   │   ├── CouponStatusCodes.md
│   │   ├── Discount.md
│   │   ├── DiscountPlan.md
│   │   ├── FixedPriceDiscount.md
│   │   ├── FixedPriceShippingDiscount.md
│   │   ├── FreeDiscount.md
│   │   ├── FreeShippingDiscount.md
│   │   ├── PercentageDiscount.md
│   │   ├── PercentageOptionDiscount.md
│   │   ├── PriceBookPriceDiscount.md
│   │   ├── Promotion.md
│   │   ├── PromotionMgr.md
│   │   ├── PromotionPlan.md
│   │   ├── SlotContent.md
│   │   ├── SourceCodeGroup.md
│   │   ├── SourceCodeInfo.md
│   │   ├── SourceCodeStatusCodes.md
│   │   └── TotalFixedPriceDiscount.md
│   ├── dw_catalog
│   │   ├── Catalog.md
│   │   ├── CatalogMgr.md
│   │   ├── Category.md
│   │   ├── CategoryAssignment.md
│   │   ├── CategoryLink.md
│   │   ├── PriceBook.md
│   │   ├── PriceBookMgr.md
│   │   ├── Product.md
│   │   ├── ProductActiveData.md
│   │   ├── ProductAttributeModel.md
│   │   ├── ProductAvailabilityLevels.md
│   │   ├── ProductAvailabilityModel.md
│   │   ├── ProductInventoryList.md
│   │   ├── ProductInventoryMgr.md
│   │   ├── ProductInventoryRecord.md
│   │   ├── ProductLink.md
│   │   ├── ProductMgr.md
│   │   ├── ProductOption.md
│   │   ├── ProductOptionModel.md
│   │   ├── ProductOptionValue.md
│   │   ├── ProductPriceInfo.md
│   │   ├── ProductPriceModel.md
│   │   ├── ProductPriceTable.md
│   │   ├── ProductSearchHit.md
│   │   ├── ProductSearchModel.md
│   │   ├── ProductSearchRefinementDefinition.md
│   │   ├── ProductSearchRefinements.md
│   │   ├── ProductSearchRefinementValue.md
│   │   ├── ProductVariationAttribute.md
│   │   ├── ProductVariationAttributeValue.md
│   │   ├── ProductVariationModel.md
│   │   ├── Recommendation.md
│   │   ├── SearchModel.md
│   │   ├── SearchRefinementDefinition.md
│   │   ├── SearchRefinements.md
│   │   ├── SearchRefinementValue.md
│   │   ├── SortingOption.md
│   │   ├── SortingRule.md
│   │   ├── Store.md
│   │   ├── StoreGroup.md
│   │   ├── StoreInventoryFilter.md
│   │   ├── StoreInventoryFilterValue.md
│   │   ├── StoreMgr.md
│   │   ├── Variant.md
│   │   └── VariationGroup.md
│   ├── dw_content
│   │   ├── Content.md
│   │   ├── ContentMgr.md
│   │   ├── ContentSearchModel.md
│   │   ├── ContentSearchRefinementDefinition.md
│   │   ├── ContentSearchRefinements.md
│   │   ├── ContentSearchRefinementValue.md
│   │   ├── Folder.md
│   │   ├── Library.md
│   │   ├── MarkupText.md
│   │   └── MediaFile.md
│   ├── dw_crypto
│   │   ├── CertificateRef.md
│   │   ├── CertificateUtils.md
│   │   ├── Cipher.md
│   │   ├── Encoding.md
│   │   ├── JWE.md
│   │   ├── JWEHeader.md
│   │   ├── JWS.md
│   │   ├── JWSHeader.md
│   │   ├── KeyRef.md
│   │   ├── Mac.md
│   │   ├── MessageDigest.md
│   │   ├── SecureRandom.md
│   │   ├── Signature.md
│   │   ├── WeakCipher.md
│   │   ├── WeakMac.md
│   │   ├── WeakMessageDigest.md
│   │   ├── WeakSignature.md
│   │   └── X509Certificate.md
│   ├── dw_customer
│   │   ├── AddressBook.md
│   │   ├── AgentUserMgr.md
│   │   ├── AgentUserStatusCodes.md
│   │   ├── AuthenticationStatus.md
│   │   ├── Credentials.md
│   │   ├── Customer.md
│   │   ├── CustomerActiveData.md
│   │   ├── CustomerAddress.md
│   │   ├── CustomerCDPData.md
│   │   ├── CustomerContextMgr.md
│   │   ├── CustomerGroup.md
│   │   ├── CustomerList.md
│   │   ├── CustomerMgr.md
│   │   ├── CustomerPasswordConstraints.md
│   │   ├── CustomerPaymentInstrument.md
│   │   ├── CustomerStatusCodes.md
│   │   ├── EncryptedObject.md
│   │   ├── ExternalProfile.md
│   │   ├── OrderHistory.md
│   │   ├── ProductList.md
│   │   ├── ProductListItem.md
│   │   ├── ProductListItemPurchase.md
│   │   ├── ProductListMgr.md
│   │   ├── ProductListRegistrant.md
│   │   ├── Profile.md
│   │   └── Wallet.md
│   ├── dw_extensions.applepay
│   │   ├── ApplePayHookResult.md
│   │   └── ApplePayHooks.md
│   ├── dw_extensions.facebook
│   │   ├── FacebookFeedHooks.md
│   │   └── FacebookProduct.md
│   ├── dw_extensions.paymentrequest
│   │   ├── PaymentRequestHookResult.md
│   │   └── PaymentRequestHooks.md
│   ├── dw_extensions.payments
│   │   ├── SalesforceBancontactPaymentDetails.md
│   │   ├── SalesforceCardPaymentDetails.md
│   │   ├── SalesforceEpsPaymentDetails.md
│   │   ├── SalesforceIdealPaymentDetails.md
│   │   ├── SalesforceKlarnaPaymentDetails.md
│   │   ├── SalesforcePaymentDetails.md
│   │   ├── SalesforcePaymentIntent.md
│   │   ├── SalesforcePaymentMethod.md
│   │   ├── SalesforcePaymentRequest.md
│   │   ├── SalesforcePaymentsHooks.md
│   │   ├── SalesforcePaymentsMgr.md
│   │   ├── SalesforcePaymentsSiteConfiguration.md
│   │   ├── SalesforcePayPalOrder.md
│   │   ├── SalesforcePayPalOrderAddress.md
│   │   ├── SalesforcePayPalOrderPayer.md
│   │   ├── SalesforcePayPalPaymentDetails.md
│   │   ├── SalesforceSepaDebitPaymentDetails.md
│   │   └── SalesforceVenmoPaymentDetails.md
│   ├── dw_extensions.pinterest
│   │   ├── PinterestAvailability.md
│   │   ├── PinterestFeedHooks.md
│   │   ├── PinterestOrder.md
│   │   ├── PinterestOrderHooks.md
│   │   └── PinterestProduct.md
│   ├── dw_io
│   │   ├── CSVStreamReader.md
│   │   ├── CSVStreamWriter.md
│   │   ├── File.md
│   │   ├── FileReader.md
│   │   ├── FileWriter.md
│   │   ├── InputStream.md
│   │   ├── OutputStream.md
│   │   ├── PrintWriter.md
│   │   ├── RandomAccessFileReader.md
│   │   ├── Reader.md
│   │   ├── StringWriter.md
│   │   ├── Writer.md
│   │   ├── XMLIndentingStreamWriter.md
│   │   ├── XMLStreamConstants.md
│   │   ├── XMLStreamReader.md
│   │   └── XMLStreamWriter.md
│   ├── dw_job
│   │   ├── JobExecution.md
│   │   └── JobStepExecution.md
│   ├── dw_net
│   │   ├── FTPClient.md
│   │   ├── FTPFileInfo.md
│   │   ├── HTTPClient.md
│   │   ├── HTTPRequestPart.md
│   │   ├── Mail.md
│   │   ├── SFTPClient.md
│   │   ├── SFTPFileInfo.md
│   │   ├── WebDAVClient.md
│   │   └── WebDAVFileInfo.md
│   ├── dw_object
│   │   ├── ActiveData.md
│   │   ├── CustomAttributes.md
│   │   ├── CustomObject.md
│   │   ├── CustomObjectMgr.md
│   │   ├── Extensible.md
│   │   ├── ExtensibleObject.md
│   │   ├── Note.md
│   │   ├── ObjectAttributeDefinition.md
│   │   ├── ObjectAttributeGroup.md
│   │   ├── ObjectAttributeValueDefinition.md
│   │   ├── ObjectTypeDefinition.md
│   │   ├── PersistentObject.md
│   │   ├── SimpleExtensible.md
│   │   └── SystemObjectMgr.md
│   ├── dw_order
│   │   ├── AbstractItem.md
│   │   ├── AbstractItemCtnr.md
│   │   ├── Appeasement.md
│   │   ├── AppeasementItem.md
│   │   ├── Basket.md
│   │   ├── BasketMgr.md
│   │   ├── BonusDiscountLineItem.md
│   │   ├── CouponLineItem.md
│   │   ├── CreateAgentBasketLimitExceededException.md
│   │   ├── CreateBasketFromOrderException.md
│   │   ├── CreateCouponLineItemException.md
│   │   ├── CreateOrderException.md
│   │   ├── CreateTemporaryBasketLimitExceededException.md
│   │   ├── GiftCertificate.md
│   │   ├── GiftCertificateLineItem.md
│   │   ├── GiftCertificateMgr.md
│   │   ├── GiftCertificateStatusCodes.md
│   │   ├── Invoice.md
│   │   ├── InvoiceItem.md
│   │   ├── LineItem.md
│   │   ├── LineItemCtnr.md
│   │   ├── Order.md
│   │   ├── OrderAddress.md
│   │   ├── OrderItem.md
│   │   ├── OrderMgr.md
│   │   ├── OrderPaymentInstrument.md
│   │   ├── OrderProcessStatusCodes.md
│   │   ├── PaymentCard.md
│   │   ├── PaymentInstrument.md
│   │   ├── PaymentMethod.md
│   │   ├── PaymentMgr.md
│   │   ├── PaymentProcessor.md
│   │   ├── PaymentStatusCodes.md
│   │   ├── PaymentTransaction.md
│   │   ├── PriceAdjustment.md
│   │   ├── PriceAdjustmentLimitTypes.md
│   │   ├── ProductLineItem.md
│   │   ├── ProductShippingCost.md
│   │   ├── ProductShippingLineItem.md
│   │   ├── ProductShippingModel.md
│   │   ├── Return.md
│   │   ├── ReturnCase.md
│   │   ├── ReturnCaseItem.md
│   │   ├── ReturnItem.md
│   │   ├── Shipment.md
│   │   ├── ShipmentShippingCost.md
│   │   ├── ShipmentShippingModel.md
│   │   ├── ShippingLineItem.md
│   │   ├── ShippingLocation.md
│   │   ├── ShippingMethod.md
│   │   ├── ShippingMgr.md
│   │   ├── ShippingOrder.md
│   │   ├── ShippingOrderItem.md
│   │   ├── SumItem.md
│   │   ├── TaxGroup.md
│   │   ├── TaxItem.md
│   │   ├── TaxMgr.md
│   │   ├── TrackingInfo.md
│   │   └── TrackingRef.md
│   ├── dw_order.hooks
│   │   ├── CalculateHooks.md
│   │   ├── OrderHooks.md
│   │   ├── PaymentHooks.md
│   │   ├── ReturnHooks.md
│   │   └── ShippingOrderHooks.md
│   ├── dw_rpc
│   │   ├── SOAPUtil.md
│   │   ├── Stub.md
│   │   └── WebReference.md
│   ├── dw_suggest
│   │   ├── BrandSuggestions.md
│   │   ├── CategorySuggestions.md
│   │   ├── ContentSuggestions.md
│   │   ├── CustomSuggestions.md
│   │   ├── ProductSuggestions.md
│   │   ├── SearchPhraseSuggestions.md
│   │   ├── SuggestedCategory.md
│   │   ├── SuggestedContent.md
│   │   ├── SuggestedPhrase.md
│   │   ├── SuggestedProduct.md
│   │   ├── SuggestedTerm.md
│   │   ├── SuggestedTerms.md
│   │   ├── Suggestions.md
│   │   └── SuggestModel.md
│   ├── dw_svc
│   │   ├── FTPService.md
│   │   ├── FTPServiceDefinition.md
│   │   ├── HTTPFormService.md
│   │   ├── HTTPFormServiceDefinition.md
│   │   ├── HTTPService.md
│   │   ├── HTTPServiceDefinition.md
│   │   ├── LocalServiceRegistry.md
│   │   ├── Result.md
│   │   ├── Service.md
│   │   ├── ServiceCallback.md
│   │   ├── ServiceConfig.md
│   │   ├── ServiceCredential.md
│   │   ├── ServiceDefinition.md
│   │   ├── ServiceProfile.md
│   │   ├── ServiceRegistry.md
│   │   ├── SOAPService.md
│   │   └── SOAPServiceDefinition.md
│   ├── dw_system
│   │   ├── AgentUserStatusCodes.md
│   │   ├── Cache.md
│   │   ├── CacheMgr.md
│   │   ├── HookMgr.md
│   │   ├── InternalObject.md
│   │   ├── JobProcessMonitor.md
│   │   ├── Log.md
│   │   ├── Logger.md
│   │   ├── LogNDC.md
│   │   ├── OrganizationPreferences.md
│   │   ├── Pipeline.md
│   │   ├── PipelineDictionary.md
│   │   ├── RemoteInclude.md
│   │   ├── Request.md
│   │   ├── RequestHooks.md
│   │   ├── Response.md
│   │   ├── RESTErrorResponse.md
│   │   ├── RESTResponseMgr.md
│   │   ├── RESTSuccessResponse.md
│   │   ├── SearchStatus.md
│   │   ├── Session.md
│   │   ├── Site.md
│   │   ├── SitePreferences.md
│   │   ├── Status.md
│   │   ├── StatusItem.md
│   │   ├── System.md
│   │   └── Transaction.md
│   ├── dw_util
│   │   ├── ArrayList.md
│   │   ├── Assert.md
│   │   ├── BigInteger.md
│   │   ├── Bytes.md
│   │   ├── Calendar.md
│   │   ├── Collection.md
│   │   ├── Currency.md
│   │   ├── DateUtils.md
│   │   ├── Decimal.md
│   │   ├── FilteringCollection.md
│   │   ├── Geolocation.md
│   │   ├── HashMap.md
│   │   ├── HashSet.md
│   │   ├── Iterator.md
│   │   ├── LinkedHashMap.md
│   │   ├── LinkedHashSet.md
│   │   ├── List.md
│   │   ├── Locale.md
│   │   ├── Map.md
│   │   ├── MapEntry.md
│   │   ├── MappingKey.md
│   │   ├── MappingMgr.md
│   │   ├── PropertyComparator.md
│   │   ├── SecureEncoder.md
│   │   ├── SecureFilter.md
│   │   ├── SeekableIterator.md
│   │   ├── Set.md
│   │   ├── SortedMap.md
│   │   ├── SortedSet.md
│   │   ├── StringUtils.md
│   │   ├── Template.md
│   │   └── UUIDUtils.md
│   ├── dw_value
│   │   ├── EnumValue.md
│   │   ├── MimeEncodedText.md
│   │   ├── Money.md
│   │   └── Quantity.md
│   ├── dw_web
│   │   ├── ClickStream.md
│   │   ├── ClickStreamEntry.md
│   │   ├── Cookie.md
│   │   ├── Cookies.md
│   │   ├── CSRFProtection.md
│   │   ├── Form.md
│   │   ├── FormAction.md
│   │   ├── FormElement.md
│   │   ├── FormElementValidationResult.md
│   │   ├── FormField.md
│   │   ├── FormFieldOption.md
│   │   ├── FormFieldOptions.md
│   │   ├── FormGroup.md
│   │   ├── FormList.md
│   │   ├── FormListItem.md
│   │   ├── Forms.md
│   │   ├── HttpParameter.md
│   │   ├── HttpParameterMap.md
│   │   ├── LoopIterator.md
│   │   ├── PageMetaData.md
│   │   ├── PageMetaTag.md
│   │   ├── PagingModel.md
│   │   ├── Resource.md
│   │   ├── URL.md
│   │   ├── URLAction.md
│   │   ├── URLParameter.md
│   │   ├── URLRedirect.md
│   │   ├── URLRedirectMgr.md
│   │   └── URLUtils.md
│   ├── sfra
│   │   ├── account.md
│   │   ├── address.md
│   │   ├── billing.md
│   │   ├── cart.md
│   │   ├── categories.md
│   │   ├── content.md
│   │   ├── locale.md
│   │   ├── order.md
│   │   ├── payment.md
│   │   ├── price-default.md
│   │   ├── price-range.md
│   │   ├── price-tiered.md
│   │   ├── product-bundle.md
│   │   ├── product-full.md
│   │   ├── product-line-items.md
│   │   ├── product-search.md
│   │   ├── product-tile.md
│   │   ├── querystring.md
│   │   ├── render.md
│   │   ├── request.md
│   │   ├── response.md
│   │   ├── server.md
│   │   ├── shipping.md
│   │   ├── store.md
│   │   ├── stores.md
│   │   └── totals.md
│   └── TopLevel
│       ├── APIException.md
│       ├── arguments.md
│       ├── Array.md
│       ├── ArrayBuffer.md
│       ├── BigInt.md
│       ├── Boolean.md
│       ├── ConversionError.md
│       ├── DataView.md
│       ├── Date.md
│       ├── Error.md
│       ├── ES6Iterator.md
│       ├── EvalError.md
│       ├── Fault.md
│       ├── Float32Array.md
│       ├── Float64Array.md
│       ├── Function.md
│       ├── Generator.md
│       ├── global.md
│       ├── Int16Array.md
│       ├── Int32Array.md
│       ├── Int8Array.md
│       ├── InternalError.md
│       ├── IOError.md
│       ├── Iterable.md
│       ├── Iterator.md
│       ├── JSON.md
│       ├── Map.md
│       ├── Math.md
│       ├── Module.md
│       ├── Namespace.md
│       ├── Number.md
│       ├── Object.md
│       ├── QName.md
│       ├── RangeError.md
│       ├── ReferenceError.md
│       ├── RegExp.md
│       ├── Set.md
│       ├── StopIteration.md
│       ├── String.md
│       ├── Symbol.md
│       ├── SyntaxError.md
│       ├── SystemError.md
│       ├── TypeError.md
│       ├── Uint16Array.md
│       ├── Uint32Array.md
│       ├── Uint8Array.md
│       ├── Uint8ClampedArray.md
│       ├── URIError.md
│       ├── WeakMap.md
│       ├── WeakSet.md
│       ├── XML.md
│       ├── XMLList.md
│       └── XMLStreamError.md
├── docs-site
│   ├── .gitignore
│   ├── App.tsx
│   ├── components
│   │   ├── Badge.tsx
│   │   ├── BreadcrumbSchema.tsx
│   │   ├── CodeBlock.tsx
│   │   ├── Collapsible.tsx
│   │   ├── ConfigBuilder.tsx
│   │   ├── ConfigHero.tsx
│   │   ├── ConfigModeTabs.tsx
│   │   ├── icons.tsx
│   │   ├── Layout.tsx
│   │   ├── LightCodeContainer.tsx
│   │   ├── NewcomerCTA.tsx
│   │   ├── NextStepsStrip.tsx
│   │   ├── OnThisPage.tsx
│   │   ├── Search.tsx
│   │   ├── SEO.tsx
│   │   ├── Sidebar.tsx
│   │   ├── StructuredData.tsx
│   │   ├── ToolCard.tsx
│   │   ├── ToolFilters.tsx
│   │   ├── Typography.tsx
│   │   └── VersionBadge.tsx
│   ├── constants.tsx
│   ├── index.html
│   ├── main.tsx
│   ├── metadata.json
│   ├── package-lock.json
│   ├── package.json
│   ├── pages
│   │   ├── AIInterfacesPage.tsx
│   │   ├── ConfigurationPage.tsx
│   │   ├── DevelopmentPage.tsx
│   │   ├── ExamplesPage.tsx
│   │   ├── FeaturesPage.tsx
│   │   ├── HomePage.tsx
│   │   ├── SecurityPage.tsx
│   │   ├── ToolsPage.tsx
│   │   └── TroubleshootingPage.tsx
│   ├── postcss.config.js
│   ├── public
│   │   ├── .well-known
│   │   │   └── security.txt
│   │   ├── 404.html
│   │   ├── android-chrome-192x192.png
│   │   ├── android-chrome-512x512.png
│   │   ├── apple-touch-icon.png
│   │   ├── explain-product-pricing-methods-no-mcp.png
│   │   ├── explain-product-pricing-methods.png
│   │   ├── favicon-16x16.png
│   │   ├── favicon-32x32.png
│   │   ├── favicon.ico
│   │   ├── llms.txt
│   │   ├── robots.txt
│   │   ├── site.webmanifest
│   │   └── sitemap.xml
│   ├── README.md
│   ├── scripts
│   │   ├── generate-search-index.js
│   │   ├── generate-sitemap.js
│   │   └── search-dev.js
│   ├── src
│   │   └── styles
│   │       ├── input.css
│   │       └── prism-theme.css
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   ├── types.ts
│   ├── utils
│   │   ├── search.ts
│   │   └── toolsData.ts
│   └── vite.config.ts
├── eslint.config.js
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   └── convert-docs.js
├── SECURITY.md
├── server.json
├── src
│   ├── clients
│   │   ├── base
│   │   │   ├── http-client.ts
│   │   │   ├── oauth-token.ts
│   │   │   └── ocapi-auth-client.ts
│   │   ├── best-practices-client.ts
│   │   ├── cartridge-generation-client.ts
│   │   ├── docs
│   │   │   ├── class-content-parser.ts
│   │   │   ├── class-name-resolver.ts
│   │   │   ├── documentation-scanner.ts
│   │   │   ├── index.ts
│   │   │   └── referenced-types-extractor.ts
│   │   ├── docs-client.ts
│   │   ├── log-client.ts
│   │   ├── logs
│   │   │   ├── index.ts
│   │   │   ├── log-analyzer.ts
│   │   │   ├── log-client.ts
│   │   │   ├── log-constants.ts
│   │   │   ├── log-file-discovery.ts
│   │   │   ├── log-file-reader.ts
│   │   │   ├── log-formatter.ts
│   │   │   ├── log-processor.ts
│   │   │   ├── log-types.ts
│   │   │   └── webdav-client-manager.ts
│   │   ├── ocapi
│   │   │   ├── code-versions-client.ts
│   │   │   ├── site-preferences-client.ts
│   │   │   └── system-objects-client.ts
│   │   ├── ocapi-client.ts
│   │   └── sfra-client.ts
│   ├── config
│   │   ├── configuration-factory.ts
│   │   └── dw-json-loader.ts
│   ├── core
│   │   ├── handlers
│   │   │   ├── abstract-log-tool-handler.ts
│   │   │   ├── base-handler.ts
│   │   │   ├── best-practices-handler.ts
│   │   │   ├── cartridge-handler.ts
│   │   │   ├── client-factory.ts
│   │   │   ├── code-version-handler.ts
│   │   │   ├── docs-handler.ts
│   │   │   ├── job-log-handler.ts
│   │   │   ├── job-log-tool-config.ts
│   │   │   ├── log-handler.ts
│   │   │   ├── log-tool-config.ts
│   │   │   ├── sfra-handler.ts
│   │   │   ├── system-object-handler.ts
│   │   │   └── validation-helpers.ts
│   │   ├── server.ts
│   │   └── tool-definitions.ts
│   ├── index.ts
│   ├── main.ts
│   ├── services
│   │   ├── file-system-service.ts
│   │   ├── index.ts
│   │   └── path-service.ts
│   ├── tool-configs
│   │   ├── best-practices-tool-config.ts
│   │   ├── cartridge-tool-config.ts
│   │   ├── code-version-tool-config.ts
│   │   ├── docs-tool-config.ts
│   │   ├── job-log-tool-config.ts
│   │   ├── log-tool-config.ts
│   │   ├── sfra-tool-config.ts
│   │   └── system-object-tool-config.ts
│   ├── types
│   │   └── types.ts
│   └── utils
│       ├── cache.ts
│       ├── job-log-tool-config.ts
│       ├── job-log-utils.ts
│       ├── log-cache.ts
│       ├── log-tool-config.ts
│       ├── log-tool-constants.ts
│       ├── log-tool-utils.ts
│       ├── logger.ts
│       ├── ocapi-url-builder.ts
│       ├── path-resolver.ts
│       ├── query-builder.ts
│       ├── utils.ts
│       └── validator.ts
├── tests
│   ├── __mocks__
│   │   ├── docs-client.ts
│   │   ├── src
│   │   │   └── clients
│   │   │       └── base
│   │   │           └── http-client.js
│   │   └── webdav.js
│   ├── base-handler.test.ts
│   ├── base-http-client.test.ts
│   ├── best-practices-handler.test.ts
│   ├── cache.test.ts
│   ├── cartridge-handler.test.ts
│   ├── class-content-parser.test.ts
│   ├── class-name-resolver.test.ts
│   ├── client-factory.test.ts
│   ├── code-version-handler.test.ts
│   ├── code-versions-client.test.ts
│   ├── config.test.ts
│   ├── configuration-factory.test.ts
│   ├── docs-handler.test.ts
│   ├── documentation-scanner.test.ts
│   ├── file-system-service.test.ts
│   ├── job-log-handler.test.ts
│   ├── job-log-utils.test.ts
│   ├── log-client.test.ts
│   ├── log-handler.test.ts
│   ├── log-processor.test.ts
│   ├── logger.test.ts
│   ├── mcp
│   │   ├── AGENTS.md
│   │   ├── node
│   │   │   ├── activate-code-version-advanced.full-mode.programmatic.test.js
│   │   │   ├── code-versions.full-mode.programmatic.test.js
│   │   │   ├── generate-cartridge-structure.docs-only.programmatic.test.js
│   │   │   ├── get-available-best-practice-guides.docs-only.programmatic.test.js
│   │   │   ├── get-available-sfra-documents.programmatic.test.js
│   │   │   ├── get-best-practice-guide.docs-only.programmatic.test.js
│   │   │   ├── get-hook-reference.docs-only.programmatic.test.js
│   │   │   ├── get-job-execution-summary.full-mode.programmatic.test.js
│   │   │   ├── get-job-log-entries.full-mode.programmatic.test.js
│   │   │   ├── get-latest-debug.full-mode.programmatic.test.js
│   │   │   ├── get-latest-error.full-mode.programmatic.test.js
│   │   │   ├── get-latest-info.full-mode.programmatic.test.js
│   │   │   ├── get-latest-job-log-files.full-mode.programmatic.test.js
│   │   │   ├── get-latest-warn.full-mode.programmatic.test.js
│   │   │   ├── get-log-file-contents.full-mode.programmatic.test.js
│   │   │   ├── get-sfcc-class-documentation.docs-only.programmatic.test.js
│   │   │   ├── get-sfcc-class-info.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-categories.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-document.programmatic.test.js
│   │   │   ├── get-sfra-documents-by-category.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definition.full-mode.programmatic.test.js
│   │   │   ├── get-system-object-definitions.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definitions.full-mode.programmatic.test.js
│   │   │   ├── list-log-files.full-mode.programmatic.test.js
│   │   │   ├── list-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-best-practices.docs-only.programmatic.test.js
│   │   │   ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs-by-name.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs.full-mode.programmatic.test.js
│   │   │   ├── search-logs.full-mode.programmatic.test.js
│   │   │   ├── search-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-sfcc-methods.docs-only.programmatic.test.js
│   │   │   ├── search-sfra-documentation.docs-only.programmatic.test.js
│   │   │   ├── search-site-preferences.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-groups.full-mode.programmatic.test.js
│   │   │   ├── summarize-logs.full-mode.programmatic.test.js
│   │   │   ├── tools.docs-only.programmatic.test.js
│   │   │   └── tools.full-mode.programmatic.test.js
│   │   ├── README.md
│   │   ├── test-fixtures
│   │   │   └── dw.json
│   │   └── yaml
│   │       ├── activate-code-version.docs-only.test.mcp.yml
│   │       ├── activate-code-version.full-mode.test.mcp.yml
│   │       ├── get_latest_error.test.mcp.yml
│   │       ├── get-available-best-practice-guides.docs-only.test.mcp.yml
│   │       ├── get-available-best-practice-guides.full-mode.test.mcp.yml
│   │       ├── get-available-sfra-documents.docs-only.test.mcp.yml
│   │       ├── get-available-sfra-documents.full-mode.test.mcp.yml
│   │       ├── get-best-practice-guide.docs-only.test.mcp.yml
│   │       ├── get-best-practice-guide.full-mode.test.mcp.yml
│   │       ├── get-code-versions.docs-only.test.mcp.yml
│   │       ├── get-code-versions.full-mode.test.mcp.yml
│   │       ├── get-hook-reference.docs-only.test.mcp.yml
│   │       ├── get-hook-reference.full-mode.test.mcp.yml
│   │       ├── get-job-execution-summary.full-mode.test.mcp.yml
│   │       ├── get-job-log-entries.full-mode.test.mcp.yml
│   │       ├── get-latest-debug.full-mode.test.mcp.yml
│   │       ├── get-latest-error.full-mode.test.mcp.yml
│   │       ├── get-latest-info.full-mode.test.mcp.yml
│   │       ├── get-latest-job-log-files.full-mode.test.mcp.yml
│   │       ├── get-latest-warn.full-mode.test.mcp.yml
│   │       ├── get-log-file-contents.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-info.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-info.full-mode.test.mcp.yml
│   │       ├── get-sfra-categories.docs-only.test.mcp.yml
│   │       ├── get-sfra-categories.full-mode.test.mcp.yml
│   │       ├── get-sfra-document.docs-only.test.mcp.yml
│   │       ├── get-sfra-document.full-mode.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.docs-only.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.full-mode.test.mcp.yml
│   │       ├── get-system-object-definition.docs-only.test.mcp.yml
│   │       ├── get-system-object-definition.full-mode.test.mcp.yml
│   │       ├── get-system-object-definitions.docs-only.test.mcp.yml
│   │       ├── get-system-object-definitions.full-mode.test.mcp.yml
│   │       ├── list-log-files.full-mode.test.mcp.yml
│   │       ├── list-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── list-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-best-practices.docs-only.test.mcp.yml
│   │       ├── search-best-practices.full-mode.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.test.mcp.yml
│   │       ├── search-job-logs-by-name.full-mode.test.mcp.yml
│   │       ├── search-job-logs.full-mode.test.mcp.yml
│   │       ├── search-logs.full-mode.test.mcp.yml
│   │       ├── search-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── search-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-sfcc-methods.docs-only.test.mcp.yml
│   │       ├── search-sfcc-methods.full-mode.test.mcp.yml
│   │       ├── search-sfra-documentation.docs-only.test.mcp.yml
│   │       ├── search-sfra-documentation.full-mode.test.mcp.yml
│   │       ├── search-site-preferences.docs-only.test.mcp.yml
│   │       ├── search-site-preferences.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.full-mode.test.mcp.yml
│   │       ├── summarize-logs.full-mode.test.mcp.yml
│   │       ├── tools.docs-only.test.mcp.yml
│   │       └── tools.full-mode.test.mcp.yml
│   ├── oauth-token.test.ts
│   ├── ocapi-auth-client.test.ts
│   ├── ocapi-client.test.ts
│   ├── path-service.test.ts
│   ├── query-builder.test.ts
│   ├── referenced-types-extractor.test.ts
│   ├── servers
│   │   ├── sfcc-mock-server
│   │   │   ├── mock-data
│   │   │   │   └── ocapi
│   │   │   │       ├── code-versions.json
│   │   │   │       ├── custom-object-attributes-customapi.json
│   │   │   │       ├── custom-object-attributes-globalsettings.json
│   │   │   │       ├── custom-object-attributes-versionhistory.json
│   │   │   │       ├── site-preferences-ccv.json
│   │   │   │       ├── site-preferences-fastforward.json
│   │   │   │       ├── site-preferences-sfra.json
│   │   │   │       ├── site-preferences-storefront.json
│   │   │   │       ├── site-preferences-system.json
│   │   │   │       ├── system-object-attribute-groups-campaign.json
│   │   │   │       ├── system-object-attribute-groups-category.json
│   │   │   │       ├── system-object-attribute-groups-order.json
│   │   │   │       ├── system-object-attribute-groups-product.json
│   │   │   │       ├── system-object-attribute-groups-sitepreferences.json
│   │   │   │       ├── system-object-attributes-customeraddress.json
│   │   │   │       ├── system-object-attributes-product-expanded.json
│   │   │   │       ├── system-object-attributes-product.json
│   │   │   │       ├── system-object-definition-category.json
│   │   │   │       ├── system-object-definition-customer.json
│   │   │   │       ├── system-object-definition-customeraddress.json
│   │   │   │       ├── system-object-definition-order.json
│   │   │   │       ├── system-object-definition-product.json
│   │   │   │       ├── system-object-definitions-old.json
│   │   │   │       └── system-object-definitions.json
│   │   │   ├── package-lock.json
│   │   │   ├── package.json
│   │   │   ├── README.md
│   │   │   ├── scripts
│   │   │   │   └── setup-logs.js
│   │   │   ├── server.js
│   │   │   └── src
│   │   │       ├── app.js
│   │   │       ├── config
│   │   │       │   └── server-config.js
│   │   │       ├── middleware
│   │   │       │   ├── auth.js
│   │   │       │   ├── cors.js
│   │   │       │   └── logging.js
│   │   │       ├── routes
│   │   │       │   ├── ocapi
│   │   │       │   │   ├── code-versions-handler.js
│   │   │       │   │   ├── oauth-handler.js
│   │   │       │   │   ├── ocapi-error-utils.js
│   │   │       │   │   ├── ocapi-utils.js
│   │   │       │   │   ├── site-preferences-handler.js
│   │   │       │   │   └── system-objects-handler.js
│   │   │       │   ├── ocapi.js
│   │   │       │   └── webdav.js
│   │   │       └── utils
│   │   │           ├── mock-data-loader.js
│   │   │           └── webdav-xml.js
│   │   └── sfcc-mock-server-manager.ts
│   ├── sfcc-mock-server.test.ts
│   ├── site-preferences-client.test.ts
│   ├── system-objects-client.test.ts
│   ├── utils.test.ts
│   ├── validation-helpers.test.ts
│   └── validator.test.ts
├── tsconfig.json
└── tsconfig.test.json
```

# Files

--------------------------------------------------------------------------------
/docs/dw_order/OrderItem.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class OrderItem

## Inheritance Hierarchy

- Object
  - dw.order.OrderItem

## Description

Defines extensions to ProductLineItems and ShippingLineItems belonging to an order. The order-item can be accessed using ProductLineItem.getOrderItem() or ShippingLineItem.getOrderItem() - these methods return null if the item is associated with a basket rather than an order. Alternative access is available using Order.getOrderItem(String) by passing the itemID used to identify the order-item in for example export files. The associated order-item can also be accessed from invoice-items, shipping-order-items, return-items and return-case-items using AbstractItem.getOrderItem(). The order-item provides an item-level status and type, methods for accessing and creating associated items, and methods used to allocate inventory for shipping-order creation. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

## Constants

### STATUS_BACKORDER

**Type:** String = "BACKORDER"

Constant for Order Item Status BACKORDER

### STATUS_CANCELLED

**Type:** String = "CANCELLED"

Constant for Order Item Status CANCELLED

### STATUS_CONFIRMED

**Type:** String = "CONFIRMED"

Constant for Order Item Status CONFIRMED

### STATUS_CREATED

**Type:** String = "CREATED"

Constant for Order Item Status CREATED

### STATUS_NEW

**Type:** String = "NEW"

Constant for Order Item Status NEW

### STATUS_OPEN

**Type:** String = "OPEN"

Constant for Order Item Status OPEN

### STATUS_SHIPPED

**Type:** String = "SHIPPED"

Constant for Order Item Status SHIPPED

### STATUS_WAREHOUSE

**Type:** String = "WAREHOUSE"

Constant for Order Item Status WAREHOUSE

### TYPE_PRODUCT

**Type:** String = "PRODUCT"

Constant for Order Item Type PRODUCT

### TYPE_SERVICE

**Type:** String = "SERVICE"

Constant for Order Item Type SERVICE

## Properties

### appeasedAmount

**Type:** Money (Read Only)

Sum of amounts appeased for this item, calculated by iterating over
 invoice items associated with the item.

### capturedAmount

**Type:** Money (Read Only)

Sum of amounts captured for this item, calculated by iterating over
 invoice items associated with the item.

### invoiceItems

**Type:** Collection (Read Only)

All invoice items associated with this item, each
 InvoiceItem will belong to a different
 Invoice, which can also be accessed using
 Order.getInvoices() or Order.getInvoice(String).

### itemID

**Type:** String (Read Only)

The itemID used to identify the OrderItem. Note this is
 not a UUID, it is created internally when the OrderItem
 instance is created, and is typically used within export files to
 identify the item.

### lineItem

**Type:** LineItem (Read Only)

The line item which is being extended by this instance.

### refundedAmount

**Type:** Money (Read Only)

Sum of amounts refunded for this item, calculated by iterating over
 invoice items associated with the item.

### returnCaseItems

**Type:** Collection (Read Only)

All return case items associated with this item,
 each ReturnCaseItem will belong to a different
 ReturnCase, which can also be accessed using
 Order.getReturnCases() or Order.getReturnCase(String).

### returnedQuantity

**Type:** Quantity (Read Only)

The quantity returned, dynamically sum of quantities held by associated
 ReturnItems.

### shippingOrderItem

**Type:** ShippingOrderItem (Read Only)

The last added non-cancelled shipping order item if one exists, otherwise null.
 
 Multiple shipping order items that are not in status ShippingOrderItem.STATUS_CANCELLED
 can exist for one OrderItem, for example if the OrderItem has been split for shipping purposes.
 The method returns null if no non-cancelled shipping order item exists.

### shippingOrderItems

**Type:** Collection (Read Only)

A collection of the ShippingOrderItems created for this item.
 ShippingOrder items represents the whole or part of this item which could
 be delivered, and belong to a shipping order.
 Note that the cancelled shipping order items are returned too.
 This method is equivalent to getShippingOrderItems(Boolean)
 called with parameter true.

### splitItems

**Type:** Collection (Read Only)

A collection of all split OrderItems associated with this item. Inverse relation to getSplitSourceItem().
 
 Split order items are created when
 
 creating a ShippingOrderItem for a ShippingOrder, see ShippingOrder.createShippingOrderItem(OrderItem, Quantity)
 splitting an existing ShippingOrderItem, see ShippingOrderItem.split(Quantity)
 
 with a specified quantity less than the existing quantity of the associated ProductLineItem. In this case the associated ProductLineItem
 is split by creating a new ProductLineItem and associating a new ShippingOrderItem with this item. The new ShippingOrderItem
 receives the specified quantity and the quantity of the item is set to the remaining quantity. All split items are associated to their originating item via
 the "split source item" association.

### splitSourceItem

**Type:** OrderItem (Read Only)

The split source item associated with this item, if existing. Inverse relation to getSplitItems().
 
 A split source item is associated after the successful creation of a split item with a quantity less than the existing quantity of the item to split.
 For details see getSplitItems().

### status

**Type:** EnumValue

Gets the order item status.
 The possible values are:
 
 STATUS_NEW
 STATUS_OPEN
 STATUS_BACKORDER
 STATUS_CONFIRMED
 STATUS_WAREHOUSE
 STATUS_SHIPPED
 STATUS_CANCELLED

### type

**Type:** EnumValue (Read Only)

The type of line item with which this instance is associated, one
 of
 
 SERVICE (method getLineItem() returns a
 ShippingLineItem
 PRODUCT (method getLineItem() returns a
 ProductLineItem

## Constructor Summary

## Method Summary

### allocateInventory

**Signature:** `allocateInventory(partialAllocation : boolean) : Quantity`

Please note that this method is disabled by default.

### getAppeasedAmount

**Signature:** `getAppeasedAmount() : Money`

Sum of amounts appeased for this item, calculated by iterating over invoice items associated with the item.

### getCapturedAmount

**Signature:** `getCapturedAmount() : Money`

Sum of amounts captured for this item, calculated by iterating over invoice items associated with the item.

### getInvoiceItems

**Signature:** `getInvoiceItems() : Collection`

Returns all invoice items associated with this item, each InvoiceItem will belong to a different Invoice, which can also be accessed using Order.getInvoices() or Order.getInvoice(String).

### getItemID

**Signature:** `getItemID() : String`

The itemID used to identify the OrderItem.

### getLineItem

**Signature:** `getLineItem() : LineItem`

Returns the line item which is being extended by this instance.

### getRefundedAmount

**Signature:** `getRefundedAmount() : Money`

Sum of amounts refunded for this item, calculated by iterating over invoice items associated with the item.

### getReturnCaseItems

**Signature:** `getReturnCaseItems() : Collection`

Returns all return case items associated with this item, each ReturnCaseItem will belong to a different ReturnCase, which can also be accessed using Order.getReturnCases() or Order.getReturnCase(String).

### getReturnedQuantity

**Signature:** `getReturnedQuantity() : Quantity`

The quantity returned, dynamically sum of quantities held by associated ReturnItems.

### getShippingOrderItem

**Signature:** `getShippingOrderItem() : ShippingOrderItem`

The last added non-cancelled shipping order item if one exists, otherwise null.

### getShippingOrderItems

**Signature:** `getShippingOrderItems() : Collection`

Returns a collection of the ShippingOrderItems created for this item.

### getShippingOrderItems

**Signature:** `getShippingOrderItems(includeCancelled : boolean) : Collection`

Returns a collection of the ShippingOrderItems created for this item.

### getSplitItems

**Signature:** `getSplitItems() : Collection`

Returns a collection of all split OrderItems associated with this item.

### getSplitSourceItem

**Signature:** `getSplitSourceItem() : OrderItem`

Returns the split source item associated with this item, if existing.

### getStatus

**Signature:** `getStatus() : EnumValue`

Gets the order item status. The possible values are: STATUS_NEW STATUS_OPEN STATUS_BACKORDER STATUS_CONFIRMED STATUS_WAREHOUSE STATUS_SHIPPED STATUS_CANCELLED

### getType

**Signature:** `getType() : EnumValue`

Returns the type of line item with which this instance is associated, one of SERVICE (method getLineItem() returns a ShippingLineItem PRODUCT (method getLineItem() returns a ProductLineItem

### setStatus

**Signature:** `setStatus(status : String) : void`

Set the status of the order item, use one of the values documented in getStatus().

## Method Detail

## Method Details

### allocateInventory

**Signature:** `allocateInventory(partialAllocation : boolean) : Quantity`

**Description:** Please note that this method is disabled by default. Please contact support for enabling it. Attempts to allocate inventory for the item and returns the quantity that could be allocated or null if no allocation was possible. All option product line items are allocated with their parent. Note that for items with option product line items no partial allocation is possible. That means the partialAllocation parameter will in this case always be considered as false

**Parameters:**

- `partialAllocation`: true accept a partial allocation as a result. Partial allocation is only possible when no option product line items are included, false only full allocation will be used, partial allocation will be released automatically

**Returns:**

successful: the newly allocated quantity failed: null

---

### getAppeasedAmount

**Signature:** `getAppeasedAmount() : Money`

**Description:** Sum of amounts appeased for this item, calculated by iterating over invoice items associated with the item.

**Returns:**

Sum of amounts refunded for this item

---

### getCapturedAmount

**Signature:** `getCapturedAmount() : Money`

**Description:** Sum of amounts captured for this item, calculated by iterating over invoice items associated with the item.

**Returns:**

Sum of amounts captured for this item

---

### getInvoiceItems

**Signature:** `getInvoiceItems() : Collection`

**Description:** Returns all invoice items associated with this item, each InvoiceItem will belong to a different Invoice, which can also be accessed using Order.getInvoices() or Order.getInvoice(String).

**Returns:**

invoice items associated with this item

---

### getItemID

**Signature:** `getItemID() : String`

**Description:** The itemID used to identify the OrderItem. Note this is not a UUID, it is created internally when the OrderItem instance is created, and is typically used within export files to identify the item.

**Returns:**

the itemID of the OrderItem

---

### getLineItem

**Signature:** `getLineItem() : LineItem`

**Description:** Returns the line item which is being extended by this instance.

**Returns:**

the line item associated with this instance

---

### getRefundedAmount

**Signature:** `getRefundedAmount() : Money`

**Description:** Sum of amounts refunded for this item, calculated by iterating over invoice items associated with the item.

**Returns:**

Sum of amounts refunded for this item

---

### getReturnCaseItems

**Signature:** `getReturnCaseItems() : Collection`

**Description:** Returns all return case items associated with this item, each ReturnCaseItem will belong to a different ReturnCase, which can also be accessed using Order.getReturnCases() or Order.getReturnCase(String).

**Returns:**

return case items associated with this item

---

### getReturnedQuantity

**Signature:** `getReturnedQuantity() : Quantity`

**Description:** The quantity returned, dynamically sum of quantities held by associated ReturnItems.

**Returns:**

quantity returned, the sum of quantities held by associated ReturnItems

---

### getShippingOrderItem

**Signature:** `getShippingOrderItem() : ShippingOrderItem`

**Description:** The last added non-cancelled shipping order item if one exists, otherwise null. Multiple shipping order items that are not in status ShippingOrderItem.STATUS_CANCELLED can exist for one OrderItem, for example if the OrderItem has been split for shipping purposes. The method returns null if no non-cancelled shipping order item exists.

**Deprecated:**

This item is deprecated.

**Returns:**

the last not cancelled shipping order item or null

---

### getShippingOrderItems

**Signature:** `getShippingOrderItems() : Collection`

**Description:** Returns a collection of the ShippingOrderItems created for this item. ShippingOrder items represents the whole or part of this item which could be delivered, and belong to a shipping order. Note that the cancelled shipping order items are returned too. This method is equivalent to getShippingOrderItems(Boolean) called with parameter true.

**Returns:**

collection of the shipping order items created for this item

---

### getShippingOrderItems

**Signature:** `getShippingOrderItems(includeCancelled : boolean) : Collection`

**Description:** Returns a collection of the ShippingOrderItems created for this item. ShippingOrder items represent the whole or part of this item which could be delivered, and belong to a shipping order. Depending on the includeCancelled parameter the cancelled shipping order items will be returned or not.

**Parameters:**

- `includeCancelled`: true all shipping order items, including the cancelled, created for this item will be returned false only non-cancelled shipping order items created for this item will be returned

**Returns:**

collection of the shipping order items created for this item

---

### getSplitItems

**Signature:** `getSplitItems() : Collection`

**Description:** Returns a collection of all split OrderItems associated with this item. Inverse relation to getSplitSourceItem(). Split order items are created when creating a ShippingOrderItem for a ShippingOrder, see ShippingOrder.createShippingOrderItem(OrderItem, Quantity) splitting an existing ShippingOrderItem, see ShippingOrderItem.split(Quantity) with a specified quantity less than the existing quantity of the associated ProductLineItem. In this case the associated ProductLineItem is split by creating a new ProductLineItem and associating a new ShippingOrderItem with this item. The new ShippingOrderItem receives the specified quantity and the quantity of the item is set to the remaining quantity. All split items are associated to their originating item via the "split source item" association.

**Returns:**

the split order items associated with this item

---

### getSplitSourceItem

**Signature:** `getSplitSourceItem() : OrderItem`

**Description:** Returns the split source item associated with this item, if existing. Inverse relation to getSplitItems(). A split source item is associated after the successful creation of a split item with a quantity less than the existing quantity of the item to split. For details see getSplitItems().

**Returns:**

the split source item or null

---

### getStatus

**Signature:** `getStatus() : EnumValue`

**Description:** Gets the order item status. The possible values are: STATUS_NEW STATUS_OPEN STATUS_BACKORDER STATUS_CONFIRMED STATUS_WAREHOUSE STATUS_SHIPPED STATUS_CANCELLED

**Returns:**

the status

---

### getType

**Signature:** `getType() : EnumValue`

**Description:** Returns the type of line item with which this instance is associated, one of SERVICE (method getLineItem() returns a ShippingLineItem PRODUCT (method getLineItem() returns a ProductLineItem

**Returns:**

the type of order item, one of TYPE_PRODUCT or TYPE_SERVICE.

---

### setStatus

**Signature:** `setStatus(status : String) : void`

**Description:** Set the status of the order item, use one of the values documented in getStatus(). If the order item has a related shipping order item (see getShippingOrderItem()) the status of the shipping order item will be adjusted to the same status. Setting the status of an order item might also change the status of the related order. The following rules apply in top-down order: all items STATUS_CANCELLED - order status is Order.ORDER_STATUS_CANCELLED at least one item in status STATUS_SHIPPED and all other items are STATUS_CANCELLED order status is Order.ORDER_STATUS_COMPLETED at least one item in status STATUS_CREATED, STATUS_OPEN, STATUS_NEW , STATUS_BACKORDER - order status is Order.ORDER_STATUS_OPEN, order confirmation status is Order.CONFIRMATION_STATUS_NOTCONFIRMED other combinations will have only items in STATUS_CONFIRMED, STATUS_CANCELLED and STATUS_SHIPPED - order status is Order.ORDER_STATUS_OPEN, order confirmation status is Order.CONFIRMATION_STATUS_CONFIRMED

**Parameters:**

- `status`: status string matching one of the values for status

---
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-job-execution-summary.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

describe('get_job_execution_summary - Advanced Programmatic Tests', () => {
  let client;
  let discoveredJobNames = [];

  before(async () => {
    client = await connect('./aegis.config.with-dw.json');
    await discoverJobNames();
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking into next tests
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  // Optimized helper functions focused on complex validation
  function assertValidMCPResponse(result) {
    assert.ok(result.content, 'Should have content');
    assert.ok(Array.isArray(result.content), 'Content should be array');
    assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
  }

  function parseResponseText(text) {
    return text.startsWith('"') && text.endsWith('"') 
      ? JSON.parse(text) 
      : text;
  }

  function assertJobExecutionSummaryFormat(result, jobName) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, false, 'Should not be an error response');
    
    const text = parseResponseText(result.content[0].text);
    
    if (text.includes('No job logs found')) {
      assert.ok(text.includes(`No job logs found for job name: ${jobName}`),
        'No results message should include job name');
      return null;
    }
    
    // Validate comprehensive execution summary format
    assert.ok(text.includes(`Job Execution Summary: ${jobName}`),
      'Should contain job execution summary header with job name');
    assert.ok(text.includes('⏱️ Timing:') && text.includes('📊 Status:'),
      'Should contain emoji-formatted sections');
    assert.ok(text.includes('Start:') && text.includes('End:') && text.includes('Duration:'),
      'Should contain complete timing information');
    assert.ok(text.includes('Status:') && text.includes('Errors:') && text.includes('Warnings:'),
      'Should contain complete status information');
    
    return parseJobExecutionSummary(text);
  }

  function parseJobExecutionSummary(text) {
    const summary = {
      jobName: null,
      timing: { start: null, end: null, duration: null },
      status: { status: null, errors: null, warnings: null }
    };
    
    // Extract structured data from response
    const jobNameMatch = text.match(/Job Execution Summary: ([^\n\r]+)/);
    if (jobNameMatch) summary.jobName = jobNameMatch[1].trim();
    
    const startMatch = text.match(/- Start: ([^\n\r]+)/);
    if (startMatch) summary.timing.start = startMatch[1].trim();
    
    const endMatch = text.match(/- End: ([^\n\r]+)/);
    if (endMatch) summary.timing.end = endMatch[1].trim();
    
    const durationMatch = text.match(/- Duration: ([^\n\r]+)/);
    if (durationMatch) summary.timing.duration = durationMatch[1].trim();
    
    const statusMatch = text.match(/- Status: ([^\n\r]+)/);
    if (statusMatch) summary.status.status = statusMatch[1].trim();
    
    const errorsMatch = text.match(/- Errors: ([^\n\r]+)/);
    if (errorsMatch) summary.status.errors = errorsMatch[1].trim();
    
    const warningsMatch = text.match(/- Warnings: ([^\n\r]+)/);
    if (warningsMatch) summary.status.warnings = warningsMatch[1].trim();
    
    return summary;
  }

  function assertSuccessResponse(result) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, false, 'Response should not be error');
  }

  function assertErrorResponse(result, expectedErrorText = null) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, true, 'Response should be error');
    
    if (expectedErrorText) {
      const responseText = parseResponseText(result.content[0].text);
      assert.ok(responseText.includes(expectedErrorText), 
        `Error message should contain: ${expectedErrorText}`);
    }
  }

  function assertTextContent(result, expectedText) {
    assertSuccessResponse(result);
    const responseText = parseResponseText(result.content[0].text);
    assert.ok(responseText.includes(expectedText), 
      `Response should contain: ${expectedText}`);
  }

  async function discoverJobNames() {
    console.log('🔍 Discovering available job names for advanced testing...');
    
    try {
      const result = await client.callTool('get_latest_job_log_files', { limit: 10 });
      if (!result.isError) {
        const text = parseResponseText(result.content[0].text);
        const jobs = extractJobInfo(text);
        discoveredJobNames = jobs.map(job => job.jobName);
        console.log(`🔧 Found ${discoveredJobNames.length} jobs:`, discoveredJobNames);
      }
    } catch (error) {
      console.warn('⚠️ Could not discover job names:', error.message);
      discoveredJobNames = ['ImportCatalog', 'ProcessOrders'];
    }
  }

  function extractJobInfo(responseText) {
    const jobs = [];
    const text = parseResponseText(responseText);
    const sections = text.split('🔧 Job: ').slice(1);
    
    for (const section of sections) {
      const lines = section.split('\n');
      const jobName = lines[0].trim();
      
      let jobId = null;
      for (const line of lines) {
        const idMatch = line.match(/ID: (\d+)/);
        if (idMatch) jobId = idMatch[1];
      }
      
      if (jobName && jobId) {
        jobs.push({ jobName, jobId });
      }
    }
    
    return jobs;
  }

  // Core functionality tests - focus on complex parsing and validation
  describe('Core Functionality & Format Validation', () => {
    test('should retrieve and parse job execution summary structure', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }
      
      const jobName = discoveredJobNames[0];
      const result = await client.callTool('get_job_execution_summary', { jobName });
      
      const summary = assertJobExecutionSummaryFormat(result, jobName);
      
      if (summary) {
        // Validate parsed structure for existing jobs
        assert.equal(summary.jobName, jobName, 'Parsed job name should match requested');
        assert.ok(summary.timing.start, 'Should have start time');
        assert.ok(summary.timing.end, 'Should have end time');
        assert.ok(summary.timing.duration !== null, 'Should have duration');
        assert.ok(summary.status.status, 'Should have status');
        assert.ok(summary.status.errors !== null, 'Should have error count');
        assert.ok(summary.status.warnings !== null, 'Should have warning count');
      }
    });

    test('should validate timing format in execution summary', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }
      
      const jobName = discoveredJobNames[0];
      const result = await client.callTool('get_job_execution_summary', { jobName });
      
      const summary = assertJobExecutionSummaryFormat(result, jobName);
      
      if (summary) {
        // Validate datetime format (YYYY-MM-DD HH:MM:SS)
        assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(summary.timing.start),
          `Start time should match datetime format: ${summary.timing.start}`);
        assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(summary.timing.end),
          `End time should match datetime format: ${summary.timing.end}`);
        assert.ok(/\d+s/.test(summary.timing.duration),
          `Duration should be in seconds format: ${summary.timing.duration}`);
      }
    });

    test('should validate status information format', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }
      
      const jobName = discoveredJobNames[0];
      const result = await client.callTool('get_job_execution_summary', { jobName });
      
      const summary = assertJobExecutionSummaryFormat(result, jobName);
      
      if (summary) {
        // Status should be a string
        assert.ok(typeof summary.status.status === 'string', 'Status should be string');
        
        // Errors and warnings should be numeric strings
        assert.ok(/\d+/.test(summary.status.errors),
          `Errors should be numeric: ${summary.status.errors}`);
        assert.ok(/\d+/.test(summary.status.warnings),
          `Warnings should be numeric: ${summary.status.warnings}`);
      }
    });

    test('should handle non-existent job gracefully with proper messaging', async () => {
      const result = await client.callTool('get_job_execution_summary', { 
        jobName: 'NonExistentJob12345' 
      });
      
      assertValidMCPResponse(result);
      assert.equal(result.isError, false, 'Should not be an error response for non-existent job');
      assertTextContent(result, 'No job logs found for job name: NonExistentJob12345');
    });
  });

  // Streamlined parameter validation - focus on key error cases
  describe('Parameter Validation', () => {
    test('should return error for missing jobName parameter', async () => {
      const result = await client.callTool('get_job_execution_summary', {});
      assertErrorResponse(result, 'jobName must be a non-empty string');
    });

    test('should return error for empty jobName parameter', async () => {
      const result = await client.callTool('get_job_execution_summary', { jobName: '' });
      assertErrorResponse(result, 'jobName must be a non-empty string');
    });

    test('should return error for null jobName parameter', async () => {
      const result = await client.callTool('get_job_execution_summary', { jobName: null });
      assertErrorResponse(result, 'jobName must be a non-empty string');
    });

    test('should return error for non-string jobName parameter', async () => {
      const result = await client.callTool('get_job_execution_summary', { jobName: 123 });
      assertErrorResponse(result, 'jobName must be a non-empty string');
    });

    test('should return error for whitespace-only jobName', async () => {
      const result = await client.callTool('get_job_execution_summary', { jobName: '   ' });
      assertErrorResponse(result, 'jobName must be a non-empty string');
    });
  });

  // Focus on meaningful edge cases that test real-world scenarios
  describe('Edge Cases & Real-World Scenarios', () => {
    test('should handle job names with special characters', async () => {
      const specialJobName = 'Job-With-Dashes_And_Underscores.123';
      const result = await client.callTool('get_job_execution_summary', { 
        jobName: specialJobName 
      });
      
      assertValidMCPResponse(result);
      assert.equal(result.isError, false);
      assertTextContent(result, `No job logs found for job name: ${specialJobName}`);
    });

    test('should handle job names with Unicode characters', async () => {
      const unicodeJobName = 'Job_测试_🔧_Тест';
      const result = await client.callTool('get_job_execution_summary', { 
        jobName: unicodeJobName 
      });
      
      assertValidMCPResponse(result);
      assert.equal(result.isError, false);
      assertTextContent(result, `No job logs found for job name: ${unicodeJobName}`);
    });

    test('should handle very long job names', async () => {
      const longJobName = 'VeryLongJobNameThatMightCauseIssuesWithSomeSystemsBecauseItExceedsTypicalLimits'.repeat(2);
      const result = await client.callTool('get_job_execution_summary', { 
        jobName: longJobName 
      });
      
      assertValidMCPResponse(result);
      assert.equal(result.isError, false);
      assertTextContent(result, 'No job logs found for job name:');
    });
  });

  // Advanced multi-job testing with discovered jobs
  describe('Multi-Job Integration Testing', () => {
    test('should handle discovered job names with execution summaries', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }

      const results = [];
      
      // Test up to 3 discovered jobs sequentially (avoid concurrent requests)
      const jobsToTest = discoveredJobNames.slice(0, 3);
      
      for (const jobName of jobsToTest) {
        const result = await client.callTool('get_job_execution_summary', { jobName });
        results.push({ jobName, result });
        
        // Each result should be valid
        assertValidMCPResponse(result);
        assert.equal(result.isError, false, `Job ${jobName} should not error`);
        
        const summary = assertJobExecutionSummaryFormat(result, jobName);
        if (summary) {
          assert.equal(summary.jobName, jobName, 'Parsed job name should match');
        }
      }
      
      assert.ok(results.length > 0, 'Should have tested at least one job');
      console.log(`✅ Successfully tested ${results.length} discovered jobs`);
    });

    test('should maintain consistent response format across different jobs', async () => {
      if (discoveredJobNames.length < 2) {
        console.log('⏭️ Skipping test - need at least 2 discovered jobs');
        return;
      }
      
      const results = [];
      
      // Test first 2 jobs for consistency
      for (const jobName of discoveredJobNames.slice(0, 2)) {
        const result = await client.callTool('get_job_execution_summary', { jobName });
        results.push(result);
        
        assertValidMCPResponse(result);
        assert.equal(result.content.length, 1, 'Should have exactly one content item');
        assert.equal(result.content[0].type, 'text', 'Content should be text type');
      }
      
      // All results should have consistent structure
      results.forEach((result, index) => {
        assert.equal(typeof result.isError, 'boolean', `Result ${index} isError should be boolean`);
        assert.ok(Array.isArray(result.content), `Result ${index} content should be array`);
      });
    });
  });

  // Integration with job log ecosystem
  describe('Integration with Job Log Ecosystem', () => {
    test('should integrate with discovered job ecosystem effectively', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }
      
      // Get job log files to validate integration
      const logFilesResult = await client.callTool('get_latest_job_log_files', { limit: 5 });
      assertValidMCPResponse(logFilesResult);
      assert.equal(logFilesResult.isError, false, 'Should get job log files successfully');
      
      // Parse available jobs
      const logText = parseResponseText(logFilesResult.content[0].text);
      const availableJobs = extractJobInfo(logText);
      
      assert.ok(availableJobs.length > 0, 'Should find available jobs from log files');
      
      // Test execution summary for first available job
      const firstJob = availableJobs[0];
      const summaryResult = await client.callTool('get_job_execution_summary', { 
        jobName: firstJob.jobName 
      });
      
      assertValidMCPResponse(summaryResult);
      
      // Should either get a summary or a "no logs found" message
      const summaryText = parseResponseText(summaryResult.content[0].text);
      const hasValidResponse = summaryText.includes('Job Execution Summary:') || 
                              summaryText.includes('No job logs found');
      
      assert.ok(hasValidResponse, 'Should get either execution summary or no logs message');
      
      console.log(`✅ Successfully integrated with job ecosystem (${availableJobs.length} jobs available)`);
    });

    test('should validate execution summary data integrity', async () => {
      if (discoveredJobNames.length === 0) {
        console.log('⏭️ Skipping test - no job names discovered');
        return;
      }
      
      const jobName = discoveredJobNames[0];
      const result = await client.callTool('get_job_execution_summary', { jobName });
      
      const summary = assertJobExecutionSummaryFormat(result, jobName);
      
      if (summary) {
        // Validate data integrity
        assert.ok(summary.jobName.length > 0, 'Job name should not be empty');
        
        // Parse numeric values for validation
        const errorCount = parseInt(summary.status.errors, 10);
        const warningCount = parseInt(summary.status.warnings, 10);
        
        assert.ok(!isNaN(errorCount), 'Error count should be numeric');
        assert.ok(!isNaN(warningCount), 'Warning count should be numeric');
        assert.ok(errorCount >= 0, 'Error count should be non-negative');
        assert.ok(warningCount >= 0, 'Warning count should be non-negative');
        
        // Validate duration format and logic
        const durationMatch = summary.timing.duration.match(/(\d+)s/);
        if (durationMatch) {
          const durationSeconds = parseInt(durationMatch[1], 10);
          assert.ok(durationSeconds >= 0, 'Duration should be non-negative');
        }
        
        console.log(`✅ Data integrity validated for job ${jobName}`);
      }
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/dw_web/FormField.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.web

# Class FormField

## Inheritance Hierarchy

- Object
  - dw.web.FormElement
  - dw.web.FormField

## Description

Represents a field in a form.

## Properties

### checked

**Type:** boolean (Read Only)

Identifies if the current selected state of this field is checked. In case of
 a boolean field the method directly represent the boolean value. In case
 of a string or int field, the method returns true if the current value
 matched with the value specified as "selected-value". In this way a
 selected status can be as determined for non-boolean fields.

### description

**Type:** String (Read Only)

An optinal description for the field.

### error

**Type:** String (Read Only)

The error text that will be shown to the user when the field is
 invalid. The error messages that may be returned by this method are
 defined in the form field definition under the following attribute names:

 
 missing-error
 parse-error
 range-error
 value-error
 

 The framework performs error checks in a specific order, and so if there
 are multiple errors for a single FormField, the following sequence
 defines which error is returned:

 
 When submitting a form entry, whitespace is first trimmed from user
 entry and the entry is parsed into native data type (boolean, date,
 integer, number, or string). A regex, if defined, is also matched against
 the input. If there is an error while parsing or matching with regex,
 "parse-error" is set as error.
 If field was marked as "mandatory" but there is no entry,
 "missing-error" is returned
 The min/max and minlength/maxlength checks are performed. If test
 failed, "range-error" is returned.
 value-error or form-error are returned when "invalidate()" was called
 programatically (or pipelet InvalidateFormElement is used)
 

 If the field is valid, this method returns null. If no error message was
 specified in the form field definition, this method also returns null.

### htmlValue

**Type:** String

The current external string representation of the
 value in this field.

### label

**Type:** String (Read Only)

An optional label text for the field.

### mandatory

**Type:** boolean (Read Only)

Indicates if the field is mandatory.

### maxLength

**Type:** Number (Read Only)

The maximum length for the form field. A maximum length can
 be specified for all form data types, but is only used to validate fields
 of type "string". For other data types the value is just provided as an
 easy way to dynamically format the user interface. If not specified in
 the form definition the default minimum length is Integer.MAX_VALUE.

### maxValue

**Type:** Object (Read Only)

The maximum value for a form field. A maximum value is only
 applicable for fields with the data type "int", "number" and "date".
 If a maximum value was not specified in the form definition the method
 returns null.

### minLength

**Type:** Number (Read Only)

The minimum length for the form field. A minimum length can
 be specified for all form data types, but is only used to validate fields
 of type "string". For other data types the value is just provided as an
 easy way to dynamically format the user interface. If not specified in
 the form definition the default minimum length is 0.

### minValue

**Type:** Object (Read Only)

The minimum value for a form field. A minimum value is only
 applicable for fields with the data type "int", "number" and "date".
 If a minimum value was not specified in the form definition the method
 returns null.

### options

**Type:** FormFieldOptions

A list of possible values for this field. The method
 is typically used to render a selection list or to render radio buttons.

### regEx

**Type:** String (Read Only)

An optional regular expression pattern, which was set in the form
 definition. A pattern is only used for validation only for string fields.
 If no pattern was set, the method returns null.

### selected

**Type:** boolean (Read Only)

Identifies if the current selected state of this field is selected. In case of
 a boolean field the method directly represent the boolean value. In case
 of a string or int field, the method returns true if the current value
 matched with the value specified as "selected-value". In this way a
 selected status can be as determined for non-boolean fields.

### selectedOption

**Type:** FormFieldOption (Read Only)

The selected options or null if the field has no option
 or non is selected.

### selectedOptionObject

**Type:** Object (Read Only)

The object that was optionally associated with the
 currently selected option.

### type

**Type:** Number (Read Only)

The method returns the type of the field. The type is one of the
 FIELD_TYPE constants defined in this class.

### value

**Type:** Object

The internal value representation, which can be a string, a
 number, a boolean or a date.

## Constructor Summary

## Method Summary

### getDescription

**Signature:** `getDescription() : String`

Returns an optinal description for the field.

### getError

**Signature:** `getError() : String`

Returns the error text that will be shown to the user when the field is invalid.

### getHtmlValue

**Signature:** `getHtmlValue() : String`

Returns the current external string representation of the value in this field.

### getLabel

**Signature:** `getLabel() : String`

Returns an optional label text for the field.

### getMaxLength

**Signature:** `getMaxLength() : Number`

Returns the maximum length for the form field.

### getMaxValue

**Signature:** `getMaxValue() : Object`

Returns the maximum value for a form field.

### getMinLength

**Signature:** `getMinLength() : Number`

Returns the minimum length for the form field.

### getMinValue

**Signature:** `getMinValue() : Object`

Returns the minimum value for a form field.

### getOptions

**Signature:** `getOptions() : FormFieldOptions`

Returns a list of possible values for this field.

### getRegEx

**Signature:** `getRegEx() : String`

Returns an optional regular expression pattern, which was set in the form definition.

### getSelectedOption

**Signature:** `getSelectedOption() : FormFieldOption`

Returns the selected options or null if the field has no option or non is selected.

### getSelectedOptionObject

**Signature:** `getSelectedOptionObject() : Object`

Returns the object that was optionally associated with the currently selected option.

### getType

**Signature:** `getType() : Number`

The method returns the type of the field.

### getValue

**Signature:** `getValue() : Object`

Returns the internal value representation, which can be a string, a number, a boolean or a date.

### isChecked

**Signature:** `isChecked() : boolean`

Identifies if the current selected state of this field is checked.

### isMandatory

**Signature:** `isMandatory() : boolean`

Indicates if the field is mandatory.

### isSelected

**Signature:** `isSelected() : boolean`

Identifies if the current selected state of this field is selected.

### setHtmlValue

**Signature:** `setHtmlValue(htmlValue : String) : void`

A form field has two value representations, the HTML value and the plain value.

### setOptions

**Signature:** `setOptions(optionValues : Map) : void`

The method can be called to update an option list based on the given key and values in the given map.

### setOptions

**Signature:** `setOptions(optionValues : Map, begin : Number, end : Number) : void`

The method can be called to update an option list based on the given key and values in the given map.

### setOptions

**Signature:** `setOptions(optionValues : Iterator, begin : Number, end : Number) : void`

The method can be called to update an option list based on the given iterator with objects.

### setOptions

**Signature:** `setOptions(optionValues : Iterator) : void`

The method can be called to update an option list based on the given iterator with objects.

### setValue

**Signature:** `setValue(value : Object) : void`

Sets the typed value of the field.

## Method Detail

## Method Details

### getDescription

**Signature:** `getDescription() : String`

**Description:** Returns an optinal description for the field.

**Returns:**

an optional description for the field.

---

### getError

**Signature:** `getError() : String`

**Description:** Returns the error text that will be shown to the user when the field is invalid. The error messages that may be returned by this method are defined in the form field definition under the following attribute names: missing-error parse-error range-error value-error The framework performs error checks in a specific order, and so if there are multiple errors for a single FormField, the following sequence defines which error is returned: When submitting a form entry, whitespace is first trimmed from user entry and the entry is parsed into native data type (boolean, date, integer, number, or string). A regex, if defined, is also matched against the input. If there is an error while parsing or matching with regex, "parse-error" is set as error. If field was marked as "mandatory" but there is no entry, "missing-error" is returned The min/max and minlength/maxlength checks are performed. If test failed, "range-error" is returned. value-error or form-error are returned when "invalidate()" was called programatically (or pipelet InvalidateFormElement is used) If the field is valid, this method returns null. If no error message was specified in the form field definition, this method also returns null.

**Returns:**

the error text that will be shown to the user when the field is invalid.

---

### getHtmlValue

**Signature:** `getHtmlValue() : String`

**Description:** Returns the current external string representation of the value in this field.

**Returns:**

the current external string representation of the value in this field.

---

### getLabel

**Signature:** `getLabel() : String`

**Description:** Returns an optional label text for the field.

**Returns:**

an optional label text for the field.

---

### getMaxLength

**Signature:** `getMaxLength() : Number`

**Description:** Returns the maximum length for the form field. A maximum length can be specified for all form data types, but is only used to validate fields of type "string". For other data types the value is just provided as an easy way to dynamically format the user interface. If not specified in the form definition the default minimum length is Integer.MAX_VALUE.

**Returns:**

maximum length or MAX_VALUE

---

### getMaxValue

**Signature:** `getMaxValue() : Object`

**Description:** Returns the maximum value for a form field. A maximum value is only applicable for fields with the data type "int", "number" and "date". If a maximum value was not specified in the form definition the method returns null.

**Returns:**

maximum value or null

---

### getMinLength

**Signature:** `getMinLength() : Number`

**Description:** Returns the minimum length for the form field. A minimum length can be specified for all form data types, but is only used to validate fields of type "string". For other data types the value is just provided as an easy way to dynamically format the user interface. If not specified in the form definition the default minimum length is 0.

**Returns:**

minimum length or 0

---

### getMinValue

**Signature:** `getMinValue() : Object`

**Description:** Returns the minimum value for a form field. A minimum value is only applicable for fields with the data type "int", "number" and "date". If a minimum value was not specified in the form definition the method returns null.

**Returns:**

minimum value or null

---

### getOptions

**Signature:** `getOptions() : FormFieldOptions`

**Description:** Returns a list of possible values for this field. The method is typically used to render a selection list or to render radio buttons.

**Returns:**

a list of possible values for this field.

---

### getRegEx

**Signature:** `getRegEx() : String`

**Description:** Returns an optional regular expression pattern, which was set in the form definition. A pattern is only used for validation only for string fields. If no pattern was set, the method returns null.

**Returns:**

the regular expression used for validation or null

---

### getSelectedOption

**Signature:** `getSelectedOption() : FormFieldOption`

**Description:** Returns the selected options or null if the field has no option or non is selected.

**Returns:**

the selected options or null if the field has no option or non is selected.

---

### getSelectedOptionObject

**Signature:** `getSelectedOptionObject() : Object`

**Description:** Returns the object that was optionally associated with the currently selected option.

**Returns:**

the object that was optionally associated with the currently selected option.

---

### getType

**Signature:** `getType() : Number`

**Description:** The method returns the type of the field. The type is one of the FIELD_TYPE constants defined in this class.

**Returns:**

the type of the form field

---

### getValue

**Signature:** `getValue() : Object`

**Description:** Returns the internal value representation, which can be a string, a number, a boolean or a date.

**Returns:**

the internal value representation, which can be a string, a number, a boolean or a date.

---

### isChecked

**Signature:** `isChecked() : boolean`

**Description:** Identifies if the current selected state of this field is checked. In case of a boolean field the method directly represent the boolean value. In case of a string or int field, the method returns true if the current value matched with the value specified as "selected-value". In this way a selected status can be as determined for non-boolean fields.

**Returns:**

true if current selected state of this field is checked.

---

### isMandatory

**Signature:** `isMandatory() : boolean`

**Description:** Indicates if the field is mandatory.

**Returns:**

true if the field is mandatory, false otherwise.

---

### isSelected

**Signature:** `isSelected() : boolean`

**Description:** Identifies if the current selected state of this field is selected. In case of a boolean field the method directly represent the boolean value. In case of a string or int field, the method returns true if the current value matched with the value specified as "selected-value". In this way a selected status can be as determined for non-boolean fields.

**Returns:**

true if current selected state of this field is checked.

---

### setHtmlValue

**Signature:** `setHtmlValue(htmlValue : String) : void`

**Description:** A form field has two value representations, the HTML value and the plain value. The HTML value is always a string representation of the field value. The plain value is the fully typed and validated field value. The sets the HTML value for a field. The method is typically called from the HTTP POST processing framework. The method than parses, validates and assigns the value to the typed field value (see getValue()). If the value is invalid the typed field value is set to null and the valid flag is set to false. The error property contains an error message for the form.

**Parameters:**

- `htmlValue`: the HTML value to use.

---

### setOptions

**Signature:** `setOptions(optionValues : Map) : void`

**Description:** The method can be called to update an option list based on the given key and values in the given map.

**Parameters:**

- `optionValues`: a Map with the values for the option list

---

### setOptions

**Signature:** `setOptions(optionValues : Map, begin : Number, end : Number) : void`

**Description:** The method can be called to update an option list based on the given key and values in the given map. The method also expects and index range. This index range only makes sense when the Map is a SortedMap.

**Parameters:**

- `optionValues`: a Map with the values for the option list.
- `begin`: the index of the first element to use as option value.
- `end`: the last of the last element to use as option value.

---

### setOptions

**Signature:** `setOptions(optionValues : Iterator, begin : Number, end : Number) : void`

**Description:** The method can be called to update an option list based on the given iterator with objects. The option list is updated using the bindings specified in the form definition. If no bindings are specified in the form definition the elements are interpreted as pure strings.

**Parameters:**

- `optionValues`: an iterator hows elements are used as option values
- `begin`: the index of the first element to use as option value
- `end`: the last of the last element to use as option value

---

### setOptions

**Signature:** `setOptions(optionValues : Iterator) : void`

**Description:** The method can be called to update an option list based on the given iterator with objects.

**Parameters:**

- `optionValues`: an iterator whose elements are used as option values

---

### setValue

**Signature:** `setValue(value : Object) : void`

**Description:** Sets the typed value of the field. The value is than immediately formatted into the external string representation, which is availble through the getHtmlValue() method. Also the valid flag is set to true. The actual value is not validated against the rules defined in the form definition. The method is typically used to directly set a typed value and to circumvent the validation rules. The type of the argument must match with the type of the field.

**Parameters:**

- `value`: the value to set.

---
```

--------------------------------------------------------------------------------
/docs/dw_rpc/SOAPUtil.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.rpc

# Class SOAPUtil

## Inheritance Hierarchy

- Object
  - dw.rpc.SOAPUtil

## Description

Utility class for working with SOAP web services. This class provides methods for setting SOAP headers and a set of constants representing the supported header names. If you want to use ws-security features, such as signing and encryption, with your RPC-style SOAP web service, use this class to construct a HashMap with security constants and values. Note: this method handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. The following example configures the ws-security actions taken for the request and response to a web service. importPackage( dw.system ); importPackage( dw.util ); importPackage( dw.rpc ); function execute( args : PipelineDictionary ) : Number { var WSU_NS : String = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; try { // define a map with all the secrets var secretsMap : Map = new HashMap(); secretsMap.put("myclientkey", "ckpass"); secretsMap.put("myservicekey", "ckpass"); secretsMap.put("username", "password"); var requestCfg : Map = new HashMap(); // define the ws actions to be performed requestCfg.put(SOAPUtil.WS_ACTION, SOAPUtil.WS_USERNAME_TOKEN + " " + SOAPUtil.WS_TIMESTAMP + " " + SOAPUtil.WS_SIGNATURE + " " + SOAPUtil.WS_ENCRYPT); requestCfg.put(SOAPUtil.WS_USER, "username"); requestCfg.put(SOAPUtil.WS_PASSWORD_TYPE, SOAPUtil.WS_PW_DIGEST ); requestCfg.put(SOAPUtil.WS_SIG_DIGEST_ALGO, "http://www.w3.org/2001/04/xmlenc#sha256" ); // define signature properties // the keystore file has the basename of the WSDL file and the // file extension based on the keystore type (e.g. HelloWorld.jks). // The keystore file has to be placed beside the WSDL file. requestCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks"); requestCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass"); requestCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myclientkey"); requestCfg.put(SOAPUtil.WS_SIGNATURE_USER, "myclientkey"); // define enrcryption properties requestCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks"); requestCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass"); requestCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myservicekey"); requestCfg.put(SOAPUtil.WS_ENCRYPTION_USER, "myservicekey"); requestCfg.put(SOAPUtil.WS_SIGNATURE_PARTS, "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"); requestCfg.put(SOAPUtil.WS_ENCRYPTION_PARTS,"{Element}{" + WSU_NS + "} Timestamp;"+"{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"); // set the secrets for the callback requestCfg.put(SOAPUtil.WS_SECRETS_MAP, secretsMap); var responseCfg : Map = new HashMap(); // define the ws actions to be performed for the response responseCfg.put(SOAPUtil.WS_ACTION, SOAPUtil.WS_TIMESTAMP + " " + SOAPUtil.WS_SIGNATURE + " " + SOAPUtil.WS_ENCRYPT); // define signature properties responseCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks"); responseCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass"); responseCfg.put(SOAPUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myservicekey"); responseCfg.put(SOAPUtil.WS_SIGNATURE_USER, "myservicekey"); // define decryption properties responseCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks"); responseCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass"); responseCfg.put(SOAPUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myclientkey"); responseCfg.put(SOAPUtil.WS_ENCRYPTION_USER, "myclientkey"); // set the secrets for the callback responseCfg.put(SOAPUtil.WS_SECRETS_MAP, secretsMap); // get the service and stub var helloWorldService : WebReference = webreferences.HelloWorld; var stub : Stub = helloWorldService.defaultService; // set the security SOAPUtil.setWSSecurityConfig(stub, requestCfg, responseCfg); //var h : Hello = new helloWorldService.Hello(); var h = new helloWorldService.com.support.ws.security.test.Hello2(); h.setName('Send Text from client Axis ...'); // call the web service var response = stub.hello2(h); //var response = stub.hello(h); var result = response.getHello2Return(); args.OutStr = result; Logger.error("Hello World We Are SIGNED old version Send Text from client ...", result); return PIPELET_NEXT; } catch (e) { Logger.error("Error in helloWorldRpc.ds is: " + e); return PIPELET_ERROR; } }

## Constants

### WS_ACTION

**Type:** String = "action"

WS-Security action property name. Allowed property values are WS_NO_SECURITY, WS_TIMESTAMP, WS_ENCRYPT, WS_SIGNATURE, WS_USERNAME_TOKEN or a space separated String with multiple values.

### WS_ENC_PROP_KEYSTORE_ALIAS

**Type:** String = "__EncryptionPropKeystoreAlias"

WS-Security encryption: the encryption/decryption keystore alias name

### WS_ENC_PROP_KEYSTORE_PW

**Type:** String = "__EncryptionPropKeystorePassword"

WS-Security encryption: the encryption/decryption keystore password

### WS_ENC_PROP_KEYSTORE_TYPE

**Type:** String = "__EncryptionPropKeystoreType"

WS-Security encryption: the encryption/decryption keystore type ( jks or pkcs12 ), default is jks. Note: the keystore file must have the basename of the WSDL file and the file extension based on the keystore type. For example: MyService.jks. The keystore file must be placed in the same cartridge directory as the WSDL file.

### WS_ENCRYPT

**Type:** String = "Encrypt"

WS-Security action: encrypt the message. The encryption-specific parameters define how to encrypt, which keys to use, and other parameters.

### WS_ENCRYPTION_PARTS

**Type:** String = "encryptionParts"

WS-Security encryption: defines which parts of the request are encrypted.

### WS_ENCRYPTION_USER

**Type:** String = "encryptionUser"

WS-Security encryption: the user's name for encryption.

### WS_NO_SECURITY

**Type:** String = "NoSecurity"

WS-Security action: no security

### WS_PASSWORD_TYPE

**Type:** String = "passwordType"

WS-Security password type: parameter for UsernameToken action to define the encoding of the password. Allowed values are PW_DIGEST or PW_TEXT.

### WS_PW_DIGEST

**Type:** String = "PasswordDigest"

WS-Security password of type digest: use a password digest to send the password information.

### WS_PW_TEXT

**Type:** String = "PasswordText"

WS-Security password of type text: send the password information in clear text.

### WS_SECRETS_MAP

**Type:** String = "__SecretsMap"

A secrets map with the username/password entries is needed to create the password callback object.

### WS_SIG_DIGEST_ALGO

**Type:** String = "signatureDigestAlgorithm"

WS-Security signature: sets the signature digest algorithm to use.

### WS_SIG_PROP_KEYSTORE_ALIAS

**Type:** String = "__SignaturePropKeystoreAlias"

WS-Security signature: the signature keystore alias name

### WS_SIG_PROP_KEYSTORE_PW

**Type:** String = "__SignaturePropKeystorePassword"

WS-Security signature: the signature keystore password.

### WS_SIG_PROP_KEYSTORE_TYPE

**Type:** String = "__SignaturePropKeystoreType"

WS-Security: the signature keystore type ( jks or pkcs12 ). The default is jks. Note: The keystore file must have the basename of the WSDL file and the file extension of the keystore type. For example: MyService.jks. The keystore file must be placed in the same cartridge directory as the WSDL file.

### WS_SIGNATURE

**Type:** String = "Signature"

WS-Security action: sign the message. The signature-specific parameters define how to sign, which keys to use, and other parameters.

### WS_SIGNATURE_PARTS

**Type:** String = "signatureParts"

WS-Security signature: defines which parts of the request are signed.

### WS_SIGNATURE_USER

**Type:** String = "signatureUser"

WS-Security signature: the user's name for signature.

### WS_TIMESTAMP

**Type:** String = "Timestamp"

WS-Security action: add a timestamp to the security header.

### WS_USER

**Type:** String = "user"

WS-Security user name.

### WS_USERNAME_TOKEN

**Type:** String = "UsernameToken"

WS-Security action: add a UsernameToken identification.

## Properties

## Constructor Summary

SOAPUtil()

## Method Summary

### getHTTPRequestHeader

**Signature:** `static getHTTPRequestHeader(svc : Object, key : String) : String`

Returns an HTTP request header property value using the specified key.

### getHTTPResponseHeader

**Signature:** `static getHTTPResponseHeader(svc : Object, key : String) : String`

Returns an HTTP response header property value using the specified key.

### setHeader

**Signature:** `static setHeader(svc : Object, xml : String) : void`

Sets a new SOAPHeaderElement in the SOAP request with the namespace of the XML content.

### setHeader

**Signature:** `static setHeader(svc : Object, xml : String, mustUnderstand : boolean) : void`

Sets a new SOAPHeaderElement in the SOAP request with the namespace of the XML content.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String, mustUnderstand : boolean) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String, mustUnderstand : boolean, actor : String) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object, mustUnderstand : boolean) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object, mustUnderstand : boolean, actor : String) : void`

Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

### setHTTPRequestHeader

**Signature:** `static setHTTPRequestHeader(svc : Object, key : String, value : String) : void`

Sets an HTTP request header property using the specified key and value.

### setWSSecurityConfig

**Signature:** `static setWSSecurityConfig(svc : Object, requestConfigMap : Object, responseConfigMap : Object) : void`

Sets the WS-Security configuration for the request and response based on the constants defined.

## Constructor Detail

## Method Detail

## Method Details

### getHTTPRequestHeader

**Signature:** `static getHTTPRequestHeader(svc : Object, key : String) : String`

**Description:** Returns an HTTP request header property value using the specified key. Null is returned if the key does not represent an HTTP header property.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService().
- `key`: the header property key.

**Returns:**

an HTTP request header property value using the specified key or null.

---

### getHTTPResponseHeader

**Signature:** `static getHTTPResponseHeader(svc : Object, key : String) : String`

**Description:** Returns an HTTP response header property value using the specified key. Null is returned if the key does not represent an HTTP response header property.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService().
- `key`: the header property key.

**Returns:**

an HTTP response header property value using the specified key or null.

---

### setHeader

**Signature:** `static setHeader(svc : Object, xml : String) : void`

**Description:** Sets a new SOAPHeaderElement in the SOAP request with the namespace of the XML content.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `xml`: a string with arbitrary XML content

---

### setHeader

**Signature:** `static setHeader(svc : Object, xml : String, mustUnderstand : boolean) : void`

**Description:** Sets a new SOAPHeaderElement in the SOAP request with the namespace of the XML content.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `xml`: a string with arbitrary XML content
- `mustUnderstand`: sets the SOAP header attribute 'mustUnderstand'

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a string with arbitrary XML content

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String, mustUnderstand : boolean) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a string with arbitrary XML content
- `mustUnderstand`: sets the SOAP header attribute mustUnderstand

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : String, mustUnderstand : boolean, actor : String) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a string with arbitrary XML content
- `mustUnderstand`: sets the SOAP header attribute mustUnderstand
- `actor`: the SOAP actor, which should be set for this header element. null removes any actor.

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a E4X XML object

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object, mustUnderstand : boolean) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a E4X XML object
- `mustUnderstand`: sets the SOAP header attribute mustUnderstand

---

### setHeader

**Signature:** `static setHeader(svc : Object, namespace : String, name : String, xml : Object, mustUnderstand : boolean, actor : String) : void`

**Description:** Creates a new SOAPHeaderElement with the name and namespace and places the given XML into it. var usernameToken : XML = <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:Username>{merchantID}</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"> {merchantPassword} </wsse:Password> </wsse:UsernameToken> SOAPUtil.setHeader( service, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", usernameToken, true, null

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `namespace`: the namespace of the header element
- `name`: the element name for the header element
- `xml`: a E4X XML object
- `mustUnderstand`: sets the SOAP header attribute 'mustUnderstand'
- `actor`: the SOAP actor, which should be set for this header element. null removes any actor.

---

### setHTTPRequestHeader

**Signature:** `static setHTTPRequestHeader(svc : Object, key : String, value : String) : void`

**Description:** Sets an HTTP request header property using the specified key and value.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService().
- `key`: the header property key.
- `value`: the header property value. If the value is null, the property identified by the key is removed from the HTTP request header.

---

### setWSSecurityConfig

**Signature:** `static setWSSecurityConfig(svc : Object, requestConfigMap : Object, responseConfigMap : Object) : void`

**Description:** Sets the WS-Security configuration for the request and response based on the constants defined.

**Deprecated:**

use webreferences2 instead

**Parameters:**

- `svc`: a service stub returned from getService()
- `requestConfigMap`: the WS-Security request config
- `responseConfigMap`: the WS-Security response config

---
```

--------------------------------------------------------------------------------
/docs/dw_campaign/PromotionMgr.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.campaign

# Class PromotionMgr

## Inheritance Hierarchy

- Object
  - dw.campaign.PromotionMgr

## Description

PromotionMgr is used to access campaigns and promotion definitions, display active or upcoming promotions in a storefront, and to calculate and apply promotional discounts to line item containers. To access campaign and promotion definitions, use methods getCampaigns(), getCampaign(String), getPromotions() and getPromotion(String). To display active or upcoming promotions in the storefront, e.g. on product pages, various methods are provided. getActivePromotions() returns a PromotionPlan with all enabled promotions scheduled for now. getActiveCustomerPromotions() returns a PromotionPlan with all enabled promotions scheduled for now and applicable for the session currency, current customer and active source code. getUpcomingPromotions(Number) returns a PromotionPlan with all promotions that are enabled, not scheduled for now, but scheduled for any time between now and now + previewTime(hours). Applying promotions to line item containers is a 3-step process, and PromotionMgr provides methods supporting each of these steps. Convenience methods can be used that execute all three steps at once, or the steps can be executed individually if you need to customize the output of the steps due to specific business rules. Step 1 - calculate active customer promotions: Determine the active promotions for the session currency, current customer, source code and redeemed coupons. Step 2 - calculate applicable discounts: Based on the active promotions determined in step 1, applicable discounts are calculated. Step 3 - apply discounts: applicable discounts are applied to a line item container by creating price adjustment line items. The simpliest way to execute steps 1-3 is to use method applyDiscounts(LineItemCtnr). The method identifies all active customer promotions, calculates and applies applicable discounts. Due to specific business rules it might be necessary to manipulate the set of applicable discounts calculated by the promotion engine before applying them to the line item container. To implement such a scenario, you want to use method getDiscounts(LineItemCtnr), which identifies all active customer promotions, and calculates and returns applicable discounts in an instance of DiscountPlan. The discount plan contains a description for all applicable discounts for the specified line item container, and you can remove discounts from it if necessary. The customized discount plan can then be passed on for application by using method applyDiscounts(DiscountPlan). Due to specific business rules it might be necessary to manipulate the set of active customer promotions before calculating applicable discounts from it. For example, you might want to add promotions to the plan or remove promotions from it. You want to use method getActiveCustomerPromotions() and getActiveCustomerPromotions(Boolean), which identifies all active customer promotions and returns an instance of PromotionPlan. You can add promotions to the promotion plan or remove promotions from the plan. The customized promotion plan can then be passed on to calculate applicable discounts by using method getDiscounts(LineItemCtnr, PromotionPlan).

## Properties

### activeCustomerPromotions

**Type:** PromotionPlan (Read Only)

All promotions scheduled for now and applicable for the
 session currency, current customer, source code, or presented coupons.

 The active promotions are returned in an instance of
 PromotionPlan. The promotion plan contains all
 promotions assigned to any customer group of the current customer, the
 current source code, or coupons in the current session basket.

### activePromotions

**Type:** PromotionPlan (Read Only)

All promotions scheduled for now, and applicable for the
 session currency but regardless of current customer or source code.
 The active promotions are returned in an instance of PromotionPlan.

### campaigns

**Type:** Collection (Read Only)

All campaigns of the current site in no specific order.

### promotions

**Type:** Collection (Read Only)

All promotions of the current site in no specific order.

## Constructor Summary

## Method Summary

### applyDiscounts

**Signature:** `static applyDiscounts(lineItemCtnr : LineItemCtnr) : void`

Identifies active promotions, calculates the applicable discounts from these promotions and applies these discounts to the specified line item container.

### applyDiscounts

**Signature:** `static applyDiscounts(discountPlan : DiscountPlan) : void`

Applies the discounts contained in the specified discount plan to the line item container associated with the discount plan.

### getActiveCustomerPromotions

**Signature:** `static getActiveCustomerPromotions() : PromotionPlan`

Returns all promotions scheduled for now and applicable for the session currency, current customer, source code, or presented coupons. The active promotions are returned in an instance of PromotionPlan.

### getActiveCustomerPromotions

**Signature:** `static getActiveCustomerPromotions(ignoreCouponCondition : boolean) : PromotionPlan`

Returns all promotions scheduled for now and applicable for the session currency, current customer, source code, or presented coupons.

### getActiveCustomerPromotionsForCampaign

**Signature:** `static getActiveCustomerPromotionsForCampaign(campaign : Campaign, from : Date, to : Date) : PromotionPlan`

Returns all promotions assigned to the passed campaign, which are active at some point within the specified date range, and are applicable for the current customer, source code, or presented coupons.

### getActivePromotions

**Signature:** `static getActivePromotions() : PromotionPlan`

Returns all promotions scheduled for now, and applicable for the session currency but regardless of current customer or source code. The active promotions are returned in an instance of PromotionPlan.

### getActivePromotionsForCampaign

**Signature:** `static getActivePromotionsForCampaign(campaign : Campaign, from : Date, to : Date) : PromotionPlan`

Returns all promotions assigned to the passed campaign which are active at some point within the specified date range.

### getCampaign

**Signature:** `static getCampaign(id : String) : Campaign`

Returns the campaign identified by the specified ID.

### getCampaigns

**Signature:** `static getCampaigns() : Collection`

Returns all campaigns of the current site in no specific order.

### getDiscounts

**Signature:** `static getDiscounts(lineItemCtnr : LineItemCtnr) : DiscountPlan`

Returns the discounts applicable for the current customer, active source code and specified line item container.

### getDiscounts

**Signature:** `static getDiscounts(lineItemCtnr : LineItemCtnr, promotionPlan : PromotionPlan) : DiscountPlan`

Returns the discounts applicable for the current customer, active source code and specified line item container, based on the specified promotion plan.

### getPromotion

**Signature:** `static getPromotion(id : String) : Promotion`

Returns the promotion identified by the specified ID.

### getPromotions

**Signature:** `static getPromotions() : Collection`

Returns all promotions of the current site in no specific order.

### getUpcomingCustomerPromotions

**Signature:** `static getUpcomingCustomerPromotions(previewTime : Number) : PromotionPlan`

Returns all promotions currently inactive, but scheduled for any time between now and now + previewTime(hours), and which are applicable based on the current customer, source code, and coupons in the current basket. The parameter previewTime specifies for how many hours promotions should be previewed.

### getUpcomingPromotions

**Signature:** `static getUpcomingPromotions(previewTime : Number) : PromotionPlan`

Returns all promotions currently inactive, but scheduled for any time between now and now + previewTime(hours).

## Method Detail

## Method Details

### applyDiscounts

**Signature:** `static applyDiscounts(lineItemCtnr : LineItemCtnr) : void`

**Description:** Identifies active promotions, calculates the applicable discounts from these promotions and applies these discounts to the specified line item container. As a result of this method, the specified line item container contains price adjustments and/or bonus product line items for all applicable discounts. The method also removes price adjustment and/or bonus product line items from the line item container if the related to promotions/discounts are no longer applicable.

**Parameters:**

- `lineItemCtnr`: Line item ctnr to apply promotions on

---

### applyDiscounts

**Signature:** `static applyDiscounts(discountPlan : DiscountPlan) : void`

**Description:** Applies the discounts contained in the specified discount plan to the line item container associated with the discount plan. As a result of this method, the specified line item container contains price adjustments and/or bonus product line items for all discounts contained in the specified discount plan. The method also removes price adjustment and/or bonus product line items from the line item container if the specified discount plan does not contain the related discount. Note that the method does not evaluate if the discounts in the specified discount plan are applicable nor that the promotions related to these discounts are active.

**Parameters:**

- `discountPlan`: Discount plan to apply to associated line item container

**See Also:**

getDiscounts(LineItemCtnr)
applyDiscounts(LineItemCtnr)

---

### getActiveCustomerPromotions

**Signature:** `static getActiveCustomerPromotions() : PromotionPlan`

**Description:** Returns all promotions scheduled for now and applicable for the session currency, current customer, source code, or presented coupons. The active promotions are returned in an instance of PromotionPlan. The promotion plan contains all promotions assigned to any customer group of the current customer, the current source code, or coupons in the current session basket.

**Returns:**

PromotionPlan with active customer promotions

**See Also:**

getActivePromotions()

---

### getActiveCustomerPromotions

**Signature:** `static getActiveCustomerPromotions(ignoreCouponCondition : boolean) : PromotionPlan`

**Description:** Returns all promotions scheduled for now and applicable for the session currency, current customer, source code, or presented coupons. The active promotions are returned in an instance of PromotionPlan. The promotion plan contains all promotions assigned to any customer group of the current customer, the current source code, or coupons in the current session basket.

**Parameters:**

- `ignoreCouponCondition`: true if coupon condition will be ignored when get active promotions.

**Returns:**

PromotionPlan with active customer promotions

---

### getActiveCustomerPromotionsForCampaign

**Signature:** `static getActiveCustomerPromotionsForCampaign(campaign : Campaign, from : Date, to : Date) : PromotionPlan`

**Description:** Returns all promotions assigned to the passed campaign, which are active at some point within the specified date range, and are applicable for the current customer, source code, or presented coupons. A promotion must be active for an actual time period within the passed date range, and not just a single point. The passed campaign and dates must not be null or an exception is thrown. It is valid for the from and to dates to be in the past. If an invalid time range is specified (i.e. from is after to), the returned PromotionPlan will be empty.

**Parameters:**

- `campaign`: The campaign, must not be null or an exception is thrown.
- `from`: The start of the date range to consider. If null, this signifies an open ended time period.
- `to`: The end of the date range to consider. If null, this signifies an open ended time period.

**Returns:**

PromotionPlan with promotions matching all the criteria.

---

### getActivePromotions

**Signature:** `static getActivePromotions() : PromotionPlan`

**Description:** Returns all promotions scheduled for now, and applicable for the session currency but regardless of current customer or source code. The active promotions are returned in an instance of PromotionPlan.

**Returns:**

PromotionPlan with active promotions

**See Also:**

getActiveCustomerPromotions()

---

### getActivePromotionsForCampaign

**Signature:** `static getActivePromotionsForCampaign(campaign : Campaign, from : Date, to : Date) : PromotionPlan`

**Description:** Returns all promotions assigned to the passed campaign which are active at some point within the specified date range. A promotion must be active for an actual time period within the passed date range, and not just a single point. A promotion must be applicable for the session currency. This method considers only the enabled flags and time conditions of the promotion and campaign. It does not consider whether the current customer satisfies the qualifiers of the promotion (customer group, source code, or coupon). The passed campaign and dates must not be null or an exception is thrown. It is valid for the from and/or to dates to be in the past. If an invalid time range is specified (i.e. from is after to), the returned PromotionPlan will be empty.

**Parameters:**

- `campaign`: The campaign. Must not be null.
- `from`: The start of the date range to consider. Must not be null
- `to`: The end of the date range to consider. Must not be null.

**Returns:**

PromotionPlan with promotions matching all the criteria.

---

### getCampaign

**Signature:** `static getCampaign(id : String) : Campaign`

**Description:** Returns the campaign identified by the specified ID.

**Parameters:**

- `id`: Campaign ID

**Returns:**

Campaign or null if not found

---

### getCampaigns

**Signature:** `static getCampaigns() : Collection`

**Description:** Returns all campaigns of the current site in no specific order.

**Returns:**

All campaigns of current site

---

### getDiscounts

**Signature:** `static getDiscounts(lineItemCtnr : LineItemCtnr) : DiscountPlan`

**Description:** Returns the discounts applicable for the current customer, active source code and specified line item container. This method identifies all active promotions assigned to the customer groups of the current customer, the active source code and any coupon contained in the specified line item container, and calculates applicable discounts from these promotions. The applicable discounts are contained in the returned instance of DiscountPlan.

**Parameters:**

- `lineItemCtnr`: Line item container

**Returns:**

Discount plan with applicable discounts

---

### getDiscounts

**Signature:** `static getDiscounts(lineItemCtnr : LineItemCtnr, promotionPlan : PromotionPlan) : DiscountPlan`

**Description:** Returns the discounts applicable for the current customer, active source code and specified line item container, based on the specified promotion plan. This method calculates applicable discounts from the promotions in the specified promotion plan. Note that any promotion in the promotion plan that is inactive, or not applicable for the current customer or source code, is being ignored. The applicable discounts are contained in the returned instance of DiscountPlan.

**Parameters:**

- `lineItemCtnr`: Line item container
- `promotionPlan`: Promotion plan with active promotions

**Returns:**

Discount plan with applicable discounts

---

### getPromotion

**Signature:** `static getPromotion(id : String) : Promotion`

**Description:** Returns the promotion identified by the specified ID. The same logical promotion may be assigned to multiple campaigns or AB-tests. In this case, the system returns the instance of highest rank. Some attributes of the returned Promotion may vary based on exactly which instance is returned. This method returns null if there is no promotion in the system with the given ID, or if a promotion with the given ID exists but it is not assigned to any campaign or AB-test.

**Parameters:**

- `id`: Promotion ID

**Returns:**

Promotion or null if not found

---

### getPromotions

**Signature:** `static getPromotions() : Collection`

**Description:** Returns all promotions of the current site in no specific order.

**Returns:**

All promotions of current site

---

### getUpcomingCustomerPromotions

**Signature:** `static getUpcomingCustomerPromotions(previewTime : Number) : PromotionPlan`

**Description:** Returns all promotions currently inactive, but scheduled for any time between now and now + previewTime(hours), and which are applicable based on the current customer, source code, and coupons in the current basket. The parameter previewTime specifies for how many hours promotions should be previewed.

**Parameters:**

- `previewTime`: Preview time in hours

**Returns:**

PromotionPlan with active promotions

**See Also:**

getActivePromotions()

---

### getUpcomingPromotions

**Signature:** `static getUpcomingPromotions(previewTime : Number) : PromotionPlan`

**Description:** Returns all promotions currently inactive, but scheduled for any time between now and now + previewTime(hours). The upcoming promotions are returned in an instance of PromotionPlan and might not necessarily be applicable for the current customer or source code. The parameter previewTime specifies for how many hours promotions should be previewed.

**Parameters:**

- `previewTime`: Preview time in hours

**Returns:**

PromotionPlan with active promotions

**See Also:**

getActivePromotions()

---
```

--------------------------------------------------------------------------------
/docs/dw_catalog/ProductInventoryRecord.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.catalog

# Class ProductInventoryRecord

## Inheritance Hierarchy

- Object
  - dw.object.PersistentObject
  - dw.object.ExtensibleObject
    - dw.catalog.ProductInventoryRecord

## Description

The ProductInventoryRecord holds information about a Product's inventory, and availability. When using Omnichannel Inventory (OCI): All ProductInventoryRecord properties are read-only. Calling any setter method throws an IllegalStateException. The ProductInventoryRecord class does not support custom properties. isPerpetual() and isPreorderable() always return false.

## Properties

### allocation

**Type:** Quantity

The allocation quantity that is currently set. The quantity unit is the same unit as the product itself.
 
 When using OCI, returns the physically available quantity. Corresponds to the On Hand quantity in OCI.

### allocationResetDate

**Type:** Date (Read Only)

The date the allocation quantity was initialized or reset.
 
 When using OCI, corresponds to the Effective Date in OCI. The value can be null.

### ATS

**Type:** Quantity (Read Only)

The quantity of items available to sell (ATS). This is calculated as the allocation
 (getAllocation()) plus the preorderBackorderAllocation (getPreorderBackorderAllocation()) minus
 the turnover (getTurnover()) minus the on order quantity (getOnOrder()).
 
 When using OCI, corresponds to the ATO (Available To Order) quantity in OCI.

### backorderable

**Type:** boolean

Determines if the product is backorderable.
 
 When using OCI, returns true if the product has at least one Future quantity in OCI.

### custom

**Type:** CustomAttributes (Read Only)

The custom attributes for this object. The returned object is used for retrieving and storing attribute
 values. See CustomAttributes for a detailed example of the syntax for working with custom
 attributes.
 
 When using Omnichannel Inventory (OCI), this class doesn't support custom attributes. If OCI is enabled, then
 attempting to set or modify a custom attribute value throws an UnsupportedOperationException.

### inStockDate

**Type:** Date

The date that the item is expected to be in stock.
 
 When using OCI, returns the date of the earliest Future quantity. If the product has no Future quantities, it
 returns null.

### onHand

**Type:** Quantity (Read Only)

The on-hand quantity, the actual quantity of available (on-hand) items.

### onOrder

**Type:** Quantity (Read Only)

The quantity that is currently on order.
 
 This is only relevant when On Order Inventory is turned on for the related inventory list. On Order is a bucket
 of inventory used for the time between order creation and order export to external (warehouse) systems. On Order
 value is increased when an order is created. It will be decreased and with that turnover will be increased when
 the order is exported, see getTurnover(). Notice that Order.setExportStatus(Number) and
 OrderItem.allocateInventory(Boolean) will decrease the On Order value. On order will be included
 in the ATS calculation, see getATS().
 
 
 When using OCI, always returns zero. OCI doesn't support On Order inventory.

### perpetual

**Type:** boolean

Determines if the product is perpetually in stock.
 
 When using OCI, always returns false.

### preorderable

**Type:** boolean

Determines if the product is preorderable.
 
 When using OCI, always returns false.

### preorderBackorderAllocation

**Type:** Quantity

The quantity of items that are allocated for sale, beyond the initial stock allocation.
 
 When using OCI, returns the sum of all Future quantities. If the product has no Future quantities, it returns
 zero.

### reserved

**Type:** Quantity (Read Only)

The quantity of items that are reserved.
 
 Note that for products with high velocity and concurrency, the return value is extremely volatile and the
 retrieval will be expensive.
 
 When using OCI, always returns zero.

### stockLevel

**Type:** Quantity (Read Only)

The current stock level. This is calculated as the allocation minus the turnover.
 
 When using OCI, corresponds to the ATF (Available To Fulfill) quantity in OCI.

### turnover

**Type:** Quantity (Read Only)

The sum of all inventory transactions (decrements and increments) recorded after the allocation reset
 date. If the total decremented quantity is greater than the total incremented quantity, then this value is
 negative.
 
 When using OCI, returns the total reserved quantity, including order, basket, and temporary reservations.

## Constructor Summary

## Method Summary

### describe

**Signature:** `describe() : ObjectTypeDefinition`

Returns the meta data of this object.

### getAllocation

**Signature:** `getAllocation() : Quantity`

Returns the allocation quantity that is currently set.

### getAllocationResetDate

**Signature:** `getAllocationResetDate() : Date`

Returns the date the allocation quantity was initialized or reset.

### getATS

**Signature:** `getATS() : Quantity`

Returns the quantity of items available to sell (ATS).

### getCustom

**Signature:** `getCustom() : CustomAttributes`

Returns the custom attributes for this object.

### getInStockDate

**Signature:** `getInStockDate() : Date`

Returns the date that the item is expected to be in stock.

### getOnHand

**Signature:** `getOnHand() : Quantity`

Returns the on-hand quantity, the actual quantity of available (on-hand) items.

### getOnOrder

**Signature:** `getOnOrder() : Quantity`

Returns the quantity that is currently on order.

### getPreorderBackorderAllocation

**Signature:** `getPreorderBackorderAllocation() : Quantity`

Returns the quantity of items that are allocated for sale, beyond the initial stock allocation.

### getReserved

**Signature:** `getReserved() : Quantity`

Returns the quantity of items that are reserved.

### getStockLevel

**Signature:** `getStockLevel() : Quantity`

Returns the current stock level.

### getTurnover

**Signature:** `getTurnover() : Quantity`

Returns the sum of all inventory transactions (decrements and increments) recorded after the allocation reset date.

### isBackorderable

**Signature:** `isBackorderable() : boolean`

Determines if the product is backorderable.

### isPerpetual

**Signature:** `isPerpetual() : boolean`

Determines if the product is perpetually in stock.

### isPreorderable

**Signature:** `isPreorderable() : boolean`

Determines if the product is preorderable.

### setAllocation

**Signature:** `setAllocation(quantity : Number) : void`

Sets the allocation quantity.

### setAllocation

**Signature:** `setAllocation(quantity : Number, allocationResetDate : Date) : void`

Sets the allocation quantity.

### setBackorderable

**Signature:** `setBackorderable(backorderableFlag : boolean) : void`

The method is used to set whether the product is backorderable.

### setInStockDate

**Signature:** `setInStockDate(inStockDate : Date) : void`

Sets the date that the item is expected to be in stock.

### setPerpetual

**Signature:** `setPerpetual(perpetualFlag : boolean) : void`

Sets if the product is perpetually in stock.

### setPreorderable

**Signature:** `setPreorderable(preorderableFlag : boolean) : void`

The method is used to set whether the product is preorderable.

### setPreorderBackorderAllocation

**Signature:** `setPreorderBackorderAllocation(quantity : Number) : void`

Sets the quantity of items that are allocated for sale, beyond the initial stock allocation.

## Method Detail

## Method Details

### describe

**Signature:** `describe() : ObjectTypeDefinition`

**Description:** Returns the meta data of this object. If no meta data is available the method returns null. The returned ObjectTypeDefinition can be used to retrieve the metadata for any of the custom attributes. When using Omnichannel Inventory (OCI), this class doesn't support custom attributes. If OCI is enabled, then attempting to set or modify a custom attribute value throws an UnsupportedOperationException.

**Returns:**

the meta data of this object. If no meta data is available the method returns null.

---

### getAllocation

**Signature:** `getAllocation() : Quantity`

**Description:** Returns the allocation quantity that is currently set. The quantity unit is the same unit as the product itself. When using OCI, returns the physically available quantity. Corresponds to the On Hand quantity in OCI.

**Returns:**

the allocation quantity or quantity N/A if not available.

---

### getAllocationResetDate

**Signature:** `getAllocationResetDate() : Date`

**Description:** Returns the date the allocation quantity was initialized or reset. When using OCI, corresponds to the Effective Date in OCI. The value can be null.

**Returns:**

the allocation reset date.

---

### getATS

**Signature:** `getATS() : Quantity`

**Description:** Returns the quantity of items available to sell (ATS). This is calculated as the allocation (getAllocation()) plus the preorderBackorderAllocation (getPreorderBackorderAllocation()) minus the turnover (getTurnover()) minus the on order quantity (getOnOrder()). When using OCI, corresponds to the ATO (Available To Order) quantity in OCI.

**Returns:**

the quantity or quantity N/A if not available.

---

### getCustom

**Signature:** `getCustom() : CustomAttributes`

**Description:** Returns the custom attributes for this object. The returned object is used for retrieving and storing attribute values. See CustomAttributes for a detailed example of the syntax for working with custom attributes. When using Omnichannel Inventory (OCI), this class doesn't support custom attributes. If OCI is enabled, then attempting to set or modify a custom attribute value throws an UnsupportedOperationException.

**Returns:**

the custom attributes for this object.

---

### getInStockDate

**Signature:** `getInStockDate() : Date`

**Description:** Returns the date that the item is expected to be in stock. When using OCI, returns the date of the earliest Future quantity. If the product has no Future quantities, it returns null.

**Returns:**

the date that the item is expected to be in stock.

---

### getOnHand

**Signature:** `getOnHand() : Quantity`

**Description:** Returns the on-hand quantity, the actual quantity of available (on-hand) items.

**API Versioned:**

No longer available as of version 21.7.

**Deprecated:**

Use getStockLevel() instead.

**Returns:**

the on-hand quantity or quantity N/A if not available.

---

### getOnOrder

**Signature:** `getOnOrder() : Quantity`

**Description:** Returns the quantity that is currently on order. This is only relevant when On Order Inventory is turned on for the related inventory list. On Order is a bucket of inventory used for the time between order creation and order export to external (warehouse) systems. On Order value is increased when an order is created. It will be decreased and with that turnover will be increased when the order is exported, see getTurnover(). Notice that Order.setExportStatus(Number) and OrderItem.allocateInventory(Boolean) will decrease the On Order value. On order will be included in the ATS calculation, see getATS(). When using OCI, always returns zero. OCI doesn't support On Order inventory.

**Returns:**

the quantity or quantity N/A if not available.

---

### getPreorderBackorderAllocation

**Signature:** `getPreorderBackorderAllocation() : Quantity`

**Description:** Returns the quantity of items that are allocated for sale, beyond the initial stock allocation. When using OCI, returns the sum of all Future quantities. If the product has no Future quantities, it returns zero.

**Returns:**

the quantity or quantity N/A if not available.

---

### getReserved

**Signature:** `getReserved() : Quantity`

**Description:** Returns the quantity of items that are reserved. Note that for products with high velocity and concurrency, the return value is extremely volatile and the retrieval will be expensive. When using OCI, always returns zero.

**Returns:**

the quantity of items reserved for this product.

---

### getStockLevel

**Signature:** `getStockLevel() : Quantity`

**Description:** Returns the current stock level. This is calculated as the allocation minus the turnover. When using OCI, corresponds to the ATF (Available To Fulfill) quantity in OCI.

**Returns:**

the stock level or quantity N/A if not available.

---

### getTurnover

**Signature:** `getTurnover() : Quantity`

**Description:** Returns the sum of all inventory transactions (decrements and increments) recorded after the allocation reset date. If the total decremented quantity is greater than the total incremented quantity, then this value is negative. When using OCI, returns the total reserved quantity, including order, basket, and temporary reservations.

**Returns:**

the turnover or quantity N/A if not available.

---

### isBackorderable

**Signature:** `isBackorderable() : boolean`

**Description:** Determines if the product is backorderable. When using OCI, returns true if the product has at least one Future quantity in OCI.

**Returns:**

true if the product is backorderable.

---

### isPerpetual

**Signature:** `isPerpetual() : boolean`

**Description:** Determines if the product is perpetually in stock. When using OCI, always returns false.

**Returns:**

true if the product is perpetually in stock.

---

### isPreorderable

**Signature:** `isPreorderable() : boolean`

**Description:** Determines if the product is preorderable. When using OCI, always returns false.

**Returns:**

true if the product is preorderable.

---

### setAllocation

**Signature:** `setAllocation(quantity : Number) : void`

**Description:** Sets the allocation quantity. This also updates the allocation reset date. All final reservations will be considered as expired and will therefore no longer be considered for ATS. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request.

**API Versioned:**

No longer available as of version 21.7.

**Parameters:**

- `quantity`: the allocation quantity to set (must be greater than or equal to zero).

---

### setAllocation

**Signature:** `setAllocation(quantity : Number, allocationResetDate : Date) : void`

**Description:** Sets the allocation quantity. This also updates the allocation reset date. Any final reservations made prior to the allocation reset date will be considered as expired and will therefore no longer be considered for ATS. When using OCI, throws an IllegalStateException. This method must not be called within a storefront request.

**Parameters:**

- `quantity`: the allocation quantity to set (must be greater than or equal to zero).
- `allocationResetDate`: the date allocation quantity was effectively calculated the reset date must not be older than 48 hours the reset date must not be older than the prior reset date. see getAllocationResetDate()

---

### setBackorderable

**Signature:** `setBackorderable(backorderableFlag : boolean) : void`

**Description:** The method is used to set whether the product is backorderable. Setting the backorderable flag to true will clear the preorderable flag. If the record's preorderable flag is set to true, calling this method with backorderableFlag==false will have no effect. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request. This method must not be called within a storefront request when the API version is 21.7 or later.

**Parameters:**

- `backorderableFlag`: the flag to set backorderable status.

---

### setInStockDate

**Signature:** `setInStockDate(inStockDate : Date) : void`

**Description:** Sets the date that the item is expected to be in stock. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request. This method must not be called within a storefront request when the API version is 21.7 or later.

**Parameters:**

- `inStockDate`: the date that the item is expected to be in stock.

---

### setPerpetual

**Signature:** `setPerpetual(perpetualFlag : boolean) : void`

**Description:** Sets if the product is perpetually in stock. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request. This method must not be called within a storefront request when the API version is 21.7 or later.

**Parameters:**

- `perpetualFlag`: true to set the product perpetually in stock.

---

### setPreorderable

**Signature:** `setPreorderable(preorderableFlag : boolean) : void`

**Description:** The method is used to set whether the product is preorderable. Setting the preorderable flag to true will clear the backorderable flag. If the record's backorderable flag is set to true, calling this method with preorderableFlag==false will have no effect. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request. This method must not be called within a storefront request when the API version is 21.7 or later.

**Parameters:**

- `preorderableFlag`: the flag to set preorderable status.

---

### setPreorderBackorderAllocation

**Signature:** `setPreorderBackorderAllocation(quantity : Number) : void`

**Description:** Sets the quantity of items that are allocated for sale, beyond the initial stock allocation. When using OCI, throws an IllegalStateException. This method should not be called within a storefront request. This method must not be called within a storefront request when the API version is 21.7 or later.

**Parameters:**

- `quantity`: the quantity to set.

---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/search-site-preferences.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
# ==================================================================================
# SFCC MCP Server - search_site_preferences Tool YAML Tests (Full Mode)
# Streamlined smoke testing and declarative validation for core functionality
# Complex business logic, edge cases, and workflows are covered in programmatic tests
# 
# Available Preference Groups in Mock Server:
# - Storefront (shopping cart, max items, welcome message, etc.)
# - System (system-level preferences)
# - SFRA (storefront reference architecture preferences)
# - CCV (custom checkout validation preferences)
# - FastForward (fast forward specific preferences)
# - Integration (integration-specific preferences)
# 
# Quick Test Commands:
# aegis "tests/mcp/yaml/search-site-preferences.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --verbose
# aegis query search_site_preferences '{"groupId": "Storefront", "instanceType": "sandbox", "searchRequest": {"query": {"match_all_query": {}}, "count": 3}}' --config "aegis.config.with-dw.json"
# ==================================================================================

description: "search_site_preferences tool smoke tests - Basic functionality validation"

tests:
  # ==================================================================================
  # TOOL AVAILABILITY VALIDATION
  # ==================================================================================
  - it: "should have search_site_preferences tool available with proper schema"
    request:
      jsonrpc: "2.0"
      id: "tool-available"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "tool-available"
        result:
          tools:
            match:arrayElements:
              match:partial:
                name: "match:type:string"
                description: "match:type:string"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:search_site_preferences"
      stderr: "toBeEmpty"

  # ==================================================================================
  # CORE FUNCTIONALITY VALIDATION - match_all_query
  # ==================================================================================
  - it: "should successfully search Storefront preferences with match_all_query and return valid structure"
    request:
      jsonrpc: "2.0"
      id: "storefront-match-all-success"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 3
    expect:
      response:
        jsonrpc: "2.0"
        id: "storefront-match-all-success"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      performance:
        maxResponseTime: "2000ms"
      stderr: "toBeEmpty"

  - it: "should return valid JSON structure with preference data in match_all_query response"
    request:
      jsonrpc: "2.0"
      id: "storefront-structure"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "storefront-structure"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"_type\"[\\s\\S]*\"preference_value_search_result\"[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should include query echo and pagination info in match_all_query response"
    request:
      jsonrpc: "2.0"
      id: "storefront-metadata"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 2
            start: 0
    expect:
      response:
        jsonrpc: "2.0"
        id: "storefront-metadata"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"match_all_query\"[\\s\\S]*\"start\"[\\s\\S]*\"total\"[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should return site preference data with attribute definitions and site values"
    request:
      jsonrpc: "2.0"
      id: "storefront-content"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
    expect:
      response:
        jsonrpc: "2.0"
        id: "storefront-content"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"attribute_definition\"[\\s\\S]*\"site_values\"[\\s\\S]*\"value_type\"[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # TEXT QUERY FUNCTIONALITY VALIDATION
  # ==================================================================================
  - it: "should successfully filter preferences with text_query"
    request:
      jsonrpc: "2.0"
      id: "text-query-success"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              text_query:
                fields: ["id", "display_name"]
                search_phrase: "cart"
            count: 5
    expect:
      response:
        jsonrpc: "2.0"
        id: "text-query-success"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"text_query\"[\\s\\S]*\"cart\"[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "2000ms"
      stderr: "toBeEmpty"

  - it: "should return filtered results when searching for cart preferences"
    request:
      jsonrpc: "2.0"
      id: "text-query-filtered"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              text_query:
                fields: ["id", "display_name"]
                search_phrase: "cart"
    expect:
      response:
        jsonrpc: "2.0"
        id: "text-query-filtered"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*(?:shoppingCartEnabled|maxCartItems)[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # DIFFERENT PREFERENCE GROUPS VALIDATION
  # ==================================================================================
  - it: "should successfully search System preferences group"
    request:
      jsonrpc: "2.0"
      id: "system-group-success"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "System"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "system-group-success"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      performance:
        maxResponseTime: "2000ms"
      stderr: "toBeEmpty"

  - it: "should successfully search SFRA preferences group"
    request:
      jsonrpc: "2.0"
      id: "sfra-group-success"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "SFRA"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-group-success"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # INSTANCE TYPE VALIDATION
  # ==================================================================================
  - it: "should accept different instance types (development)"
    request:
      jsonrpc: "2.0"
      id: "instance-development"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "development"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
    expect:
      response:
        jsonrpc: "2.0"
        id: "instance-development"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  - it: "should accept staging instance type"
    request:
      jsonrpc: "2.0"
      id: "instance-staging"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "staging"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
    expect:
      response:
        jsonrpc: "2.0"
        id: "instance-staging"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # PAGINATION VALIDATION
  # ==================================================================================
  - it: "should respect count parameter for result limiting"
    request:
      jsonrpc: "2.0"
      id: "pagination-count"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
    expect:
      response:
        jsonrpc: "2.0"
        id: "pagination-count"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"count\"[\\s\\S]*:[\\s\\S]*1[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should respect start parameter for pagination"
    request:
      jsonrpc: "2.0"
      id: "pagination-start"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            start: 1
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "pagination-start"
        result:
          content:
            - type: "text"
              text: "match:regex:[\\s\\S]*\"start\"[\\s\\S]*:[\\s\\S]*1[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # ERROR HANDLING VALIDATION
  # ==================================================================================
  - it: "should return 404 error for non-existent preference group"
    request:
      jsonrpc: "2.0"
      id: "error-nonexistent-group"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "NonExistentGroup"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "error-nonexistent-group"
        result:
          content:
            - type: "text"
              text: "match:contains:CustomPreferenceGroupNotFoundException"
          isError: true
      performance:
        maxResponseTime: "1000ms"
      stderr: "toBeEmpty"

  - it: "should handle invalid parameters gracefully"
    request:
      jsonrpc: "2.0"
      id: "error-invalid-params"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: ""
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "error-invalid-params"
        result:
          content:
            - type: "text"
              text: "match:contains:groupId must be a non-empty string"
          isError: true
      performance:
        maxResponseTime: "1000ms"
      stderr: "toBeEmpty"

  # ==================================================================================
  # ADVANCED QUERY VALIDATION
  # ==================================================================================
  - it: "should handle term_query with exact matches"
    request:
      jsonrpc: "2.0"
      id: "term-query-success"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              term_query:
                fields: ["value_type"]
                operator: "is"
                values: ["boolean"]
    expect:
      response:
        jsonrpc: "2.0"
        id: "term-query-success"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      performance:
        maxResponseTime: "2000ms"
      stderr: "toBeEmpty"

  # ==================================================================================
  # OPTIONS VALIDATION
  # ==================================================================================
  - it: "should accept options parameter with maskPasswords"
    request:
      jsonrpc: "2.0"
      id: "options-mask-passwords"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
          options:
            maskPasswords: true
    expect:
      response:
        jsonrpc: "2.0"
        id: "options-mask-passwords"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  - it: "should accept options parameter with expand value"
    request:
      jsonrpc: "2.0"
      id: "options-expand-value"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 1
          options:
            expand: "value"
    expect:
      response:
        jsonrpc: "2.0"
        id: "options-expand-value"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # SORTING VALIDATION
  # ==================================================================================
  - it: "should accept sorting parameters"
    request:
      jsonrpc: "2.0"
      id: "sorting-test"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            sorts:
              - field: "id"
                sort_order: "asc"
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "sorting-test"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # PERFORMANCE VALIDATION
  # ==================================================================================
  - it: "should complete large requests within reasonable time"
    request:
      jsonrpc: "2.0"
      id: "performance-large-request"
      method: "tools/call"
      params:
        name: "search_site_preferences"
        arguments:
          groupId: "Storefront"
          instanceType: "sandbox"
          searchRequest:
            query:
              match_all_query: {}
            count: 50
    expect:
      response:
        jsonrpc: "2.0"
        id: "performance-large-request"
        result:
          content:
            - type: "text"
              text: "match:contains:preference_value_search_result"
          isError: false
      performance:
        maxResponseTime: "3000ms"
      stderr: "toBeEmpty"
```

--------------------------------------------------------------------------------
/tests/mcp/node/search-logs.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

describe('search_logs - Full Mode Programmatic Tests (Complex Logic & Content Analysis)', () => {
  let client;

  before(async () => {
    client = await connect('./aegis.config.with-dw.json');
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking into next tests
    client.clearAllBuffers();
  });

  // Helper functions for complex content analysis
  function parseResponseText(text) {
    return text.startsWith('"') && text.endsWith('"') 
      ? JSON.parse(text) 
      : text;
  }

  function extractLogEntries(text) {
    // Extract individual log entries from the response - more flexible parsing
    const entries = [];
    const lines = text.split('\n');
    
    for (const line of lines) {
      const trimmed = line.trim();
      // Look for lines that contain log file references or timestamp patterns
      if (trimmed && (trimmed.includes('[') || /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(trimmed))) {
        // Skip header lines and summary lines
        if (!trimmed.startsWith('Found ') && !trimmed.startsWith('No matches') && trimmed.length > 10) {
          entries.push(trimmed);
        }
      }
    }
    return entries;
  }

  function parseLogEntry(entry) {
    // Parse structured information from a log entry - more robust parsing
    const logFileMatch = entry.match(/\[([^\]]+)\]/) || entry.match(/(\w+-blade-\d{8}-\d{6}\.log)/);
    const timestampMatch = entry.match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{3})? GMT)/);
    const levelMatch = entry.match(/(ERROR|WARN|INFO|DEBUG)/i);
    const threadMatch = entry.match(/\|(\d+)\|/);
    const siteMatch = entry.match(/Sites-([^|\s]+)/);
    
    return {
      logFile: logFileMatch?.[1] || logFileMatch?.[0] || null,
      timestamp: timestampMatch?.[1] || null,
      level: levelMatch?.[1]?.toUpperCase() || null,
      threadId: threadMatch?.[1] || null,
      site: siteMatch?.[1] || null,
      fullEntry: entry,
      isValid: !!(logFileMatch || timestampMatch) // Entry is valid if it has either log file or timestamp
    };
  }

  function analyzeSearchResults(result, pattern) {
    assert.equal(result.isError, false, 'Should not be an error response');
    const text = parseResponseText(result.content[0].text);
    
    if (text.includes('No matches found')) {
      return { hasMatches: false, count: 0, pattern, entries: [] };
    }
    
    const foundMatch = text.match(/Found (\d+) matches for "([^"]+)"/);
    assert.ok(foundMatch, 'Should contain "Found X matches for" message');
    
    const count = parseInt(foundMatch[1]);
    const searchPattern = foundMatch[2];
    assert.equal(searchPattern, pattern, 'Pattern should match what was searched');
    
    const entries = extractLogEntries(text);
    return { hasMatches: true, count, pattern, entries, rawText: text };
  }

  function getCurrentDateString() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    return `${year}${month}${day}`;
  }

  // Complex content analysis and business logic validation
  describe('Advanced Content Analysis', () => {
    test('should parse log entry structure and validate SFCC patterns', async () => {
      const result = await client.callTool('search_logs', {
        pattern: 'PipelineCallServlet',
        limit: 3
      });
      
      const analysis = analyzeSearchResults(result, 'PipelineCallServlet');
      
      if (analysis.hasMatches && analysis.entries.length > 0) {
        let validEntries = 0;
        
        // Parse each log entry for detailed validation
        analysis.entries.forEach(entry => {
          const parsed = parseLogEntry(entry);
          
          // Only validate entries that have recognizable structure
          if (parsed.isValid) {
            validEntries++;
            
            // Validate log file naming convention if present
            if (parsed.logFile) {
              assert.ok(/^(error|warn|info|debug)-blade-\d{8}-\d{6}\.log$/.test(parsed.logFile),
                `Log file "${parsed.logFile}" should follow SFCC naming convention`);
            }
            
            // Validate timestamp format if present
            if (parsed.timestamp) {
              assert.ok(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d{3})? GMT$/.test(parsed.timestamp),
                `Timestamp "${parsed.timestamp}" should be in GMT format`);
            }
            
            // Validate thread ID structure if present
            if (parsed.threadId) {
              assert.ok(/^\d+$/.test(parsed.threadId),
                `Thread ID "${parsed.threadId}" should be numeric`);
            }
            
            // Validate SFCC site context if present
            if (parsed.site) {
              assert.ok(parsed.site.length > 0,
                'Site name should not be empty');
            }
          }
        });
        
        // At least some entries should be valid and parseable
        assert.ok(validEntries > 0, 
          `Should have at least some valid entries, found ${validEntries} out of ${analysis.entries.length}`);
      }
    });

    test('should validate log level filtering accuracy', async () => {
      const logLevels = ['error', 'info'];
      const results = {};
      
      // Test multiple log levels sequentially
      for (const level of logLevels) {
        const result = await client.callTool('search_logs', {
          pattern: 'Sites',
          logLevel: level,
          limit: 5
        });
        
        results[level] = analyzeSearchResults(result, 'Sites');
        
        if (results[level].hasMatches) {
          // Validate that entries are related to the requested log level
          results[level].entries.forEach(entry => {
            const parsed = parseLogEntry(entry);
            if (parsed.logFile) {
              const levelPattern = new RegExp(`${level}-blade`, 'i');
              assert.ok(levelPattern.test(parsed.logFile),
                `Log file should contain "${level}-blade" pattern: ${parsed.logFile}`);
            }
          });
        }
      }
      
      // Cross-validate that different log levels return different files (if both have results)
      if (results.error.hasMatches && results.info.hasMatches) {
        const errorLogFiles = results.error.entries
          .map(e => parseLogEntry(e))
          .filter(p => p.logFile)
          .map(p => p.logFile);
        
        const infoLogFiles = results.info.entries
          .map(e => parseLogEntry(e))
          .filter(p => p.logFile)
          .map(p => p.logFile);
        
        // Should have different log files for different levels (if we have sufficient data)
        if (errorLogFiles.length > 0 && infoLogFiles.length > 0) {
          const hasOverlap = errorLogFiles.some(file => infoLogFiles.includes(file));
          // Note: This assertion might fail in some environments - that's useful information!
          if (hasOverlap) {
            // Warning: Error and info searches returned overlapping log files - this might indicate they search across all levels
          } else {
            // Good: Error and info searches returned different log files as expected
          }
        }
      } else {
        // Log level filtering test: insufficient data for cross-validation
      }
    });

    test('should validate chronological ordering of results', async () => {
      const result = await client.callTool('search_logs', {
        pattern: 'Job',
        limit: 10
      });
      
      const analysis = analyzeSearchResults(result, 'Job');
      
      if (analysis.hasMatches && analysis.entries.length > 1) {
        const timestampEntries = analysis.entries
          .map(entry => parseLogEntry(entry))
          .filter(parsed => parsed.timestamp && parsed.isValid);
        
        if (timestampEntries.length > 1) {
          const timestamps = timestampEntries.map(parsed => new Date(parsed.timestamp));
          
          // Validate that timestamps are in descending order (most recent first)
          let orderingCorrect = true;
          let orderingIssues = [];
          
          for (let i = 1; i < timestamps.length; i++) {
            if (timestamps[i-1] < timestamps[i]) {
              orderingCorrect = false;
              orderingIssues.push({
                previous: timestamps[i-1].toISOString(),
                current: timestamps[i].toISOString(),
                index: i
              });
            }
          }
          
          if (!orderingCorrect) {
            // Log the ordering issues for debugging but don't fail the test
          } else {
            // Chronological ordering verified: timestamps are in descending order
          }
          
          // Less strict assertion - log ordering may vary due to distributed logging or processing
          const outOfOrderCount = orderingIssues.length;
          const totalPairs = timestamps.length - 1;
          const outOfOrderRatio = outOfOrderCount / totalPairs;
          
          // More realistic threshold - allow up to 70% out of order for distributed systems
          assert.ok(outOfOrderRatio < 0.7, 
            `More than 70% of timestamp pairs are out of order (${outOfOrderCount}/${totalPairs}). This suggests the system may have significant ordering challenges.`);
          
          if (outOfOrderRatio > 0.3) {
            // Note: ${Math.round(outOfOrderRatio * 100)}% of entries are out of chronological order - this may be normal for distributed logging systems
          }
        } else {
          // Insufficient timestamp data for chronological validation
        }
      } else {
        // No Job entries found or insufficient data for chronological validation
      }
    });
  });

  // Multi-step workflow testing
  describe('Multi-Step Search Workflows', () => {
    test('should support progressive search refinement', async () => {
      // Step 1: Broad search
      const broadResult = await client.callTool('search_logs', {
        pattern: 'Order',
        limit: 20
      });
      
      const broadAnalysis = analyzeSearchResults(broadResult, 'Order');
      
      if (broadAnalysis.hasMatches) {
        // Step 2: Refined search for specific context
        const refinedResult = await client.callTool('search_logs', {
          pattern: 'Order.*confirmation',
          logLevel: 'info',
          limit: 10
        });
        
        const refinedAnalysis = analyzeSearchResults(refinedResult, 'Order.*confirmation');
        
        // Validate refinement effectiveness
        if (refinedAnalysis.hasMatches) {
          assert.ok(refinedAnalysis.count <= broadAnalysis.count,
            'Refined search should return fewer or equal results');
          
          // All refined results should contain both Order and confirmation
          refinedAnalysis.entries.forEach(entry => {
            assert.ok(entry.toLowerCase().includes('order'),
              'Refined entry should contain "order"');
          });
        }
      }
    });

    test('should maintain search consistency across multiple calls', async () => {
      const searchParams = {
        pattern: 'ERROR',
        logLevel: 'error',
        limit: 5
      };
      
      // Execute same search multiple times
      const results = [];
      for (let i = 0; i < 3; i++) {
        const result = await client.callTool('search_logs', searchParams);
        results.push(analyzeSearchResults(result, searchParams.pattern));
      }
      
      // Validate consistency
      if (results[0].hasMatches) {
        const baseCounts = results[0].count;
        
        results.slice(1).forEach((analysis, index) => {
          assert.equal(analysis.count, baseCounts,
            `Search ${index + 2} should return same count as first search`);
          
          if (analysis.hasMatches) {
            assert.equal(analysis.entries.length, results[0].entries.length,
              `Search ${index + 2} should return same number of entries`);
          }
        });
      }
    });
  });

  // Dynamic parameter validation and boundary testing
  describe('Dynamic Parameter Analysis', () => {
    test('should validate limit parameter effectiveness across ranges', async () => {
      const limits = [1, 3, 10];
      const searchResults = [];
      
      // Test different limits sequentially
      for (const limit of limits) {
        const result = await client.callTool('search_logs', {
          pattern: 'Sites',
          limit
        });
        
        const analysis = analyzeSearchResults(result, 'Sites');
        searchResults.push({ limit, analysis });
        
        if (analysis.hasMatches) {
          // Validate that returned count doesn't exceed requested limit
          assert.ok(analysis.entries.length <= limit,
            `Should return at most ${limit} entries, got ${analysis.entries.length}`);
          
          // Validate that limit is respected in the reported count
          assert.ok(analysis.count <= limit,
            `Reported count ${analysis.count} should not exceed limit ${limit}`);
        }
      }
      
      // Cross-validate limit behavior
      const hasResults = searchResults.filter(r => r.analysis.hasMatches);
      if (hasResults.length > 1) {
        // Smaller limits should return subset of larger limits
        hasResults.sort((a, b) => a.limit - b.limit);
        
        for (let i = 1; i < hasResults.length; i++) {
          const smaller = hasResults[i-1];
          const larger = hasResults[i];
          
          assert.ok(smaller.analysis.entries.length <= larger.analysis.entries.length,
            `Limit ${smaller.limit} should return fewer or equal entries than limit ${larger.limit}`);
        }
      }
    });

    test('should handle date-based search filtering accurately', async () => {
      const currentDate = getCurrentDateString();
      const pastDate = '20200101';
      
      // Current date search
      const currentResult = await client.callTool('search_logs', {
        pattern: 'INFO',
        date: currentDate,
        limit: 5
      });
      
      const currentAnalysis = analyzeSearchResults(currentResult, 'INFO');
      
      // Past date search (should have no results)
      const pastResult = await client.callTool('search_logs', {
        pattern: 'INFO',
        date: pastDate,
        limit: 5
      });
      
      const pastAnalysis = analyzeSearchResults(pastResult, 'INFO');
      
      // Validate date filtering logic
      if (currentAnalysis.hasMatches) {
        // Current date entries should contain current date in log file names
        currentAnalysis.entries.forEach(entry => {
          const parsed = parseLogEntry(entry);
          if (parsed.logFile && parsed.isValid) {
            assert.ok(parsed.logFile.includes(currentDate),
              `Log file "${parsed.logFile}" should contain current date ${currentDate}`);
          }
        });
      } else {
        // No current date entries found - test still valid
      }
      
      // Past date should return no matches
      assert.ok(!pastAnalysis.hasMatches,
        'Past date search should return no matches');
    });
  });

  // Business logic validation for SFCC-specific patterns
  describe('SFCC Business Logic Validation', () => {
    test('should identify and parse SFCC job execution patterns', async () => {
      const result = await client.callTool('search_logs', {
        pattern: 'SystemJobThread',
        limit: 5
      });
      
      const analysis = analyzeSearchResults(result, 'SystemJobThread');
      
      if (analysis.hasMatches) {
        analysis.entries.forEach(entry => {
          // Should contain job execution context
          assert.ok(/SystemJobThread\|\d+\|/.test(entry),
            'Should contain SystemJobThread with thread ID pattern');
          
          // Should contain job name or step information
          const jobPatterns = [
            /ProcessOrders/,
            /ImportCustomers/,
            /ExportCatalog/,
            /SiteGenesis/
          ];
          
          const hasJobPattern = jobPatterns.some(pattern => pattern.test(entry));
          if (hasJobPattern) {
            assert.ok(true, 'Entry contains recognizable SFCC job pattern');
          }
        });
      }
    });

    test('should validate customer activity tracking patterns', async () => {
      const result = await client.callTool('search_logs', {
        pattern: 'Customer',
        logLevel: 'info',
        limit: 10
      });
      
      const analysis = analyzeSearchResults(result, 'Customer');
      
      if (analysis.hasMatches) {
        
        analysis.entries.forEach(entry => {
          // Look for customer-related activities
          const customerActivities = [
            /customer\.id=/,
            /Customer registration/,
            /Customer login/,
            /Customer profile/,
            /Account-/
          ];
          
          const hasActivity = customerActivities.some(pattern => pattern.test(entry));
          if (hasActivity) {
            
            // Validate customer ID format if present
            const customerIdMatch = entry.match(/customer\.id=([^\s]+)/);
            if (customerIdMatch) {
              const customerId = customerIdMatch[1];
              assert.ok(customerId.length > 0,
                'Customer ID should not be empty');
            }
          }
        });
      }
    });
  });
});
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-latest-job-log-files.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

describe('get_latest_job_log_files - Full Mode Programmatic Tests (Optimized)', () => {
  let client;
  let discoveredJobLogs = [];

  before(async () => {
    client = await connect('./aegis.config.with-dw.json');
    
    // Discover available job logs for advanced testing
    await discoverJobLogFiles();
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking into next tests
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  // Helper functions for common validations
  function assertValidMCPResponse(result) {
    assert.ok(result.content, 'Should have content');
    assert.ok(Array.isArray(result.content), 'Content should be array');
    assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
  }

  function parseResponseText(text) {
    // The response may come wrapped in quotes, so parse if needed
    return text.startsWith('"') && text.endsWith('"') 
      ? JSON.parse(text) 
      : text;
  }

  function assertTextContent(result, expectedSubstring) {
    assertValidMCPResponse(result);
    assert.equal(result.content[0].type, 'text');
    const actualText = parseResponseText(result.content[0].text);
    assert.ok(actualText.includes(expectedSubstring),
      `Expected "${expectedSubstring}" in "${actualText}"`);
  }

  function assertSuccessResponse(result) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, false, 'Should not be an error response');
    assert.equal(result.content[0].type, 'text');
  }

  function extractJobLogInfo(responseText) {
    const jobs = [];
    const text = parseResponseText(responseText);
    const sections = text.split('🔧 Job: ').slice(1); // Remove empty first element
    
    for (const section of sections) {
      const lines = section.split('\n');
      const jobName = lines[0].trim();
      
      let jobId = null;
      let fileName = null;
      let modified = null;
      let size = null;
      
      for (const line of lines) {
        const idMatch = line.match(/ID: (\d+)/);
        if (idMatch) jobId = idMatch[1];
        
        const fileMatch = line.match(/File: (.+\.log)/);
        if (fileMatch) fileName = fileMatch[1];
        
        const modifiedMatch = line.match(/Modified: (.+)/);
        if (modifiedMatch) modified = modifiedMatch[1];
        
        const sizeMatch = line.match(/Size: (.+)/);
        if (sizeMatch) size = sizeMatch[1];
      }
      
      if (jobName && jobId && fileName) {
        jobs.push({ jobName, jobId, fileName, modified, size });
      }
    }
    
    return jobs;
  }

  async function discoverJobLogFiles() {
    console.log('🔍 Discovering available job log files using MCP server...');
    
    try {
      const result = await client.callTool('get_latest_job_log_files', { limit: 10 });
      if (!result.isError) {
        discoveredJobLogs = extractJobLogInfo(result.content[0].text);
        console.log(`🔧 Found ${discoveredJobLogs.length} job logs:`, 
          discoveredJobLogs.map(j => `${j.jobName}(${j.jobId})`));
      }
    } catch (error) {
      console.warn('⚠️ Could not discover job log files:', error.message);
    }
  }

  // Advanced functionality tests (basic functionality covered by YAML tests)
  describe('Advanced Functionality', () => {
    test('should return consistent job information across calls', async () => {
      const result1 = await client.callTool('get_latest_job_log_files', { limit: 5 });
      const result2 = await client.callTool('get_latest_job_log_files', { limit: 5 });
      
      assertSuccessResponse(result1);
      assertSuccessResponse(result2);
      
      const jobs1 = extractJobLogInfo(result1.content[0].text);
      const jobs2 = extractJobLogInfo(result2.content[0].text);
      
      // Job logs should be consistent (same files, same metadata)
      assert.equal(jobs1.length, jobs2.length, 'Should return same number of jobs');
      
      for (let i = 0; i < jobs1.length; i++) {
        assert.equal(jobs1[i].jobName, jobs2[i].jobName, 'Job names should be consistent');
        assert.equal(jobs1[i].jobId, jobs2[i].jobId, 'Job IDs should be consistent');
        assert.equal(jobs1[i].fileName, jobs2[i].fileName, 'File names should be consistent');
        assert.equal(jobs1[i].size, jobs2[i].size, 'File sizes should be consistent');
      }
    });
  });

  // Advanced parameter validation (basic validation covered by YAML tests)
  describe('Advanced Parameter Validation', () => {
    test('should handle large limit values correctly', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 100 });
      
      assertSuccessResponse(result);
      
      const jobs = extractJobLogInfo(result.content[0].text);
      assert.ok(jobs.length <= 100, 'Should handle large limits');
      
      // Should return all available jobs if limit exceeds available count
      assert.equal(jobs.length, discoveredJobLogs.length, 
        'Should return all available jobs when limit exceeds count');
    });

    test('should handle missing arguments object gracefully', async () => {
      const result = await client.callTool('get_latest_job_log_files');
      
      assertSuccessResponse(result);
      
      const jobs = extractJobLogInfo(result.content[0].text);
      assert.ok(jobs.length <= 10, 'Should use default limit when no arguments provided');
    });

    test('should ignore extraneous parameters', async () => {
      const result = await client.callTool('get_latest_job_log_files', {
        limit: 2,
        invalidParam: 'should be ignored',
        anotherParam: 123
      });
      
      assertSuccessResponse(result);
      
      const jobs = extractJobLogInfo(result.content[0].text);
      assert.ok(jobs.length <= 2, 'Should respect limit and ignore extraneous parameters');
    });
  });

  // Content validation tests (require complex programmatic logic)
  describe('Content Validation', () => {
    test('should include realistic SFCC job names', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 5 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      // Should contain common SFCC job patterns
      const commonJobPatterns = [
        'ImportCatalog',
        'ProcessOrders', 
        'ExportCustomers',
        'UpdateInventory',
        'GenerateReports'
      ];
      
      const foundPatterns = jobs.some(job => 
        commonJobPatterns.some(pattern => job.jobName.includes(pattern))
      );
      
      assert.ok(foundPatterns, 'Should contain common SFCC job name patterns');
    });

    test('should validate job ID format consistency', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 5 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      for (const job of jobs) {
        // Job ID should be exactly 10 digits (timestamp-based)
        assert.ok(/^\d{10}$/.test(job.jobId), 
          `Job ID "${job.jobId}" should be 10 digits`);
        
        // File name should include job name and ID
        assert.ok(job.fileName.includes(job.jobName), 
          'File name should include job name');
        assert.ok(job.fileName.includes(job.jobId), 
          'File name should include job ID');
      }
    });

    test('should validate timestamp format consistency', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 3 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      for (const job of jobs) {
        // Should be valid HTTP date format
        assert.ok(/^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(job.modified),
          `Modified time "${job.modified}" should match HTTP date format`);
        
        // Should parse as valid date
        const date = new Date(job.modified);
        assert.ok(!isNaN(date.getTime()), 'Modified time should be valid date');
        
        // Should be reasonable (not too far in past/future)
        const now = new Date();
        const daysDiff = Math.abs(now - date) / (1000 * 60 * 60 * 24);
        assert.ok(daysDiff < 365, 'Modified time should be within last year');
      }
    });

    test('should validate file size format and reasonableness', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 3 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      for (const job of jobs) {
        // Should match format: "X.XX KB" or "X.XX MB"
        assert.ok(/^\d+\.\d{2} [KM]B$/.test(job.size),
          `Size "${job.size}" should match expected format`);
        
        // Parse size and validate reasonableness
        const [value, unit] = job.size.split(' ');
        const sizeValue = parseFloat(value);
        
        assert.ok(sizeValue > 0, 'Size value should be positive');
        assert.ok(sizeValue < 1000, 'Size value should be reasonable');
        assert.ok(['KB', 'MB'].includes(unit), 'Size unit should be KB or MB');
        
        // Job logs should typically be small (under 10MB)
        if (unit === 'MB') {
          assert.ok(sizeValue < 10, 'Job logs should typically be under 10MB');
        }
      }
    });

    test('should maintain chronological ordering', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 5 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      if (jobs.length > 1) {
        // Jobs should be ordered by modification time (newest first)
        for (let i = 0; i < jobs.length - 1; i++) {
          const current = new Date(jobs[i].modified);
          const next = new Date(jobs[i + 1].modified);
          
          assert.ok(current >= next, 
            'Jobs should be ordered by modification time (newest first)');
        }
      }
    });
  });

  // Integration tests with other tools (require multi-tool workflows)
  describe('Integration Tests', () => {
    test('should integrate with get_log_file_contents for job logs', async () => {
      const jobFilesResult = await client.callTool('get_latest_job_log_files', { limit: 1 });
      
      assertSuccessResponse(jobFilesResult);
      const jobs = extractJobLogInfo(jobFilesResult.content[0].text);
      
      if (jobs.length > 0) {
        const job = jobs[0];
        const jobLogPath = `jobs/${job.jobName}/${job.fileName}`;
        
        // Try to read the job log file
        const contentsResult = await client.callTool('get_log_file_contents', {
          filename: jobLogPath
        });
        
        assertSuccessResponse(contentsResult);
        assertTextContent(contentsResult, 'Log File Contents:');
        
        console.log(`✅ Successfully integrated job file discovery with content reading: ${jobLogPath}`);
      }
    });

    test('should validate job log file paths for get_log_file_contents compatibility', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 3 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      for (const job of jobs) {
        // Construct expected path format for get_log_file_contents
        const expectedPath = `jobs/${job.jobName}/${job.fileName}`;
        
        // Validate path components
        assert.ok(!expectedPath.includes('//'), 'Path should not have double slashes');
        assert.ok(!expectedPath.includes(' '), 'Path should not contain spaces');
        assert.ok(expectedPath.startsWith('jobs/'), 'Path should start with jobs/');
        assert.ok(expectedPath.endsWith('.log'), 'Path should end with .log');
        
        console.log(`📝 Validated job log path format: ${expectedPath}`);
      }
    });

    test('should discover unique job types and patterns', async () => {
      const result = await client.callTool('get_latest_job_log_files', { limit: 10 });
      
      assertSuccessResponse(result);
      const jobs = extractJobLogInfo(result.content[0].text);
      
      // Extract unique job name patterns
      const jobPatterns = new Set();
      const jobTypes = new Set();
      
      for (const job of jobs) {
        // Extract job type pattern (e.g., "ImportCatalog" from "ImportCatalogProducts")
        const basePattern = job.jobName.replace(/[0-9]+$/, ''); // Remove trailing numbers
        jobPatterns.add(basePattern);
        
        // Categorize job types
        if (basePattern.includes('Import')) jobTypes.add('Import');
        if (basePattern.includes('Export')) jobTypes.add('Export');
        if (basePattern.includes('Process')) jobTypes.add('Process');
        if (basePattern.includes('Generate')) jobTypes.add('Generate');
        if (basePattern.includes('Update')) jobTypes.add('Update');
      }
      
      console.log(`🔍 Found ${jobPatterns.size} unique job patterns:`, Array.from(jobPatterns));
      console.log(`📊 Found ${jobTypes.size} job categories:`, Array.from(jobTypes));
      
      // Should have some variety in job types for a real SFCC environment
      assert.ok(jobPatterns.size > 0, 'Should have discovered job patterns');
    });
  });

  // Edge cases and resilience testing (require complex validation logic)
  describe('Edge Cases and Resilience', () => {
    test('should handle boundary limit values', async () => {
      const testLimits = [1, 50, 99];
      
      for (const limit of testLimits) {
        const result = await client.callTool('get_latest_job_log_files', { limit });
        
        assertSuccessResponse(result);
        const jobs = extractJobLogInfo(result.content[0].text);
        
        assert.ok(jobs.length <= limit, `Should not exceed limit ${limit}`);
        assert.ok(jobs.length <= discoveredJobLogs.length, 
          'Should not exceed available job count');
        
        console.log(`✅ Boundary test: limit=${limit}, returned=${jobs.length}`);
      }
    });

    test('should maintain response format consistency across different scenarios', async () => {
      const scenarios = [
        { limit: 1, description: 'single job' },
        { limit: 5, description: 'multiple jobs' },
        { limit: 100, description: 'exceed available' }
      ];
      
      for (const scenario of scenarios) {
        const result = await client.callTool('get_latest_job_log_files', { limit: scenario.limit });
        
        assertSuccessResponse(result);
        const jobs = extractJobLogInfo(result.content[0].text);
        
        // Consistent response structure regardless of scenario
        assert.equal(result.content.length, 1, 'Should have single content item');
        assert.equal(result.content[0].type, 'text', 'Content should be text type');
        
        const text = parseResponseText(result.content[0].text);
        assert.ok(text.includes('Found'), 'Should include count message');
        
        if (jobs.length > 0) {
          assert.ok(text.includes('🔧 Job:'), 'Should include job emoji for non-empty results');
        }
        
        console.log(`✅ Format consistency verified for ${scenario.description}: ${jobs.length} jobs`);
      }
    });

    test('should handle rapid sequential requests without interference', async () => {
      const promises = [];
      const requestCount = 5;
      
      // Fire multiple requests simultaneously
      for (let i = 0; i < requestCount; i++) {
        promises.push(client.callTool('get_latest_job_log_files', { limit: 2 }));
      }
      
      const results = await Promise.all(promises);
      
      // All requests should succeed
      for (let i = 0; i < requestCount; i++) {
        assertSuccessResponse(results[i]);
        
        const jobs = extractJobLogInfo(results[i].content[0].text);
        assert.ok(jobs.length <= 2, `Request ${i} should respect limit`);
      }
      
      // Results should be consistent across requests
      const firstJobsCount = extractJobLogInfo(results[0].content[0].text).length;
      for (let i = 1; i < requestCount; i++) {
        const jobsCount = extractJobLogInfo(results[i].content[0].text).length;
        assert.equal(jobsCount, firstJobsCount, 
          'Concurrent requests should return consistent results');
      }
      
      console.log(`✅ Handled ${requestCount} concurrent requests successfully`);
    });

    test('should have consistent content format across different parameters', async () => {
      const testParams = [
        {},
        { limit: 1 },
        { limit: 5 },
        { limit: 10 }
      ];
      
      for (const params of testParams) {
        const result = await client.callTool('get_latest_job_log_files', params);
        
        assertValidMCPResponse(result);
        assert.equal(result.isError, false, 'Should be successful response');
        
        const text = parseResponseText(result.content[0].text);
        
        // Consistent format elements
        assert.ok(/Found \d+ job logs:/.test(text), 'Should have count pattern');
        
        const jobs = extractJobLogInfo(text);
        if (jobs.length > 0) {
          // Each job should have consistent format
          for (const job of jobs) {
            assert.ok(job.jobName && job.jobId && job.fileName, 
              'Job should have required fields');
          }
        }
      }
    });
  });
});
```

--------------------------------------------------------------------------------
/docs/best-practices/scapi_hooks.md:
--------------------------------------------------------------------------------

```markdown
# Quick Guide: Salesforce B2C Commerce SCAPI Hooks for AI Agents

This guide provides essential best practices and code examples for implementing Salesforce Commerce API (SCAPI) hooks. It is designed to be a quick reference for development with AI code assistants.

**IMPORTANT**: Before implementing SCAPI hooks, consult the **Performance and Stability Best Practices** guide from this MCP server. Review the index-friendly APIs section and job development standards to ensure your hooks follow SFCC performance requirements and avoid database-intensive operations.

## 1. Core Concepts

SCAPI hooks are server-side scripts that intercept SCAPI requests to add custom logic. They are used to augment, validate, or modify the behavior of existing API endpoints. For creating entirely new endpoints, use Custom APIs.

### Hook Types & Execution Order

For any state-changing request (POST, PATCH, PUT, DELETE), hooks execute in a specific order:

- **`before<HTTP_Method>`**: Executes before core logic. Ideal for validation, preprocessing, and authorization.
- **`after<HTTP_Method>`**: Executes after core logic succeeds and the database transaction is committed. Use for business logic side effects, like calling an external system or triggering recalculations.
- **`modify<HTTP_Method>Response`**: Executes last, after the default JSON response is generated. Use only to format the final JSON payload sent to the client.

### Transactional Integrity

A hook's ability to modify data depends on its transactional context.

| Hook Type | Transactional? | Can Modify Persistent Data? | Primary Purpose |
|-----------|---------------|----------------------------|-----------------|
| `before<HTTP_Method>` | Yes | Yes | Validation & Preprocessing |
| `after<HTTP_Method>` | Yes | Yes | Business Logic & Side Effects |
| `modifyResponse` | No | No | Formatting the JSON Response |

> **Note**: Attempting to modify persistent data (e.g., `basket.setCustomerEmail()`) in a `modifyResponse` hook will throw an ORM TransactionException.

## 2. Registration

Hooks must be enabled in Business Manager (Administration > Global Preferences > Feature Switches) and registered in a custom cartridge via two files.

### `package.json` (Cartridge Root)

This file points to your hooks configuration.

```json
{
  "name": "int_scapi_hooks_extension",
  "hooks": "./cartridge/hooks.json"
}
```

### `hooks.json` (e.g., `/cartridge/hooks.json`)

This file maps the hook extension point name to your script file.

```json
{
  "hooks": [
    {
      "name": "dw.ocapi.shop.basket.items.beforePOST",
      "script": "./basket/validateItems.js"
    },
    {
      "name": "dw.ocapi.shop.customer.modifyGETResponse",
      "script": "./customer/enrichResponse.js"
    },
    {
      "name": "dw.ocapi.shop.order.afterPOST",
      "script": "./order/notifyOms.js"
    }
  ]
}
```
### Recommended Cartridge Structure

Organize hook scripts by the resource they modify for better maintainability.

```
/cartridge
├── hooks.json
└──/scripts
   └──/hooks
      ├──/basket
      │  └── validateItems.js
      ├──/customer
      │  └── enrichResponse.js
      └──/order
         └── notifyOms.js
```

## 3. Core Implementation Patterns

### Script Structure (CommonJS)

Hook scripts are CommonJS modules. The exported function name must match the hook's method name (e.g., `afterPOST`).

```javascript
'use strict';
var Status = require('dw/system/Status');

/**
 * @param {dw.order.Order} order - The newly created order object.
 * @returns {dw.system.Status | void}
 */
exports.afterPOST = function (order) {
    // Custom logic here
    return; // Return void for success to allow hook chain to continue
};
```

### Signaling Success vs. Failure (`dw.system.Status`)

Use the Status object to control the execution flow.

**Controlled Failure**: Halts execution and rolls back the transaction. Returns an HTTP 400 error with a fault document.

```javascript
return new Status(Status.ERROR, 'YOUR_ERROR_CODE', 'A descriptive error message.');
```

**Success (Allow Chain to Continue)**: For Shopper APIs, returning void is the best practice. It allows other hooks in the cartridge path to run.

```javascript
return;
```

**Success (Terminate Chain)**: Returning `Status.OK` signals success but stops any subsequent hooks for the same extension point from running.

```javascript
return new Status(Status.OK);
```

## 4. Code Examples

### Example 1: Custom Validation (beforePOST)

Reject adding a restricted product to the cart for non-wholesale customers.

**Hook**: `dw.ocapi.shop.basket.items.beforePOST`

```javascript
'use strict';
var Status = require('dw/system/Status');
var ProductMgr = require('dw/catalog/ProductMgr');

exports.beforePOST = function (basket, items) {
    var customer = basket.customer;
    var isWholesaleCustomer = customer ? customer.isMemberOfCustomerGroup('Wholesale') : false;

    for (var i = 0; i < items.length; i++) {
        var product = ProductMgr.getProduct(items[i].product_id);
        if (product && product.custom.isRestricted && !isWholesaleCustomer) {
            var errorMessage = 'Product ' + product.ID + ' is restricted.';
            return new Status(Status.ERROR, 'ITEM_RESTRICTION', errorMessage);
        }
    }
    return; // Success
};
```

### Example 2: Enriching a Response (modifyGETResponse)

Add a calculated `c_loyaltyTier` attribute to the customer GET response.

**Hook**: `dw.ocapi.shop.customer.modifyGETResponse`

```javascript
'use strict';
var Status = require('dw/system/Status');

exports.modifyGETResponse = function (customer, customerResponse) {
    var loyaltyTier = 'Standard';
    if (customer.isMemberOfCustomerGroup('GoldMembers')) {
        loyaltyTier = 'Gold';
    }
    // Add a non-persistent attribute to the JSON response
    customerResponse.c_loyaltyTier = loyaltyTier;
    return new Status(Status.OK);
};
```

### Example 3: External Integration (afterPOST)

Notify an external Order Management System (OMS) after an order is created. The integration is wrapped in a try/catch to prevent an OMS failure from affecting the order creation status.

**Hook**: `dw.ocapi.shop.order.afterPOST`

```javascript
'use strict';
var Status = require('dw/system/Status');
var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry');
var Logger = require('dw/system/Logger').getLogger('OmsIntegration');

exports.afterPOST = function (order) {
    try {
        var omsService = LocalServiceRegistry.createService('oms.http.service', { /*... service config... */ });
        var payload = { orderNo: order.getOrderNo(), total: order.getTotalGrossPrice().getValue() };
        var result = omsService.call({ payload: payload });

        if (!result.isOk()) {
            // Log the error for monitoring, but do NOT return Status.ERROR.
            // The order is already created; returning an error here would be misleading.
            Logger.error('Failed to notify OMS for order {0}. Error: {1}', order.getOrderNo(), result.getErrorMessage());
        }
    } catch (e) {
        Logger.error('Exception notifying OMS for order {0}. Exception: {1}', order.getOrderNo(), e.toString());
    }
    // Always return OK because the primary operation (order creation) was successful.
    return new Status(Status.OK);
};
```

## 5. Key Best Practices Checklist

### Performance

- [ ] **DON'T** perform expensive API lookups inside a hook (e.g., `ProductMgr.getProduct()`).
- [ ] **DO** be aware of caching. Hooks on cacheable GET endpoints only run on a cache miss.
- [ ] **DO** use the Service Framework with aggressive timeouts and circuit breaker settings for all external calls.
- [ ] **DO** use the Code Profiler to measure script performance before deploying to production.

### Security

- [ ] **DO** treat all client input as untrusted. Sanitize and validate data in before hooks.
- [ ] **DO** re-authorize resource ownership. For example, in a basket hook, verify `basket.customer.ID` matches the logged-in shopper's ID.
- [ ] **DON'T** use hooks to bypass the platform's built-in security model or authentication.

### Error Handling & Resilience

- [ ] **DO** wrap all hook logic in `try/catch` blocks to prevent unhandled exceptions.
- [ ] **DO** use `dw.system.Logger` with custom categories and include the `request.requestID` for easy tracing in logs.
- [ ] **BE AWARE** of the Hook Circuit Breaker. If a hook fails more than 50% of the time in its last 100 executions, it will be temporarily disabled (returning HTTP 503) to protect system stability.

## 6. Comprehensive Hook Reference

This section provides a reference list of the available hook extension points for the SCAPI Shopper APIs, organized by resource.

### Shopper Baskets API Hooks

| API Endpoint (Method & Path) | Hook Extension Point | Function Signature |
|------------------------------|---------------------|-------------------|
| `POST /baskets` | `dw.ocapi.shop.basket.beforePOST_v2` | `beforePOST_v2(basketRequest : Basket) : dw.system.Status` |
| `POST /baskets` | `dw.ocapi.shop.basket.afterPOST` | `afterPOST(basket : dw.order.Basket) : dw.system.Status` |
| `POST /baskets` | `dw.ocapi.shop.basket.modifyPOSTResponse` | `modifyPOSTResponse(basket : dw.order.Basket, basketResponse : Basket) : dw.system.Status` |
| `GET /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforeGET` | `beforeGET(basketId : String) : dw.system.Status` |
| `GET /baskets/{basket_id}` | `dw.ocapi.shop.basket.modifyGETResponse` | `modifyGETResponse(basket : dw.order.Basket, basketResponse : Basket) : dw.system.Status` |
| `PATCH /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforePATCH` | `beforePATCH(basket : dw.order.Basket, basketInput : Basket) : dw.system.Status` |
| `PATCH /baskets/{basket_id}` | `dw.ocapi.shop.basket.afterPATCH` | `afterPATCH(basket : dw.order.Basket, basketInput : Basket) : dw.system.Status` |
| `PATCH /baskets/{basket_id}` | `dw.ocapi.shop.basket.modifyPATCHResponse` | `modifyPATCHResponse(basket : dw.order.Basket, basketResponse : Basket) : dw.system.Status` |
| `DELETE /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforeDELETE` | `beforeDELETE(basket : dw.order.Basket) : dw.system.Status` |
| `DELETE /baskets/{basket_id}` | `dw.ocapi.shop.basket.afterDELETE` | `afterDELETE(basketId : String) : dw.system.Status` |
| `POST /baskets/{basket_id}/items` | `dw.ocapi.shop.basket.items.beforePOST` | `beforePOST(basket : dw.order.Basket, items : ProductItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/items` | `dw.ocapi.shop.basket.items.afterPOST` | `afterPOST(basket : dw.order.Basket, items : ProductItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/items` | `dw.ocapi.shop.basket.items.modifyPOSTResponse` | `modifyPOSTResponse(basket : dw.order.Basket, basketResponse : Basket, productItems : ProductItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/coupons` | `dw.ocapi.shop.basket.coupon.beforePOST` | `beforePOST(basket : dw.order.Basket, couponItem : CouponItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/coupons` | `dw.ocapi.shop.basket.coupon.afterPOST` | `afterPOST(basket : dw.order.Basket, couponItem : CouponItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/coupons` | `dw.ocapi.shop.basket.coupon.modifyPOSTResponse` | `modifyPOSTResponse(basket : dw.order.Basket, basketResponse : Basket, couponRequest : CouponItem) : dw.system.Status` |
| `POST /baskets/{basket_id}/payment_instruments` | `dw.ocapi.shop.basket.payment_instrument.beforePOST` | `beforePOST(basket : dw.order.Basket, paymentInstrument : BasketPaymentInstrumentRequest) : dw.system.Status` |
| `POST /baskets/{basket_id}/payment_instruments` | `dw.ocapi.shop.basket.payment_instrument.afterPOST` | `afterPOST(basket : dw.order.Basket, paymentInstrument : BasketPaymentInstrumentRequest) : dw.system.Status` |
| `POST /baskets/{basket_id}/payment_instruments` | `dw.ocapi.shop.basket.payment_instrument.modifyPOSTResponse` | `modifyPOSTResponse(basket : dw.order.Basket, basketResponse : Basket, paymentInstrumentRequest : BasketPaymentInstrumentRequest) : dw.system.Status` |
| Various | `dw.ocapi.shop.basket.validateBasket` | `validateBasket(basketResponse : Basket, duringSubmit : Boolean) : dw.system.Status` |

### Shopper Customers API Hooks

| API Endpoint (Method & Path) | Hook Extension Point | Function Signature |
|------------------------------|---------------------|-------------------|
| `POST /customers` | `dw.ocapi.shop.customer.beforePOST` | `beforePOST(registration : CustomerRegistration) : dw.system.Status` |
| `POST /customers` | `dw.ocapi.shop.customer.afterPOST` | `afterPOST(customer : dw.customer.Customer, registration : CustomerRegistration) : dw.system.Status` |
| `POST /customers` | `dw.ocapi.shop.customer.modifyPOSTResponse` | `modifyPOSTResponse(customer : dw.customer.Customer, customerResponse : Customer) : dw.system.Status` |
| `GET /customers/{customer_id}` | `dw.ocapi.shop.customer.beforeGET` | `beforeGET(customerId : String) : dw.system.Status` |
| `GET /customers/{customer_id}` | `dw.ocapi.shop.customer.modifyGETResponse` | `modifyGETResponse(customer : dw.customer.Customer, customerResponse : Customer) : dw.system.Status` |
| `PATCH /customers/{customer_id}` | `dw.ocapi.shop.customer.beforePATCH` | `beforePATCH(customer : dw.customer.Customer, customerInput : Customer) : dw.system.Status` |
| `PATCH /customers/{customer_id}` | `dw.ocapi.shop.customer.afterPATCH` | `afterPATCH(customer : dw.customer.Customer, customerInput : Customer) : dw.system.Status` |
| `PATCH /customers/{customer_id}` | `dw.ocapi.shop.customer.modifyPATCHResponse` | `modifyPATCHResponse(customer : dw.customer.Customer, customerResponse : Customer) : dw.system.Status` |
| `POST /customers/auth` | `dw.ocapi.shop.auth.beforePOST` | `beforePOST(authorizationHeader : String, authRequestType : dw.value.EnumValue) : dw.system.Status` |
| `POST /customers/auth` | `dw.ocapi.shop.auth.afterPOST` | `afterPOST(customer : dw.customer.Customer, authRequestType : dw.value.EnumValue) : dw.system.Status` |
| `POST /customers/auth` | `dw.ocapi.shop.auth.modifyPOSTResponse` | `modifyPOSTResponse(customer : dw.customer.Customer, customerResponse : Customer, authRequestType : dw.value.EnumValue) : dw.system.Status` |
| `PATCH /customers/{customer_id}/addresses/{address_name}` | `dw.ocapi.shop.customer.address.beforePATCH` | `beforePATCH(customer : dw.customer.Customer, addressName : String, customerAddress : CustomerAddress) : dw.system.Status` |
| `PATCH /customers/{customer_id}/addresses/{address_name}` | `dw.ocapi.shop.customer.address.afterPATCH` | `afterPATCH(customer : dw.customer.Customer, addressName : String, customerAddress : CustomerAddress) : dw.system.Status` |

### Shopper Orders API Hooks

| API Endpoint (Method & Path) | Hook Extension Point | Function Signature |
|------------------------------|---------------------|-------------------|
| `POST /orders` | `dw.ocapi.shop.order.beforePOST` | `beforePOST(basket : dw.order.Basket) : dw.system.Status` |
| `POST /orders` | `dw.ocapi.shop.order.afterPOST` | `afterPOST(order : dw.order.Order) : dw.system.Status` |
| `POST /orders` | `dw.ocapi.shop.order.modifyPOSTResponse` | `modifyPOSTResponse(order : dw.order.Order, orderResponse : Order) : dw.system.Status` |
| `GET /orders/{order_no}` | `dw.ocapi.shop.order.beforeGET` | `beforeGET(orderNo : String) : dw.system.Status` |
| `GET /orders/{order_no}` | `dw.ocapi.shop.order.modifyGETResponse` | `modifyGETResponse(order : dw.order.Order, orderResponse : Order) : dw.system.Status` |
| `PATCH /orders/{order_no}` | `dw.ocapi.shop.order.beforePATCH` | `beforePATCH(order : dw.order.Order, orderInput : Order) : dw.system.Status` |
| `PATCH /orders/{order_no}` | `dw.ocapi.shop.order.afterPATCH` | `afterPATCH(order : dw.order.Order, orderInput : Order) : dw.system.Status` |
| `PATCH /orders/{order_no}` | `dw.ocapi.shop.order.modifyPATCHResponse` | `modifyPATCHResponse(order : dw.order.Order, orderResponse : Order) : dw.system.Status` |

### Other Key Shopper API Hooks

| API Endpoint (Method & Path) | Hook Extension Point | Function Signature |
|------------------------------|---------------------|-------------------|
| `GET /products/{id}` | `dw.ocapi.shop.product.beforeGET` | `beforeGET(productId : String) : dw.system.Status` |
| `GET /products/{id}` | `dw.ocapi.shop.product.modifyGETResponse` | `modifyGETResponse(scriptProduct : dw.catalog.Product, doc : Product) : dw.system.Status` |
| `GET /product_search` | `dw.ocapi.shop.product_search.beforeGET` | `beforeGET() : dw.system.Status` |
| `GET /product_search` | `dw.ocapi.shop.product_search.modifyGETResponse` | `modifyGETResponse(doc : ProductSearchResult) : dw.system.Status` |
| `GET /categories/{id}` | `dw.ocapi.shop.category.beforeGET` | `beforeGET(categoryId : String) : dw.system.Status` |
| `GET /categories/{id}` | `dw.ocapi.shop.category.modifyGETResponse` | `modifyGETResponse(scriptCategory : dw.catalog.Category, doc : Category) : dw.system.Status` |

## Troubleshooting Hook Registration

**If SCAPI hooks are not executing after deployment:**

1. **Verify Feature Switches**: Ensure hooks are enabled in Business Manager (Administration > Global Preferences > Feature Switches)
2. **Check Code Version**: If hooks still don't execute:
   - **Check Available Versions**: Use MCP `get_code_versions` tool to see all code versions on the instance
   - **Activate Different Version**: Use MCP `activate_code_version` tool to switch code versions
   - **Alternative Manual Method**: Switch code versions in Business Manager (Administration > Site Development > Code Deployment > Activate)
3. **Verify Hook Registration**: Check logs for hook registration confirmations after version activation
4. **Test Hook Execution**: Make API calls to endpoints that should trigger your hooks and verify they execute

**Common Hook Issues:**
- Hooks not triggering → Check feature switches and code version activation
- Hook scripts not found → Verify file paths match registration in hooks.json
- Runtime errors in hooks → Check logs for specific error messages during hook execution

```
Page 22/43FirstPrevNextLast