@revenexx/node 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.mts +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +3 -3
- package/dist/client.mjs.map +1 -1
- package/dist/enums/market-readiness-report-warning-reasons.d.mts +9 -0
- package/dist/enums/market-readiness-report-warning-reasons.d.ts +9 -0
- package/dist/enums/market-readiness-report-warning-reasons.js +14 -0
- package/dist/enums/market-readiness-report-warning-reasons.js.map +1 -0
- package/dist/enums/market-readiness-report-warning-reasons.mjs +13 -0
- package/dist/enums/market-readiness-report-warning-reasons.mjs.map +1 -0
- package/dist/enums/market-readiness-warning-reasons.d.mts +9 -0
- package/dist/enums/market-readiness-warning-reasons.d.ts +9 -0
- package/dist/enums/market-readiness-warning-reasons.js +14 -0
- package/dist/enums/market-readiness-warning-reasons.js.map +1 -0
- package/dist/enums/market-readiness-warning-reasons.mjs +13 -0
- package/dist/enums/market-readiness-warning-reasons.mjs.map +1 -0
- package/dist/enums/product-defaults-result-reason.d.mts +6 -0
- package/dist/enums/product-defaults-result-reason.d.ts +6 -0
- package/dist/enums/product-defaults-result-reason.js +11 -0
- package/dist/enums/product-defaults-result-reason.js.map +1 -0
- package/dist/enums/product-defaults-result-reason.mjs +10 -0
- package/dist/enums/product-defaults-result-reason.mjs.map +1 -0
- package/dist/enums/punchout-vocabularies-get-name.d.mts +1 -0
- package/dist/enums/punchout-vocabularies-get-name.d.ts +1 -0
- package/dist/enums/punchout-vocabularies-get-name.js +1 -0
- package/dist/enums/punchout-vocabularies-get-name.js.map +1 -1
- package/dist/enums/punchout-vocabularies-get-name.mjs +1 -0
- package/dist/enums/punchout-vocabularies-get-name.mjs.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/models.d.mts +160 -12
- package/dist/models.d.ts +160 -12
- package/dist/services/ads-accounts.d.mts +1 -0
- package/dist/services/ads-accounts.d.ts +1 -0
- package/dist/services/ads-conversions.d.mts +1 -0
- package/dist/services/ads-conversions.d.ts +1 -0
- package/dist/services/ads-feeds.d.mts +1 -0
- package/dist/services/ads-feeds.d.ts +1 -0
- package/dist/services/ads-jobs.d.mts +1 -0
- package/dist/services/ads-jobs.d.ts +1 -0
- package/dist/services/ads-links.d.mts +1 -0
- package/dist/services/ads-links.d.ts +1 -0
- package/dist/services/ads-rules.d.mts +1 -0
- package/dist/services/ads-rules.d.ts +1 -0
- package/dist/services/ads-spend.d.mts +1 -0
- package/dist/services/ads-spend.d.ts +1 -0
- package/dist/services/ads-syncs.d.mts +1 -0
- package/dist/services/ads-syncs.d.ts +1 -0
- package/dist/services/ads-vocabularies.d.mts +1 -0
- package/dist/services/ads-vocabularies.d.ts +1 -0
- package/dist/services/apps.d.mts +1 -0
- package/dist/services/apps.d.ts +1 -0
- package/dist/services/audiences-audiences.d.mts +1 -0
- package/dist/services/audiences-audiences.d.ts +1 -0
- package/dist/services/audiences-members.d.mts +1 -0
- package/dist/services/audiences-members.d.ts +1 -0
- package/dist/services/audiences-rules.d.mts +1 -0
- package/dist/services/audiences-rules.d.ts +1 -0
- package/dist/services/audit.d.mts +1 -0
- package/dist/services/audit.d.ts +1 -0
- package/dist/services/avatars.d.mts +1 -0
- package/dist/services/avatars.d.ts +1 -0
- package/dist/services/campaigns-assets.d.mts +1 -0
- package/dist/services/campaigns-assets.d.ts +1 -0
- package/dist/services/campaigns-attribution.d.mts +1 -0
- package/dist/services/campaigns-attribution.d.ts +1 -0
- package/dist/services/campaigns-budgets.d.mts +1 -0
- package/dist/services/campaigns-budgets.d.ts +1 -0
- package/dist/services/campaigns-calendar.d.mts +1 -0
- package/dist/services/campaigns-calendar.d.ts +1 -0
- package/dist/services/campaigns-campaigns.d.mts +1 -0
- package/dist/services/campaigns-campaigns.d.ts +1 -0
- package/dist/services/campaigns-spend.d.mts +1 -0
- package/dist/services/campaigns-spend.d.ts +1 -0
- package/dist/services/campaigns-tasks.d.mts +1 -0
- package/dist/services/campaigns-tasks.d.ts +1 -0
- package/dist/services/campaigns-touches.d.mts +1 -0
- package/dist/services/campaigns-touches.d.ts +1 -0
- package/dist/services/campaigns-vocabularies.d.mts +1 -0
- package/dist/services/campaigns-vocabularies.d.ts +1 -0
- package/dist/services/carts-io.d.mts +1 -0
- package/dist/services/carts-io.d.ts +1 -0
- package/dist/services/carts-items.d.mts +1 -0
- package/dist/services/carts-items.d.ts +1 -0
- package/dist/services/carts.d.mts +1 -0
- package/dist/services/carts.d.ts +1 -0
- package/dist/services/channels.d.mts +1 -0
- package/dist/services/channels.d.ts +1 -0
- package/dist/services/consent-manager-delivery.d.mts +1 -0
- package/dist/services/consent-manager-delivery.d.ts +1 -0
- package/dist/services/consent-manager-policy.d.mts +1 -0
- package/dist/services/consent-manager-policy.d.ts +1 -0
- package/dist/services/consent-manager-records.d.mts +1 -0
- package/dist/services/consent-manager-records.d.ts +1 -0
- package/dist/services/consent-manager-registry.d.mts +1 -0
- package/dist/services/consent-manager-registry.d.ts +1 -0
- package/dist/services/cost-centers.d.mts +9 -8
- package/dist/services/cost-centers.d.ts +9 -8
- package/dist/services/cost-centers.js +19 -13
- package/dist/services/cost-centers.js.map +1 -1
- package/dist/services/cost-centers.mjs +19 -13
- package/dist/services/cost-centers.mjs.map +1 -1
- package/dist/services/customers-contacts.d.mts +1 -0
- package/dist/services/customers-contacts.d.ts +1 -0
- package/dist/services/customers-follow-ups.d.mts +1 -0
- package/dist/services/customers-follow-ups.d.ts +1 -0
- package/dist/services/customers-leads.d.mts +1 -0
- package/dist/services/customers-leads.d.ts +1 -0
- package/dist/services/customers-organizations.d.mts +1 -0
- package/dist/services/customers-organizations.d.ts +1 -0
- package/dist/services/customers-roles.d.mts +1 -0
- package/dist/services/customers-roles.d.ts +1 -0
- package/dist/services/customers-segments.d.mts +1 -0
- package/dist/services/customers-segments.d.ts +1 -0
- package/dist/services/customers-value-lists.d.mts +1 -0
- package/dist/services/customers-value-lists.d.ts +1 -0
- package/dist/services/customers.d.mts +1 -0
- package/dist/services/customers.d.ts +1 -0
- package/dist/services/documents-kinds.d.mts +1 -0
- package/dist/services/documents-kinds.d.ts +1 -0
- package/dist/services/documents-register.d.mts +1 -0
- package/dist/services/documents-register.d.ts +1 -0
- package/dist/services/events.d.mts +1 -0
- package/dist/services/events.d.ts +1 -0
- package/dist/services/forms.d.mts +1 -0
- package/dist/services/forms.d.ts +1 -0
- package/dist/services/health.d.mts +1 -0
- package/dist/services/health.d.ts +1 -0
- package/dist/services/inventories-locations.d.mts +1 -0
- package/dist/services/inventories-locations.d.ts +1 -0
- package/dist/services/inventories-reservations.d.mts +1 -0
- package/dist/services/inventories-reservations.d.ts +1 -0
- package/dist/services/inventories-stock.d.mts +1 -0
- package/dist/services/inventories-stock.d.ts +1 -0
- package/dist/services/io.d.mts +1 -0
- package/dist/services/io.d.ts +1 -0
- package/dist/services/journeys-enrollments.d.mts +1 -0
- package/dist/services/journeys-enrollments.d.ts +1 -0
- package/dist/services/journeys-jobs.d.mts +1 -0
- package/dist/services/journeys-jobs.d.ts +1 -0
- package/dist/services/journeys-journeys.d.mts +1 -0
- package/dist/services/journeys-journeys.d.ts +1 -0
- package/dist/services/journeys-stats.d.mts +1 -0
- package/dist/services/journeys-stats.d.ts +1 -0
- package/dist/services/journeys-steps.d.mts +1 -0
- package/dist/services/journeys-steps.d.ts +1 -0
- package/dist/services/journeys-templates.d.mts +1 -0
- package/dist/services/journeys-templates.d.ts +1 -0
- package/dist/services/journeys-timeline.d.mts +1 -0
- package/dist/services/journeys-timeline.d.ts +1 -0
- package/dist/services/journeys-versions.d.mts +1 -0
- package/dist/services/journeys-versions.d.ts +1 -0
- package/dist/services/journeys-vocabularies.d.mts +1 -0
- package/dist/services/journeys-vocabularies.d.ts +1 -0
- package/dist/services/journeys-watches.d.mts +1 -0
- package/dist/services/journeys-watches.d.ts +1 -0
- package/dist/services/locale.d.mts +1 -0
- package/dist/services/locale.d.ts +1 -0
- package/dist/services/loyalty-bonus.d.mts +1 -0
- package/dist/services/loyalty-bonus.d.ts +1 -0
- package/dist/services/loyalty-jobs.d.mts +1 -0
- package/dist/services/loyalty-jobs.d.ts +1 -0
- package/dist/services/loyalty-memberships.d.mts +1 -0
- package/dist/services/loyalty-memberships.d.ts +1 -0
- package/dist/services/loyalty-points.d.mts +1 -0
- package/dist/services/loyalty-points.d.ts +1 -0
- package/dist/services/loyalty-programs.d.mts +1 -0
- package/dist/services/loyalty-programs.d.ts +1 -0
- package/dist/services/loyalty-referrals.d.mts +1 -0
- package/dist/services/loyalty-referrals.d.ts +1 -0
- package/dist/services/loyalty-rewards.d.mts +1 -0
- package/dist/services/loyalty-rewards.d.ts +1 -0
- package/dist/services/loyalty-tiers.d.mts +1 -0
- package/dist/services/loyalty-tiers.d.ts +1 -0
- package/dist/services/loyalty-vocabularies.d.mts +1 -0
- package/dist/services/loyalty-vocabularies.d.ts +1 -0
- package/dist/services/mailings-analysis.d.mts +1 -0
- package/dist/services/mailings-analysis.d.ts +1 -0
- package/dist/services/mailings-blocks.d.mts +1 -0
- package/dist/services/mailings-blocks.d.ts +1 -0
- package/dist/services/mailings-health.d.mts +1 -0
- package/dist/services/mailings-health.d.ts +1 -0
- package/dist/services/mailings-mailings.d.mts +1 -0
- package/dist/services/mailings-mailings.d.ts +1 -0
- package/dist/services/mailings-recipients.d.mts +1 -0
- package/dist/services/mailings-recipients.d.ts +1 -0
- package/dist/services/mailings-send.d.mts +1 -0
- package/dist/services/mailings-send.d.ts +1 -0
- package/dist/services/mailings-variants.d.mts +1 -0
- package/dist/services/mailings-variants.d.ts +1 -0
- package/dist/services/mailings-vocabularies.d.mts +1 -0
- package/dist/services/mailings-vocabularies.d.ts +1 -0
- package/dist/services/marketing-consents-evidence.d.mts +1 -0
- package/dist/services/marketing-consents-evidence.d.ts +1 -0
- package/dist/services/marketing-consents-subscriptions.d.mts +1 -0
- package/dist/services/marketing-consents-subscriptions.d.ts +1 -0
- package/dist/services/marketing-consents-types.d.mts +1 -0
- package/dist/services/marketing-consents-types.d.ts +1 -0
- package/dist/services/markets.d.mts +5 -4
- package/dist/services/markets.d.ts +5 -4
- package/dist/services/markets.js.map +1 -1
- package/dist/services/markets.mjs.map +1 -1
- package/dist/services/messaging.d.mts +1 -0
- package/dist/services/messaging.d.ts +1 -0
- package/dist/services/orderlists.d.mts +1 -0
- package/dist/services/orderlists.d.ts +1 -0
- package/dist/services/orders.d.mts +1 -0
- package/dist/services/orders.d.ts +1 -0
- package/dist/services/pages-collaboration.d.mts +1 -0
- package/dist/services/pages-collaboration.d.ts +1 -0
- package/dist/services/pages-delivery.d.mts +1 -0
- package/dist/services/pages-delivery.d.ts +1 -0
- package/dist/services/pages-editor.d.mts +1 -0
- package/dist/services/pages-editor.d.ts +1 -0
- package/dist/services/pages.d.mts +1 -0
- package/dist/services/pages.d.ts +1 -0
- package/dist/services/payments-ledger.d.mts +1 -0
- package/dist/services/payments-ledger.d.ts +1 -0
- package/dist/services/payments-methods.d.mts +20 -0
- package/dist/services/payments-methods.d.ts +20 -0
- package/dist/services/payments-methods.js +24 -0
- package/dist/services/payments-methods.js.map +1 -1
- package/dist/services/payments-methods.mjs +24 -0
- package/dist/services/payments-methods.mjs.map +1 -1
- package/dist/services/payments-providers.d.mts +3 -2
- package/dist/services/payments-providers.d.ts +3 -2
- package/dist/services/payments-providers.js.map +1 -1
- package/dist/services/payments-providers.mjs.map +1 -1
- package/dist/services/prices.d.mts +1 -0
- package/dist/services/prices.d.ts +1 -0
- package/dist/services/procurement.d.mts +1 -0
- package/dist/services/procurement.d.ts +1 -0
- package/dist/services/products-assets.d.mts +1 -0
- package/dist/services/products-assets.d.ts +1 -0
- package/dist/services/products-categories.d.mts +1 -0
- package/dist/services/products-categories.d.ts +1 -0
- package/dist/services/products-data-model.d.mts +20 -0
- package/dist/services/products-data-model.d.ts +20 -0
- package/dist/services/products-data-model.js +29 -0
- package/dist/services/products-data-model.js.map +1 -1
- package/dist/services/products-data-model.mjs +29 -0
- package/dist/services/products-data-model.mjs.map +1 -1
- package/dist/services/products-manufacturers.d.mts +1 -0
- package/dist/services/products-manufacturers.d.ts +1 -0
- package/dist/services/products-references.d.mts +1 -0
- package/dist/services/products-references.d.ts +1 -0
- package/dist/services/products.d.mts +20 -0
- package/dist/services/products.d.ts +20 -0
- package/dist/services/products.js +24 -0
- package/dist/services/products.js.map +1 -1
- package/dist/services/products.mjs +24 -0
- package/dist/services/products.mjs.map +1 -1
- package/dist/services/promotions-evaluation.d.mts +1 -0
- package/dist/services/promotions-evaluation.d.ts +1 -0
- package/dist/services/promotions-promotions.d.mts +1 -0
- package/dist/services/promotions-promotions.d.ts +1 -0
- package/dist/services/promotions-redemptions.d.mts +1 -0
- package/dist/services/promotions-redemptions.d.ts +1 -0
- package/dist/services/promotions-vouchers.d.mts +1 -0
- package/dist/services/promotions-vouchers.d.ts +1 -0
- package/dist/services/punchout.d.mts +4 -3
- package/dist/services/punchout.d.ts +4 -3
- package/dist/services/punchout.js +1 -1
- package/dist/services/punchout.js.map +1 -1
- package/dist/services/punchout.mjs +1 -1
- package/dist/services/punchout.mjs.map +1 -1
- package/dist/services/quotes-acceptance.d.mts +1 -0
- package/dist/services/quotes-acceptance.d.ts +1 -0
- package/dist/services/quotes-pricing.d.mts +1 -0
- package/dist/services/quotes-pricing.d.ts +1 -0
- package/dist/services/quotes-quotes.d.mts +1 -0
- package/dist/services/quotes-quotes.d.ts +1 -0
- package/dist/services/quotes-ranges.d.mts +1 -0
- package/dist/services/quotes-ranges.d.ts +1 -0
- package/dist/services/quotes-trail.d.mts +1 -0
- package/dist/services/quotes-trail.d.ts +1 -0
- package/dist/services/reviews-aggregates.d.mts +1 -0
- package/dist/services/reviews-aggregates.d.ts +1 -0
- package/dist/services/reviews-jobs.d.mts +1 -0
- package/dist/services/reviews-jobs.d.ts +1 -0
- package/dist/services/reviews-moderation.d.mts +1 -0
- package/dist/services/reviews-moderation.d.ts +1 -0
- package/dist/services/reviews-privacy.d.mts +1 -0
- package/dist/services/reviews-privacy.d.ts +1 -0
- package/dist/services/reviews-products.d.mts +1 -0
- package/dist/services/reviews-products.d.ts +1 -0
- package/dist/services/reviews-requests.d.mts +1 -0
- package/dist/services/reviews-requests.d.ts +1 -0
- package/dist/services/reviews-reviews.d.mts +1 -0
- package/dist/services/reviews-reviews.d.ts +1 -0
- package/dist/services/reviews-vocabularies.d.mts +1 -0
- package/dist/services/reviews-vocabularies.d.ts +1 -0
- package/dist/services/sales-reps-acting-for.d.mts +1 -0
- package/dist/services/sales-reps-acting-for.d.ts +1 -0
- package/dist/services/sales-reps-coverage.d.mts +1 -0
- package/dist/services/sales-reps-coverage.d.ts +1 -0
- package/dist/services/sales-reps-roster.d.mts +1 -0
- package/dist/services/sales-reps-roster.d.ts +1 -0
- package/dist/services/search.d.mts +1 -0
- package/dist/services/search.d.ts +1 -0
- package/dist/services/settings.d.mts +1 -0
- package/dist/services/settings.d.ts +1 -0
- package/dist/services/shipping-carriers.d.mts +1 -0
- package/dist/services/shipping-carriers.d.ts +1 -0
- package/dist/services/shipping-methods.d.mts +5 -4
- package/dist/services/shipping-methods.d.ts +5 -4
- package/dist/services/shipping-methods.js.map +1 -1
- package/dist/services/shipping-methods.mjs.map +1 -1
- package/dist/services/shipping-value-lists.d.mts +1 -0
- package/dist/services/shipping-value-lists.d.ts +1 -0
- package/dist/services/sites.d.mts +1 -0
- package/dist/services/sites.d.ts +1 -0
- package/dist/services/storage.d.mts +1 -0
- package/dist/services/storage.d.ts +1 -0
- package/dist/services/tag-manager-container.d.mts +1 -0
- package/dist/services/tag-manager-container.d.ts +1 -0
- package/dist/services/tag-manager-delivery.d.mts +1 -0
- package/dist/services/tag-manager-delivery.d.ts +1 -0
- package/dist/services/tag-manager-tags.d.mts +1 -0
- package/dist/services/tag-manager-tags.d.ts +1 -0
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/products.ts"],"names":[],"mappings":"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","sourcesContent":["import { RevenexxException, Client, type Payload, UploadProgress } from '../client';\nimport type { Models } from '../models';\n\nimport { ProductsListTrashed } from '../enums/products-list-trashed';\nimport { ProductsListKind } from '../enums/products-list-kind';\nimport { ProductsListStatus } from '../enums/products-list-status';\nimport { ProductsKind } from '../enums/products-kind';\nimport { ProductsStatus } from '../enums/products-status';\nimport { ProductsChannelsAssignMode } from '../enums/products-channels-assign-mode';\nimport { ProductsCopyParts } from '../enums/products-copy-parts';\n\nexport class Products {\n client: Client;\n\n constructor(client: Client) {\n this.client = client;\n }\n\n /**\n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * Every column of `products` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Records in the trash are left out: `trashed=only` reads the trash itself, `trashed=include` both. Any other value of `trashed` is refused with 400.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {ProductsListTrashed} params.trashed - Which records to read besides the live ones: `only` reads the trash, `include` reads both. Omitted, records in the trash are left out.\n * @param {string} params.id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} params.sku - Exact match on `sku`. The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {ProductsListKind} params.kind - Exact match on `kind`. Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.parentId - Exact match on `parent_id`. The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.familyId - Exact match on `family_id`. The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Exact match on `family_variant_id`. Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {string} params.manufacturerId - Exact match on `manufacturer_id`. The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {boolean} params.enabled - Exact match on `enabled`. Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {ProductsListStatus} params.status - Exact match on `status`. Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.statusChangedAt - Exact match on `status_changed_at`. When `status` last changed. Set by this app on every write through it that changes the status, and on a create; a write that names it has it dropped. Null on a product nobody has given a status through this app since the column exists.\n * @param {string} params.channelRelease - Exact match on `channel_release`. Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.channels - Exact match on `channels`. The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist. The whole list is compared, written as a list literal in braces (`{a,b}`) — equality in this order, not containment.\n * @param {string} params.taxClass - Exact match on `tax_class`. The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.baseValues - Exact match on `base_values`. The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.attributeValues - Exact match on `attribute_values`. Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.label - Exact match on `label`. The display name, maintained by the DATABASE so a grid of twenty thousand rows can sort and filter on a name with no join. It is the first of `attribute_values.common.name`, `…common.label`, the `de-DE`/`en-US`/`de`/`en` locale buckets (and the older `de_DE`/`en_US`, which no write can create any longer), `…common.manufacturer_aid`, and finally the SKU — so a value is ALWAYS present, and a label equal to the SKU means the catalog holds no name for this product. A generated column: it cannot be written, and a create or update that names it has it dropped rather than refused. The family-aware answer, which can consult `families.label_attribute` and report where the name came from, is `POST /products/labels`.\n * @param {string} params.quantifiedAssociations - Exact match on `quantified_associations`. The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.completeness - Exact match on `completeness`. How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {boolean} params.manufacturerGpsrComplete - Exact match on `manufacturer_gpsr_complete`. Whether the product's manufacturer carries complete GPSR data — the manufacturer's own `gpsr_complete`, copied onto the product so a list can filter the products whose offer lacks it. Null without a manufacturer. Kept by this app: on every write through it that names a manufacturer, and on all of a manufacturer's products when that manufacturer's GPSR state changes. Read-only: a write that names it has it dropped.\n * @param {string} params.externalId - Exact match on `external_id`. The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {string} params.externalRefs - Exact match on `external_refs`. Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.sourceSyncedAt - Exact match on `source_synced_at`. When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {string} params.sourceData - Exact match on `source_data`. What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} params.updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @param {string} params.deletedAt - Exact match on `deleted_at`. When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Exact match on `deleted_by`. Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - Exact match on `deleted_by_id`. The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {string} params.trashBatch - Exact match on `trash_batch`. The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @param {string} params.purgeAt - Exact match on `purge_at`. When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Exact match on `purge_blocked_reason`. Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsList(params?: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string }): Promise<{}>;\n /**\n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * Every column of `products` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Records in the trash are left out: `trashed=only` reads the trash itself, `trashed=include` both. Any other value of `trashed` is refused with 400.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {ProductsListTrashed} trashed - Which records to read besides the live ones: `only` reads the trash, `include` reads both. Omitted, records in the trash are left out.\n * @param {string} id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} sku - Exact match on `sku`. The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {ProductsListKind} kind - Exact match on `kind`. Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} parentId - Exact match on `parent_id`. The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} familyId - Exact match on `family_id`. The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Exact match on `family_variant_id`. Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {string} manufacturerId - Exact match on `manufacturer_id`. The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {boolean} enabled - Exact match on `enabled`. Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {ProductsListStatus} status - Exact match on `status`. Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} statusChangedAt - Exact match on `status_changed_at`. When `status` last changed. Set by this app on every write through it that changes the status, and on a create; a write that names it has it dropped. Null on a product nobody has given a status through this app since the column exists.\n * @param {string} channelRelease - Exact match on `channel_release`. Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} channels - Exact match on `channels`. The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist. The whole list is compared, written as a list literal in braces (`{a,b}`) — equality in this order, not containment.\n * @param {string} taxClass - Exact match on `tax_class`. The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} baseValues - Exact match on `base_values`. The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} attributeValues - Exact match on `attribute_values`. Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} label - Exact match on `label`. The display name, maintained by the DATABASE so a grid of twenty thousand rows can sort and filter on a name with no join. It is the first of `attribute_values.common.name`, `…common.label`, the `de-DE`/`en-US`/`de`/`en` locale buckets (and the older `de_DE`/`en_US`, which no write can create any longer), `…common.manufacturer_aid`, and finally the SKU — so a value is ALWAYS present, and a label equal to the SKU means the catalog holds no name for this product. A generated column: it cannot be written, and a create or update that names it has it dropped rather than refused. The family-aware answer, which can consult `families.label_attribute` and report where the name came from, is `POST /products/labels`.\n * @param {string} quantifiedAssociations - Exact match on `quantified_associations`. The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} completeness - Exact match on `completeness`. How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {boolean} manufacturerGpsrComplete - Exact match on `manufacturer_gpsr_complete`. Whether the product's manufacturer carries complete GPSR data — the manufacturer's own `gpsr_complete`, copied onto the product so a list can filter the products whose offer lacks it. Null without a manufacturer. Kept by this app: on every write through it that names a manufacturer, and on all of a manufacturer's products when that manufacturer's GPSR state changes. Read-only: a write that names it has it dropped.\n * @param {string} externalId - Exact match on `external_id`. The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {string} externalRefs - Exact match on `external_refs`. Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} sourceSyncedAt - Exact match on `source_synced_at`. When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {string} sourceData - Exact match on `source_data`. What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @param {string} deletedAt - Exact match on `deleted_at`. When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Exact match on `deleted_by`. Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - Exact match on `deleted_by_id`. The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {string} trashBatch - Exact match on `trash_batch`. The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @param {string} purgeAt - Exact match on `purge_at`. When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Exact match on `purge_blocked_reason`. Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsList(limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string): Promise<{}>;\n productsList(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string } | number,\n ...rest: [(number)?, (string)?, (ProductsListTrashed)?, (string)?, (string)?, (ProductsListKind)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (ProductsListStatus)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n trashed: rest[2] as ProductsListTrashed,\n id: rest[3] as string,\n sku: rest[4] as string,\n kind: rest[5] as ProductsListKind,\n parentId: rest[6] as string,\n familyId: rest[7] as string,\n familyVariantId: rest[8] as string,\n manufacturerId: rest[9] as string,\n enabled: rest[10] as boolean,\n status: rest[11] as ProductsListStatus,\n statusChangedAt: rest[12] as string,\n channelRelease: rest[13] as string,\n channels: rest[14] as string,\n taxClass: rest[15] as string,\n baseValues: rest[16] as string,\n attributeValues: rest[17] as string,\n label: rest[18] as string,\n quantifiedAssociations: rest[19] as string,\n completeness: rest[20] as string,\n manufacturerGpsrComplete: rest[21] as boolean,\n externalId: rest[22] as string,\n externalRefs: rest[23] as string,\n sourceSyncedAt: rest[24] as string,\n sourceData: rest[25] as string,\n createdAt: rest[26] as string,\n updatedAt: rest[27] as string,\n deletedAt: rest[28] as string,\n deletedBy: rest[29] as string,\n deletedById: rest[30] as string,\n trashBatch: rest[31] as string,\n purgeAt: rest[32] as string,\n purgeBlockedReason: rest[33] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const trashed = params.trashed;\n const id = params.id;\n const sku = params.sku;\n const kind = params.kind;\n const parentId = params.parentId;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const manufacturerId = params.manufacturerId;\n const enabled = params.enabled;\n const status = params.status;\n const statusChangedAt = params.statusChangedAt;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const taxClass = params.taxClass;\n const baseValues = params.baseValues;\n const attributeValues = params.attributeValues;\n const label = params.label;\n const quantifiedAssociations = params.quantifiedAssociations;\n const completeness = params.completeness;\n const manufacturerGpsrComplete = params.manufacturerGpsrComplete;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const sourceSyncedAt = params.sourceSyncedAt;\n const sourceData = params.sourceData;\n const createdAt = params.createdAt;\n const updatedAt = params.updatedAt;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const trashBatch = params.trashBatch;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n\n\n const apiPath = '/v1/products';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof trashed !== 'undefined') {\n apiPayload['trashed'] = trashed;\n }\n if (typeof id !== 'undefined') {\n apiPayload['id'] = id;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof statusChangedAt !== 'undefined') {\n apiPayload['status_changed_at'] = statusChangedAt;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof label !== 'undefined') {\n apiPayload['label'] = label;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof manufacturerGpsrComplete !== 'undefined') {\n apiPayload['manufacturer_gpsr_complete'] = manufacturerGpsrComplete;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof createdAt !== 'undefined') {\n apiPayload['created_at'] = createdAt;\n }\n if (typeof updatedAt !== 'undefined') {\n apiPayload['updated_at'] = updatedAt;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates one product and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * `sku` is the only column the database refuses the row without; everything else has a default or is nullable. A second row with the same `sku` answers 409. This app owns the create: `enabled` defaults from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import cannot publish twenty thousand unfinished products the moment it lands, and a product that names no family gets the `default_product_family` one. An explicit value in the body always wins over both. It also takes three keys that are no column — `name`, `category_id` and `copy_from` — for the two-step create, and stamps `status_changed_at`.\n *\n * @param {string} params.sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} params.attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} params.baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {string} params.categoryId - A category to file the new product into by hand, in the same call. It has to exist, be live and be active, or the product is not created.\n * @param {object} params.channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} params.channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} params.completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} params.copyFrom - A product to start from: its family, family variant, manufacturer, tax class, channels, base values (without the GTIN) and attribute values are taken, and anything the body sends wins. The copy is a new draft; nothing else is linked.\n * @param {string} params.deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} params.enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} params.externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} params.externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} params.familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} params.kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} params.name - The product's name, stored as the `name` value under the first of the `label_locales` (German when none is set). A shortcut for writing that one value of `attribute_values`.\n * @param {string} params.parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} params.quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {object} params.sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} params.sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} params.status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsCreate(params: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string }): Promise<Models.Products>;\n /**\n * Creates one product and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * `sku` is the only column the database refuses the row without; everything else has a default or is nullable. A second row with the same `sku` answers 409. This app owns the create: `enabled` defaults from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import cannot publish twenty thousand unfinished products the moment it lands, and a product that names no family gets the `default_product_family` one. An explicit value in the body always wins over both. It also takes three keys that are no column — `name`, `category_id` and `copy_from` — for the two-step create, and stamps `status_changed_at`.\n *\n * @param {string} sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {string} categoryId - A category to file the new product into by hand, in the same call. It has to exist, be live and be active, or the product is not created.\n * @param {object} channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} copyFrom - A product to start from: its family, family variant, manufacturer, tax class, channels, base values (without the GTIN) and attribute values are taken, and anything the body sends wins. The copy is a new draft; nothing else is linked.\n * @param {string} deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} name - The product's name, stored as the `name` value under the first of the `label_locales` (German when none is set). A shortcut for writing that one value of `attribute_values`.\n * @param {string} parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {object} sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCreate(sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string): Promise<Models.Products>;\n productsCreate(\n paramsOrFirst: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string } | string,\n ...rest: [(object)?, (object)?, (string)?, (object)?, (string[])?, (object)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (object)?, (string)?, (string)?, (ProductsKind)?, (string)?, (string)?, (string)?, (string)?, (string)?, (object)?, (object)?, (string)?, (ProductsStatus)?, (string)?, (string)?] \n ): Promise<Models.Products> {\n let params: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n } else {\n params = {\n sku: paramsOrFirst as string,\n attributeValues: rest[0] as object,\n baseValues: rest[1] as object,\n categoryId: rest[2] as string,\n channelRelease: rest[3] as object,\n channels: rest[4] as string[],\n completeness: rest[5] as object,\n copyFrom: rest[6] as string,\n deletedAt: rest[7] as string,\n deletedBy: rest[8] as string,\n deletedById: rest[9] as string,\n enabled: rest[10] as boolean,\n externalId: rest[11] as string,\n externalRefs: rest[12] as object,\n familyId: rest[13] as string,\n familyVariantId: rest[14] as string,\n kind: rest[15] as ProductsKind,\n manufacturerId: rest[16] as string,\n name: rest[17] as string,\n parentId: rest[18] as string,\n purgeAt: rest[19] as string,\n purgeBlockedReason: rest[20] as string,\n quantifiedAssociations: rest[21] as object,\n sourceData: rest[22] as object,\n sourceSyncedAt: rest[23] as string,\n status: rest[24] as ProductsStatus,\n taxClass: rest[25] as string,\n trashBatch: rest[26] as string \n };\n }\n \n const sku = params.sku;\n const attributeValues = params.attributeValues;\n const baseValues = params.baseValues;\n const categoryId = params.categoryId;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const completeness = params.completeness;\n const copyFrom = params.copyFrom;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const enabled = params.enabled;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const kind = params.kind;\n const manufacturerId = params.manufacturerId;\n const name = params.name;\n const parentId = params.parentId;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n const quantifiedAssociations = params.quantifiedAssociations;\n const sourceData = params.sourceData;\n const sourceSyncedAt = params.sourceSyncedAt;\n const status = params.status;\n const taxClass = params.taxClass;\n const trashBatch = params.trashBatch;\n\n if (typeof sku === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"sku\"');\n }\n\n const apiPath = '/v1/products';\n const apiPayload: Payload = {};\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof categoryId !== 'undefined') {\n apiPayload['category_id'] = categoryId;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof copyFrom !== 'undefined') {\n apiPayload['copy_from'] = copyFrom;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof name !== 'undefined') {\n apiPayload['name'] = name;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Answers four fields — id, sku, tax_class and the resolved display name — for a list of ids and/or SKUs in ONE call. It exists for the app on the other side of a product reference: the prices app holds SKUs and needs a tax class, a feed builder holds ids and needs names, and neither should page through the catalog or fire a request per line. Ask by either identifier or both; the two are unioned and a product named twice comes back once. At most 500 of each per call; more is refused with 400 rather than cut short.\n * \n * With `full: true` each item is the whole row instead — what `GET /products/{id}` answers, every column and the complete `attribute_values` document, with `label` still the resolved name. That mode is for a caller that has to hand a product on and cannot know in advance which fields it will need: punchout's hand-back writes whatever columns and attribute codes a merchant's field mappings name, for every line of a cart, and needs them in one read. Leave it off when four fields will do; the short shape is unchanged by it.\n * \n * Either way it answers what it FOUND: an id or SKU that names nothing is simply absent from `items` rather than an error, so compare the length of what you sent with what came back if a miss matters. For a scannable list use `GET /products/grid`.\n *\n * @param {boolean} params.full - True answers each product as its whole row (`ProductBatchRow`) instead of the four-field reference (`ProductTaxRef`). For a caller that must hand a whole product on — every column and attribute value — without a read per line.\n * @param {string[]} params.ids - Product ids, when the caller already holds them. At most 500.\n * @param {string[]} params.skus - Product SKUs — the identifier a foreign system carries, which is why this route exists at all. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsBatch(params?: { full?: boolean, ids?: string[], skus?: string[] }): Promise<{}>;\n /**\n * Answers four fields — id, sku, tax_class and the resolved display name — for a list of ids and/or SKUs in ONE call. It exists for the app on the other side of a product reference: the prices app holds SKUs and needs a tax class, a feed builder holds ids and needs names, and neither should page through the catalog or fire a request per line. Ask by either identifier or both; the two are unioned and a product named twice comes back once. At most 500 of each per call; more is refused with 400 rather than cut short.\n * \n * With `full: true` each item is the whole row instead — what `GET /products/{id}` answers, every column and the complete `attribute_values` document, with `label` still the resolved name. That mode is for a caller that has to hand a product on and cannot know in advance which fields it will need: punchout's hand-back writes whatever columns and attribute codes a merchant's field mappings name, for every line of a cart, and needs them in one read. Leave it off when four fields will do; the short shape is unchanged by it.\n * \n * Either way it answers what it FOUND: an id or SKU that names nothing is simply absent from `items` rather than an error, so compare the length of what you sent with what came back if a miss matters. For a scannable list use `GET /products/grid`.\n *\n * @param {boolean} full - True answers each product as its whole row (`ProductBatchRow`) instead of the four-field reference (`ProductTaxRef`). For a caller that must hand a whole product on — every column and attribute value — without a read per line.\n * @param {string[]} ids - Product ids, when the caller already holds them. At most 500.\n * @param {string[]} skus - Product SKUs — the identifier a foreign system carries, which is why this route exists at all. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsBatch(full?: boolean, ids?: string[], skus?: string[]): Promise<{}>;\n productsBatch(\n paramsOrFirst?: { full?: boolean, ids?: string[], skus?: string[] } | boolean,\n ...rest: [(string[])?, (string[])?] \n ): Promise<{}> {\n let params: { full?: boolean, ids?: string[], skus?: string[] };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { full?: boolean, ids?: string[], skus?: string[] };\n } else {\n params = {\n full: paramsOrFirst as boolean,\n ids: rest[0] as string[],\n skus: rest[1] as string[] \n };\n }\n \n const full = params.full;\n const ids = params.ids;\n const skus = params.skus;\n\n\n const apiPath = '/v1/products/batch';\n const apiPayload: Payload = {};\n if (typeof full !== 'undefined') {\n apiPayload['full'] = full;\n }\n if (typeof ids !== 'undefined') {\n apiPayload['ids'] = ids;\n }\n if (typeof skus !== 'undefined') {\n apiPayload['skus'] = skus;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * The list a merchant can actually scan, as opposed to `GET /products`, which answers SKUs and a jsonb blob. Every row arrives already flattened: its resolved display name and where that name came from, its family code, its stored completeness, and the value of every attribute the catalog marks `usable_in_grid` — no join, no second call. `q` is a case-insensitive substring of the stored `label` column, which falls back to the SKU, so one box finds a product by either. Soft-deleted products are excluded here, unlike `GET /products`.\n * \n * It filters on `q`, `kind`, `enabled` and `family_id`, and on NOTHING ELSE — a query parameter it does not accept is refused with 400 rather than dropped. That matters because of `filters`: the array reports the attributes marked `is_filterable`, which is what a filter bar should OFFER, and it is not a query surface. Filtering on an attribute value is not offered by this API at all — the values live inside a four-bucket jsonb document and are read through a fallback chain, so it is a feature with a design of its own rather than a parameter that was forgotten.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} params.q - Case-insensitive substring of the display name (or the SKU, when there is no name).\n * @param {ProductsListKind} params.kind - Restrict to one product kind.\n * @param {boolean} params.enabled - Restrict to enabled or disabled products.\n * @param {string} params.familyId - Restrict to one family. A family id that does not exist is not an error here — it simply matches nothing.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsGrid(params?: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string }): Promise<{}>;\n /**\n * The list a merchant can actually scan, as opposed to `GET /products`, which answers SKUs and a jsonb blob. Every row arrives already flattened: its resolved display name and where that name came from, its family code, its stored completeness, and the value of every attribute the catalog marks `usable_in_grid` — no join, no second call. `q` is a case-insensitive substring of the stored `label` column, which falls back to the SKU, so one box finds a product by either. Soft-deleted products are excluded here, unlike `GET /products`.\n * \n * It filters on `q`, `kind`, `enabled` and `family_id`, and on NOTHING ELSE — a query parameter it does not accept is refused with 400 rather than dropped. That matters because of `filters`: the array reports the attributes marked `is_filterable`, which is what a filter bar should OFFER, and it is not a query surface. Filtering on an attribute value is not offered by this API at all — the values live inside a four-bucket jsonb document and are read through a fallback chain, so it is a feature with a design of its own rather than a parameter that was forgotten.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} q - Case-insensitive substring of the display name (or the SKU, when there is no name).\n * @param {ProductsListKind} kind - Restrict to one product kind.\n * @param {boolean} enabled - Restrict to enabled or disabled products.\n * @param {string} familyId - Restrict to one family. A family id that does not exist is not an error here — it simply matches nothing.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsGrid(limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string): Promise<{}>;\n productsGrid(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string } | number,\n ...rest: [(number)?, (string)?, (string)?, (ProductsListKind)?, (boolean)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n q: rest[2] as string,\n kind: rest[3] as ProductsListKind,\n enabled: rest[4] as boolean,\n familyId: rest[5] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const q = params.q;\n const kind = params.kind;\n const enabled = params.enabled;\n const familyId = params.familyId;\n\n\n const apiPath = '/v1/products/grid';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof q !== 'undefined') {\n apiPayload['q'] = q;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * What is this product CALLED? A product's name is an attribute rather than a column, and which attribute it is, is per family — so no plain read can answer it. This resolves up to 500 products at once, by id and/or SKU: it reads families.label_attribute (falling back to the default_label_attribute setting, then to the conventional `name`) and looks the value up through the scoped attribute_values document — common, then locale_specific in the label_locales order, then the channel buckets.\n * \n * It reports WHERE the name was found, which is the half that matters: `source: \"sku\"` means the catalog holds no name for this product and the SKU is standing in for one, so show it as a missing name rather than as a name. Writes nothing, and answers only what it found.\n *\n * @param {string[]} params.ids - Product ids to name. At most 500.\n * @param {string[]} params.skus - Product SKUs to name. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsLabels(params?: { ids?: string[], skus?: string[] }): Promise<{}>;\n /**\n * What is this product CALLED? A product's name is an attribute rather than a column, and which attribute it is, is per family — so no plain read can answer it. This resolves up to 500 products at once, by id and/or SKU: it reads families.label_attribute (falling back to the default_label_attribute setting, then to the conventional `name`) and looks the value up through the scoped attribute_values document — common, then locale_specific in the label_locales order, then the channel buckets.\n * \n * It reports WHERE the name was found, which is the half that matters: `source: \"sku\"` means the catalog holds no name for this product and the SKU is standing in for one, so show it as a missing name rather than as a name. Writes nothing, and answers only what it found.\n *\n * @param {string[]} ids - Product ids to name. At most 500.\n * @param {string[]} skus - Product SKUs to name. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsLabels(ids?: string[], skus?: string[]): Promise<{}>;\n productsLabels(\n paramsOrFirst?: { ids?: string[], skus?: string[] } | string[],\n ...rest: [(string[])?] \n ): Promise<{}> {\n let params: { ids?: string[], skus?: string[] };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { ids?: string[], skus?: string[] };\n } else {\n params = {\n ids: paramsOrFirst as string[],\n skus: rest[0] as string[] \n };\n }\n \n const ids = params.ids;\n const skus = params.skus;\n\n\n const apiPath = '/v1/products/labels';\n const apiPayload: Payload = {};\n if (typeof ids !== 'undefined') {\n apiPayload['ids'] = ids;\n }\n if (typeof skus !== 'undefined') {\n apiPayload['skus'] = skus;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * Every column of `product_associations` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} params.id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} params.productId - Exact match on `product_id`. The product the relation starts at — the one whose detail page shows it.\n * @param {string} params.associationTypeId - Exact match on `association_type_id`. Which kind of relation this is — the `association_types` row.\n * @param {string} params.targetProductId - Exact match on `target_product_id`. The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} params.quantity - Exact match on `quantity`. How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {number} params.position - Exact match on `position`. Order in which the targets are shown, ascending.\n * @param {string} params.createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} params.updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsProductAssociationsList(params?: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string }): Promise<{}>;\n /**\n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * Every column of `product_associations` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} productId - Exact match on `product_id`. The product the relation starts at — the one whose detail page shows it.\n * @param {string} associationTypeId - Exact match on `association_type_id`. Which kind of relation this is — the `association_types` row.\n * @param {string} targetProductId - Exact match on `target_product_id`. The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} quantity - Exact match on `quantity`. How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {number} position - Exact match on `position`. Order in which the targets are shown, ascending.\n * @param {string} createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsList(limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string): Promise<{}>;\n productsProductAssociationsList(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string } | number,\n ...rest: [(number)?, (string)?, (string)?, (string)?, (string)?, (string)?, (number)?, (number)?, (string)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n id: rest[2] as string,\n productId: rest[3] as string,\n associationTypeId: rest[4] as string,\n targetProductId: rest[5] as string,\n quantity: rest[6] as number,\n position: rest[7] as number,\n createdAt: rest[8] as string,\n updatedAt: rest[9] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const id = params.id;\n const productId = params.productId;\n const associationTypeId = params.associationTypeId;\n const targetProductId = params.targetProductId;\n const quantity = params.quantity;\n const position = params.position;\n const createdAt = params.createdAt;\n const updatedAt = params.updatedAt;\n\n\n const apiPath = '/v1/products/product_associations';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof id !== 'undefined') {\n apiPayload['id'] = id;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof createdAt !== 'undefined') {\n apiPayload['created_at'] = createdAt;\n }\n if (typeof updatedAt !== 'undefined') {\n apiPayload['updated_at'] = updatedAt;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates one product association and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * `product_id`, `association_type_id`, `target_product_id` are the only columns the database refuses the row without; everything else has a default or is nullable. A second row with the same `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} params.associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {string} params.productId - The product the relation starts at — the one whose detail page shows it.\n * @param {string} params.targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} params.position - Order in which the targets are shown, ascending.\n * @param {number} params.quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsCreate(params: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number }): Promise<Models.ProductAssociations>;\n /**\n * Creates one product association and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * `product_id`, `association_type_id`, `target_product_id` are the only columns the database refuses the row without; everything else has a default or is nullable. A second row with the same `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {string} productId - The product the relation starts at — the one whose detail page shows it.\n * @param {string} targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} position - Order in which the targets are shown, ascending.\n * @param {number} quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsCreate(associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number): Promise<Models.ProductAssociations>;\n productsProductAssociationsCreate(\n paramsOrFirst: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number } | string,\n ...rest: [(string)?, (string)?, (number)?, (number)?] \n ): Promise<Models.ProductAssociations> {\n let params: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number };\n } else {\n params = {\n associationTypeId: paramsOrFirst as string,\n productId: rest[0] as string,\n targetProductId: rest[1] as string,\n position: rest[2] as number,\n quantity: rest[3] as number \n };\n }\n \n const associationTypeId = params.associationTypeId;\n const productId = params.productId;\n const targetProductId = params.targetProductId;\n const position = params.position;\n const quantity = params.quantity;\n\n if (typeof associationTypeId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"associationTypeId\"');\n }\n if (typeof productId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"productId\"');\n }\n if (typeof targetProductId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"targetProductId\"');\n }\n\n const apiPath = '/v1/products/product_associations';\n const apiPayload: Payload = {};\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Deletes one product association by id. It is a hard delete — the row is gone, and the answer is a confirmation rather than a result to branch on.\n * \n * Nothing in this schema references it, so nothing else changes.\n * \n * An id no product association of this tenant carries answers 404; there is no 409, because every foreign key pointing at this entity resolves itself on delete rather than blocking one.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsProductAssociationsDelete(params: { id: string }): Promise<{}>;\n /**\n * Deletes one product association by id. It is a hard delete — the row is gone, and the answer is a confirmation rather than a result to branch on.\n * \n * Nothing in this schema references it, so nothing else changes.\n * \n * An id no product association of this tenant carries answers 404; there is no 409, because every foreign key pointing at this entity resolves itself on delete rather than blocking one.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsDelete(id: string): Promise<{}>;\n productsProductAssociationsDelete(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'delete',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Reads one product association by its id — the whole row, every column, as it is stored.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * An id no product association of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsGet(params: { id: string }): Promise<Models.ProductAssociations>;\n /**\n * Reads one product association by its id — the whole row, every column, as it is stored.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * An id no product association of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsGet(id: string): Promise<Models.ProductAssociations>;\n productsProductAssociationsGet(\n paramsOrFirst: { id: string } | string \n ): Promise<Models.ProductAssociations> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Updates one product association by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {number} params.position - Order in which the targets are shown, ascending.\n * @param {string} params.productId - The product the relation starts at — the one whose detail page shows it.\n * @param {number} params.quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {string} params.targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsUpdate(params: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string }): Promise<Models.ProductAssociations>;\n /**\n * Updates one product association by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {number} position - Order in which the targets are shown, ascending.\n * @param {string} productId - The product the relation starts at — the one whose detail page shows it.\n * @param {number} quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {string} targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsUpdate(id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string): Promise<Models.ProductAssociations>;\n productsProductAssociationsUpdate(\n paramsOrFirst: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string } | string,\n ...rest: [(string)?, (number)?, (string)?, (number)?, (string)?] \n ): Promise<Models.ProductAssociations> {\n let params: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n associationTypeId: rest[0] as string,\n position: rest[1] as number,\n productId: rest[2] as string,\n quantity: rest[3] as number,\n targetProductId: rest[4] as string \n };\n }\n \n const id = params.id;\n const associationTypeId = params.associationTypeId;\n const position = params.position;\n const productId = params.productId;\n const quantity = params.quantity;\n const targetProductId = params.targetProductId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'put',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * What this app needs once before it is ready, as the checklist the Cockpit's „Einrichtung“ shows: every step of `cockpit.json#setup`, done or not. A step is done when what it names exists — at least one live row of a table, or a setting with a value.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsSetup(): Promise<{}> {\n\n const apiPath = '/v1/products/setup';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Deletes the products in the trash for good, oldest first, up to 200 per call, each through the same checks as a second `DELETE`. One that is held is reported with its reason and the rest go on; `more` says another call has work left. Refused with 403 for a storefront contact.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsEmptyTrash(): Promise<{}> {\n\n const apiPath = '/v1/products/trash/empty';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * The index of the enums this app ENFORCES — `product-kinds`, `product-status`, `membership-sources`, `rule-matches`, `asset-sources`, `asset-licenses` — served by the app that owns the CHECK constraint each one is parsed out of, so a UI never has to keep its own copy of a status map and watch it drift. Names and titles only: fetch one by name for its values, badge tones and descriptions.\n * \n * The set is a fixed property of this app rather than tenant data, so it is the same list for every tenant. `attributes.type` is deliberately absent: it carries no CHECK, because the whole point of an attribute-driven PIM is that the type list is data an integrator extends.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsVocabulariesList(): Promise<{}> {\n\n const apiPath = '/v1/products/vocabularies';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * One vocabulary with every value it admits, each with a title, a description and the badge tone a UI should paint it in. The value set is parsed out of the CHECK constraint in schema.json, so what is served IS what is enforced. Labels are curated on top and can only add words and colour — a permitted value nobody labelled still appears, titled from its own key.\n *\n * @param {string} params.name - The vocabulary name, as listed by GET /products/vocabularies.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsVocabulariesGet(params: { name: string }): Promise<{}>;\n /**\n * One vocabulary with every value it admits, each with a title, a description and the badge tone a UI should paint it in. The value set is parsed out of the CHECK constraint in schema.json, so what is served IS what is enforced. Labels are curated on top and can only add words and colour — a permitted value nobody labelled still appears, titled from its own key.\n *\n * @param {string} name - The vocabulary name, as listed by GET /products/vocabularies.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsVocabulariesGet(name: string): Promise<{}>;\n productsVocabulariesGet(\n paramsOrFirst: { name: string } | string \n ): Promise<{}> {\n let params: { name: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { name: string };\n } else {\n params = {\n name: paramsOrFirst as string \n };\n }\n \n const name = params.name;\n\n if (typeof name === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"name\"');\n }\n\n const apiPath = '/v1/products/vocabularies/{name}'.replace('{name}', name);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Moves one product to the trash — or, called on one that is already there, deletes it for good.\n * \n * The first call stamps the product, and the variants of a model go with it; all of them share one `trash_batch` and come back together on `POST …/{id}/restore`. Nothing that points at it changes: its assignments stay and stop acting, and the answer lists them as warnings with a count. Its sku stays reserved while it is in the trash.\n * \n * The second call deletes it for good, with the rows of its group, and only then do the cascades and `*.deleted` happen. It is refused with 409 while a blocker holds: stock on hand or an active reservation in inventories, a position of an open quote in quotes, or a line of an open purchase request in procurement holds it; price entries are a warning, because prices removes them when the product is deleted. A blocker that could not be checked counts as one. It is refused with 403 for a storefront contact — deleting for good is back-office work.\n * \n * An id no product of this tenant carries answers 404.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsDelete(params: { id: string }): Promise<{}>;\n /**\n * Moves one product to the trash — or, called on one that is already there, deletes it for good.\n * \n * The first call stamps the product, and the variants of a model go with it; all of them share one `trash_batch` and come back together on `POST …/{id}/restore`. Nothing that points at it changes: its assignments stay and stop acting, and the answer lists them as warnings with a count. Its sku stays reserved while it is in the trash.\n * \n * The second call deletes it for good, with the rows of its group, and only then do the cascades and `*.deleted` happen. It is refused with 409 while a blocker holds: stock on hand or an active reservation in inventories, a position of an open quote in quotes, or a line of an open purchase request in procurement holds it; price entries are a warning, because prices removes them when the product is deleted. A blocker that could not be checked counts as one. It is refused with 403 for a storefront contact — deleting for good is back-office work.\n * \n * An id no product of this tenant carries answers 404.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsDelete(id: string): Promise<{}>;\n productsDelete(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'delete',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Reads one product by its id — the whole row, every column, as it is stored.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * An id no product of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached. Nothing is resolved for you here — for the display name, the family code and the grid attributes already unpacked, use `GET /products/grid` or `POST /products/labels`.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsGet(params: { id: string }): Promise<Models.Products>;\n /**\n * Reads one product by its id — the whole row, every column, as it is stored.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * An id no product of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached. Nothing is resolved for you here — for the display name, the family code and the grid attributes already unpacked, use `GET /products/grid` or `POST /products/labels`.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsGet(id: string): Promise<Models.Products>;\n productsGet(\n paramsOrFirst: { id: string } | string \n ): Promise<Models.Products> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Updates one product by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `sku` answers 409. `label` is a generated column: naming it is dropped rather than refused, and `completeness` is written by the two metadata routes, not here.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} params.attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} params.baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {object} params.channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} params.channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} params.completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} params.deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} params.enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} params.externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} params.externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} params.familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} params.kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} params.parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} params.quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {string} params.sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} params.sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} params.sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} params.status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsUpdate(params: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string }): Promise<Models.Products>;\n /**\n * Updates one product by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `sku` answers 409. `label` is a generated column: naming it is dropped rather than refused, and `completeness` is written by the two metadata routes, not here.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {object} channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {string} sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsUpdate(id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string): Promise<Models.Products>;\n productsUpdate(\n paramsOrFirst: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string } | string,\n ...rest: [(object)?, (object)?, (object)?, (string[])?, (object)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (object)?, (string)?, (string)?, (ProductsKind)?, (string)?, (string)?, (string)?, (string)?, (object)?, (string)?, (object)?, (string)?, (ProductsStatus)?, (string)?, (string)?] \n ): Promise<Models.Products> {\n let params: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n attributeValues: rest[0] as object,\n baseValues: rest[1] as object,\n channelRelease: rest[2] as object,\n channels: rest[3] as string[],\n completeness: rest[4] as object,\n deletedAt: rest[5] as string,\n deletedBy: rest[6] as string,\n deletedById: rest[7] as string,\n enabled: rest[8] as boolean,\n externalId: rest[9] as string,\n externalRefs: rest[10] as object,\n familyId: rest[11] as string,\n familyVariantId: rest[12] as string,\n kind: rest[13] as ProductsKind,\n manufacturerId: rest[14] as string,\n parentId: rest[15] as string,\n purgeAt: rest[16] as string,\n purgeBlockedReason: rest[17] as string,\n quantifiedAssociations: rest[18] as object,\n sku: rest[19] as string,\n sourceData: rest[20] as object,\n sourceSyncedAt: rest[21] as string,\n status: rest[22] as ProductsStatus,\n taxClass: rest[23] as string,\n trashBatch: rest[24] as string \n };\n }\n \n const id = params.id;\n const attributeValues = params.attributeValues;\n const baseValues = params.baseValues;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const completeness = params.completeness;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const enabled = params.enabled;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const kind = params.kind;\n const manufacturerId = params.manufacturerId;\n const parentId = params.parentId;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n const quantifiedAssociations = params.quantifiedAssociations;\n const sku = params.sku;\n const sourceData = params.sourceData;\n const sourceSyncedAt = params.sourceSyncedAt;\n const status = params.status;\n const taxClass = params.taxClass;\n const trashBatch = params.trashBatch;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'put',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Sets one attribute's value on one product — what a bulk „Attribute bearbeiten“ over a selection calls once per product. With `locale` the value goes under that language (re-keyed to the tenant's write key like any write); with `locale: \"all\"` or none it goes into the common bucket, the same in every language. The rest of the product's values stay as they are.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.attribute - The code of a product attribute.\n * @param {any} params.value - The value, in the shape the attribute stores. Null clears it.\n * @param {string} params.locale - A language key the tenant writes, or 'all' (the default) for the common bucket.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsAttributeSet(params: { id: string, attribute: string, value: any, locale?: string }): Promise<{}>;\n /**\n * Sets one attribute's value on one product — what a bulk „Attribute bearbeiten“ over a selection calls once per product. With `locale` the value goes under that language (re-keyed to the tenant's write key like any write); with `locale: \"all\"` or none it goes into the common bucket, the same in every language. The rest of the product's values stay as they are.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} attribute - The code of a product attribute.\n * @param {any} value - The value, in the shape the attribute stores. Null clears it.\n * @param {string} locale - A language key the tenant writes, or 'all' (the default) for the common bucket.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsAttributeSet(id: string, attribute: string, value: any, locale?: string): Promise<{}>;\n productsAttributeSet(\n paramsOrFirst: { id: string, attribute: string, value: any, locale?: string } | string,\n ...rest: [(string)?, (any)?, (string)?] \n ): Promise<{}> {\n let params: { id: string, attribute: string, value: any, locale?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, attribute: string, value: any, locale?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n attribute: rest[0] as string,\n value: rest[1] as any,\n locale: rest[2] as string \n };\n }\n \n const id = params.id;\n const attribute = params.attribute;\n const value = params.value;\n const locale = params.locale;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof attribute === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"attribute\"');\n }\n if (typeof value === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"value\"');\n }\n\n const apiPath = '/v1/products/{id}/attribute-value'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof attribute !== 'undefined') {\n apiPayload['attribute'] = attribute;\n }\n if (typeof locale !== 'undefined') {\n apiPayload['locale'] = locale;\n }\n if (typeof value !== 'undefined') {\n apiPayload['value'] = value;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Adds, removes or replaces channel codes on one product's `channels` — what a bulk „Kanäle zuordnen“ over a selection calls once per product. The codes are the channels app's; this app stores them as sent, trimmed, each once, and does not ask the channels app whether they exist.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string[]} params.channels - The channel codes to add, remove or set.\n * @param {ProductsChannelsAssignMode} params.mode - 'add' (the default) keeps what the product has and adds these, 'remove' takes these out, 'replace' sets exactly these.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsChannelsAssign(params: { id: string, channels: string[], mode?: ProductsChannelsAssignMode }): Promise<{}>;\n /**\n * Adds, removes or replaces channel codes on one product's `channels` — what a bulk „Kanäle zuordnen“ over a selection calls once per product. The codes are the channels app's; this app stores them as sent, trimmed, each once, and does not ask the channels app whether they exist.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string[]} channels - The channel codes to add, remove or set.\n * @param {ProductsChannelsAssignMode} mode - 'add' (the default) keeps what the product has and adds these, 'remove' takes these out, 'replace' sets exactly these.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsChannelsAssign(id: string, channels: string[], mode?: ProductsChannelsAssignMode): Promise<{}>;\n productsChannelsAssign(\n paramsOrFirst: { id: string, channels: string[], mode?: ProductsChannelsAssignMode } | string,\n ...rest: [(string[])?, (ProductsChannelsAssignMode)?] \n ): Promise<{}> {\n let params: { id: string, channels: string[], mode?: ProductsChannelsAssignMode };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, channels: string[], mode?: ProductsChannelsAssignMode };\n } else {\n params = {\n id: paramsOrFirst as string,\n channels: rest[0] as string[],\n mode: rest[1] as ProductsChannelsAssignMode \n };\n }\n \n const id = params.id;\n const channels = params.channels;\n const mode = params.mode;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof channels === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"channels\"');\n }\n\n const apiPath = '/v1/products/{id}/channels'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof mode !== 'undefined') {\n apiPayload['mode'] = mode;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * How much of what its family REQUIRES does this product actually carry — the number a merchandiser works down. products.completeness is jsonb that nothing had ever written. This computes it from family_attributes (is_required) against the product's own scoped attribute_values and stores the result. A product with no family answers 400 rather than an invented 0 % — it has nothing to be measured against.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} params.data - Request body\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsCompleteness(params: { id: string, data: object }): Promise<{}>;\n /**\n * How much of what its family REQUIRES does this product actually carry — the number a merchandiser works down. products.completeness is jsonb that nothing had ever written. This computes it from family_attributes (is_required) against the product's own scoped attribute_values and stores the result. A product with no family answers 400 rather than an invented 0 % — it has nothing to be measured against.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} data - Request body\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCompleteness(id: string, data: object): Promise<{}>;\n productsCompleteness(\n paramsOrFirst: { id: string, data: object } | string,\n ...rest: [(object)?] \n ): Promise<{}> {\n let params: { id: string, data: object };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, data: object };\n } else {\n params = {\n id: paramsOrFirst as string,\n data: rest[0] as object \n };\n }\n \n const id = params.id;\n const data = params.data;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof data === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"data\"');\n }\n\n const apiPath = '/v1/products/{id}/completeness'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof data !== 'undefined') {\n Object.assign(apiPayload, data);\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates a copy of one product under a new SKU and answers 201 with the new row. The copy keeps the name and starts as a switched-off draft without a GTIN, release or completeness. `parts` adds what else comes along: `attributes` (every value but files and images), `images` (the file and image values), `variants` (each variant copied under the new model, its SKU prefixed with the new one) and `categories` (the hand-picked memberships; rule memberships follow from the next recompute). Movement data is never copied, and prices live in the prices app. There is no transaction across rows: if a later step fails, what this call created is deleted again.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.sku - The copy's own SKU.\n * @param {ProductsCopyParts[]} params.parts - What comes along besides the product itself.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsCopy(params: { id: string, sku: string, parts?: ProductsCopyParts[] }): Promise<Models.Products>;\n /**\n * Creates a copy of one product under a new SKU and answers 201 with the new row. The copy keeps the name and starts as a switched-off draft without a GTIN, release or completeness. `parts` adds what else comes along: `attributes` (every value but files and images), `images` (the file and image values), `variants` (each variant copied under the new model, its SKU prefixed with the new one) and `categories` (the hand-picked memberships; rule memberships follow from the next recompute). Movement data is never copied, and prices live in the prices app. There is no transaction across rows: if a later step fails, what this call created is deleted again.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} sku - The copy's own SKU.\n * @param {ProductsCopyParts[]} parts - What comes along besides the product itself.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCopy(id: string, sku: string, parts?: ProductsCopyParts[]): Promise<Models.Products>;\n productsCopy(\n paramsOrFirst: { id: string, sku: string, parts?: ProductsCopyParts[] } | string,\n ...rest: [(string)?, (ProductsCopyParts[])?] \n ): Promise<Models.Products> {\n let params: { id: string, sku: string, parts?: ProductsCopyParts[] };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, sku: string, parts?: ProductsCopyParts[] };\n } else {\n params = {\n id: paramsOrFirst as string,\n sku: rest[0] as string,\n parts: rest[1] as ProductsCopyParts[] \n };\n }\n \n const id = params.id;\n const sku = params.sku;\n const parts = params.parts;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof sku === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"sku\"');\n }\n\n const apiPath = '/v1/products/{id}/copy'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof parts !== 'undefined') {\n apiPayload['parts'] = parts;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Names the family in the body — by `family_id` or by `family_code`, whichever the caller holds — and computes the product's completeness in the same call. The step every family-driven surface waits on: a product with no family has no required attributes, so its completeness cannot be computed and its family's label attribute never resolves. Assigning the family recomputes and STORES products.completeness immediately, so the metadata cannot go stale between the two operations.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.familyCode - Alternative to family_id — a `families.code` this tenant holds, from `GET /products/families`. No example: a code is tenant data, and any value published here names a family somebody does not have.\n * @param {string} params.familyId - The family to assign.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsFamilyAssign(params: { id: string, familyCode?: string, familyId?: string }): Promise<{}>;\n /**\n * Names the family in the body — by `family_id` or by `family_code`, whichever the caller holds — and computes the product's completeness in the same call. The step every family-driven surface waits on: a product with no family has no required attributes, so its completeness cannot be computed and its family's label attribute never resolves. Assigning the family recomputes and STORES products.completeness immediately, so the metadata cannot go stale between the two operations.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} familyCode - Alternative to family_id — a `families.code` this tenant holds, from `GET /products/families`. No example: a code is tenant data, and any value published here names a family somebody does not have.\n * @param {string} familyId - The family to assign.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsFamilyAssign(id: string, familyCode?: string, familyId?: string): Promise<{}>;\n productsFamilyAssign(\n paramsOrFirst: { id: string, familyCode?: string, familyId?: string } | string,\n ...rest: [(string)?, (string)?] \n ): Promise<{}> {\n let params: { id: string, familyCode?: string, familyId?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, familyCode?: string, familyId?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n familyCode: rest[0] as string,\n familyId: rest[1] as string \n };\n }\n \n const id = params.id;\n const familyCode = params.familyCode;\n const familyId = params.familyId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}/family'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof familyCode !== 'undefined') {\n apiPayload['family_code'] = familyCode;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Links one product to a manufacturer record and copies the manufacturer's GPSR state onto the product (`manufacturer_gpsr_complete`) — what a bulk „Hersteller setzen“ calls once per product. A plain `PUT /products/{id}` with `manufacturer_id` does the same; this route exists so a bulk action has a body of one field and an answer that names the manufacturer. It does not create a manufacturer.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.manufacturerId - The manufacturer to link.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsManufacturerAssign(params: { id: string, manufacturerId: string }): Promise<{}>;\n /**\n * Links one product to a manufacturer record and copies the manufacturer's GPSR state onto the product (`manufacturer_gpsr_complete`) — what a bulk „Hersteller setzen“ calls once per product. A plain `PUT /products/{id}` with `manufacturer_id` does the same; this route exists so a bulk action has a body of one field and an answer that names the manufacturer. It does not create a manufacturer.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} manufacturerId - The manufacturer to link.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsManufacturerAssign(id: string, manufacturerId: string): Promise<{}>;\n productsManufacturerAssign(\n paramsOrFirst: { id: string, manufacturerId: string } | string,\n ...rest: [(string)?] \n ): Promise<{}> {\n let params: { id: string, manufacturerId: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, manufacturerId: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n manufacturerId: rest[0] as string \n };\n }\n \n const id = params.id;\n const manufacturerId = params.manufacturerId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof manufacturerId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"manufacturerId\"');\n }\n\n const apiPath = '/v1/products/{id}/manufacturer'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Brings a product back from the trash, together with every record that went with it (its `trash_batch`). A record trashed on its own before stays in the trash. Everything that pointed at it acts again as it did, because nothing was changed when it went.\n * \n * Restoring needs the same right as deleting.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsRestore(params: { id: string }): Promise<{}>;\n /**\n * Brings a product back from the trash, together with every record that went with it (its `trash_batch`). A record trashed on its own before stays in the trash. Everything that pointed at it acts again as it did, because nothing was changed when it went.\n * \n * Restoring needs the same right as deleting.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsRestore(id: string): Promise<{}>;\n productsRestore(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}/restore'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/services/products.ts"],"names":[],"mappings":"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","sourcesContent":["import { RevenexxException, Client, type Payload, UploadProgress } from '../client';\nimport type { Models } from '../models';\n\nimport { ProductsListTrashed } from '../enums/products-list-trashed';\nimport { ProductsListKind } from '../enums/products-list-kind';\nimport { ProductsListStatus } from '../enums/products-list-status';\nimport { ProductsKind } from '../enums/products-kind';\nimport { ProductsStatus } from '../enums/products-status';\nimport { ProductsChannelsAssignMode } from '../enums/products-channels-assign-mode';\nimport { ProductsCopyParts } from '../enums/products-copy-parts';\n\nexport class Products {\n client: Client;\n\n constructor(client: Client) {\n this.client = client;\n }\n\n /**\n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * Every column of `products` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Records in the trash are left out: `trashed=only` reads the trash itself, `trashed=include` both. Any other value of `trashed` is refused with 400.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {ProductsListTrashed} params.trashed - Which records to read besides the live ones: `only` reads the trash, `include` reads both. Omitted, records in the trash are left out.\n * @param {string} params.id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} params.sku - Exact match on `sku`. The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {ProductsListKind} params.kind - Exact match on `kind`. Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.parentId - Exact match on `parent_id`. The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.familyId - Exact match on `family_id`. The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Exact match on `family_variant_id`. Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {string} params.manufacturerId - Exact match on `manufacturer_id`. The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {boolean} params.enabled - Exact match on `enabled`. Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {ProductsListStatus} params.status - Exact match on `status`. Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.statusChangedAt - Exact match on `status_changed_at`. When `status` last changed. Set by this app on every write through it that changes the status, and on a create; a write that names it has it dropped. Null on a product nobody has given a status through this app since the column exists.\n * @param {string} params.channelRelease - Exact match on `channel_release`. Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.channels - Exact match on `channels`. The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist. The whole list is compared, written as a list literal in braces (`{a,b}`) — equality in this order, not containment.\n * @param {string} params.taxClass - Exact match on `tax_class`. The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.baseValues - Exact match on `base_values`. The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.attributeValues - Exact match on `attribute_values`. Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.label - Exact match on `label`. The display name, maintained by the DATABASE so a grid of twenty thousand rows can sort and filter on a name with no join. It is the first of `attribute_values.common.name`, `…common.label`, the `de-DE`/`en-US`/`de`/`en` locale buckets (and the older `de_DE`/`en_US`, which no write can create any longer), `…common.manufacturer_aid`, and finally the SKU — so a value is ALWAYS present, and a label equal to the SKU means the catalog holds no name for this product. A generated column: it cannot be written, and a create or update that names it has it dropped rather than refused. The family-aware answer, which can consult `families.label_attribute` and report where the name came from, is `POST /products/labels`.\n * @param {string} params.quantifiedAssociations - Exact match on `quantified_associations`. The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.completeness - Exact match on `completeness`. How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {boolean} params.manufacturerGpsrComplete - Exact match on `manufacturer_gpsr_complete`. Whether the product's manufacturer carries complete GPSR data — the manufacturer's own `gpsr_complete`, copied onto the product so a list can filter the products whose offer lacks it. Null without a manufacturer. Kept by this app: on every write through it that names a manufacturer, and on all of a manufacturer's products when that manufacturer's GPSR state changes. Read-only: a write that names it has it dropped.\n * @param {string} params.externalId - Exact match on `external_id`. The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {string} params.externalRefs - Exact match on `external_refs`. Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.sourceSyncedAt - Exact match on `source_synced_at`. When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {string} params.sourceData - Exact match on `source_data`. What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} params.createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} params.updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @param {string} params.deletedAt - Exact match on `deleted_at`. When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Exact match on `deleted_by`. Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - Exact match on `deleted_by_id`. The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {string} params.trashBatch - Exact match on `trash_batch`. The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @param {string} params.purgeAt - Exact match on `purge_at`. When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Exact match on `purge_blocked_reason`. Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsList(params?: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string }): Promise<{}>;\n /**\n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * Every column of `products` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Records in the trash are left out: `trashed=only` reads the trash itself, `trashed=include` both. Any other value of `trashed` is refused with 400.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {ProductsListTrashed} trashed - Which records to read besides the live ones: `only` reads the trash, `include` reads both. Omitted, records in the trash are left out.\n * @param {string} id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} sku - Exact match on `sku`. The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {ProductsListKind} kind - Exact match on `kind`. Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} parentId - Exact match on `parent_id`. The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} familyId - Exact match on `family_id`. The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Exact match on `family_variant_id`. Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {string} manufacturerId - Exact match on `manufacturer_id`. The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {boolean} enabled - Exact match on `enabled`. Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {ProductsListStatus} status - Exact match on `status`. Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} statusChangedAt - Exact match on `status_changed_at`. When `status` last changed. Set by this app on every write through it that changes the status, and on a create; a write that names it has it dropped. Null on a product nobody has given a status through this app since the column exists.\n * @param {string} channelRelease - Exact match on `channel_release`. Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} channels - Exact match on `channels`. The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist. The whole list is compared, written as a list literal in braces (`{a,b}`) — equality in this order, not containment.\n * @param {string} taxClass - Exact match on `tax_class`. The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} baseValues - Exact match on `base_values`. The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} attributeValues - Exact match on `attribute_values`. Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} label - Exact match on `label`. The display name, maintained by the DATABASE so a grid of twenty thousand rows can sort and filter on a name with no join. It is the first of `attribute_values.common.name`, `…common.label`, the `de-DE`/`en-US`/`de`/`en` locale buckets (and the older `de_DE`/`en_US`, which no write can create any longer), `…common.manufacturer_aid`, and finally the SKU — so a value is ALWAYS present, and a label equal to the SKU means the catalog holds no name for this product. A generated column: it cannot be written, and a create or update that names it has it dropped rather than refused. The family-aware answer, which can consult `families.label_attribute` and report where the name came from, is `POST /products/labels`.\n * @param {string} quantifiedAssociations - Exact match on `quantified_associations`. The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} completeness - Exact match on `completeness`. How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {boolean} manufacturerGpsrComplete - Exact match on `manufacturer_gpsr_complete`. Whether the product's manufacturer carries complete GPSR data — the manufacturer's own `gpsr_complete`, copied onto the product so a list can filter the products whose offer lacks it. Null without a manufacturer. Kept by this app: on every write through it that names a manufacturer, and on all of a manufacturer's products when that manufacturer's GPSR state changes. Read-only: a write that names it has it dropped.\n * @param {string} externalId - Exact match on `external_id`. The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {string} externalRefs - Exact match on `external_refs`. Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} sourceSyncedAt - Exact match on `source_synced_at`. When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {string} sourceData - Exact match on `source_data`. What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it. The whole jsonb document is compared, serialized as JSON — this is equality, not a path or containment query, and `null` cannot be matched this way. A value that does not parse as JSON is refused with 400 `invalid_value`, naming this filter, before the request reaches the data plane.\n * @param {string} createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @param {string} deletedAt - Exact match on `deleted_at`. When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Exact match on `deleted_by`. Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - Exact match on `deleted_by_id`. The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {string} trashBatch - Exact match on `trash_batch`. The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @param {string} purgeAt - Exact match on `purge_at`. When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Exact match on `purge_blocked_reason`. Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsList(limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string): Promise<{}>;\n productsList(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string } | number,\n ...rest: [(number)?, (string)?, (ProductsListTrashed)?, (string)?, (string)?, (ProductsListKind)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (ProductsListStatus)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, trashed?: ProductsListTrashed, id?: string, sku?: string, kind?: ProductsListKind, parentId?: string, familyId?: string, familyVariantId?: string, manufacturerId?: string, enabled?: boolean, status?: ProductsListStatus, statusChangedAt?: string, channelRelease?: string, channels?: string, taxClass?: string, baseValues?: string, attributeValues?: string, label?: string, quantifiedAssociations?: string, completeness?: string, manufacturerGpsrComplete?: boolean, externalId?: string, externalRefs?: string, sourceSyncedAt?: string, sourceData?: string, createdAt?: string, updatedAt?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, trashBatch?: string, purgeAt?: string, purgeBlockedReason?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n trashed: rest[2] as ProductsListTrashed,\n id: rest[3] as string,\n sku: rest[4] as string,\n kind: rest[5] as ProductsListKind,\n parentId: rest[6] as string,\n familyId: rest[7] as string,\n familyVariantId: rest[8] as string,\n manufacturerId: rest[9] as string,\n enabled: rest[10] as boolean,\n status: rest[11] as ProductsListStatus,\n statusChangedAt: rest[12] as string,\n channelRelease: rest[13] as string,\n channels: rest[14] as string,\n taxClass: rest[15] as string,\n baseValues: rest[16] as string,\n attributeValues: rest[17] as string,\n label: rest[18] as string,\n quantifiedAssociations: rest[19] as string,\n completeness: rest[20] as string,\n manufacturerGpsrComplete: rest[21] as boolean,\n externalId: rest[22] as string,\n externalRefs: rest[23] as string,\n sourceSyncedAt: rest[24] as string,\n sourceData: rest[25] as string,\n createdAt: rest[26] as string,\n updatedAt: rest[27] as string,\n deletedAt: rest[28] as string,\n deletedBy: rest[29] as string,\n deletedById: rest[30] as string,\n trashBatch: rest[31] as string,\n purgeAt: rest[32] as string,\n purgeBlockedReason: rest[33] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const trashed = params.trashed;\n const id = params.id;\n const sku = params.sku;\n const kind = params.kind;\n const parentId = params.parentId;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const manufacturerId = params.manufacturerId;\n const enabled = params.enabled;\n const status = params.status;\n const statusChangedAt = params.statusChangedAt;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const taxClass = params.taxClass;\n const baseValues = params.baseValues;\n const attributeValues = params.attributeValues;\n const label = params.label;\n const quantifiedAssociations = params.quantifiedAssociations;\n const completeness = params.completeness;\n const manufacturerGpsrComplete = params.manufacturerGpsrComplete;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const sourceSyncedAt = params.sourceSyncedAt;\n const sourceData = params.sourceData;\n const createdAt = params.createdAt;\n const updatedAt = params.updatedAt;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const trashBatch = params.trashBatch;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n\n\n const apiPath = '/v1/products';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof trashed !== 'undefined') {\n apiPayload['trashed'] = trashed;\n }\n if (typeof id !== 'undefined') {\n apiPayload['id'] = id;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof statusChangedAt !== 'undefined') {\n apiPayload['status_changed_at'] = statusChangedAt;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof label !== 'undefined') {\n apiPayload['label'] = label;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof manufacturerGpsrComplete !== 'undefined') {\n apiPayload['manufacturer_gpsr_complete'] = manufacturerGpsrComplete;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof createdAt !== 'undefined') {\n apiPayload['created_at'] = createdAt;\n }\n if (typeof updatedAt !== 'undefined') {\n apiPayload['updated_at'] = updatedAt;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates one product and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * `sku` is the only column the database refuses the row without; everything else has a default or is nullable. A second row with the same `sku` answers 409. This app owns the create: `enabled` defaults from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import cannot publish twenty thousand unfinished products the moment it lands, and a product that names no family gets the `default_product_family` one. An explicit value in the body always wins over both. It also takes three keys that are no column — `name`, `category_id` and `copy_from` — for the two-step create, and stamps `status_changed_at`.\n *\n * @param {string} params.sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} params.attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} params.baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {string} params.categoryId - A category to file the new product into by hand, in the same call. It has to exist, be live and be active, or the product is not created.\n * @param {object} params.channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} params.channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} params.completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} params.copyFrom - A product to start from: its family, family variant, manufacturer, tax class, channels, base values (without the GTIN) and attribute values are taken, and anything the body sends wins. The copy is a new draft; nothing else is linked.\n * @param {string} params.deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} params.enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} params.externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} params.externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} params.familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} params.kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} params.name - The product's name, stored as the `name` value under the first of the `label_locales` (German when none is set). A shortcut for writing that one value of `attribute_values`.\n * @param {string} params.parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} params.quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {object} params.sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} params.sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} params.status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsCreate(params: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string }): Promise<Models.Products>;\n /**\n * Creates one product and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * `sku` is the only column the database refuses the row without; everything else has a default or is nullable. A second row with the same `sku` answers 409. This app owns the create: `enabled` defaults from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import cannot publish twenty thousand unfinished products the moment it lands, and a product that names no family gets the `default_product_family` one. An explicit value in the body always wins over both. It also takes three keys that are no column — `name`, `category_id` and `copy_from` — for the two-step create, and stamps `status_changed_at`.\n *\n * @param {string} sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {string} categoryId - A category to file the new product into by hand, in the same call. It has to exist, be live and be active, or the product is not created.\n * @param {object} channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} copyFrom - A product to start from: its family, family variant, manufacturer, tax class, channels, base values (without the GTIN) and attribute values are taken, and anything the body sends wins. The copy is a new draft; nothing else is linked.\n * @param {string} deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} name - The product's name, stored as the `name` value under the first of the `label_locales` (German when none is set). A shortcut for writing that one value of `attribute_values`.\n * @param {string} parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {object} sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCreate(sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string): Promise<Models.Products>;\n productsCreate(\n paramsOrFirst: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string } | string,\n ...rest: [(object)?, (object)?, (string)?, (object)?, (string[])?, (object)?, (string)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (object)?, (string)?, (string)?, (ProductsKind)?, (string)?, (string)?, (string)?, (string)?, (string)?, (object)?, (object)?, (string)?, (ProductsStatus)?, (string)?, (string)?] \n ): Promise<Models.Products> {\n let params: { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { sku: string, attributeValues?: object, baseValues?: object, categoryId?: string, channelRelease?: object, channels?: string[], completeness?: object, copyFrom?: string, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, name?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n } else {\n params = {\n sku: paramsOrFirst as string,\n attributeValues: rest[0] as object,\n baseValues: rest[1] as object,\n categoryId: rest[2] as string,\n channelRelease: rest[3] as object,\n channels: rest[4] as string[],\n completeness: rest[5] as object,\n copyFrom: rest[6] as string,\n deletedAt: rest[7] as string,\n deletedBy: rest[8] as string,\n deletedById: rest[9] as string,\n enabled: rest[10] as boolean,\n externalId: rest[11] as string,\n externalRefs: rest[12] as object,\n familyId: rest[13] as string,\n familyVariantId: rest[14] as string,\n kind: rest[15] as ProductsKind,\n manufacturerId: rest[16] as string,\n name: rest[17] as string,\n parentId: rest[18] as string,\n purgeAt: rest[19] as string,\n purgeBlockedReason: rest[20] as string,\n quantifiedAssociations: rest[21] as object,\n sourceData: rest[22] as object,\n sourceSyncedAt: rest[23] as string,\n status: rest[24] as ProductsStatus,\n taxClass: rest[25] as string,\n trashBatch: rest[26] as string \n };\n }\n \n const sku = params.sku;\n const attributeValues = params.attributeValues;\n const baseValues = params.baseValues;\n const categoryId = params.categoryId;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const completeness = params.completeness;\n const copyFrom = params.copyFrom;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const enabled = params.enabled;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const kind = params.kind;\n const manufacturerId = params.manufacturerId;\n const name = params.name;\n const parentId = params.parentId;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n const quantifiedAssociations = params.quantifiedAssociations;\n const sourceData = params.sourceData;\n const sourceSyncedAt = params.sourceSyncedAt;\n const status = params.status;\n const taxClass = params.taxClass;\n const trashBatch = params.trashBatch;\n\n if (typeof sku === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"sku\"');\n }\n\n const apiPath = '/v1/products';\n const apiPayload: Payload = {};\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof categoryId !== 'undefined') {\n apiPayload['category_id'] = categoryId;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof copyFrom !== 'undefined') {\n apiPayload['copy_from'] = copyFrom;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof name !== 'undefined') {\n apiPayload['name'] = name;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Answers four fields — id, sku, tax_class and the resolved display name — for a list of ids and/or SKUs in ONE call. It exists for the app on the other side of a product reference: the prices app holds SKUs and needs a tax class, a feed builder holds ids and needs names, and neither should page through the catalog or fire a request per line. Ask by either identifier or both; the two are unioned and a product named twice comes back once. At most 500 of each per call; more is refused with 400 rather than cut short.\n * \n * With `full: true` each item is the whole row instead — what `GET /products/{id}` answers, every column and the complete `attribute_values` document, with `label` still the resolved name. That mode is for a caller that has to hand a product on and cannot know in advance which fields it will need: punchout's hand-back writes whatever columns and attribute codes a merchant's field mappings name, for every line of a cart, and needs them in one read. Leave it off when four fields will do; the short shape is unchanged by it.\n * \n * Either way it answers what it FOUND: an id or SKU that names nothing is simply absent from `items` rather than an error, so compare the length of what you sent with what came back if a miss matters. For a scannable list use `GET /products/grid`.\n *\n * @param {boolean} params.full - True answers each product as its whole row (`ProductBatchRow`) instead of the four-field reference (`ProductTaxRef`). For a caller that must hand a whole product on — every column and attribute value — without a read per line.\n * @param {string[]} params.ids - Product ids, when the caller already holds them. At most 500.\n * @param {string[]} params.skus - Product SKUs — the identifier a foreign system carries, which is why this route exists at all. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsBatch(params?: { full?: boolean, ids?: string[], skus?: string[] }): Promise<{}>;\n /**\n * Answers four fields — id, sku, tax_class and the resolved display name — for a list of ids and/or SKUs in ONE call. It exists for the app on the other side of a product reference: the prices app holds SKUs and needs a tax class, a feed builder holds ids and needs names, and neither should page through the catalog or fire a request per line. Ask by either identifier or both; the two are unioned and a product named twice comes back once. At most 500 of each per call; more is refused with 400 rather than cut short.\n * \n * With `full: true` each item is the whole row instead — what `GET /products/{id}` answers, every column and the complete `attribute_values` document, with `label` still the resolved name. That mode is for a caller that has to hand a product on and cannot know in advance which fields it will need: punchout's hand-back writes whatever columns and attribute codes a merchant's field mappings name, for every line of a cart, and needs them in one read. Leave it off when four fields will do; the short shape is unchanged by it.\n * \n * Either way it answers what it FOUND: an id or SKU that names nothing is simply absent from `items` rather than an error, so compare the length of what you sent with what came back if a miss matters. For a scannable list use `GET /products/grid`.\n *\n * @param {boolean} full - True answers each product as its whole row (`ProductBatchRow`) instead of the four-field reference (`ProductTaxRef`). For a caller that must hand a whole product on — every column and attribute value — without a read per line.\n * @param {string[]} ids - Product ids, when the caller already holds them. At most 500.\n * @param {string[]} skus - Product SKUs — the identifier a foreign system carries, which is why this route exists at all. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsBatch(full?: boolean, ids?: string[], skus?: string[]): Promise<{}>;\n productsBatch(\n paramsOrFirst?: { full?: boolean, ids?: string[], skus?: string[] } | boolean,\n ...rest: [(string[])?, (string[])?] \n ): Promise<{}> {\n let params: { full?: boolean, ids?: string[], skus?: string[] };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { full?: boolean, ids?: string[], skus?: string[] };\n } else {\n params = {\n full: paramsOrFirst as boolean,\n ids: rest[0] as string[],\n skus: rest[1] as string[] \n };\n }\n \n const full = params.full;\n const ids = params.ids;\n const skus = params.skus;\n\n\n const apiPath = '/v1/products/batch';\n const apiPayload: Payload = {};\n if (typeof full !== 'undefined') {\n apiPayload['full'] = full;\n }\n if (typeof ids !== 'undefined') {\n apiPayload['ids'] = ids;\n }\n if (typeof skus !== 'undefined') {\n apiPayload['skus'] = skus;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * The list a merchant can actually scan, as opposed to `GET /products`, which answers SKUs and a jsonb blob. Every row arrives already flattened: its resolved display name and where that name came from, its family code, its stored completeness, and the value of every attribute the catalog marks `usable_in_grid` — no join, no second call. `q` is a case-insensitive substring of the stored `label` column, which falls back to the SKU, so one box finds a product by either. Soft-deleted products are excluded here, unlike `GET /products`.\n * \n * It filters on `q`, `kind`, `enabled` and `family_id`, and on NOTHING ELSE — a query parameter it does not accept is refused with 400 rather than dropped. That matters because of `filters`: the array reports the attributes marked `is_filterable`, which is what a filter bar should OFFER, and it is not a query surface. Filtering on an attribute value is not offered by this API at all — the values live inside a four-bucket jsonb document and are read through a fallback chain, so it is a feature with a design of its own rather than a parameter that was forgotten.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} params.q - Case-insensitive substring of the display name (or the SKU, when there is no name).\n * @param {ProductsListKind} params.kind - Restrict to one product kind.\n * @param {boolean} params.enabled - Restrict to enabled or disabled products.\n * @param {string} params.familyId - Restrict to one family. A family id that does not exist is not an error here — it simply matches nothing.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsGrid(params?: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string }): Promise<{}>;\n /**\n * The list a merchant can actually scan, as opposed to `GET /products`, which answers SKUs and a jsonb blob. Every row arrives already flattened: its resolved display name and where that name came from, its family code, its stored completeness, and the value of every attribute the catalog marks `usable_in_grid` — no join, no second call. `q` is a case-insensitive substring of the stored `label` column, which falls back to the SKU, so one box finds a product by either. Soft-deleted products are excluded here, unlike `GET /products`.\n * \n * It filters on `q`, `kind`, `enabled` and `family_id`, and on NOTHING ELSE — a query parameter it does not accept is refused with 400 rather than dropped. That matters because of `filters`: the array reports the attributes marked `is_filterable`, which is what a filter bar should OFFER, and it is not a query surface. Filtering on an attribute value is not offered by this API at all — the values live inside a four-bucket jsonb document and are read through a fallback chain, so it is a feature with a design of its own rather than a parameter that was forgotten.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} q - Case-insensitive substring of the display name (or the SKU, when there is no name).\n * @param {ProductsListKind} kind - Restrict to one product kind.\n * @param {boolean} enabled - Restrict to enabled or disabled products.\n * @param {string} familyId - Restrict to one family. A family id that does not exist is not an error here — it simply matches nothing.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsGrid(limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string): Promise<{}>;\n productsGrid(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string } | number,\n ...rest: [(number)?, (string)?, (string)?, (ProductsListKind)?, (boolean)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, q?: string, kind?: ProductsListKind, enabled?: boolean, familyId?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n q: rest[2] as string,\n kind: rest[3] as ProductsListKind,\n enabled: rest[4] as boolean,\n familyId: rest[5] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const q = params.q;\n const kind = params.kind;\n const enabled = params.enabled;\n const familyId = params.familyId;\n\n\n const apiPath = '/v1/products/grid';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof q !== 'undefined') {\n apiPayload['q'] = q;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * What is this product CALLED? A product's name is an attribute rather than a column, and which attribute it is, is per family — so no plain read can answer it. This resolves up to 500 products at once, by id and/or SKU: it reads families.label_attribute (falling back to the default_label_attribute setting, then to the conventional `name`) and looks the value up through the scoped attribute_values document — common, then locale_specific in the label_locales order, then the channel buckets.\n * \n * It reports WHERE the name was found, which is the half that matters: `source: \"sku\"` means the catalog holds no name for this product and the SKU is standing in for one, so show it as a missing name rather than as a name. Writes nothing, and answers only what it found.\n *\n * @param {string[]} params.ids - Product ids to name. At most 500.\n * @param {string[]} params.skus - Product SKUs to name. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsLabels(params?: { ids?: string[], skus?: string[] }): Promise<{}>;\n /**\n * What is this product CALLED? A product's name is an attribute rather than a column, and which attribute it is, is per family — so no plain read can answer it. This resolves up to 500 products at once, by id and/or SKU: it reads families.label_attribute (falling back to the default_label_attribute setting, then to the conventional `name`) and looks the value up through the scoped attribute_values document — common, then locale_specific in the label_locales order, then the channel buckets.\n * \n * It reports WHERE the name was found, which is the half that matters: `source: \"sku\"` means the catalog holds no name for this product and the SKU is standing in for one, so show it as a missing name rather than as a name. Writes nothing, and answers only what it found.\n *\n * @param {string[]} ids - Product ids to name. At most 500.\n * @param {string[]} skus - Product SKUs to name. At most 500.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsLabels(ids?: string[], skus?: string[]): Promise<{}>;\n productsLabels(\n paramsOrFirst?: { ids?: string[], skus?: string[] } | string[],\n ...rest: [(string[])?] \n ): Promise<{}> {\n let params: { ids?: string[], skus?: string[] };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { ids?: string[], skus?: string[] };\n } else {\n params = {\n ids: paramsOrFirst as string[],\n skus: rest[0] as string[] \n };\n }\n \n const ids = params.ids;\n const skus = params.skus;\n\n\n const apiPath = '/v1/products/labels';\n const apiPayload: Payload = {};\n if (typeof ids !== 'undefined') {\n apiPayload['ids'] = ids;\n }\n if (typeof skus !== 'undefined') {\n apiPayload['skus'] = skus;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * Every column of `product_associations` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {number} params.limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} params.offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} params.order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} params.id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} params.productId - Exact match on `product_id`. The product the relation starts at — the one whose detail page shows it.\n * @param {string} params.associationTypeId - Exact match on `association_type_id`. Which kind of relation this is — the `association_types` row.\n * @param {string} params.targetProductId - Exact match on `target_product_id`. The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} params.quantity - Exact match on `quantity`. How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {number} params.position - Exact match on `position`. Order in which the targets are shown, ascending.\n * @param {string} params.createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} params.updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsProductAssociationsList(params?: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string }): Promise<{}>;\n /**\n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * Every column of `product_associations` is an exact-match query parameter, `order` sorts by one column, and `limit`/`offset` page through `page.total`. A query key that is NOT a column is dropped rather than refused, and the `filter` object echoes the ones that were understood — that echo is the only way to tell an unfiltered answer from an empty one. It reads rows exactly as they are stored: no join is resolved, no jsonb value is unpacked.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {number} limit - Page size (default 50, max 200). A larger value is clamped rather than refused.\n * @param {number} offset - Row offset for pagination (default 0). Page with `page.total` and `page.hasMore`.\n * @param {string} order - Sort by one column: 'column' | 'column.asc' | 'column.desc'. A bare column sorts ascending. Anything else is refused with 400.\n * @param {string} id - Exact match on `id`. The row's own id, generated by the database. A client never sends one; it reads one back and uses it in the path of every later call.\n * @param {string} productId - Exact match on `product_id`. The product the relation starts at — the one whose detail page shows it.\n * @param {string} associationTypeId - Exact match on `association_type_id`. Which kind of relation this is — the `association_types` row.\n * @param {string} targetProductId - Exact match on `target_product_id`. The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} quantity - Exact match on `quantity`. How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {number} position - Exact match on `position`. Order in which the targets are shown, ascending.\n * @param {string} createdAt - Exact match on `created_at`. When the row was created. Server-set — it is not part of any request body.\n * @param {string} updatedAt - Exact match on `updated_at`. When the row was last written. Server-set — it is not part of any request body.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsList(limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string): Promise<{}>;\n productsProductAssociationsList(\n paramsOrFirst?: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string } | number,\n ...rest: [(number)?, (string)?, (string)?, (string)?, (string)?, (string)?, (number)?, (number)?, (string)?, (string)?] \n ): Promise<{}> {\n let params: { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string };\n \n if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { limit?: number, offset?: number, order?: string, id?: string, productId?: string, associationTypeId?: string, targetProductId?: string, quantity?: number, position?: number, createdAt?: string, updatedAt?: string };\n } else {\n params = {\n limit: paramsOrFirst as number,\n offset: rest[0] as number,\n order: rest[1] as string,\n id: rest[2] as string,\n productId: rest[3] as string,\n associationTypeId: rest[4] as string,\n targetProductId: rest[5] as string,\n quantity: rest[6] as number,\n position: rest[7] as number,\n createdAt: rest[8] as string,\n updatedAt: rest[9] as string \n };\n }\n \n const limit = params.limit;\n const offset = params.offset;\n const order = params.order;\n const id = params.id;\n const productId = params.productId;\n const associationTypeId = params.associationTypeId;\n const targetProductId = params.targetProductId;\n const quantity = params.quantity;\n const position = params.position;\n const createdAt = params.createdAt;\n const updatedAt = params.updatedAt;\n\n\n const apiPath = '/v1/products/product_associations';\n const apiPayload: Payload = {};\n if (typeof limit !== 'undefined') {\n apiPayload['limit'] = limit;\n }\n if (typeof offset !== 'undefined') {\n apiPayload['offset'] = offset;\n }\n if (typeof order !== 'undefined') {\n apiPayload['order'] = order;\n }\n if (typeof id !== 'undefined') {\n apiPayload['id'] = id;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof createdAt !== 'undefined') {\n apiPayload['created_at'] = createdAt;\n }\n if (typeof updatedAt !== 'undefined') {\n apiPayload['updated_at'] = updatedAt;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates one product association and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * `product_id`, `association_type_id`, `target_product_id` are the only columns the database refuses the row without; everything else has a default or is nullable. A second row with the same `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} params.associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {string} params.productId - The product the relation starts at — the one whose detail page shows it.\n * @param {string} params.targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} params.position - Order in which the targets are shown, ascending.\n * @param {number} params.quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsCreate(params: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number }): Promise<Models.ProductAssociations>;\n /**\n * Creates one product association and answers 201 with the stored row, including the id and the timestamps the database filled in — a client never sends an id, it reads one back and uses it in the path of every later call.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * `product_id`, `association_type_id`, `target_product_id` are the only columns the database refuses the row without; everything else has a default or is nullable. A second row with the same `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {string} productId - The product the relation starts at — the one whose detail page shows it.\n * @param {string} targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @param {number} position - Order in which the targets are shown, ascending.\n * @param {number} quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsCreate(associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number): Promise<Models.ProductAssociations>;\n productsProductAssociationsCreate(\n paramsOrFirst: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number } | string,\n ...rest: [(string)?, (string)?, (number)?, (number)?] \n ): Promise<Models.ProductAssociations> {\n let params: { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { associationTypeId: string, productId: string, targetProductId: string, position?: number, quantity?: number };\n } else {\n params = {\n associationTypeId: paramsOrFirst as string,\n productId: rest[0] as string,\n targetProductId: rest[1] as string,\n position: rest[2] as number,\n quantity: rest[3] as number \n };\n }\n \n const associationTypeId = params.associationTypeId;\n const productId = params.productId;\n const targetProductId = params.targetProductId;\n const position = params.position;\n const quantity = params.quantity;\n\n if (typeof associationTypeId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"associationTypeId\"');\n }\n if (typeof productId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"productId\"');\n }\n if (typeof targetProductId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"targetProductId\"');\n }\n\n const apiPath = '/v1/products/product_associations';\n const apiPayload: Payload = {};\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Deletes one product association by id. It is a hard delete — the row is gone, and the answer is a confirmation rather than a result to branch on.\n * \n * Nothing in this schema references it, so nothing else changes.\n * \n * An id no product association of this tenant carries answers 404; there is no 409, because every foreign key pointing at this entity resolves itself on delete rather than blocking one.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsProductAssociationsDelete(params: { id: string }): Promise<{}>;\n /**\n * Deletes one product association by id. It is a hard delete — the row is gone, and the answer is a confirmation rather than a result to branch on.\n * \n * Nothing in this schema references it, so nothing else changes.\n * \n * An id no product association of this tenant carries answers 404; there is no 409, because every foreign key pointing at this entity resolves itself on delete rather than blocking one.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsDelete(id: string): Promise<{}>;\n productsProductAssociationsDelete(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'delete',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Reads one product association by its id — the whole row, every column, as it is stored.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * An id no product association of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsGet(params: { id: string }): Promise<Models.ProductAssociations>;\n /**\n * Reads one product association by its id — the whole row, every column, as it is stored.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * An id no product association of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached.\n * \n * Answered from the gateway's tenant cache for up to 30 minutes and dropped the moment this entity is written, because the data model changes weekly at most and every product page asks the same question.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsGet(id: string): Promise<Models.ProductAssociations>;\n productsProductAssociationsGet(\n paramsOrFirst: { id: string } | string \n ): Promise<Models.ProductAssociations> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Updates one product association by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} params.id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {number} params.position - Order in which the targets are shown, ascending.\n * @param {string} params.productId - The product the relation starts at — the one whose detail page shows it.\n * @param {number} params.quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {string} params.targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n */\n productsProductAssociationsUpdate(params: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string }): Promise<Models.ProductAssociations>;\n /**\n * Updates one product association by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * One relation from one product to another, of a declared type: this drill's accessories, this bundle's parts, this article's cross-sells. `quantity` is the number in \"this bundle contains 4 casters\" and is meaningful only when the association type carries `is_quantified`. This relational surface is the one this app serves; the `products.quantified_associations` column is an importer's blob that no route here reads or writes.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `product_id`, `association_type_id`, `target_product_id` answers 409.\n *\n * @param {string} id - The `product_associations` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products/product_associations`. An id no product association of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} associationTypeId - Which kind of relation this is — the `association_types` row.\n * @param {number} position - Order in which the targets are shown, ascending.\n * @param {string} productId - The product the relation starts at — the one whose detail page shows it.\n * @param {number} quantity - How many of the target belong to the source — the 4 in \"this bundle contains 4 casters\". Only meaningful when the association type carries `is_quantified`; null on an ordinary cross-sell.\n * @param {string} targetProductId - The product the relation points at — the accessory, the spare part, the cross-sell.\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductAssociations>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsProductAssociationsUpdate(id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string): Promise<Models.ProductAssociations>;\n productsProductAssociationsUpdate(\n paramsOrFirst: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string } | string,\n ...rest: [(string)?, (number)?, (string)?, (number)?, (string)?] \n ): Promise<Models.ProductAssociations> {\n let params: { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, associationTypeId?: string, position?: number, productId?: string, quantity?: number, targetProductId?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n associationTypeId: rest[0] as string,\n position: rest[1] as number,\n productId: rest[2] as string,\n quantity: rest[3] as number,\n targetProductId: rest[4] as string \n };\n }\n \n const id = params.id;\n const associationTypeId = params.associationTypeId;\n const position = params.position;\n const productId = params.productId;\n const quantity = params.quantity;\n const targetProductId = params.targetProductId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/product_associations/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof associationTypeId !== 'undefined') {\n apiPayload['association_type_id'] = associationTypeId;\n }\n if (typeof position !== 'undefined') {\n apiPayload['position'] = position;\n }\n if (typeof productId !== 'undefined') {\n apiPayload['product_id'] = productId;\n }\n if (typeof quantity !== 'undefined') {\n apiPayload['quantity'] = quantity;\n }\n if (typeof targetProductId !== 'undefined') {\n apiPayload['target_product_id'] = targetProductId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'put',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * What this app needs once before it is ready, as the checklist the Cockpit's „Einrichtung“ shows: every step of `cockpit.json#setup`, done or not. A step is done when what it names exists — at least one live row of a table, or a setting with a value.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsSetup(): Promise<{}> {\n\n const apiPath = '/v1/products/setup';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * markets.tax_classes is the source of record for the rate and a product points at it by CODE in `tax_class` — a variant is a product row and carries its own. It is not a foreign key and could not be (ADR-0055), so integrity is a question one app asks the other, and this is the answering half: markets calls it when an operator tries to delete a tax class, and a count above zero is what stops the delete. A tax class is unique per market, so 'reduced' may exist in several: the caller names the class's market in `x-revenexx-market`, and only the products visible in that market count — the ones assigned to it plus the unassigned ones, which are sold in every market and so are taxed through its class too. Without the header every product of the tenant counts. Products in the trash count as well, because a restore brings them back naming the class; `trashed` says how many of them are. Reports at most 1000 products and names the first 20 by SKU. Every code answers, used or not — a code nobody points at is `in_use: false`, never a 404.\n *\n * @param {string} params.code - The tax-class CODE, as markets spells it — not a row id. Matched against the `tax_class` of every product visible in the request's market (every product when it names none).\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductTaxClassUsage>}\n */\n productsTaxClassesUsage(params: { code: string }): Promise<Models.ProductTaxClassUsage>;\n /**\n * markets.tax_classes is the source of record for the rate and a product points at it by CODE in `tax_class` — a variant is a product row and carries its own. It is not a foreign key and could not be (ADR-0055), so integrity is a question one app asks the other, and this is the answering half: markets calls it when an operator tries to delete a tax class, and a count above zero is what stops the delete. A tax class is unique per market, so 'reduced' may exist in several: the caller names the class's market in `x-revenexx-market`, and only the products visible in that market count — the ones assigned to it plus the unassigned ones, which are sold in every market and so are taxed through its class too. Without the header every product of the tenant counts. Products in the trash count as well, because a restore brings them back naming the class; `trashed` says how many of them are. Reports at most 1000 products and names the first 20 by SKU. Every code answers, used or not — a code nobody points at is `in_use: false`, never a 404.\n *\n * @param {string} code - The tax-class CODE, as markets spells it — not a row id. Matched against the `tax_class` of every product visible in the request's market (every product when it names none).\n * @throws {RevenexxException}\n * @returns {Promise<Models.ProductTaxClassUsage>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsTaxClassesUsage(code: string): Promise<Models.ProductTaxClassUsage>;\n productsTaxClassesUsage(\n paramsOrFirst: { code: string } | string \n ): Promise<Models.ProductTaxClassUsage> {\n let params: { code: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { code: string };\n } else {\n params = {\n code: paramsOrFirst as string \n };\n }\n \n const code = params.code;\n\n if (typeof code === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"code\"');\n }\n\n const apiPath = '/v1/products/tax-classes/{code}/usage'.replace('{code}', code);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Deletes the products in the trash for good, oldest first, up to 200 per call, each through the same checks as a second `DELETE`. One that is held is reported with its reason and the rest go on; `more` says another call has work left. Refused with 403 for a storefront contact.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsEmptyTrash(): Promise<{}> {\n\n const apiPath = '/v1/products/trash/empty';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * The index of the enums this app ENFORCES — `product-kinds`, `product-status`, `membership-sources`, `rule-matches`, `asset-sources`, `asset-licenses` — served by the app that owns the CHECK constraint each one is parsed out of, so a UI never has to keep its own copy of a status map and watch it drift. Names and titles only: fetch one by name for its values, badge tones and descriptions.\n * \n * The set is a fixed property of this app rather than tenant data, so it is the same list for every tenant. `attributes.type` is deliberately absent: it carries no CHECK, because the whole point of an attribute-driven PIM is that the type list is data an integrator extends.\n *\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsVocabulariesList(): Promise<{}> {\n\n const apiPath = '/v1/products/vocabularies';\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * One vocabulary with every value it admits, each with a title, a description and the badge tone a UI should paint it in. The value set is parsed out of the CHECK constraint in schema.json, so what is served IS what is enforced. Labels are curated on top and can only add words and colour — a permitted value nobody labelled still appears, titled from its own key.\n *\n * @param {string} params.name - The vocabulary name, as listed by GET /products/vocabularies.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsVocabulariesGet(params: { name: string }): Promise<{}>;\n /**\n * One vocabulary with every value it admits, each with a title, a description and the badge tone a UI should paint it in. The value set is parsed out of the CHECK constraint in schema.json, so what is served IS what is enforced. Labels are curated on top and can only add words and colour — a permitted value nobody labelled still appears, titled from its own key.\n *\n * @param {string} name - The vocabulary name, as listed by GET /products/vocabularies.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsVocabulariesGet(name: string): Promise<{}>;\n productsVocabulariesGet(\n paramsOrFirst: { name: string } | string \n ): Promise<{}> {\n let params: { name: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { name: string };\n } else {\n params = {\n name: paramsOrFirst as string \n };\n }\n \n const name = params.name;\n\n if (typeof name === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"name\"');\n }\n\n const apiPath = '/v1/products/vocabularies/{name}'.replace('{name}', name);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Moves one product to the trash — or, called on one that is already there, deletes it for good.\n * \n * The first call stamps the product, and the variants of a model go with it; all of them share one `trash_batch` and come back together on `POST …/{id}/restore`. Nothing that points at it changes: its assignments stay and stop acting, and the answer lists them as warnings with a count. Its sku stays reserved while it is in the trash.\n * \n * The second call deletes it for good, with the rows of its group, and only then do the cascades and `*.deleted` happen. It is refused with 409 while a blocker holds: stock on hand or an active reservation in inventories, a position of an open quote in quotes, or a line of an open purchase request in procurement holds it; price entries are a warning, because prices removes them when the product is deleted. A blocker that could not be checked counts as one. It is refused with 403 for a storefront contact — deleting for good is back-office work.\n * \n * An id no product of this tenant carries answers 404.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsDelete(params: { id: string }): Promise<{}>;\n /**\n * Moves one product to the trash — or, called on one that is already there, deletes it for good.\n * \n * The first call stamps the product, and the variants of a model go with it; all of them share one `trash_batch` and come back together on `POST …/{id}/restore`. Nothing that points at it changes: its assignments stay and stop acting, and the answer lists them as warnings with a count. Its sku stays reserved while it is in the trash.\n * \n * The second call deletes it for good, with the rows of its group, and only then do the cascades and `*.deleted` happen. It is refused with 409 while a blocker holds: stock on hand or an active reservation in inventories, a position of an open quote in quotes, or a line of an open purchase request in procurement holds it; price entries are a warning, because prices removes them when the product is deleted. A blocker that could not be checked counts as one. It is refused with 403 for a storefront contact — deleting for good is back-office work.\n * \n * An id no product of this tenant carries answers 404.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsDelete(id: string): Promise<{}>;\n productsDelete(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'delete',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Reads one product by its id — the whole row, every column, as it is stored.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * An id no product of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached. Nothing is resolved for you here — for the display name, the family code and the grid attributes already unpacked, use `GET /products/grid` or `POST /products/labels`.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsGet(params: { id: string }): Promise<Models.Products>;\n /**\n * Reads one product by its id — the whole row, every column, as it is stored.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * An id no product of this tenant carries answers 404, and so does one belonging to another tenant: row-level security makes that row invisible rather than forbidden. A malformed id answers 400 before the route is reached. Nothing is resolved for you here — for the display name, the family code and the grid attributes already unpacked, use `GET /products/grid` or `POST /products/labels`.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsGet(id: string): Promise<Models.Products>;\n productsGet(\n paramsOrFirst: { id: string } | string \n ): Promise<Models.Products> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'get',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Updates one product by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `sku` answers 409. `label` is a generated column: naming it is dropped rather than refused, and `completeness` is written by the two metadata routes, not here.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} params.attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} params.baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {object} params.channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} params.channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} params.completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} params.deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} params.deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} params.deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} params.enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} params.externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} params.externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} params.familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} params.familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} params.kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} params.manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} params.parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} params.purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} params.purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} params.quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {string} params.sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} params.sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} params.sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} params.status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} params.taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} params.trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsUpdate(params: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string }): Promise<Models.Products>;\n /**\n * Updates one product by id. A partial patch: the body names only the columns to change and every column it leaves out keeps its current value, so there is no read-modify-write and no way to blank a field by forgetting it.\n * \n * The catalog itself. A product row carries only what every product has — SKU, kind, family, enabled, tax class — and everything the tenant modelled lives in the `attribute_values` jsonb document, keyed by attribute CODE inside one of four scope buckets (common, per locale, per channel, per channel and locale). `label` is a generated column, maintained by the database so a grid of twenty thousand rows can sort and filter on a name with no join. `kind` says where the row sits in the variant hierarchy: a `model` carries what its variants share and is never sold itself.\n * \n * A body that names nothing writable is refused with 400 rather than answered as a no-op, an id nobody carries answers 404, and a value that collides on `sku` answers 409. `label` is a generated column: naming it is dropped rather than refused, and `completeness` is written by the two metadata routes, not here.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} attributeValues - Every attribute value the record carries, in ONE jsonb document — the core of an attribute-driven PIM. A record's properties are not columns here: they are rows in `attributes`, selected per family by `family_attributes`, and their values live under their attribute CODE inside this object.\n\nFour buckets, and an attribute's own flags decide which one it writes to:\n\n `common` the attribute is neither localizable nor scopable — one value, full stop.\n `{\"common\": {\"net_weight\": 2.4, \"colour\": \"black\"}}`\n `locale_specific` `localizable`: one value per locale key.\n `{\"locale_specific\": {\"de\": {\"name\": \"Akku-Bohrschrauber\"}}}`\n `channel_specific` `scopable`: one value per channel.\n `{\"channel_specific\": {\"b2b\": {\"minimum_order_quantity\": 6}}}`\n `channel_locale_specific` both: one value per channel AND locale key.\n `{\"channel_locale_specific\": {\"b2b\": {\"de\": {\"description\": \"…\"}}}}`\n\nA locale key is the WRITE KEY of this tenant's locale policy (`GET /v1/markets/locale-policy`, `locales[].write`): the bare language (`de`) when the tenant keys translations by language, the full locale (`de-DE`) when it keys them by region. Another spelling of the same locale (`de_DE`, `de-de`, `deu`) is accepted and stored under that key; a language no market of the tenant configures is refused with 422 `unknown_locale`, a bare language two regional keys share with 422 `ambiguous_locale`, and anything that is not a language tag with 422 `invalid_locale`. The database refuses any key that is not `xx` or `xx-YY` whichever way it is written.\n\nA reader takes the most specific bucket that carries the code and falls back through locale, then channel, then `common`. `common` is always last and always consulted, because early imports wrote everything there whatever an attribute's flags said — a reader that skipped it reports an imported catalog as empty. `GET /products/attribute-schema` answers, per field, the exact path a value belongs at (`storage.path`) and that full fallback order (`from`), so no client has to re-derive any of this.\n\nThe value itself is whatever the attribute's `type` implies: a string, a number, a boolean, an option CODE for a select (never its label), a list of codes for a multi-select, `{\"amount\": …, \"unit\": …}` for a measure, a list of `{\"amount\": …, \"currency\": …}` for a price, an asset code for media.\n\nDefaults to `{}`, and an empty object is a normal state — a record nobody has enriched yet. The declared type also admits an array only because every jsonb column of this app shares one mapping; an array is not meaningful here and every reader in this app treats a non-object as empty.\n * @param {object} baseValues - The commercial basics of the article, kept beside the attribute values because every family has them: `unit` (sales unit), `min_order_quantity`, `order_interval`, `packaging`, `customs_tariff_number`, `country_of_origin`, `weight`, `gtin`. The keys are the Cockpit's; this app stores the object as sent and refuses anything that is not an object. A copy keeps everything but `gtin`.\n * @param {object} channelRelease - Release per sales channel, for a tenant that releases channel by channel: an object keyed by the channel's `code` from the channels app, each value one of the `status` words — usually 'released', 'in_review' or 'blocked'. Null when the product is released as a whole. The database refuses anything that is not an object.\n * @param {string[]} channels - The sales channels that offer this product, as the channels app's channel codes. An empty list (the default) offers it in none. `POST /products/{id}/channels` adds, removes or replaces codes for one product, which is what a bulk assignment over a selection calls. This app stores the codes as sent; it does not ask the channels app whether they exist.\n * @param {object} completeness - How much of what this product's family REQUIRES it actually carries — the number a merchandiser works down. `required` counts the attributes the family marks `is_required`, `filled` how many of those carry a value in ANY bucket, `ratio` is filled/required between 0 and 1 (a family that requires nothing is 1, not undefined), `missing` lists the codes with no value anywhere, sorted, and `computed_at` is when it was measured.\n\nWritten only by `POST /products/{id}/completeness` and by `POST /products/{id}/family`; a plain create or update never touches it, so it is null until one of the two has run. It also stays null for a product with no family — there is nothing to measure it against, and 0 % would be a lie.\n * @param {string} deletedAt - When the record went to the trash; null while it is live. A record in the trash acts nowhere — lists leave it out unless asked with `trashed`, and the grid, the batch read and the category rules ignore it — but a read by its id still answers it, so every reference to it keeps resolving. Written by `DELETE` and cleared by `POST …/{id}/restore`, never by an update.\n * @param {string} deletedBy - Who moved the record to the trash: the acting person's e-mail as the gateway stated it, `system` for a scheduled job. Attribution for a person reading the trash, not an identity to check against.\n * @param {string} deletedById - The platform subject of the person who moved the record to the trash — the stable id a privacy request pseudonymises, where `deleted_by` is only the name shown. Null for a scheduled job.\n * @param {boolean} enabled - Whether the product is offered. A create defaults it from the `new_products_enabled_by_default` tenant setting rather than blindly to true, so an import does not publish twenty thousand unfinished products the moment it lands. An explicit value in the body always wins.\n * @param {string} externalId - The key this article has in the system that OWNS it — for a catalog fed by BMEcat, the PIM's own id for the row. It is NOT the `sku`: the SKU is the merchant's article number, typed by people and printed on paper, while this is whatever the feeding system calls the same article, often a number nobody outside it sees. Unique per tenant where set, so a repeated import upserts on it instead of matching on SKU and founding a second product. Null for a product created here.\n * @param {object} externalRefs - Every OTHER system that knows this row, keyed by system name — a second PIM, a supplier's feed, a GTIN register. `external_id` is the system that OWNS the row; this is the rest, so the next identifier standard costs a key in here rather than a column and a migration. It filters the way every jsonb column of this app filters: the WHOLE document is compared, so there is no asking for one key — read the row by `external_id` and take this off the answer.\n * @param {string} familyId - The family that decides which attributes this product HAS. Without one nothing is required, completeness cannot be computed and the display name never resolves — `POST /products/{id}/family` is the call that sets it and computes completeness in the same step.\n * @param {string} familyVariantId - Which variant structure of the family this product follows — the axes it splits on. Null on a simple product.\n * @param {ProductsKind} kind - Where the product sits in the variant hierarchy. 'simple' stands on its own. 'model' carries the values its variants share and is never sold itself. 'variant' carries the axis values and points at its model through `parent_id`.\n * @param {string} manufacturerId - The manufacturer record this product is made by — the one that carries the GPSR data a product offer has to show. Null when no manufacturer is linked; the older free text `attribute_values.common.manufacturer` stays readable while catalogs move over (`POST /products/manufacturers/migrate` links them). Deleting the manufacturer leaves the product unlinked, not deleted.\n * @param {string} parentId - The product MODEL this variant belongs to. Only a `variant` carries one. Deleting the model leaves its variants behind with a null parent rather than deleting them.\n * @param {string} purgeAt - When the daily purge deletes the record for good: when it went to the trash plus the tenant's trash retention (90 days unless the tenant set another). The purge brings it in line when the retention changes.\n * @param {string} purgeBlockedReason - Why the daily purge could not delete the record for good — stock still on hand, an active promotion naming the category, a switched-on product still made by the manufacturer, or a check that could not be made. The record stays in the trash until the reason is gone; null when nothing held it.\n * @param {object} quantifiedAssociations - The import-side mirror of associations that carry a quantity — a bundle, a bill of materials, a spare-parts set. NOTHING IN THIS APP READS OR WRITES IT: no route produces it, no route consumes it, and it is null on every product this app has created. The surface that IS served is relational — `product_associations`, whose `quantity` column holds the number, guarded by `association_types.is_quantified`.\n\nIt exists because a PIM import (Akeneo, BMEcat) carries these in one blob keyed by association type code, and the column lets that document round-trip instead of being dropped. The database enforces no shape on it, so what a reader finds is whatever the importer wrote; the example is the conventional form.\n * @param {string} sku - The merchant's own article number — unique per tenant, and the value every integration (ERP, shop, feed, price list) joins on. The one identifier a person types, and the fallback this app shows when the catalog holds no name.\n * @param {object} sourceData - What the source said about this row, kept as it said it: `{\"system\": …, \"etag\": …, \"raw\": {…}}`. The `etag` is what a write-back has to hand back in `If-Match`, and between two runs there is nowhere else to keep it. `raw` carries the source fields this app does not model, so they survive a round trip instead of being lost the first time somebody edits here. Written by whatever imports the row; nothing in this app reads or maintains it.\n * @param {string} sourceSyncedAt - When this row was last CONFIRMED against its source — not when it last changed. A delta run asks for everything it has not seen since its last pass, and over 25 000 products that is the difference between an index scan and reading the whole catalog, which is the reason the column is indexed. An edit made here leaves it alone, so a value that has stopped moving says the feed has gone quiet, not that nobody works the record. Null for a row no source owns.\n * @param {ProductsStatus} status - Where the product stands in its release — 'draft' (newly created, the default), 'in_progress' (being worked on), 'in_review' (waiting for release), 'released' (released to the channels), 'phase_out' (sold off, not reordered) or 'blocked' (must not be sold). It sits beside `enabled`, which stays what a shop reads; this app does not derive one from the other. `GET /products/vocabularies/product-status` serves the words and tones.\n * @param {string} taxClass - The tax class key the prices app resolves a VAT rate from. Free text here — the vocabulary belongs to the app that prices, and `POST /products/batch` exists to hand exactly this column to it in bulk.\n * @param {string} trashBatch - The group the record went to the trash with. One delete stamps the record and everything that goes with it — the variants of a model, the whole subtree of a category — with the same value, and a restore brings back exactly that group. A record trashed on its own earlier keeps its own.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsUpdate(id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string): Promise<Models.Products>;\n productsUpdate(\n paramsOrFirst: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string } | string,\n ...rest: [(object)?, (object)?, (object)?, (string[])?, (object)?, (string)?, (string)?, (string)?, (boolean)?, (string)?, (object)?, (string)?, (string)?, (ProductsKind)?, (string)?, (string)?, (string)?, (string)?, (object)?, (string)?, (object)?, (string)?, (ProductsStatus)?, (string)?, (string)?] \n ): Promise<Models.Products> {\n let params: { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, attributeValues?: object, baseValues?: object, channelRelease?: object, channels?: string[], completeness?: object, deletedAt?: string, deletedBy?: string, deletedById?: string, enabled?: boolean, externalId?: string, externalRefs?: object, familyId?: string, familyVariantId?: string, kind?: ProductsKind, manufacturerId?: string, parentId?: string, purgeAt?: string, purgeBlockedReason?: string, quantifiedAssociations?: object, sku?: string, sourceData?: object, sourceSyncedAt?: string, status?: ProductsStatus, taxClass?: string, trashBatch?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n attributeValues: rest[0] as object,\n baseValues: rest[1] as object,\n channelRelease: rest[2] as object,\n channels: rest[3] as string[],\n completeness: rest[4] as object,\n deletedAt: rest[5] as string,\n deletedBy: rest[6] as string,\n deletedById: rest[7] as string,\n enabled: rest[8] as boolean,\n externalId: rest[9] as string,\n externalRefs: rest[10] as object,\n familyId: rest[11] as string,\n familyVariantId: rest[12] as string,\n kind: rest[13] as ProductsKind,\n manufacturerId: rest[14] as string,\n parentId: rest[15] as string,\n purgeAt: rest[16] as string,\n purgeBlockedReason: rest[17] as string,\n quantifiedAssociations: rest[18] as object,\n sku: rest[19] as string,\n sourceData: rest[20] as object,\n sourceSyncedAt: rest[21] as string,\n status: rest[22] as ProductsStatus,\n taxClass: rest[23] as string,\n trashBatch: rest[24] as string \n };\n }\n \n const id = params.id;\n const attributeValues = params.attributeValues;\n const baseValues = params.baseValues;\n const channelRelease = params.channelRelease;\n const channels = params.channels;\n const completeness = params.completeness;\n const deletedAt = params.deletedAt;\n const deletedBy = params.deletedBy;\n const deletedById = params.deletedById;\n const enabled = params.enabled;\n const externalId = params.externalId;\n const externalRefs = params.externalRefs;\n const familyId = params.familyId;\n const familyVariantId = params.familyVariantId;\n const kind = params.kind;\n const manufacturerId = params.manufacturerId;\n const parentId = params.parentId;\n const purgeAt = params.purgeAt;\n const purgeBlockedReason = params.purgeBlockedReason;\n const quantifiedAssociations = params.quantifiedAssociations;\n const sku = params.sku;\n const sourceData = params.sourceData;\n const sourceSyncedAt = params.sourceSyncedAt;\n const status = params.status;\n const taxClass = params.taxClass;\n const trashBatch = params.trashBatch;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof attributeValues !== 'undefined') {\n apiPayload['attribute_values'] = attributeValues;\n }\n if (typeof baseValues !== 'undefined') {\n apiPayload['base_values'] = baseValues;\n }\n if (typeof channelRelease !== 'undefined') {\n apiPayload['channel_release'] = channelRelease;\n }\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof completeness !== 'undefined') {\n apiPayload['completeness'] = completeness;\n }\n if (typeof deletedAt !== 'undefined') {\n apiPayload['deleted_at'] = deletedAt;\n }\n if (typeof deletedBy !== 'undefined') {\n apiPayload['deleted_by'] = deletedBy;\n }\n if (typeof deletedById !== 'undefined') {\n apiPayload['deleted_by_id'] = deletedById;\n }\n if (typeof enabled !== 'undefined') {\n apiPayload['enabled'] = enabled;\n }\n if (typeof externalId !== 'undefined') {\n apiPayload['external_id'] = externalId;\n }\n if (typeof externalRefs !== 'undefined') {\n apiPayload['external_refs'] = externalRefs;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n if (typeof familyVariantId !== 'undefined') {\n apiPayload['family_variant_id'] = familyVariantId;\n }\n if (typeof kind !== 'undefined') {\n apiPayload['kind'] = kind;\n }\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n if (typeof parentId !== 'undefined') {\n apiPayload['parent_id'] = parentId;\n }\n if (typeof purgeAt !== 'undefined') {\n apiPayload['purge_at'] = purgeAt;\n }\n if (typeof purgeBlockedReason !== 'undefined') {\n apiPayload['purge_blocked_reason'] = purgeBlockedReason;\n }\n if (typeof quantifiedAssociations !== 'undefined') {\n apiPayload['quantified_associations'] = quantifiedAssociations;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n if (typeof sourceData !== 'undefined') {\n apiPayload['source_data'] = sourceData;\n }\n if (typeof sourceSyncedAt !== 'undefined') {\n apiPayload['source_synced_at'] = sourceSyncedAt;\n }\n if (typeof status !== 'undefined') {\n apiPayload['status'] = status;\n }\n if (typeof taxClass !== 'undefined') {\n apiPayload['tax_class'] = taxClass;\n }\n if (typeof trashBatch !== 'undefined') {\n apiPayload['trash_batch'] = trashBatch;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'put',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Sets one attribute's value on one product — what a bulk „Attribute bearbeiten“ over a selection calls once per product. With `locale` the value goes under that language (re-keyed to the tenant's write key like any write); with `locale: \"all\"` or none it goes into the common bucket, the same in every language. The rest of the product's values stay as they are.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.attribute - The code of a product attribute.\n * @param {any} params.value - The value, in the shape the attribute stores. Null clears it.\n * @param {string} params.locale - A language key the tenant writes, or 'all' (the default) for the common bucket.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsAttributeSet(params: { id: string, attribute: string, value: any, locale?: string }): Promise<{}>;\n /**\n * Sets one attribute's value on one product — what a bulk „Attribute bearbeiten“ over a selection calls once per product. With `locale` the value goes under that language (re-keyed to the tenant's write key like any write); with `locale: \"all\"` or none it goes into the common bucket, the same in every language. The rest of the product's values stay as they are.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} attribute - The code of a product attribute.\n * @param {any} value - The value, in the shape the attribute stores. Null clears it.\n * @param {string} locale - A language key the tenant writes, or 'all' (the default) for the common bucket.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsAttributeSet(id: string, attribute: string, value: any, locale?: string): Promise<{}>;\n productsAttributeSet(\n paramsOrFirst: { id: string, attribute: string, value: any, locale?: string } | string,\n ...rest: [(string)?, (any)?, (string)?] \n ): Promise<{}> {\n let params: { id: string, attribute: string, value: any, locale?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, attribute: string, value: any, locale?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n attribute: rest[0] as string,\n value: rest[1] as any,\n locale: rest[2] as string \n };\n }\n \n const id = params.id;\n const attribute = params.attribute;\n const value = params.value;\n const locale = params.locale;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof attribute === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"attribute\"');\n }\n if (typeof value === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"value\"');\n }\n\n const apiPath = '/v1/products/{id}/attribute-value'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof attribute !== 'undefined') {\n apiPayload['attribute'] = attribute;\n }\n if (typeof locale !== 'undefined') {\n apiPayload['locale'] = locale;\n }\n if (typeof value !== 'undefined') {\n apiPayload['value'] = value;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Adds, removes or replaces channel codes on one product's `channels` — what a bulk „Kanäle zuordnen“ over a selection calls once per product. The codes are the channels app's; this app stores them as sent, trimmed, each once, and does not ask the channels app whether they exist.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string[]} params.channels - The channel codes to add, remove or set.\n * @param {ProductsChannelsAssignMode} params.mode - 'add' (the default) keeps what the product has and adds these, 'remove' takes these out, 'replace' sets exactly these.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsChannelsAssign(params: { id: string, channels: string[], mode?: ProductsChannelsAssignMode }): Promise<{}>;\n /**\n * Adds, removes or replaces channel codes on one product's `channels` — what a bulk „Kanäle zuordnen“ over a selection calls once per product. The codes are the channels app's; this app stores them as sent, trimmed, each once, and does not ask the channels app whether they exist.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string[]} channels - The channel codes to add, remove or set.\n * @param {ProductsChannelsAssignMode} mode - 'add' (the default) keeps what the product has and adds these, 'remove' takes these out, 'replace' sets exactly these.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsChannelsAssign(id: string, channels: string[], mode?: ProductsChannelsAssignMode): Promise<{}>;\n productsChannelsAssign(\n paramsOrFirst: { id: string, channels: string[], mode?: ProductsChannelsAssignMode } | string,\n ...rest: [(string[])?, (ProductsChannelsAssignMode)?] \n ): Promise<{}> {\n let params: { id: string, channels: string[], mode?: ProductsChannelsAssignMode };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, channels: string[], mode?: ProductsChannelsAssignMode };\n } else {\n params = {\n id: paramsOrFirst as string,\n channels: rest[0] as string[],\n mode: rest[1] as ProductsChannelsAssignMode \n };\n }\n \n const id = params.id;\n const channels = params.channels;\n const mode = params.mode;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof channels === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"channels\"');\n }\n\n const apiPath = '/v1/products/{id}/channels'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof channels !== 'undefined') {\n apiPayload['channels'] = channels;\n }\n if (typeof mode !== 'undefined') {\n apiPayload['mode'] = mode;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * How much of what its family REQUIRES does this product actually carry — the number a merchandiser works down. products.completeness is jsonb that nothing had ever written. This computes it from family_attributes (is_required) against the product's own scoped attribute_values and stores the result. A product with no family answers 400 rather than an invented 0 % — it has nothing to be measured against.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} params.data - Request body\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsCompleteness(params: { id: string, data: object }): Promise<{}>;\n /**\n * How much of what its family REQUIRES does this product actually carry — the number a merchandiser works down. products.completeness is jsonb that nothing had ever written. This computes it from family_attributes (is_required) against the product's own scoped attribute_values and stores the result. A product with no family answers 400 rather than an invented 0 % — it has nothing to be measured against.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {object} data - Request body\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCompleteness(id: string, data: object): Promise<{}>;\n productsCompleteness(\n paramsOrFirst: { id: string, data: object } | string,\n ...rest: [(object)?] \n ): Promise<{}> {\n let params: { id: string, data: object };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, data: object };\n } else {\n params = {\n id: paramsOrFirst as string,\n data: rest[0] as object \n };\n }\n \n const id = params.id;\n const data = params.data;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof data === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"data\"');\n }\n\n const apiPath = '/v1/products/{id}/completeness'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof data !== 'undefined') {\n Object.assign(apiPayload, data);\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Creates a copy of one product under a new SKU and answers 201 with the new row. The copy keeps the name and starts as a switched-off draft without a GTIN, release or completeness. `parts` adds what else comes along: `attributes` (every value but files and images), `images` (the file and image values), `variants` (each variant copied under the new model, its SKU prefixed with the new one) and `categories` (the hand-picked memberships; rule memberships follow from the next recompute). Movement data is never copied, and prices live in the prices app. There is no transaction across rows: if a later step fails, what this call created is deleted again.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.sku - The copy's own SKU.\n * @param {ProductsCopyParts[]} params.parts - What comes along besides the product itself.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n */\n productsCopy(params: { id: string, sku: string, parts?: ProductsCopyParts[] }): Promise<Models.Products>;\n /**\n * Creates a copy of one product under a new SKU and answers 201 with the new row. The copy keeps the name and starts as a switched-off draft without a GTIN, release or completeness. `parts` adds what else comes along: `attributes` (every value but files and images), `images` (the file and image values), `variants` (each variant copied under the new model, its SKU prefixed with the new one) and `categories` (the hand-picked memberships; rule memberships follow from the next recompute). Movement data is never copied, and prices live in the prices app. There is no transaction across rows: if a later step fails, what this call created is deleted again.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} sku - The copy's own SKU.\n * @param {ProductsCopyParts[]} parts - What comes along besides the product itself.\n * @throws {RevenexxException}\n * @returns {Promise<Models.Products>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsCopy(id: string, sku: string, parts?: ProductsCopyParts[]): Promise<Models.Products>;\n productsCopy(\n paramsOrFirst: { id: string, sku: string, parts?: ProductsCopyParts[] } | string,\n ...rest: [(string)?, (ProductsCopyParts[])?] \n ): Promise<Models.Products> {\n let params: { id: string, sku: string, parts?: ProductsCopyParts[] };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, sku: string, parts?: ProductsCopyParts[] };\n } else {\n params = {\n id: paramsOrFirst as string,\n sku: rest[0] as string,\n parts: rest[1] as ProductsCopyParts[] \n };\n }\n \n const id = params.id;\n const sku = params.sku;\n const parts = params.parts;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof sku === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"sku\"');\n }\n\n const apiPath = '/v1/products/{id}/copy'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof parts !== 'undefined') {\n apiPayload['parts'] = parts;\n }\n if (typeof sku !== 'undefined') {\n apiPayload['sku'] = sku;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Names the family in the body — by `family_id` or by `family_code`, whichever the caller holds — and computes the product's completeness in the same call. The step every family-driven surface waits on: a product with no family has no required attributes, so its completeness cannot be computed and its family's label attribute never resolves. Assigning the family recomputes and STORES products.completeness immediately, so the metadata cannot go stale between the two operations.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.familyCode - Alternative to family_id — a `families.code` this tenant holds, from `GET /products/families`. No example: a code is tenant data, and any value published here names a family somebody does not have.\n * @param {string} params.familyId - The family to assign.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsFamilyAssign(params: { id: string, familyCode?: string, familyId?: string }): Promise<{}>;\n /**\n * Names the family in the body — by `family_id` or by `family_code`, whichever the caller holds — and computes the product's completeness in the same call. The step every family-driven surface waits on: a product with no family has no required attributes, so its completeness cannot be computed and its family's label attribute never resolves. Assigning the family recomputes and STORES products.completeness immediately, so the metadata cannot go stale between the two operations.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} familyCode - Alternative to family_id — a `families.code` this tenant holds, from `GET /products/families`. No example: a code is tenant data, and any value published here names a family somebody does not have.\n * @param {string} familyId - The family to assign.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsFamilyAssign(id: string, familyCode?: string, familyId?: string): Promise<{}>;\n productsFamilyAssign(\n paramsOrFirst: { id: string, familyCode?: string, familyId?: string } | string,\n ...rest: [(string)?, (string)?] \n ): Promise<{}> {\n let params: { id: string, familyCode?: string, familyId?: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, familyCode?: string, familyId?: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n familyCode: rest[0] as string,\n familyId: rest[1] as string \n };\n }\n \n const id = params.id;\n const familyCode = params.familyCode;\n const familyId = params.familyId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}/family'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof familyCode !== 'undefined') {\n apiPayload['family_code'] = familyCode;\n }\n if (typeof familyId !== 'undefined') {\n apiPayload['family_id'] = familyId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Links one product to a manufacturer record and copies the manufacturer's GPSR state onto the product (`manufacturer_gpsr_complete`) — what a bulk „Hersteller setzen“ calls once per product. A plain `PUT /products/{id}` with `manufacturer_id` does the same; this route exists so a bulk action has a body of one field and an answer that names the manufacturer. It does not create a manufacturer.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} params.manufacturerId - The manufacturer to link.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsManufacturerAssign(params: { id: string, manufacturerId: string }): Promise<{}>;\n /**\n * Links one product to a manufacturer record and copies the manufacturer's GPSR state onto the product (`manufacturer_gpsr_complete`) — what a bulk „Hersteller setzen“ calls once per product. A plain `PUT /products/{id}` with `manufacturer_id` does the same; this route exists so a bulk action has a body of one field and an answer that names the manufacturer. It does not create a manufacturer.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @param {string} manufacturerId - The manufacturer to link.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsManufacturerAssign(id: string, manufacturerId: string): Promise<{}>;\n productsManufacturerAssign(\n paramsOrFirst: { id: string, manufacturerId: string } | string,\n ...rest: [(string)?] \n ): Promise<{}> {\n let params: { id: string, manufacturerId: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string, manufacturerId: string };\n } else {\n params = {\n id: paramsOrFirst as string,\n manufacturerId: rest[0] as string \n };\n }\n \n const id = params.id;\n const manufacturerId = params.manufacturerId;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n if (typeof manufacturerId === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"manufacturerId\"');\n }\n\n const apiPath = '/v1/products/{id}/manufacturer'.replace('{id}', id);\n const apiPayload: Payload = {};\n if (typeof manufacturerId !== 'undefined') {\n apiPayload['manufacturer_id'] = manufacturerId;\n }\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n 'content-type': 'application/json',\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n\n /**\n * Brings a product back from the trash, together with every record that went with it (its `trash_batch`). A record trashed on its own before stays in the trash. Everything that pointed at it acts again as it did, because nothing was changed when it went.\n * \n * Restoring needs the same right as deleting.\n *\n * @param {string} params.id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n */\n productsRestore(params: { id: string }): Promise<{}>;\n /**\n * Brings a product back from the trash, together with every record that went with it (its `trash_batch`). A record trashed on its own before stays in the trash. Everything that pointed at it acts again as it did, because nothing was changed when it went.\n * \n * Restoring needs the same right as deleting.\n *\n * @param {string} id - The `products` row to address, by id. It names a row THIS TENANT holds, so no example is published — a uuid this app invented would document a call that answers 404, and a real one would be another tenant's data. Read one from `GET /v1/products`. An id no product of this tenant carries answers 404; a malformed one answers 400 before the route is reached.\n * @throws {RevenexxException}\n * @returns {Promise<{}>}\n * @deprecated Use the object parameter style method for a better developer experience.\n */\n productsRestore(id: string): Promise<{}>;\n productsRestore(\n paramsOrFirst: { id: string } | string \n ): Promise<{}> {\n let params: { id: string };\n \n if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {\n params = (paramsOrFirst || {}) as { id: string };\n } else {\n params = {\n id: paramsOrFirst as string \n };\n }\n \n const id = params.id;\n\n if (typeof id === 'undefined') {\n throw new RevenexxException('Missing required parameter: \"id\"');\n }\n\n const apiPath = '/v1/products/{id}/restore'.replace('{id}', id);\n const apiPayload: Payload = {};\n const uri = new URL(this.client.config.endpoint + apiPath);\n\n const apiHeaders: { [header: string]: string } = {\n }\n\n return this.client.call(\n 'post',\n uri,\n apiHeaders,\n apiPayload,\n );\n }\n}\n"]}
|