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
}

// BridgeTaskTemplateDto (from taskTemplates.list)
interface BridgeTaskTemplateDto {
  id: string;            // GUID — pass as LinkedItemId on a "task" line
  code: string;          // Stable code (e.g. "T_DEFAULT_INSTALLATION_TASK")
  name: string;
}

// BridgeCostItemDto (from costItems.list)
interface BridgeCostItemDto {
  id: string;            // GUID — pass as LinkedItemId on a "cost" line
  code: string;          // Stable code (e.g. "COS-TRAVEL-TIME")
  name: string;
  type: string;          // T_Type token (e.g. "T_FINANCIAL", "T_TRAVEL")
}

// 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;
  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

  // -- Line linking (preferred over productId) --
  lineType?: string;        // "product" | "task" | "cost"  (defaults to "product")
  linkedItemId?: string;    // GUID into Stock_SKU / G_Task_Templates / COB_Items based on lineType.
                            // Replaces productId for new tools. Validation: rejects the whole batch
                            // if the GUID does not resolve to an active row in the indicated table.

  // -- Categorical grouping --
  section?: string;         // Free-text section label (e.g. "Blinds", "Installation", "Extras").
                            // Persisted on QuoteLine.Section. Host UI groups lines by section.
                            // Max 100 chars; trimmed; null/empty renders under "Ungrouped".

  // -- Deprecated (still supported) --
  /** @deprecated Use linkedItemId with lineType="product" instead. Retained for back-compat
   *  with Ziptrak v2.1.2 and earlier. New tools should not set this field. */
  productId?: string;
}

Three line kinds (lineType)

lineTypelinkedItemId resolves toBehaviour
"product" (default)Stock_SKU.IdGuidIf the linked SKU has a task sequence configured, the host auto-injects its task template lines (e.g. installation tasks).
"task"G_Task_Templates.IdGuidThe line is the task itself. No auto-injection. Use for explicit installation, removal, configuration tasks.
"cost"COB_Items.IdGuidThe line is a cost-of-business item (shipping, travel, disposal, site visit, etc.). No auto-injection.
When lineType is omitted (or set to "product") and linkedItemId is null, the host falls back to the legacy productId field — keeping Ziptrak v2.1.2 and earlier extensions working unchanged.

Section grouping

Quote lines are visually grouped by section in both the operator’s quote editor and the customer-facing quote preview. Use a small set of stable labels per tool — e.g. for an indoor blinds extension: "Blinds", "Motorisation", "Installation", "Removal", "Extras". Lines with no section render in a trailing “Ungrouped” block.