# BOM Change Analyzer — Compare Parts & Prices (`synthex-labs/synthex-ai-factory`) Actor

Compare two JSON bills of materials. Detect added and removed parts, quantity, unit price and description changes. Export a JSON report and change dataset.

- **URL**: https://apify.com/synthex-labs/synthex-ai-factory.md
- **Developed by:** [Dawid](https://apify.com/synthex-labs) (community)
- **Categories:** Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 25.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.01 / actor start

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## BOM Change Analyzer — CSV, Excel & Cost Analysis

**Compare Engineering BOM Revisions, Track Component Changes and Analyze Cost Differences.**

Compare two flat bills of materials from uploaded CSV/XLSX files or JSON arrays. Find added, removed and modified components; review quantity and unit-price effects; download JSON, CSV and Excel reports. No AI service, ERP connection or external API key is required.

### Quick Start

1. Upload **Old BOM file** and **New BOM file** in Input. CSV and XLSX may be mixed.
2. Leave the alternative JSON and pasted CSV fields unset. Select an Excel sheet if the workbook has multiple sheets.
3. If needed, map column names and set a common currency such as `EUR`.
4. Run the Actor. Download **Full report (Excel)**, **Parts report (CSV)**, **Summary (CSV)** or the complete JSON report from Output.

For the JSON API, keep sending `oldBom` and `newBom`. No migration is required:

```json
{
  "oldBom": [
    {"part_number": "A-100", "quantity": 2, "unit_price": "10.00"},
    {"part_number": "B-200", "quantity": 1, "unit_price": "5.00"}
  ],
  "newBom": [
    {"part_number": "A-100", "quantity": 3, "unit_price": "12.00"},
    {"part_number": "C-300", "quantity": 3, "unit_price": "2.50"}
  ],
  "currency": "EUR"
}
```

Expected: one added, one removed, one modified part. Old cost **25.00 EUR**, new cost **43.50 EUR**, increase **18.50 EUR (74%)**. A-100 contributes +16 EUR: +10 from quantity and +6 from price. C-300 adds 7.50 EUR and removing B-200 saves 5 EUR.

### Input sources

Supply exactly one source **per side**:

| Source | Old field | New field | Format |
| --- | --- | --- | --- |
| Native uploaded file | `oldFile` | `newFile` | File URL string from the Apify upload widget; a one-element URL array is also accepted. |
| Existing JSON API | `oldBom` | `newBom` | Parts array; empty arrays supported. |
| Pasted CSV | `oldCsv` | `newCsv` | UTF-8 CSV text including a header. |

Source requirements are validated by the Actor, because JSON arrays and uploaded files are alternatives. The Input schema intentionally does not mark both JSON arrays as required. Missing or conflicting sources fail with a specific error. Start fees apply to runtime validation failures.

Uploaded files must be accessible HTTPS Apify key-value-store records or S3 objects, optionally with a signed URL. Redirects, credentials in URLs and arbitrary external hosts are not accepted. No account token is attached to downloads. File formats are detected automatically, or use `oldFormat` / `newFormat` (`auto`, `csv`, `xlsx`). Legacy `.xls` is unsupported.

### Columns and validation

Each part requires a nonempty **text** `part_number`. Matching trims surrounding whitespace and ignores Unicode case. Duplicates after normalization are rejected. Numeric Excel identifiers must be converted to text before upload to preserve leading zeroes.

Optional fields: `quantity`, `unit_price`, `description`, `currency`, `unit`. Quantity and price accept nonnegative finite numbers or decimal numeric strings. Booleans, negative values and non-finite numbers are rejected. Empty CSV/Excel cells become missing values, never zero. Formulas, Excel error cells, macros and external workbook links are rejected: upload calculated values only.

Common headers are detected, including `Part Number`, `Part No`, `PN`, `MPN`, `SKU`, `Quantity`, `Qty`, `Unit Price`, `Unit Cost`, `Price`, `Description`, `Desc`, `Opis`, `Currency`, `UOM` and `Unit`. Case, spaces and punctuation do not affect header matching. If multiple columns match a field, specify a mapping; the Actor does not guess.

```json
{
  "oldColumnMapping": {
    "part_number": "Component ID",
    "quantity": "Count",
    "unit_price": "EUR/unit"
  }
}
```

Use separate `oldColumnMapping` and `newColumnMapping` objects when layouts differ. Map to exact header text. Other columns are ignored on file import. JSON extra fields remain preserved on added/removed records and do not trigger changes.

CSV accepts UTF-8 with or without BOM; comma, semicolon, tab and pipe delimiters. Set `csvDelimiter` if needed. Decimal separator is a dot; thousands separators and currency symbols are not automatically removed. XLSX requires a single worksheet or an explicit `oldSheet` / `newSheet` name.

### Financial analysis

Line cost is quantity × unit price. Decimal arithmetic is used; financial results are returned as **decimal strings**, preserving precision without binary floating-point rounding. Percentage change is rounded to six decimal places using half-even rounding.

- Missing quantity or price makes that part unpriced, including a zero quantity with a missing price.
- `old_total` / `new_total` are null when the corresponding BOM is incomplete. `known_old_subtotal` / `known_new_subtotal` contain only priced rows and are explicitly not full costs.
- `difference` and `percentage_change` are null if overall comparison is incomplete or units/currencies are incompatible. Percentage change is also null for a zero old total.
- `currency` supplies a common three-letter code for missing row currencies; it never overrides an explicit row value. Mixed currencies, or a mix of declared and undeclared currencies without a common currency, disable aggregate costs and cross-currency ranking. No conversion is performed.
- If every currency is omitted, calculations assume one common unspecified currency, and the report includes that assumption. Verify the assumption before making financial decisions.
- `unit` describes the unit in which quantity and unit price are expressed. A unit mismatch between matched revisions, including declared versus undeclared, prevents a comparable financial impact. No conversion is performed. Different components may have different units.
- Numeric costs are supported up to 100 significant digits and decimal exponents of magnitude 100. Values beyond this range still compare through the legacy JSON comparator but are marked unpriced in financial analysis.

For matching comparable parts:

```
quantity_effect = (new_quantity - old_quantity) × old_price
price_effect = new_quantity × (new_price - old_price)
net_impact = quantity_effect + price_effect
```

This convention assigns the price/quantity interaction to the price effect. Added and removed parts have a net impact without artificial price/quantity decomposition. `topN` (1–100, default 10) ranks nonzero comparable impacts by absolute magnitude. Incomplete rankings are labelled `ranking_complete: false`.

### Reports and API compatibility

`OUTPUT` retains the existing `summary` and `changes` fields and adds `report_version`, `parts` and `cost_analysis`. Existing change types, matching rules, ordering and dataset row types remain unchanged. Integrations should ignore new JSON fields.

The default dataset contains one `record_type: summary` row and change rows only, as before. `OUTPUT.parts` and file reports include **all** parts with `added`, `removed`, `modified` or `unchanged` status. Only quantity, price and description affect the legacy change status; currency/unit metadata additionally determine financial comparability.

| Record | Contents |
| --- | --- |
| `OUTPUT` | Full JSON: legacy summary/changes plus costs and all-part detail. |
| `REPORT-CSV` | UTF-8 BOM CSV with all parts, quantities, prices, line costs, differences and effects. |
| `SUMMARY-CSV` | Summary counts, cost completeness, totals and warnings. |
| `REPORT-XLSX` | Excel workbook with Summary, Parts and Top cost drivers sheets. |

Exports escape formula-like text to avoid spreadsheet formula execution. Exact signed decimal values remain numeric text. CSV/JSON preserve long text; Excel follows its 32,767-character cell limit. Reports do not calculate engineering equivalence, assembly hierarchies, supplier substitutions or unit/currency conversion.

### Limits, pricing and data handling

- Up to **10,000 parts per input**, **10 MiB per file**, 128 file columns, 64 MiB expanded XLSX content and 2,000 ZIP entries. XLSX has a header row plus up to 10,000 data rows.
- Default runtime allocation is 256 MiB with a 120-second timeout. Hosted 10,000-part CSV/XLSX benchmarking is recorded in the release evidence. Input size and content affect runtime costs.
- Existing price remains **$0.01 per start up to 1 GB RAM**, with no per-result charge. Apify's start event is billed once per started GB (minimum one). Failed runtime validation also incurs a start event. Platform usage is included; always check the live Pricing tab.
- Inputs and reports are stored on Apify under your storage/access settings. Uploaded files are retrieved from their supplied storage URLs; no data is sent to AI services. Upload only information you are authorized to process.
- Logs contain counts and safe validation messages, not BOM contents or signed upload URLs. Reports intentionally contain the submitted part details; control access to their storage.

### Examples, support and development

The repository `examples/` contains JSON input/output, sample old CSV/new XLSX and rendered reports. For support use the Actor Issues tab with a run ID and a minimal synthetic example; do not attach confidential BOMs or credentials.

Run tests with `python -m unittest discover -s tests -v` after installing `requirements.txt`. Production releases use a separate candidate build, hosted validation, unchanged pricing and a saved previous build for rollback. See `docs/releases/` for audit and measured results.

# Changelog

This Actor's version history is a separate document: https://apify.com/synthex-labs/synthex-ai-factory/changelog.md

# Actor input Schema

## `oldFile` (type: `string`):

Upload one file or provide a signed Apify/S3 HTTPS URL. Leave alternative JSON and pasted CSV sources empty. Max 10 MiB.

## `oldBom` (type: `array`):

Legacy JSON API: parts with unique text part_number and optional quantity, unit_price, description, currency, unit. Leave unset when uploading a file.

## `oldCsv` (type: `string`):

UTF-8 CSV with a header. Leave unset when using another source.

## `oldColumnMapping` (type: `object`):

Map canonical fields to exact headers, e.g. {"part_number":"Component ID","quantity":"Count","unit_price":"EUR/unit"}. Fields: part_number, quantity, unit_price, description, currency, unit. Ambiguous headers require explicit mapping.

## `oldSheet` (type: `string`):

Required when the workbook has multiple worksheets; exact worksheet name.

## `oldFormat` (type: `string`):

Auto detects XLSX ZIP files; otherwise reads UTF-8 CSV.

## `newFile` (type: `string`):

Upload one file or provide a signed Apify/S3 HTTPS URL. Leave alternative JSON and pasted CSV sources empty. Max 10 MiB.

## `newBom` (type: `array`):

Legacy JSON API: parts with unique text part_number and optional quantity, unit_price, description, currency, unit. Leave unset when uploading a file.

## `newCsv` (type: `string`):

UTF-8 CSV with a header. Leave unset when using another source.

## `newColumnMapping` (type: `object`):

Map canonical fields to exact headers, e.g. {"part_number":"Component ID","quantity":"Count","unit_price":"EUR/unit"}. Fields: part_number, quantity, unit_price, description, currency, unit. Ambiguous headers require explicit mapping.

## `newSheet` (type: `string`):

Required when the workbook has multiple worksheets; exact worksheet name.

## `newFormat` (type: `string`):

Auto detects XLSX ZIP files; otherwise reads UTF-8 CSV.

## `currency` (type: `string`):

Three-letter code, e.g. EUR. Fills unspecified currency fields; explicit row currencies are preserved. No exchange conversion.

## `topN` (type: `integer`):

Rank nonzero comparable part impacts by absolute cost difference. Partial rankings are labelled incomplete.

## `csvDelimiter` (type: `string`):

If unset, detect delimiter from the header. Numeric decimal separator is always a dot.

## Actor input object example

```json
{
  "oldFormat": "auto",
  "newFormat": "auto",
  "topN": 10
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `excel` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("synthex-labs/synthex-ai-factory").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("synthex-labs/synthex-ai-factory").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call synthex-labs/synthex-ai-factory --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,synthex-labs/synthex-ai-factory"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hrdu5kA9dg0RabShr/builds/mk4yyGp8RQQ3ipUva/openapi.json
