(function (root, factory) {
  "use strict";
  const api = factory();
  if (typeof module === "object" && module.exports) module.exports = api;
  if (root) root.JIANGLAI_QUOTE_ACCURACY = Object.freeze(api);
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
  "use strict";

  const SCHEMA_VERSION = 1;
  const SOURCE = "factory_settled_orders";
  const CONFIDENCE_RANK = Object.freeze({
    insufficient: 0,
    low: 1,
    medium: 2,
    high: 3
  });
  const CONFIDENCE_LABELS = Object.freeze({
    insufficient: "样本不足",
    low: "低",
    medium: "中",
    high: "高"
  });
  const SETTLED_STATUSES = new Set([
    "won", "paid", "settled", "completed", "closedwon", "closed_won",
    "已成交", "已回款", "已结算", "已完成", "成交", "回款完成", "结算完成"
  ]);
  const COST_LABELS = Object.freeze({
    material: "材料",
    printing: "印刷",
    labor: "人工",
    machine: "设备",
    operations: "工艺",
    overhead: "制造间接费",
    outsource: "外协",
    packaging: "包装",
    other: "其他成本",
    residual: "未分摊成本"
  });

  function finiteNumber(value) {
    if (typeof value === "string") value = value.trim().replace(/,/g, "");
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }

  function nonNegativeNumber(value) {
    const parsed = finiteNumber(value);
    return parsed !== null && parsed >= 0 ? parsed : null;
  }

  function firstNumber(object, names) {
    const source = object && typeof object === "object" ? object : {};
    for (const name of names) {
      const value = finiteNumber(source[name]);
      if (value !== null) return value;
    }
    return null;
  }

  function normalizedText(value) {
    return String(value === undefined || value === null ? "" : value)
      .trim()
      .replace(/\s+/g, " ");
  }

  function normalizeKey(value) {
    return normalizedText(value).toLocaleLowerCase();
  }

  function unique(values) {
    return [...new Set(values.filter(Boolean))];
  }

  function contextKeys(scope, source) {
    const item = source && typeof source === "object" ? source : {};
    const prefixes = {
      product: [
        ["id", item.productId ?? item.product_id ?? item.skuId ?? item.sku_id],
        ["name", item.productName ?? item.product_name ?? item.product ?? item.templateName],
        ["category", item.category ?? item.categoryName ?? item.industry]
      ],
      material: [
        ["id", item.materialId ?? item.material_id],
        ["name", item.materialName ?? item.material_name ?? item.material]
      ],
      process: [
        ["id", item.processId ?? item.process_id ?? item.operationId ?? item.operation_id],
        ["name", item.processName ?? item.process_name ?? item.operationName ?? item.operation_name ?? item.name]
      ]
    };
    return unique((prefixes[scope] || []).map(([prefix, value]) => {
      const key = normalizeKey(value);
      return key ? `${prefix}:${key}` : "";
    }));
  }

  function normalizedStatus(value) {
    return normalizeKey(value).replace(/[\s-]+/g, "_");
  }

  function isSettledSample(sample, options) {
    if (sample && sample.calibrationEligible === false) return false;
    if (sample && sample.calibrationEligible === true) return true;
    if (options && options.requireSettled === false) return true;
    if (sample && (sample.isSettled === true || sample.settled === true || sample.paid === true)) return true;
    const status = normalizedStatus(sample && (sample.status ?? sample.orderStatus ?? sample.paymentStatus));
    return SETTLED_STATUSES.has(status) || SETTLED_STATUSES.has(status.replace(/_/g, ""));
  }

  function costPair(source) {
    const item = source && typeof source === "object" ? source : {};
    const estimated = firstNumber(item, [
      "estimatedCost", "calculatedCost", "formulaCost", "originalCost", "baseCost", "predictedCost"
    ]);
    const actual = firstNumber(item, [
      "actualCost", "settledCost", "finalCost", "realizedCost", "realCost"
    ]);
    if (estimated !== null && actual !== null) return { estimated, actual };

    const estimatedCosts = item.estimatedCosts && typeof item.estimatedCosts === "object"
      ? item.estimatedCosts
      : item.costs && typeof item.costs === "object" ? item.costs : {};
    const actualCosts = item.actualCosts && typeof item.actualCosts === "object"
      ? item.actualCosts
      : item.settledCosts && typeof item.settledCosts === "object" ? item.settledCosts : {};
    return {
      estimated: firstNumber(estimatedCosts, ["total", "totalCost"]),
      actual: firstNumber(actualCosts, ["total", "totalCost"])
    };
  }

  function normalizedLineKind(value, fallbackName) {
    const raw = normalizeKey(value || fallbackName);
    if (["material", "materials", "paper", "材料", "纸张"].includes(raw)) return "material";
    if (["process", "operation", "operations", "finishing", "printing", "工艺", "印刷", "后道"].includes(raw)) return "process";
    return "cost";
  }

  function lineItemsFromSample(sample) {
    const source = sample && typeof sample === "object" ? sample : {};
    const result = [];
    const arrays = [source.lineItems, source.costItems, source.breakdown, source.operations];
    const selected = arrays.find(Array.isArray);
    if (selected) {
      selected.forEach((item, index) => {
        const pair = costPair(item);
        result.push({
          ...item,
          id: item && (item.id ?? item.lineId) || `line-${index + 1}`,
          kind: normalizedLineKind(item && (item.kind ?? item.type ?? item.category), item && item.name),
          estimatedCost: pair.estimated,
          actualCost: pair.actual
        });
      });
    }

    const estimated = source.estimatedBreakdown && typeof source.estimatedBreakdown === "object"
      ? source.estimatedBreakdown
      : null;
    const actual = source.actualBreakdown && typeof source.actualBreakdown === "object"
      ? source.actualBreakdown
      : null;
    if (!selected && estimated && actual) {
      Object.keys(estimated).forEach(name => {
        if (!Object.prototype.hasOwnProperty.call(actual, name)) return;
        result.push({
          id: `breakdown-${name}`,
          name,
          kind: normalizedLineKind(name, name),
          estimatedCost: finiteNumber(estimated[name]),
          actualCost: finiteNumber(actual[name])
        });
      });
    }
    return result;
  }

  function percentile(sortedValues, probability) {
    if (!sortedValues.length) return null;
    if (sortedValues.length === 1) return sortedValues[0];
    const position = Math.max(0, Math.min(1, probability)) * (sortedValues.length - 1);
    const lower = Math.floor(position);
    const upper = Math.ceil(position);
    if (lower === upper) return sortedValues[lower];
    const fraction = position - lower;
    return sortedValues[lower] + (sortedValues[upper] - sortedValues[lower]) * fraction;
  }

  function median(values) {
    const sorted = values.slice().sort((left, right) => left - right);
    return percentile(sorted, 0.5);
  }

  function robustObservations(observations) {
    const valid = observations.filter(item => Number.isFinite(item.ratio) && item.ratio > 0);
    if (valid.length < 4) return { accepted: valid, rejected: [] };
    const ratios = valid.map(item => item.ratio).sort((left, right) => left - right);
    const center = median(ratios);
    const deviations = ratios.map(value => Math.abs(value - center));
    const mad = median(deviations);
    const q1 = percentile(ratios, 0.25);
    const q3 = percentile(ratios, 0.75);
    const iqr = q3 - q1;
    const tolerance = mad > 1e-12
      ? Math.max(3.5 * mad / 0.6745, Math.abs(center) * 0.03)
      : Math.max(3 * iqr, Math.abs(center) * 0.10, 1e-9);
    const accepted = [];
    const rejected = [];
    valid.forEach(item => {
      if (Math.abs(item.ratio - center) <= tolerance) accepted.push(item);
      else rejected.push({ ...item, rejectionReason: "偏离同层历史中位数，已按稳健规则剔除" });
    });
    return { accepted, rejected };
  }

  function confidenceFor(ratios, rawCount, rejectedCount) {
    const sorted = ratios.slice().sort((left, right) => left - right);
    const center = median(sorted);
    const absoluteDeviations = sorted.map(value => Math.abs(value - center));
    const relativeMad = center > 0 ? median(absoluteDeviations) / center : null;
    const rejectedRate = rawCount > 0 ? rejectedCount / rawCount : 0;
    let level = "insufficient";
    if (sorted.length >= 12 && relativeMad <= 0.08 && rejectedRate <= 0.20) level = "high";
    else if (sorted.length >= 5 && relativeMad <= 0.20 && rejectedRate <= 0.35) level = "medium";
    else if (sorted.length >= 2) level = "low";
    const intervalLow = percentile(sorted, sorted.length >= 5 ? 0.10 : 0);
    const intervalHigh = percentile(sorted, sorted.length >= 5 ? 0.90 : 1);
    return Object.freeze({
      level,
      label: CONFIDENCE_LABELS[level],
      relativeMad,
      rejectedRate,
      interval: Object.freeze({
        low: intervalLow,
        high: intervalHigh,
        method: sorted.length >= 5 ? "empirical_p10_p90" : "observed_range"
      })
    });
  }

  function calibrationModel(scope, key, observations) {
    const robust = robustObservations(observations);
    const acceptedRatios = robust.accepted.map(item => item.ratio);
    const factor = median(acceptedRatios);
    const confidence = confidenceFor(acceptedRatios, observations.length, robust.rejected.length);
    return Object.freeze({
      scope,
      key,
      factor,
      rawCount: observations.length,
      acceptedCount: robust.accepted.length,
      rejectedCount: robust.rejected.length,
      confidence,
      acceptedSampleIds: Object.freeze(unique(robust.accepted.map(item => item.sampleId))),
      rejectedSamples: Object.freeze(robust.rejected.map(item => Object.freeze({
        sampleId: item.sampleId,
        ratio: item.ratio,
        reason: item.rejectionReason
      }))),
      source: SOURCE
    });
  }

  function observation(scope, key, pair, sampleId, lineId) {
    if (!pair || !(pair.estimated > 0) || !(pair.actual >= 0)) return null;
    const ratio = pair.actual / pair.estimated;
    if (!Number.isFinite(ratio) || !(ratio > 0)) return null;
    return { scope, key, ratio, sampleId, lineId: lineId || "" };
  }

  function addObservation(groups, item) {
    if (!item) return;
    const groupKey = `${item.scope}\u0000${item.key}`;
    if (!groups.has(groupKey)) groups.set(groupKey, []);
    groups.get(groupKey).push(item);
  }

  function buildCalibration(samples, options) {
    const input = Array.isArray(samples) ? samples : [];
    const settings = options && typeof options === "object" ? options : {};
    const groups = new Map();
    const rejectedSamples = [];
    const usedSampleIds = new Set();
    let eligibleSamples = 0;

    input.forEach((sample, sampleIndex) => {
      const item = sample && typeof sample === "object" ? sample : {};
      const sampleId = normalizedText(item.id ?? item.orderId ?? item.quoteId) || `sample-${sampleIndex + 1}`;
      if (!isSettledSample(item, settings)) {
        rejectedSamples.push(Object.freeze({ sampleId, reason: "不是已成交/已结算样本" }));
        return;
      }
      eligibleSamples += 1;
      let observationCount = 0;
      const totalPair = costPair(item);
      const globalObservation = observation("global", "all", totalPair, sampleId);
      if (globalObservation) {
        addObservation(groups, globalObservation);
        contextKeys("product", item).forEach(key => {
          addObservation(groups, observation("product", key, totalPair, sampleId));
        });
        observationCount += 1;
      }

      lineItemsFromSample(item).forEach((line, lineIndex) => {
        const pair = { estimated: line.estimatedCost, actual: line.actualCost };
        if (line.kind === "material") {
          const keys = contextKeys("material", { ...item, ...line });
          keys.forEach(key => addObservation(groups, observation("material", key, pair, sampleId, line.id || `line-${lineIndex + 1}`)));
          if (keys.length && pair.estimated > 0 && pair.actual >= 0) observationCount += 1;
        } else if (line.kind === "process") {
          const keys = contextKeys("process", { ...item, ...line });
          keys.forEach(key => addObservation(groups, observation("process", key, pair, sampleId, line.id || `line-${lineIndex + 1}`)));
          if (keys.length && pair.estimated > 0 && pair.actual >= 0) observationCount += 1;
        }
      });

      if (observationCount) usedSampleIds.add(sampleId);
      else rejectedSamples.push(Object.freeze({
        sampleId,
        reason: "缺少厂家实际成本与公式估算成本的明确配对"
      }));
    });

    const modelMaps = {
      product: Object.create(null),
      material: Object.create(null),
      process: Object.create(null)
    };
    let global = null;
    let outlierCount = 0;
    groups.forEach((observations, combinedKey) => {
      const separator = combinedKey.indexOf("\u0000");
      const scope = combinedKey.slice(0, separator);
      const key = combinedKey.slice(separator + 1);
      const model = calibrationModel(scope, key, observations);
      outlierCount += model.rejectedCount;
      if (scope === "global") global = model;
      else modelMaps[scope][key] = model;
    });

    return Object.freeze({
      schemaVersion: SCHEMA_VERSION,
      source: SOURCE,
      doesNotModifyFactoryPrices: true,
      generatedAt: normalizedText(settings.generatedAt) || new Date().toISOString(),
      models: Object.freeze({
        global,
        product: Object.freeze(modelMaps.product),
        material: Object.freeze(modelMaps.material),
        process: Object.freeze(modelMaps.process)
      }),
      diagnostics: Object.freeze({
        inputSamples: input.length,
        eligibleSamples,
        usedSamples: usedSampleIds.size,
        rejectedSamples: Object.freeze(rejectedSamples),
        outlierCount
      })
    });
  }

  function quoteCostItems(quote) {
    const source = quote && typeof quote === "object" ? quote : {};
    const explicit = [source.costItems, source.items, source.breakdown].find(Array.isArray);
    if (explicit) {
      return explicit.map((item, index) => Object.freeze({
        id: normalizedText(item.id ?? item.lineId) || `item-${index + 1}`,
        label: normalizedText(item.label ?? item.name) || `成本项${index + 1}`,
        kind: normalizedLineKind(item.kind ?? item.type, item.name),
        originalCost: nonNegativeNumber(item.originalCost ?? item.cost ?? item.amount ?? item.estimatedCost),
        calibratable: item.calibratable !== false,
        context: Object.freeze({ ...source, ...item })
      })).filter(item => item.originalCost !== null);
    }

    const costs = source.costs && typeof source.costs === "object" ? source.costs : {};
    const operationItems = Array.isArray(source.operationItems) ? source.operationItems : [];
    const skip = new Set(["total", "totalCost"]);
    if (operationItems.length) skip.add("operations");
    const result = Object.keys(costs).filter(key => !skip.has(key)).map(key => Object.freeze({
      id: `cost-${key}`,
      label: COST_LABELS[key] || key,
      kind: key === "material" ? "material" : key === "printing" || key === "operations" ? "process" : "cost",
      originalCost: nonNegativeNumber(costs[key]),
      calibratable: !["tax", "profit", "selling", "sellingPrice", "discount"].includes(key),
      context: Object.freeze({ ...source, name: COST_LABELS[key] || key, kind: key })
    })).filter(item => item.originalCost !== null);

    operationItems.forEach((operation, index) => {
      const originalCost = nonNegativeNumber(operation.total ?? operation.cost ?? operation.amount);
      if (originalCost === null) return;
      result.push(Object.freeze({
        id: normalizedText(operation.id) || `operation-${index + 1}`,
        label: normalizedText(operation.name) || `工艺${index + 1}`,
        kind: "process",
        originalCost,
        calibratable: operation.calibratable !== false,
        context: Object.freeze({ ...source, ...operation, processId: operation.id, processName: operation.name })
      }));
    });

    const explicitTotal = firstNumber(source, ["originalTotalCost", "totalCost", "costTotal"])
      ?? firstNumber(costs, ["total", "totalCost"]);
    const itemTotal = result.reduce((sum, item) => sum + item.originalCost, 0);
    if (explicitTotal !== null && Math.abs(explicitTotal - itemTotal) > 1e-7) {
      result.push(Object.freeze({
        id: "cost-residual",
        label: COST_LABELS.residual,
        kind: "cost",
        originalCost: explicitTotal - itemTotal,
        calibratable: false,
        context: Object.freeze({ ...source, kind: "residual" })
      }));
    }
    return result;
  }

  function modelCandidates(item, calibration) {
    const models = calibration && calibration.models ? calibration.models : {};
    const result = [];
    const push = (scope, keys) => {
      const collection = models[scope] || {};
      keys.forEach(key => {
        if (collection[key]) result.push(collection[key]);
      });
    };
    if (item.kind === "process") push("process", contextKeys("process", item.context));
    if (item.kind === "material") push("material", contextKeys("material", item.context));
    push("product", contextKeys("product", item.context));
    if (models.global) result.push(models.global);
    return result;
  }

  function qualifyingModel(item, calibration, minimumConfidence) {
    const requiredRank = CONFIDENCE_RANK[minimumConfidence] ?? CONFIDENCE_RANK.low;
    return modelCandidates(item, calibration).find(model => {
      const level = model && model.confidence && model.confidence.level || "insufficient";
      return Number.isFinite(model.factor) && model.factor > 0
        && (CONFIDENCE_RANK[level] ?? 0) >= requiredRank;
    }) || null;
  }

  function calibrateQuote(quote, calibration, options) {
    const settings = options && typeof options === "object" ? options : {};
    const minimumConfidence = String(settings.minimumConfidence || "low");
    if (!Object.prototype.hasOwnProperty.call(CONFIDENCE_RANK, minimumConfidence)) {
      throw new Error(`不支持的最低置信等级：${minimumConfidence}`);
    }
    const items = quoteCostItems(quote);
    if (!items.length) throw new Error("报价缺少可追溯的成本明细");
    const trace = items.map(item => {
      const model = item.calibratable ? qualifyingModel(item, calibration, minimumConfidence) : null;
      const factor = model ? model.factor : 1;
      const calibratedCost = item.originalCost * factor;
      return Object.freeze({
        id: item.id,
        label: item.label,
        kind: item.kind,
        originalCost: item.originalCost,
        factor,
        adjustment: calibratedCost - item.originalCost,
        calibratedCost,
        applied: Boolean(model),
        scope: model ? model.scope : "none",
        scopeKey: model ? model.key : "",
        confidence: model ? model.confidence : null,
        sampleCount: model ? model.acceptedCount : 0,
        source: model ? SOURCE : "factory_price_unchanged"
      });
    });
    const beforeTotal = trace.reduce((sum, item) => sum + item.originalCost, 0);
    const afterTotal = trace.reduce((sum, item) => sum + item.calibratedCost, 0);
    const difference = afterTotal - beforeTotal;
    return Object.freeze({
      schemaVersion: SCHEMA_VERSION,
      source: SOURCE,
      doesNotModifyFactoryPrices: true,
      original: Object.freeze({
        totalCost: beforeTotal,
        items: Object.freeze(trace.map(item => Object.freeze({
          id: item.id,
          label: item.label,
          cost: item.originalCost
        })))
      }),
      calibrated: Object.freeze({
        totalCost: afterTotal,
        items: Object.freeze(trace.map(item => Object.freeze({
          id: item.id,
          label: item.label,
          cost: item.calibratedCost
        })))
      }),
      comparison: Object.freeze({
        beforeTotal,
        afterTotal,
        difference,
        percent: beforeTotal > 0 ? difference / beforeTotal : 0
      }),
      trace: Object.freeze(trace),
      appliedItemCount: trace.filter(item => item.applied).length,
      warning: "校准只参考本厂已成交订单的实际成本，不修改材料价、工艺价或厂家原始报价。"
    });
  }

  return Object.freeze({
    schemaVersion: SCHEMA_VERSION,
    source: SOURCE,
    confidenceRanks: CONFIDENCE_RANK,
    normalizeKey,
    isSettledSample,
    robustFactor(observations) {
      const normalized = (Array.isArray(observations) ? observations : []).map((value, index) => ({
        ratio: finiteNumber(value),
        sampleId: `sample-${index + 1}`
      }));
      const robust = robustObservations(normalized);
      return Object.freeze({
        factor: median(robust.accepted.map(item => item.ratio)),
        acceptedCount: robust.accepted.length,
        rejectedCount: robust.rejected.length
      });
    },
    buildCalibration,
    quoteCostItems,
    calibrateQuote
  });
});

