Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
import type { OffersRecord, CostsTimeRecord, CostsMaterialRecord, MenusRecord, MenuTiersRecord, TiersRecord } from "@/pocketbase-types";
import { getDb } from "@/dataAccess/getDb";
import calculatePrice, { type FormulaStep } from "@/framework/calculatePrice";
import { convertToPrefs, type ConvertedValue } from "@/framework/currencies";

export interface EnrichedCostsMaterial extends CostsMaterialRecord {
  quantityConverted: ConvertedValue | null;
}

export interface EnrichedOffer extends Omit<OffersRecord, "costsTime" | "costsMaterial"> {
  costsTime: CostsTimeRecord[];
  costsMaterial: EnrichedCostsMaterial[];
  menuName: string | null;
  menuTier: string | null;
  finalPrice: number | null;
  finalPriceConverted: ConvertedValue | null;
  formulaSteps: FormulaStep[] | null;
  derivedVars: Record<string, number> | null;
  derivedVarsConverted: Record<string, ConvertedValue> | null;
}

export interface PriceListData {
  offers: EnrichedOffer[];
  total: number;
}

export interface LoadPriceListOptions {
  formulaFactory?: () => any;
  limit?: number;
}

export async function loadPriceList(options: LoadPriceListOptions = {}): Promise<PriceListData> {
  const { formulaFactory, limit = 25 } = options;
  const db = await getDb();

  // Load all data in parallel
  const [offers, costsTime, costsMaterial, menus, menuTiers, tiers] = await Promise.all([
    db.selectAll("offers"),
    db.selectAll("costsTime"),
    db.selectAll("costsMaterial"),
    db.selectAll("menus"),
    db.selectAll("menuTiers"),
    db.selectAll("tiers"),
  ]);

  // Build lookup maps for O(1) access
  const timeMap = new Map<string, CostsTimeRecord>();
  for (const cost of costsTime || []) {
    timeMap.set(cost.id, cost);
  }

  const materialMap = new Map<string, CostsMaterialRecord>();
  for (const cost of costsMaterial || []) {
    materialMap.set(cost.id, cost);
  }

  const menusMap = new Map<string, MenusRecord>();
  for (const menu of menus || []) {
    menusMap.set(menu.id, menu);
  }

  const tiersMap = new Map<string, TiersRecord>();
  for (const tier of tiers || []) {
    tiersMap.set(tier.id, tier);
  }

  // Parse JSON string or return array as-is (defined early for use in lookups)
  function parseIds(value: unknown): string[] {
    if (!value) return [];
    if (Array.isArray(value)) return value;
    if (typeof value === "string") {
      try {
        return JSON.parse(value);
      } catch {
        return [];
      }
    }
    return [];
  }

  // Build reverse lookup: offer ID -> menuTier
  // offers field may be an array of offer IDs
  const offerToMenuTier = new Map<string, MenuTiersRecord>();
  for (const mt of menuTiers || []) {
    const offerIds = parseIds(mt.offers);
    for (const offerId of offerIds) {
      offerToMenuTier.set(offerId, mt);
    }
  }

  // Get total count and apply limit
  const allOffers = offers || [];
  const total = allOffers.length;
  const limitedOffers = allOffers.slice(0, limit);

  // Enrich offers with resolved cost objects and menu info
  const enrichedOffers: EnrichedOffer[] = await Promise.all(
    limitedOffers.map(async (offer: OffersRecord) => {
      const timeIds = parseIds(offer.costsTime);
      const materialIds = parseIds(offer.costsMaterial);

      // Find menu and tier info via menuTiers
      const menuTierRecord = offerToMenuTier.get(offer.id);
      // menus is an array - take the first one
      const menuIds = menuTierRecord ? parseIds(menuTierRecord.menus) : [];
      const menu = menuIds.length > 0 ? menusMap.get(menuIds[0]) : null;
      // tiers may be a single ID or need parsing
      const tierIds = menuTierRecord ? parseIds(menuTierRecord.tiers) : [];
      const tier = tierIds.length > 0 ? tiersMap.get(tierIds[0]) : null;

      // Resolve cost arrays
      const resolvedCostsTime = timeIds
        .map((id) => timeMap.get(id))
        .filter((c): c is CostsTimeRecord => c !== undefined);
      const resolvedCostsMaterialRaw = materialIds
        .map((id) => materialMap.get(id))
        .filter((c): c is CostsMaterialRecord => c !== undefined);

      // Convert costsMaterial quantities to user's preferred currency
      const resolvedCostsMaterial: EnrichedCostsMaterial[] = await Promise.all(
        resolvedCostsMaterialRaw.map(async (cost) => ({
          ...cost,
          quantityConverted: cost.quantity ? await convertToPrefs(Number(cost.quantity)) : null,
        }))
      );

      // Calculate price if formula provided
      let finalPrice: number | null = null;
      let finalPriceConverted: ConvertedValue | null = null;
      let formulaSteps: FormulaStep[] | null = null;
      let derivedVars: Record<string, number> | null = null;
      let derivedVarsConverted: Record<string, ConvertedValue> | null = null;

      if (formulaFactory) {
        const formula = formulaFactory();
        const result = await calculatePrice(resolvedCostsMaterial, resolvedCostsTime, formula);
        finalPrice = result.finalPrice;
        finalPriceConverted = result.finalPriceConverted;
        formulaSteps = result.formulaSteps;
        derivedVars = result.derivedVars;
        derivedVarsConverted = result.derivedVarsConverted;
      }

      return {
        ...offer,
        costsTime: resolvedCostsTime,
        costsMaterial: resolvedCostsMaterial,
        menuName: menu?.refId || null,
        menuTier: tier?.refId || null,
        finalPrice,
        finalPriceConverted,
        formulaSteps,
        derivedVars,
        derivedVarsConverted,
      };
    })
  );

  return {
    offers: enrichedOffers,
    total,
  };
}