## Overview

The Parts importer lets you bulk-create or revise parts in a single CSV. It supports the same revision behavior as the UI: rows with an explicit revision create or update that exact `(part_number, revision)`, while rows that omit revision either initialize a new part at the org’s default revision scheme or auto-revise an existing part using the latest-revision flow (carrying over the source part’s mBOM and extensible attributes).

## Columns

| Column                        | Required | Description                                                                                                                                                   |
|-------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `part_number`                 | Yes      | The part number.                                                                                                                                             |
| `revision`                    | No       | The part revision. Omit to let ION assign the next revision automatically (or the scheme’s initial value if the part is new).                                |
| `revision_scheme`             | No       | Name of the revision scheme to use when creating a brand-new part. Defaults to the org’s default scheme.                                                     |
| `description`                 | No       | Free-text description.                                                                                                                                      |
| `status`                      | No       | Part status. One of `released` or `archived`. Matched case-insensitively.                                                                                  |
| `purchase_type`              | No       | Purchase type. One of `receivable_inventory`, `receivable_non_inventory`, or `non_receivable_non_inventory`. Matched case-insensitively.                    |
| `tracking_type`              | No       | Tracking type, such as `serial`, `lot`, or `none`. Matched case-insensitively.                                                                             |
| `sourcing_strategy`          | No       | Sourcing strategy. One of `make`, `buy`, or `dual_source`. Matched case-insensitively.                                                                      |
| `cost`                       | No       | Default unit cost. Empty cells become `0`.                                                                                                                  |
| `lead_time`                  | No       | Lead time as a duration. See [Duration format](https://docs.firstresonance.io/administration/ion-importers/parts#duration-format).                        |
| `reorder_minimum_quantity`   | No       | Reorder minimum. Must be non-negative. Empty cells become `0`.                                                                                              |
| `reorder_maximum_quantity`   | No       | Reorder maximum. Must be non-negative. Empty cells become `0`.                                                                                              |
| `export_controlled`          | No       | Whether the part is export controlled. Boolean field (see [accepted values](https://docs.firstresonance.io/administration/ion-importers#accepted-boolean-values)). |
| `unit_of_measure`            | No       | Unit of measure name, such as `each` or `kg`. Must match an existing unit in ION.                                                                          |
| `quality_clauses`            | No       | Semicolon-delimited list of quality clause reference names to attach to the part.                                                                           |
| `supplier_parts`             | No       | Semicolon-delimited list of supplier-part records. See [Supplier parts](https://docs.firstresonance.io/administration/ion-importers/parts#supplier-parts) below. |

Part matching is case-insensitive. Enum fields (`status`, `purchase_type`, `tracking_type`, `sourcing_strategy`) only accept the values listed above. An invalid value produces a row-level error listing the allowed options.

## Revisions

The Parts importer mirrors the UI’s revision flow:

- **Row with an explicit revision**: looks up `(part_number, revision)`. If it exists, the row updates that part; otherwise, it creates a new part at that exact revision.
- **Row without a revision, no existing part with that `part_number`**: ION assigns the initial revision from the configured `revision_scheme` (or the org’s default scheme, typically `"A"` or `"1"`).
- **Row without a revision, existing part with that `part_number`**: ION computes the next revision from the latest existing revision and creates a revised part. The new part inherits the mBOM and extensible attributes of the source part.

You cannot have two rows that both omit `revision` for the same `part_number`. ION cannot disambiguate which one should auto-bump. Specify revisions explicitly to fix the conflict. Likewise, an explicit-revision row that collides with an auto-bumped revision from another row is rejected as a duplicate. This happens when one row sets `revision = B` and another auto-bumps to `B`.

## Duration format

The `lead_time` column accepts a duration written in days, hours, and minutes. Use the units `d`, `h`, and `m`, in that order, with optional spaces between terms. Each unit is optional. The value is rounded down to the nearest minute. A plain number with no unit is read as a number of seconds. Valid values include:

| Value   | Meaning                |
|---------|------------------------|
| `11d`   | 11 days                |
| `1d 2h` | 1 day, 2 hours         |
| `10d 21m`| 10 days, 21 minutes    |
| `45m`   | 45 minutes             |
| `3600`  | 3,600 seconds (1 hour) |

The **Review** step flags a value that uses any other unit (such as weeks or seconds), lists the units out of order, or is otherwise unparseable.

## Supplier parts

The `supplier_parts` column attaches one or more SupplierParts to the part. Use semicolons (`;`) to separate records and pipes (`|`) to separate fields within a record:

```
supplier_name|supplier_part_number|cost|conversion_factor;supplier_name|...
```

| Field                      | Required | Notes                                                                                                      |
|----------------------------|----------|------------------------------------------------------------------------------------------------------------|
| `supplier_name`            | Yes      | Must match an existing supplier in ION.                                                                   |
| `supplier_part_number`     | Yes      | The supplier’s part number for this part.                                                                  |
| `cost`                     | No       | Falls back to the Part’s `cost` if omitted.                                                               |
| `conversion_factor`        | No       | Defaults to `1.0`. Must be greater than `0`.                                                                |

Example: `Acme|ACME-WID-001|4.25|1;Globex|GLX-W1` If a SupplierParts row already exists for the `(part, supplier, supplier_part_number)` combination, the importer updates its `cost` and `conversion_factor` in place. Otherwise, it inserts a new row.

## Quality clauses

The `quality_clauses` column accepts a semicolon-delimited list of quality clause `reference_name` values. Each must match an existing Requirement with `requirement_type = QUALITY_CLAUSE`. The importer skips associations that already exist, so re-imports are idempotent. Example: `QC-001;QC-002`

## Custom attributes

Any column not in the standard list above is treated as a custom attribute on the part. Custom attribute columns must match a configured attribute key for your organization; unknown columns cause a validation error.
