Get product Offers

The getProductPaginatedOffers function retrieves a paginated list of offers (pricing and availability information) for a specific product. This function is essential for displaying pricing options from multiple suppliers, enabling users to compare prices, availability, and terms before making purchasing decisions.

Product offers contain comprehensive pricing information including unit prices, bulk pricing tiers, supplier details, availability status, and delivery terms. The function supports pagination to efficiently handle products with numerous offer options from different suppliers, ensuring optimal performance even for products with extensive pricing catalogs.

This function is particularly crucial for B2B e-commerce scenarios where price comparison, supplier selection, and bulk purchasing decisions are key factors in the procurement process.

Process Flow

sequenceDiagram
    participant User as User
    participant Component as Vue Component
    participant useProduct as useProduct
    participant SDK as Djust SDK
    participant API as API Server
    participant Auth as Auth Service

    User->>Component: Request Product Offers
    Component->>useProduct: getProductPaginatedOffers(params)

    useProduct->>useProduct: Validate Parameters
    useProduct->>SDK: useFetch('/api/product/{id}/offers')

    SDK->>Auth: Verify Authentication Token
    Auth-->>SDK: Token Valid

    SDK->>API: GET /api/product/{productId}/offers

    alt Success Response
        API-->>SDK: 200 - Offers List
        SDK-->>useProduct: GetProductOffersResponse
        useProduct->>useProduct: Process Pagination
        useProduct-->>Component: Paginated Offers
        Component-->>User: Display Pricing Options
    else No Offers Found
        API-->>SDK: 404 - No Offers
        SDK-->>useProduct: Empty Response
        useProduct->>useProduct: Log No Offers
        useProduct-->>Component: Empty Offers List
        Component-->>User: "No pricing available"
    else Authentication Error
        Auth-->>SDK: 401 - Unauthorized
        SDK-->>useProduct: Auth Error
        useProduct-->>Component: Authentication Error
        Component-->>User: Redirect to Login
    else Permission Error
        API-->>SDK: 403 - Pricing Access Denied
        SDK-->>useProduct: Permission Error
        useProduct-->>Component: Access Denied
        Component-->>User: "Pricing not accessible"
    else Server Error
        API-->>SDK: 500 - Internal Error
        SDK-->>useProduct: Server Error
        useProduct->>useProduct: Log Server Error
        useProduct-->>Component: Error Response
        Component-->>User: "Unable to load pricing"
    end

Call Parameters

The function accepts a GetProductOffersRequest object with the following structure:

GetProductOffersRequest Interface (from SDK)

interface GetProductOffersRequest {
  productIdentifier: string;
  productIdType?: ProductIdType;
  currency?: string;
  locale?: string;
  withoutPrices?: boolean;
  pageable?: Pageable;
}

type ProductIdType = "EXTERNAL_ID" | "SKU" | "ID";

interface Pageable {
  page?: number;
  size?: number;
  sort?: string[];
}

Parameter Details

ParameterTypeRequiredDefaultExampleBusiness Impact
productIdentifierstring✅ Yes-"LAPTOP-001"Critical: Product identifier for offer lookup
productIdTypeProductIdType❌ Optional"EXTERNAL_ID""SKU", "ID"Important: Determines lookup strategy
currencystring❌ OptionalRuntime config"EUR", "USD"Pricing: Currency for price display
localestring❌ OptionalRuntime config"en-GB", "fr-FR"Localization: Affects supplier names and terms
withoutPricesboolean❌ Optionalfalsetrue, falseAccess Control: Hide pricing for restricted users
pageable.pagenumber❌ Optional00, 1, 2Performance: Controls result pagination
pageable.sizenumber❌ OptionalRuntime config10, 20, 50Performance: Limits offers per request

Complete Example Call

const offersParams: GetProductOffersRequest = {
  productIdentifier: "LAPTOP-DELL-001",
  productIdType: "SKU",
  currency: "EUR",
  locale: "en-GB",
  withoutPrices: false,
  pageable: {
    page: 0,
    size: 20,
  },
};

const response = await getProductPaginatedOffers(offersParams);

Returns from Composable

The function returns a GetProductOffersResponse object with the following structure:

GetProductOffersResponse Interface (from SDK)

interface GetProductOffersResponse {
  success: boolean;
  offers: ProductOfferPage;
}

interface ProductOfferPage {
  content: ProductOffer[];
  pageable: Pageable;
  totalElements: number;
  totalPages: number;
  size: number;
  number: number;
  first: boolean;
  last: boolean;
  empty?: boolean;
}