(function (root) {
  "use strict";

  function resolveCustomerPrice(profiles, customer, standardUnitPrice, today = new Date().toISOString().slice(0, 10)) {
    const standard = Number(standardUnitPrice || 0);
    const profile = (Array.isArray(profiles) ? profiles : []).find(item => item?.company === customer);
    const pricing = profile?.pricing && typeof profile.pricing === "object" ? profile.pricing : {};
    const activeFrom = String(pricing.agreement_valid_from || "");
    const activeUntil = String(pricing.agreement_valid_until || "");
    const agreementActive = (!activeFrom || today >= activeFrom) && (!activeUntil || today <= activeUntil);
    const candidates = [
      ["manual_price", "本次手工价", pricing.manual_price, true],
      ["agreement_price", "客户协议价", pricing.agreement_price, agreementActive],
      ["history_price", "最近成交价", pricing.history_price, true],
      ["level_price", "客户等级价", pricing.level_price, true],
      ["standard_price", "系统标准报价", standard, true]
    ];
    for (const [source, sourceLabel, rawPrice, enabled] of candidates) {
      const price = Number(rawPrice);
      if (enabled && Number.isFinite(price) && price > 0) {
        return { price, source, sourceLabel, customized: source !== "standard_price", agreementActive };
      }
    }
    return { price: standard, source: "standard_price", sourceLabel: "系统标准报价", customized: false, agreementActive };
  }

  function line({ category, name, usageUnit, unitPrice, usage, subtotal, source = "", detail = "" }) {
    const numericUnitPrice = Number(unitPrice || 0);
    const numericUsage = Number(usage || 0);
    const numericSubtotal = Number(subtotal || 0);
    return {
      category: String(category || "").slice(0, 20),
      name: String(name || "").slice(0, 120),
      pricingUnit: `元/${String(usageUnit || "单").slice(0, 20)}`,
      unitPrice: Number.isFinite(numericUnitPrice) ? numericUnitPrice : 0,
      usage: Number.isFinite(numericUsage) ? numericUsage : 0,
      usageUnit: String(usageUnit || "单").slice(0, 20),
      subtotal: Number.isFinite(numericSubtotal) ? numericSubtotal : 0,
      source: String(source || "").slice(0, 80),
      detail: String(detail || "").slice(0, 240)
    };
  }

  function build(context) {
    const c = context || {};
    if (c.costMode === "printing" && c.formulaBreakdown?.formulaResult?.outsourced) {
      const result = c.formulaBreakdown.formulaResult;
      return [line({
        category: "外发",
        name: result.supplier?.name || "外发成品采购",
        usageUnit: "个",
        unitPrice: Number(result.costs?.outsource || 0) / Math.max(Number(c.qty || 1), 1),
        usage: Number(c.qty || 0),
        subtotal: Number(result.costs?.outsource || 0),
        source: "供应商同规格实时报价",
        detail: [result.supplier?.quotedAt, result.supplier?.scope].filter(Boolean).join(" · ")
      })];
    }
    if (Array.isArray(c.formulaBreakdown?.detailLineItems)) {
      const detailed = c.formulaBreakdown.detailLineItems.map(item => line({
        category:item.category, name:item.name, usageUnit:item.usageUnit,
        unitPrice:item.unitPrice, usage:item.usage, subtotal:item.subtotal,
        source:item.source, detail:item.detail
      }));
      (c.processCosts || []).forEach(item => detailed.push(line({
        category:"工艺", name:item.name,
        usageUnit:item.basis ? (c.operationBasisLabels[item.basis] || item.basis) : "个",
        unitPrice:Number(item.appliedUnitRate ?? item.unitPrice ?? item.price ?? 0),
        usage:Number(item.basisQuantity ?? c.qty), subtotal:Number(item.total || 0),
        source:item.source || "本厂工艺价格",
        detail:item.basis ? c.operationDescription(item, c.qty) : ""
      })));
      return detailed;
    }
    const operationItems = c.formulaBreakdown?.operationItems || [];
    const materialUsageUnit = c.costMode === "printing"
      ? "kg"
      : c.materialConversionMode
        ? String(c.materialConversion?.usageUnit || c.materialConversion?.unit || "用量").replace(/^元\//, "")
        : "个";
    const materialUsage = c.costMode === "printing"
      ? Number(c.formulaBreakdown?.formulaResult?.paper?.weightKg || 0)
      : c.materialConversionMode ? Number(c.materialConversion?.totalUsage || 0) : c.qty;
    const materialSourceUnitPrice = c.costMode === "printing"
      ? Number(c.formulaBreakdown?.formulaInput?.materialPricePerKg || 0)
      : c.materialConversionMode ? Number(c.materialConversion?.price || 0) : c.effectiveMaterialUnitCost;
    const items = [line({
      category: "材料",
      name: c.materialConversion?.source?.name || c.materialName,
      usageUnit: materialUsageUnit,
      unitPrice: materialSourceUnitPrice,
      usage: materialUsage,
      subtotal: c.materialCost,
      source: c.materialConversionMode ? "本厂材料采购价" : c.costMode === "printing" ? "精细印刷材料价" : "本厂材料成本",
      detail: c.costMode === "printing" ? c.formulaBreakdown?.materialLabel : ""
    })];
    if (c.costMode === "printing" && c.printing > 0) {
      const printTier = c.formulaBreakdown?.formulaResult?.printTier;
      if (printTier && Number(printTier.total || 0) > 0) {
        items.push(line({
          category: "印刷",
          name: `${c.printRoute || "印刷"} · ${c.formulaBreakdown?.formulaResult?.printSheetFormat?.label || "开数价"}`,
          usageUnit: "生产印张",
          unitPrice: Number(printTier.unitPrice || 0),
          usage: Number(printTier.basisQuantity || 0),
          subtotal: Number(printTier.total || 0),
          source: "本厂印刷开数价",
          detail: Number(printTier.minimumCharge || 0) > 0 ? `最低收费 ¥${Number(printTier.minimumCharge).toFixed(2)}` : ""
        }));
      }
      const otherPrinting = c.printing - Number(printTier?.total || 0);
      if (otherPrinting > .000001) {
        const impressions = Number(c.formulaBreakdown?.formulaResult?.impressions || 0);
        items.push(line({
          category: "印刷",
          name: printTier ? "印版及其他印刷费" : `${c.printRoute || "印刷"}印工`,
          usageUnit: impressions > 0 && !printTier ? "印次" : "单",
          unitPrice: impressions > 0 && !printTier ? otherPrinting / impressions : otherPrinting,
          usage: impressions > 0 && !printTier ? impressions : 1,
          subtotal: otherPrinting,
          source: "精细印刷公式",
          detail: c.formulaBreakdown?.basis || ""
        }));
      }
    } else if (c.catalogPrintPrices?.length) {
      c.catalogPrintPrices.forEach(item => {
        const calculation = c.operationCalculation(item, c.qty);
        items.push(line({
          category: "印刷", name: item.name,
          usageUnit: c.operationBasisLabels[item.basis] || item.basis || "件",
          unitPrice: calculation.unitRate, usage: calculation.basisQuantity,
          subtotal: calculation.total, source: "本厂印刷价格",
          detail: c.operationDescription(item, c.qty)
        }));
      });
    } else if (c.printing > 0) {
      items.push(line({
        category: "印刷", name: "基础印刷/加工",
        usageUnit: c.costMode === "industry" ? "单" : "个",
        unitPrice: c.costMode === "industry" ? c.printing : c.printing / Math.max(c.qty, 1),
        usage: c.costMode === "industry" ? 1 : c.qty, subtotal: c.printing,
        source: "本厂基础印刷价", detail: c.printingBasisText
      }));
    }
    operationItems.forEach(item => items.push(line({
      category: "工艺", name: item.name,
      usageUnit: c.operationBasisLabels[item.basis] || item.basis || "件",
      unitPrice: Number(item.unitRate || 0), usage: Number(item.basisQuantity || 0),
      subtotal: Number(item.total || 0), source: "本厂工艺价格",
      detail: item.sheetFormat?.label || (item.laminationSideCount ? `${item.laminationSideCount}面` : "")
    })));
    (c.processCosts || []).forEach(item => items.push(line({
      category: "工艺", name: item.name,
      usageUnit: item.basis ? (c.operationBasisLabels[item.basis] || item.basis) : "个",
      unitPrice: Number(item.appliedUnitRate ?? item.unitPrice ?? item.price ?? 0),
      usage: Number(item.basisQuantity ?? c.qty), subtotal: Number(item.total || 0),
      source: item.source || "本厂工艺价格",
      detail: item.basis ? c.operationDescription(item, c.qty) : ""
    })));
    return items;
  }

  function escapeHtml(text) {
    return String(text || "").replace(/[&<>"']/g, char => ({
      "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"
    }[char]));
  }

  function render(documentObject, items = [], record = null) {
    const box = documentObject?.getElementById("quoteCostLineItems");
    if (!box) return;
    const rows = Array.isArray(items) ? items.filter(item => item && typeof item === "object") : [];
    if (!rows.length) {
      box.innerHTML = "";
      return;
    }
    const priceSource = record?.customerPriceSourceLabel ? `；对客单价来源：${record.customerPriceSourceLabel}` : "";
    const numberText = (value, digits = 4) => Number(value || 0).toFixed(digits).replace(/\.?0+$/, "") || "0";
    box.innerHTML = `<div class="quote-cost-lines">
      <div class="quote-cost-lines-head"><strong>材料 / 印刷 / 工艺逐项计价</strong><span>单价 × 用量 → 小计${escapeHtml(priceSource)}</span></div>
      <div class="quote-cost-lines-scroll"><table>
        <thead><tr><th>类别</th><th>项目</th><th>计价单位</th><th>单价</th><th>用量</th><th>小计</th></tr></thead>
        <tbody>${rows.map(item => `<tr>
          <td class="quote-cost-line-category">${escapeHtml(item.category || "成本")}</td>
          <td>${escapeHtml(item.name || "未命名")}${item.source || item.detail ? `<span class="quote-cost-line-note">${escapeHtml([item.source, item.detail].filter(Boolean).join(" · "))}</span>` : ""}</td>
          <td>${escapeHtml(item.pricingUnit || `元/${item.usageUnit || "单"}`)}</td>
          <td>¥${numberText(item.unitPrice)}</td>
          <td>${numberText(item.usage, 3)} ${escapeHtml(item.usageUnit || "")}</td>
          <td>¥${Number(item.subtotal || 0).toFixed(2)}</td>
        </tr>`).join("")}</tbody>
      </table></div>
    </div>`;
  }

  if (root) root.JIANGLAI_QUOTE_COST_DETAILS = Object.freeze({
    resolveCustomerPrice,
    build,
    render
  });
})(typeof globalThis !== "undefined" ? globalThis : this);
