Skip to main content

skuIdMap Pattern

For quoting_extension tools that work with known SKU codes, the recommended pattern is:
  1. At tool init, call products.list() and build a local { [sku]: productId } lookup.
  2. Your tool’s internal logic works with human-readable SKU codes (from a pricing spreadsheet, config object, or product catalogue).
  3. When building lines for quotes.addLines, look up each SKU in skuIdMap and pass both productCode (the SKU string) and productId (the GUID). This triggers task template injection on the host.
  4. Fall back gracefully: if a SKU is not found in skuIdMap, send the line without productId. The line still appears in the quote as free-form text — no task injection, but no error.
let skuIdMap = {};

async function initBridge() {
  // Load all products and index by SKU (uppercased for case-insensitive lookup)
  const productResult = await window.pipeline.products.list({ page: 1, pageSize: 500 });
  const products = (productResult && productResult.items) ? productResult.items : [];
  products.forEach(function(p) {
    if (p.sku && p.id) {
      skuIdMap[p.sku.toUpperCase()] = p.id;
    }
  });
}

// Later, when building a line:
function resolveProduct(skuCode) {
  return {
    productCode: skuCode,
    productId: skuIdMap[skuCode.toUpperCase()] || null  // null = no task injection (still sends)
  };
}

DTO Shapes

// BridgeContextDto
interface BridgeContextDto {
  userName: string;
  userEmail: string;
  userRole: string;       // e.g. "H_Admin", "H_Field"
  tenantName: string;
  tenantId: string;
  cultureCode: string;   // e.g. "en-NZ"
}

// BridgeCustomerDto
interface BridgeCustomerDto {
  id: string;            // GUID
  name: string;
  email: string;
  phone: string;
}

// BridgeProductDto
interface BridgeProductDto {
  id: string;            // GUID
  sku: string;           // Human-readable product code (Stock_SKU.SKU)
  name: string;
  price: number;         // Retail sell price (Stock_SKU.Sell)
  unit: string;          // UOM token
}

// BridgeQuoteDto
interface BridgeQuoteDto {
  id: string;            // GUID
  ref: string;
  customerName: string;
  total: number;
  status: string;
  date: string;          // ISO 8601 datetime
}

// BridgeJobDto
interface BridgeJobDto {
  id: string;            // GUID
  ref: string;           // Human-readable job reference (e.g. "JOB-0042")
  customerName: string;
  customerIdGuid: string; // GUID
  status: string;
}

// BridgePagedResult<T>
interface BridgePagedResult<T> {
  items: T[];
  totalCount: number;
  page: number;
  pageSize: number;
}

// BridgeCreateQuoteRequest
interface BridgeCreateQuoteRequest {
  jobId?: string;
  customerName?: string;
  customerEmail?: string;
  customerPhone?: string;
  upsertByEmail: boolean;
  title?: string;
  quoteRef?: string;
  lines: BridgeQuoteLineRequest[];
}

// BridgeQuoteLineRequest (for quotes.create)
interface BridgeQuoteLineRequest {
  description?: string;
  productCode?: string;
  qty: number;
  unitPrice: number;
  taxRate: number;
}

// BridgeCreateQuoteResult
interface BridgeCreateQuoteResult {
  id: string;
  ref: string;
  total: number;
  url?: string;          // Relative deep-link to the created quote, if available
}

// BridgeAddLineDto (for quotes.addLines — quoting_extension only)
interface BridgeAddLineDto {
  description?: string;
  productCode?: string;
  productId?: string;    // GUID — triggers task template injection when set
  qty: number;
  unitPrice: number;
  costPrice?: number;    // When set: BUY=costPrice, SELL=unitPrice. When omitted: BUY=SELL=unitPrice
  taxRate: number;
  location?: string;     // Maps to FreeTextField2
  specification?: string; // Maps to FreeTextField
  unit?: string;         // "T_ITEM" | "T_SQUARE_METRE" | "T_LINEAR_METRE"
  groupKey?: string;     // Lines sharing the same groupKey get the same QuoteLine.GroupId
}