> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alforse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import and export

> Bring records into Alforse from a CSV, and download any table's current view as a CSV.

Alforse supports CSV export for every shared data table and CSV import for selected entities. Export runs in the browser against the current table result set. Import uses a guided mapping workflow and a privileged background function that inserts or updates records in a transaction.

## Exporting a table to CSV

Use **Download CSV** in a data table's header. Every table built on Alforse's shared data grid includes this action.

The download reflects **what you are currently looking at**, not a full server dump:

* Only the **rows on the current page / result set** are written. Filter or search first to widen or narrow what lands in the file, then export.
* Only the **columns visible in your current saved view** are included, in the order you've arranged them. Hidden columns are left out; the synthetic select, expand, and actions columns never export.
* The CSV headers are the same human labels shown in the grid.

For id-bearing columns like item, supplier, employee, and customer, the export substitutes the readable **name** for the raw id so the file is legible. A few columns are marked export-only: they stay hidden in the grid but are always written to the CSV.

NOTE

**The file is always named data.csv**

Export builds the file entirely in your browser and downloads it as `data.csv` with no server round-trip. Rename it after downloading if you're exporting several tables.

[![Parts list with the Download CSV icon in the table header](/screens/reference-parts-csv.png)](/screens/reference-parts-csv.png)

Every Alforse data table exports its current view. Filter and arrange columns first, since the export mirrors exactly what's on screen.

## What you can import

Bulk import is available only for supported entities. Import each entity from the **Bulk Import** menu on its list screen:

FieldType

<details>
  <summary>**Customers and customer contacts**</summary>

  From the customers list (**Customers** and **Contacts**). Customers optionally ride address, payment term, and contact-style fields on the same row.
</details>

<details>
  <summary>**Quotes**</summary>

  From the quotes list: **Quotes with Lines**, **Quotes**, and **Quote Lines**. Needs sales update permission.
</details>

<details>
  <summary>**Suppliers**</summary>

  From the suppliers list. Also carries address, payment term, shipping method, and incoterm fields.
</details>

<details>
  <summary>**Parts, Materials, Tools, Consumables, Services**</summary>

  From each item type's list. A single row can optionally create a supplier-part link, set purchasing lead time, and seed unit cost alongside the item.
</details>

<details>
  <summary>**Work Centers**</summary>

  From the work centers list, including labor, machine, and overhead rates and a location.
</details>

<details>
  <summary>**Processes**</summary>

  From the processes list.
</details>

<details>
  <summary>**Lookup lists**</summary>

  Storage units and storage types, scrap reasons, units of measure, item posting groups, departments, and the material lookups (substances, forms, finishes, grades, types, dimensions), each from its own list.
</details>

