# How to import data with Epicor DMT (Data Migration Tool)

> Source: https://netray.co/erp-help/epicor-dmt-import-how-to
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

In DMT, pick the target data type (e.g. Part, Customer, PartWhse), Generate a template from the connected Epicor server so column names match your version's schema exactly, populate the spreadsheet, load it into DMT, run a Pre-Process/validate pass first to surface errors before committing, then Process to write the records.

## Kind

how-to

## System

epicor

## Erp

Epicor Kinetic

## Module

Data Migration / DMT

## Query

how to import data with epicor dmt

## Alternate Queries

- epicor data migration tool tutorial
- epicor dmt template errors
- epicor dmt import failed rows
- epicor dmt part master import

## Applies To

Epicor Kinetic and Epicor 10.x; DMT is a separate Windows client application, not a browser tool, installed from the Epicor client install media.

## Steps

### Run a clean DMT import

**Items:**

- Launch DMT and connect it to the target Epicor database/company using the same server, database, and credentials as the Epicor client.
- Select the Data Type from the tree (e.g. Part, PartPlant, Customer, ARInvoice, JobHead) - each data type maps to a specific DMT template with its own required and optional columns.
- Click Generate Template to export a fresh Excel template for your exact Epicor version and data type - never reuse an old template from a different version, since required fields can change.
- Fill in the spreadsheet; required columns are typically marked, and there is often a matching "lookup" tab showing valid values for coded fields (e.g. valid UOM codes, class IDs).
- In DMT, browse to the completed file and load it into the grid.
- Run Pre-Process (or the validate-only option depending on version) first - this checks each row against business logic without committing, surfacing errors like invalid part class or missing required field.
- Fix flagged rows in the source spreadsheet, reload, and re-validate until the error count is acceptable.
- Run Process to commit; DMT writes a results/error log file (path shown after the run) listing each row's success or failure and the exact error message - review it row by row for partial failures.
- For large imports, batch in chunks (e.g. 500-2000 rows) rather than one massive file, since a single bad row can abort or slow the whole batch depending on data type and version.

## Sections

### Why DMT instead of BAQ import or REST

**Body:**

DMT calls the same underlying business object methods the Kinetic UI uses (e.g. PartSvc.Update), which means it runs the same validations, defaulting logic, and BPM directives that would fire if you entered the record by hand. This is slower than a raw SQL insert but far safer - it is the supported path for master data loads (parts, customers, BOMs) precisely because it will not create records that violate business rules the UI would otherwise enforce.

REST API or Function-based imports are viable alternatives for scripted, repeatable, non-interactive loads, but DMT remains the standard tool for one-off or periodic migrations because of its template generation, built-in validation pass, and readable error log aimed at functional users, not just developers.

### Reading and fixing common DMT errors

**Body:**

Errors typically echo the same BO validation message you would see in the Kinetic UI - "Part Class is required" or "Site is invalid" - because DMT is calling the same service layer. Cross-reference the error text against the relevant setup table (Part Class Maintenance, Site Maintenance) rather than guessing.

A common source of confusing failures is import order and dependency: importing PartWhse rows before the corresponding Part records exist, or ARInvoice lines before the customer or invoice header exists, produces "not found" errors that are really sequencing issues, not data quality issues.

BPM directives that fire on the same business object as the import (e.g. a Pre-Processing directive requiring a custom field to be filled) apply to DMT-driven saves too - if a BPM was written assuming manual UI entry, it can unexpectedly block or alter DMT-loaded records; test with BPM logging on for the first batch.

**Code:**

-- After a DMT part import, spot check counts against source file
SELECT COUNT(*) FROM Part WHERE Company = 'EPIC06' AND SysRowID IN (
  SELECT SysRowID FROM Part WHERE ChangedOn >= '2026-09-25'
);

### Preparing data before it reaches DMT

**Body:**

Clean coded/reference fields (UOM, part class, site) against Epicor's actual master lists before building the DMT sheet - the fastest way to reduce error-log volume is to validate lookups in Excel with a VLOOKUP against an exported list of valid codes rather than discovering each bad code one failed row at a time.

For BOM, routing, or multi-level structures, load parent records first in one DMT pass, then child/detail records in a second pass, respecting the same header-then-detail order the UI enforces.

## Pitfalls

Reusing a template generated against a different Epicor version or with customized required fields (added by a BPM) omitted, causing silent validation failures at process time.

Skipping the Pre-Process/validate step and running straight to Process on a large file, then having to untangle a mix of successful and failed rows from the log after the fact.

Importing detail records (PartWhse, BOM detail) before their parent header exists in a separate DMT pass.

Ignoring BPM directives that fire during DMT saves - a required custom field enforced by BPM will fail DMT rows the same way it would block manual entry, and the error message may reference the BPM's custom text rather than an obvious standard field.

Running massive single-batch imports (tens of thousands of rows) without chunking, risking long-running locks or timeouts that are hard to diagnose after the fact.

Not reconciling row counts between source file and Epicor after the run - DMT's success/fail count in the log is the authoritative check, not assuming everything landed.

## Ai Angle

For a large migration project, an ERPray-style agent grounded in the target tenant's schema and BPM rules can pre-validate a DMT spreadsheet against live lookup tables and known BPM requirements before it is ever loaded into DMT, catching invalid UOM codes or missing BPM-required custom fields in bulk rather than one failed row at a time in the DMT error log.

## Faq

### Can DMT update existing records, not just create new ones?

**Answer:** Yes - most DMT data types support Add/Update mode, matching on the primary key columns (e.g. PartNum) already in the spreadsheet; if a matching record exists it updates rather than errors, but check the specific data type's template notes since behavior varies.

### Does DMT trigger the same BPM directives as manual entry?

**Answer:** Yes, because DMT calls the standard business object update methods, both Pre-Processing and Post-Processing BPM directives on that object fire during a DMT import exactly as they would for a UI save.

### Why does DMT process slowly on large files?

**Answer:** Each row goes through full business object validation and any attached BPM logic, which is inherently row-by-row rather than a bulk SQL operation - for very large one-time loads, consider chunking the file or, for pure master data with no complex validation needs, evaluating a scripted approach via Functions or REST instead.

### Where does DMT write its error log?

**Answer:** DMT shows a results screen after each run with a link/path to the log file (typically a text or CSV file in a local results folder); the log lists row number, key fields, and the exact validation message returned by the business object for each failed row.

## Search terms

epicor dmt import, epicor data migration tool, epicor dmt template, epicor dmt error log, epicor dmt part import, epicor dmt pre-process, epicor bulk data load

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