interface ProductOffer {
  id: string;
  productSku: string;
  supplierId: string;
  supplierName: string;
  price: ProductPrice;
  availability: ProductAvailability;
  terms: OfferTerms;
  validFrom: string;
  validTo?: string;
  minimumQuantity: number;
  packageSize: number;
  leadTime: number;
  deliveryOptions: DeliveryOption[];
}

interface ProductPrice {
  unitPrice: number;
  currency: string;
  taxIncluded: boolean;
  discounts: PriceDiscount[];
  bulkPricing: BulkPrice[];
}

interface ProductAvailability {
  inStock: boolean;
  quantity?: number;
  stockLocation: string;
  availableFrom?: string;
  estimatedDelivery?: string;
}

Realistic Example JSON Response

{
  "success": true,
  "offers": {
    "content": [
      {
        "id": "offer-001",
        "productSku": "LAPTOP-DELL-001",
        "supplierId": "supplier-tech-pro",
        "supplierName": "TechPro Distribution",
        "price": {
          "unitPrice": 899.99,
          "currency": "EUR",
          "taxIncluded": false,
          "discounts": [
            {
              "type": "VOLUME_DISCOUNT",
              "minQuantity": 10,
              "percentage": 5.0,
              "description": "5% discount for orders of 10+ units"
            }
          ],
          "bulkPricing": [
            {
              "minQuantity": 1,
              "maxQuantity": 9,
              "unitPrice": 899.99
            },
            {
              "minQuantity": 10,
              "maxQuantity": 49,
              "unitPrice": 854.99
            },
            {
              "minQuantity": 50,
              "maxQuantity": null,
              "unitPrice": 809.99
            }
          ]
        },
        "availability": {
          "inStock": true,
          "quantity": 150,
          "stockLocation": "Warehouse Paris",
          "estimatedDelivery": "2024-01-25T10:00:00Z"
        },
        "terms": {
          "paymentTerms": "NET30",
          "warranty": "24 months",
          "returnPolicy": "30 days",
          "shippingTerms": "FOB destination"
        },
        "validFrom": "2024-01-01T00:00:00Z",
        "validTo": "2024-03-31T23:59:59Z",
        "minimumQuantity": 1,
        "packageSize": 1,
        "leadTime": 2,
        "deliveryOptions": [
          {
            "method": "EXPRESS",
            "cost": 15.99,
            "estimatedDays": 1
          },
          {
            "method": "STANDARD",
            "cost": 5.99,
            "estimatedDays": 3
          }
        ]
      },
      {
        "id": "offer-002",
        "productSku": "LAPTOP-DELL-001",
        "supplierId": "supplier-global-tech",
        "supplierName": "Global Tech Solutions",
        "price": {
          "unitPrice": 925.0,
          "currency": "EUR",
          "taxIncluded": false,
          "discounts": [
            {
              "type": "EARLY_PAYMENT",
              "percentage": 2.0,
              "description": "2% discount for payment within 10 days"
            }
          ],
          "bulkPricing": [
            {
              "minQuantity": 1,
              "maxQuantity": 19,
              "unitPrice": 925.0
            },
            {
              "minQuantity": 20,
              "maxQuantity": null,
              "unitPrice": 888.0
            }
          ]
        },
        "availability": {
          "inStock": true,
          "quantity": 75,
          "stockLocation": "Warehouse Amsterdam",
          "estimatedDelivery": "2024-01-26T14:00:00Z"
        },
        "terms": {
          "paymentTerms": "NET15",
          "warranty": "36 months",
          "returnPolicy": "14 days",
          "shippingTerms": "CIF"
        },
        "validFrom": "2024-01-01T00:00:00Z",
        "minimumQuantity": 1,
        "packageSize": 1,
        "leadTime": 3,
        "deliveryOptions": [
          {
            "method": "PREMIUM",
            "cost": 25.99,
            "estimatedDays": 1
          },
          {
            "method": "STANDARD",
            "cost": 9.99,
            "estimatedDays": 4
          }
        ]
      }
    ],
    "pageable": {
      "page": 0,
      "size": 20
    },
    "totalElements": 8,
    "totalPages": 1,
    "size": 20,
    "number": 0,
    "first": true,
    "last": true,
    "empty": false
  }
}

Error Management

HTTP Error Codes and Handling

Error CodeCauseSystem ActionUser MessageReturn Format
400Invalid product identifier or parametersLog validation error, return null"Invalid product information"null
401Authentication token missing/invalidTrigger re-authentication flow"Please log in to view pricing"Authentication redirect
403User lacks pricing permissionsLog access attempt, return null"Pricing information not accessible"null
404No offers found for productLog not found, return empty list"No pricing available for this product"{ success: true, offers: { content: [] } }
412Precondition failed (business rules)Log business rule violation"Pricing access restricted for your account"null
500Internal server errorLog server error, return null"Unable to load pricing information"null