<details>
  <summary>**Inventory, Batch, and Serial Quantities**</summary>

  From **Inventory → Quantities**. Adds opening stock for items that already exist, one import per tracking type. See [Opening stock](#opening-stock).
</details>

The **Bulk Import** menu on the parts and tools lists also offers three **method** imports that load bills of materials and operations: **BOM**, **Operations**, and **Parts with Methods**, which creates parts and their full BOM and routing together. These are row-typed CSVs: each row is tagged `PART`, `BOM`, `BOP`, `STEP`, `TOOL`, or `PARAM` and names its parent part.

A method import adds to the make method that is already there. With **Make Method Version** blank it writes into the highest version that is not archived; a version you name must be a number above 0 with at most 2 decimals. New BOM lines go after the existing ones. An **Op No** is the operation's order, so it must be a number: one that matches an existing operation's order adds its steps, tools, and parameters to that operation, and `10`, `10.0` and `010` all name the same operation. Rows already on the method (the same material, operation, step name, tool, or parameter key) are skipped and listed, so a file can run again safely. The import changes only a Draft version and follows the same change notice and release locks as the BOM and BOP editors. A part it refuses is listed with the reason, and none of its rows are written. When release control is set to warn, the import writes into a released revision as the editors do, and the results list that part under a warning.

Blank cells take the values the editors fill in: a BOM line with no **Unit of Measure** takes the material's own unit, and an operation with no **Operation Type** or **Operation Description** takes its process's type and name. The import also refuses what the editors refuse: a BOM line that is the part itself or a tool or service, a tool that is not a tool item, a negative quantity, time or cost, and a step whose maximum is below its minimum. An Inspection operation needs an Inspection Plan, which a CSV cannot carry, so add Inspection operations in the app.

HEADS UP

**Fixed assets aren't importable here**

The models list a `fixedAsset` import shape, but the import function marks it as not importable, so a fixed-asset CSV is rejected. No list offers a fixture import either. Create fixed assets and fixtures in the app. BOM lines are loaded with the **BOM** method import above or in the app.

## The import flow

Import uses a four-step wizard followed by a background write.

**Upload.** Click **Bulk Import**, pick the entity, and drop your CSV. Alforse parses it in the browser to read the header row, and uploads the file to your company's private storage. You can download a **template** for the entity first: it's a one-row CSV whose header is every field label and whose single data row is a hint marking each column `REQUIRED` or `optional` and listing valid enum values. Overwrite that hint row with your data and re-upload.

**Map columns.** Match each of your CSV columns to an Alforse field. Required fields must be mapped; anything you leave as **N/A** is skipped. For enum fields (like replenishment system or tracking type), you also map your raw values to Alforse's, and the wizard fuzzy-matches obvious ones for you (a CSV `"B"` proposes `"Buy"`). Some lookups can be created inline: a type or status that doesn't exist yet can be added without leaving the wizard.

**Validate.** The mapped data is checked against the entity's schema. Missing a required field or an unparseable value surfaces before anything is written.

**Import.** Submitting posts your mappings to Alforse, which invokes a privileged **import** function. It re-downloads the file from storage, applies your column and enum mappings, decides insert-versus-update per row, and writes the results inside a database transaction.

NOTE

**Enum values fall back to a default**

When a CSV value has no mapping to an Alforse enum, the row imports with that field's configured **default** rather than failing. An unmapped `"Replenishment System"` cell, for example, lands as `"Buy and Make"`. Map your values deliberately if the default isn't what you want.

## Insert, update, or skip

For standard entity imports, re-importing the same identifiers updates existing records instead of duplicating them. The importer classifies each row before writing:

<details>
  <summary>**Insert**</summary>

  The row is new. No existing record matches its Unique ID or its Name, so Alforse creates it and records a CSV id mapping for next time.
</details>

<details open>
  <summary>**Update**</summary>

  An existing record matches. Alforse matches first by the CSV's Unique ID (via a stored CSV-to-record mapping), then falls back to matching by Name, and updates that record in place.
</details>

<details>
  <summary>**Skip**</summary>

  The row is a duplicate of another row in the same file (same id, or same name) and is dropped as informational.
</details>

<details>
  <summary>**Error**</summary>

  The row is missing required data, most often a blank Name. It's reported so you can fix and re-import.
</details>

Matching by Unique ID uses a shared external-mapping table keyed to the `csv` source, so the second import of a file with the same ids takes the update path instead of creating duplicates. If a record was created in the app and you later import a CSV using the same **Name**, the name fallback catches it and updates rather than duplicates.

NOTE

**Custom fields aren't populated on import**

Import writes the entity's standard fields plus its documented side-tables (address, payment, supplier-part, cost). It does **not** map into per-company custom fields. Set those in the app after importing, or via the record's own form.

## Opening stock

The **Inventory → Quantities** table carries three imports that load on-hand stock, typically once during go-live. Each row is posted as a **Positive Adjmt.** through the same posting core as a manual inventory adjustment: an item ledger entry at the item's current cost, dated today, and a line pair on **one** inventory adjustment journal for the whole file. A row whose item has zero cost posts no journal lines. Inventory adjustment business rules are not evaluated on import. These imports only ever **add** stock; they never reduce or set a quantity.

Pick the import that matches the item's tracking type. A row whose item is tracked differently is reported as an error naming the right import, and Non-Inventory items can't be stocked at all.

FieldType

<details>
  <summary>**Inventory Quantities**</summary>

  For items tracked as **Inventory**. Columns: Part Number, Revision, Location, Storage Unit, Quantity, Comment.
</details>

<details>
  <summary>**Batch Quantities**</summary>

  For items tracked as **Batch**. Columns: Part Number, Revision, Location, Storage Unit, Batch Number, Quantity, Expiration Date, Comment. Each row creates a new batch.
</details>

<details>
  <summary>**Serial Quantities**</summary>

  For items tracked as **Serial**. Columns: Part Number, Revision, Location, Storage Unit, Serial Number, Expiration Date, Comment. There's no Quantity column: every row is one unit.
</details>

Rules shared by all three:

* **Part Number + Revision** must match an existing item; a blank Revision means `0`. An unknown item is an error.
* **Location** is mapped to one of your locations in the wizard. **Storage Unit** is optional and matched by name within that location, ignoring case. A named storage unit that doesn't exist there is an error; the import never creates one.
* **Quantity** must be a positive number.
* **Expiration Date** is optional and must be written `YYYY-MM-DD`. Left blank, an item with a fixed-duration shelf life gets its expiry calculated the same way a manual adjustment does.
* There's no Unique ID column, so these imports don't record CSV id mappings.

HEADS UP

**Re-importing an Inventory Quantities file adds the stock again**

Inventory-tracked rows have no natural key, so every valid row posts, every time. Importing the same file twice doubles the quantities. Batch and serial rows are protected: a batch or serial number that already exists on that item (in any status) is **skipped**, and a number repeated in the file is skipped after its first row. The same serial number on two different items is allowed.

## Errors and partial failures

Import handles validation failures per row. Invalid rows are reported while valid rows continue. Results include:

* **inserted** and **updated** — counts of records written.
* **errors** — rows you should fix and re-import (missing required data), each with its row number, a reason, and the original cell values.
* **skipped** — rows intentionally not written (in-file duplicates, already-existing), same shape.

A BOM, Operations, or Parts with Methods import counts **parts** as inserted and updated, and also reports how many BOM lines, operations, steps, tools, and parameters it added. The results read, for example, "4 parts updated. Added: 6 BOM lines. 0 rows need fixing, 2 rows skipped."

Because the results carry each failed row's original cells, the wizard's results view shows you exactly which rows to fix without re-parsing your file. A hard failure (the file can't be downloaded, or an unexpected database error) is the only case that fails the whole import and returns an error message instead of a per-row report.

HEADS UP

**Import bypasses row-level security by design**

The import function writes with a privileged database connection that bypasses RLS, so your **permission to import is checked once**, up front, when you submit: sales for customers, purchasing for suppliers, parts for items and methods, production for work centers and processes, inventory for storage units and opening stock. If you lack that module's update permission, the import is refused before any write. Opening stock imports also need inventory create permission, the same as a manual inventory adjustment.

## Related

**Items**

The part, material, tool, consumable, and fixture records the item importers create.

[Open](/docs/reference/items.md)

**Methods**

Bills of materials and routings, loaded by the BOM, Operations, and combined method imports.

[Open](/docs/reference/methods.md)

**Suppliers and customers**

The partner records the customer and supplier importers create, including their addresses and terms.

[Open](/docs/reference/suppliers-and-customers.md)

**Work centers**

The capacity records the work-center importer creates.

[Open](/docs/reference/work-centers.md)

**Inventory**

On-hand quantities and the manual adjustments the opening-stock imports post.

[Open](/docs/reference/inventory.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.