Error Handling Example Code

// Global error handling in composable
const getProductPaginatedOffers = async ({
  productIdentifier,
  productIdType,
  currency,
  locale,
  withoutPrices = false,
  pageable,
}: GetProductOffersRequest): Promise<GetProductOffersResponse | null> => {
  try {
    // Parameter validation
    if (!productIdentifier?.trim()) {
      console.error("Product identifier is required for offers lookup");
      return null;
    }

    const { data, error } = await useFetch<GetProductOffersResponse>(
      `/api/product/${productIdentifier}/offers`,
      {
        method: "GET",
        params: {
          productIdType,
          currency,
          locale,
          withoutPrices,
          page: pageable?.page,
          size: pageable?.size,
        },
      }
    );

    if (error.value) {
      console.error("Failed to fetch product paginated offers:", {
        productIdentifier,
        currency,
        error: error.value,
      });
      return null;
    }

    return data.value || null;
  } catch (err) {
    console.error(
      "Unexpected error while fetching product paginated offers:",
      err
    );
    return null;
  }
};

// Component-level error handling
const handleOffersFetch = async (productId: string) => {
  try {
    const response = await getProductPaginatedOffers({
      productIdentifier: productId,
      currency: userCurrency.value,
      pageable: { page: 0, size: 20 },
    });

    if (!response?.success || !response.offers) {
      throw new Error("Offers not available");
    }

    // Process successful response
    productOffers.value = response.offers.content;
    totalOffers.value = response.offers.totalElements;
  } catch (error) {
    // User notification
    useToast().add({
      title: "Pricing Error",
      description: "Unable to load pricing information",
      color: "yellow",
    });

    // Fallback to empty state
    productOffers.value = [];
    totalOffers.value = 0;
  }
};

Use Cases

1. Price Comparison Display

Show all available offers for a product to enable price and terms comparison across suppliers.

Scenario: User views product detail page and wants to compare pricing options from different suppliers. Parameters: { productIdentifier: product.sku, currency: userCurrency, pageable: { page: 0, size: 10 } } Expected Outcome: Comprehensive pricing table with supplier comparison, bulk pricing tiers, and delivery options.

2. Bulk Purchase Planning

Retrieve pricing information with bulk discounts for procurement planning and budget estimation.

Scenario: Purchasing manager evaluates bulk pricing options for large quantity orders. Parameters: { productIdentifier: "ITEM-001", currency: "EUR", withoutPrices: false } Expected Outcome: Detailed bulk pricing tiers, minimum quantities, and volume discounts displayed.

3. Supplier Selection

Compare supplier terms, delivery options, and availability for informed supplier selection.

Scenario: User needs to select optimal supplier based on delivery time, payment terms, and pricing. Parameters: { productIdentifier: product.externalId, locale: userLocale.value } Expected Outcome: Supplier comparison matrix with terms, availability, and delivery options.

4. Progressive Offers Loading

Load offers in batches for products with many suppliers to optimize page performance.

Scenario: Popular product with 50+ suppliers needs progressive loading for better user experience. Parameters: { productIdentifier: "POPULAR-001", pageable: { page: currentPage, size: 15 } } Expected Outcome: Paginated offers loading with smooth pagination controls and performance optimization.

Important Points

Performance Considerations

  • Pagination Strategy: Use appropriate page sizes to balance information completeness and load times
  • Currency Caching: Cache offers by currency to reduce redundant API calls for multi-currency scenarios
  • Lazy Loading: Load offers progressively as users scroll through large supplier lists
  • Price Formatting: Format prices client-side to reduce server processing overhead

Security Aspects

  • Pricing Permissions: Strict validation of user permissions to view pricing information
  • Supplier Filtering: Backend filters offers based on user's approved supplier list
  • Data Privacy: Sensitive pricing information protected based on user role and agreements
  • Rate Limiting: API calls limited to prevent pricing scraping or abuse

Flexibility Features

  • Multi-Currency Support: Automatic currency conversion and display based on user preferences
  • Configurable Pagination: Adaptive page sizing based on screen size and user preferences
  • Price Without Display: Support for showing availability without pricing for restricted users
  • Supplier Preferences: Integration with user's preferred supplier settings and contracts

Integration Capabilities

  • Cart Integration: Seamless integration with cart system for adding selected offers
  • Quote Integration: Direct connection to quote generation system for bulk requests
  • Analytics Integration: Track pricing views and supplier selection patterns
  • Procurement Workflows: Integration with approval workflows for enterprise purchasing

Technical Implementation

Composable Function with Complete JSDoc

/**
 * Retrieves paginated offers for a specific product
 *
 * @param params - Product offers request parameters
 * @param params.productIdentifier - Product identifier (SKU, ID, External ID)
 * @param params.productIdType - Type of identifier used for lookup
 * @param params.currency - Currency for pricing display
 * @param params.locale - Locale for internationalized content
 * @param params.withoutPrices - Whether to hide pricing information
 * @param params.pageable - Pagination configuration
 * @returns Promise resolving to paginated offers response or null
 *
 * @example
 * ```typescript
 * const { getProductPaginatedOffers } = useProduct();
 *
 * const response = await getProductPaginatedOffers({
 *   productIdentifier: "LAPTOP-001",
 *   currency: "EUR",
 *   pageable: { page: 0, size: 20 }
 * });
 *
 * if (response?.success) {
 *   console.log('Offers found:', response.offers.totalElements);
 *   response.offers.content.forEach(offer => {
 *     console.log(`${offer.supplierName}: €${offer.price.unitPrice}`);
 *   });
 * }
 * ```
 *
 * @throws {Error} When network request fails or invalid parameters provided
 * @since 1.0.0
 */
const getProductPaginatedOffers = async ({
  productIdentifier,
  productIdType,
  currency,
  locale,
  withoutPrices = false,
  pageable,
}: GetProductOffersRequest): Promise<GetProductOffersResponse | null> => {
  try {
    // Parameter validation
    if (!productIdentifier?.trim()) {
      throw new Error("Product identifier is required for offers lookup");
    }

    // Runtime configuration for defaults
    const runtimeConfig = useRuntimeConfig();
    const defaultCurrency = currency || runtimeConfig.public.currency;
    const defaultLocale = locale || runtimeConfig.public.locale;
    const defaultPageSize =
      pageable?.size ||
      Number(runtimeConfig.public.defaultPaginationSize) ||
      20;

    const { data, error } = await useFetch<GetProductOffersResponse>(
      `/api/product/${productIdentifier}/offers`,
      {
        method: "GET",
        params: {
          productIdType: productIdType || "EXTERNAL_ID",
          currency: defaultCurrency,
          locale: defaultLocale,
          withoutPrices,
          page: pageable?.page || 0,
          size: defaultPageSize,
        },
        // Enable caching with unique key
        key: `product-offers-${productIdentifier}-${defaultCurrency}-${
          pageable?.page || 0
        }-${defaultPageSize}`,
        // Default response structure
        default: () => ({
          success: false,
          offers: null,
        }),
      }
    );

    if (error.value) {
      console.error("Failed to fetch product paginated offers:", {
        productIdentifier,
        currency: defaultCurrency,
        withoutPrices,
        error: error.value,
      });
      return null;
    }

    return data.value || null;
  } catch (err) {
    console.error(
      "Unexpected error while fetching product paginated offers:",
      err
    );
    return null;
  }
};

Execution Flow

Step-by-Step Process

  1. Parameter Validation

    // Validate required product identifier
    if (!productIdentifier?.trim()) {
      throw new Error("Product identifier is required for offers lookup");
    }
  2. Configuration Setup

    // Apply runtime defaults for optional parameters
    const runtimeConfig = useRuntimeConfig();
    const effectiveCurrency = currency || runtimeConfig.public.currency;
    const effectiveLocale = locale || runtimeConfig.public.locale;
  3. Permission Check

    // Verify user has pricing access permissions
    const userStore = useUserStore();
    const canViewPricing = userStore.account?.permissions?.canViewPricing;
  4. Request Construction

    // Build API request with all parameters
    const requestUrl = `/api/product/${productIdentifier}/offers`;
    const requestParams = {
      productIdType: productIdType || "EXTERNAL_ID",
      currency: effectiveCurrency,
      locale: effectiveLocale,
      withoutPrices: withoutPrices || !canViewPricing,
      page: pageable?.page || 0,
      size: pageable?.size || defaultPageSize,
    };
  5. Network Request Execution

    // Execute paginated fetch with caching
    const { data, error } = await useFetch<GetProductOffersResponse>(
      requestUrl,
      {
        method: "GET",
        params: requestParams,
      }
    );
  6. Response Processing

    // Handle response and errors
    if (error.value) {
      logError("Failed to fetch offers", {
        productIdentifier,
        error: error.value,
      });
      return null;
    }
  7. Data Validation

    // Validate response structure
    if (!data.value?.offers?.content) {
      console.warn("No offers content in response");
      return { success: true, offers: { content: [], totalElements: 0 } };
    }
  8. Return Processed Response

    // Return structured offers data
    return {
      success: true,
      offers: data.value.offers,
    };