Data Migration Pipeline
This document describes how to get data from Captain Van’s legacy system into Odoo. This process will need to be repeated whenever there’s a fresh data export from the store.
Pipeline Overview
[Legacy Access DB] → [CSV Export] → [convert_data.py] → [Odoo CSVs] → [import_data.py] → [Odoo]
(.mdb) (37 tables) (format mapping) (4 files) (XML-RPC API)
Step 1: Export from Legacy System
The store runs a Microsoft Access database (CVAND*.mdb, ~205 MB). Data must be exported as CSV files.
Key tables needed from Access:
| Access Table | Maps To | Purpose |
|---|---|---|
| Items / Products | odoo_products.csv |
Product catalog |
| Customers | odoo_customers.csv |
Customer list |
| Suppliers | odoo_suppliers.csv |
Supplier contacts |
| Categories | odoo_categories.csv |
Product categories |
The original January 2026 export produced 37 CSV tables. Only 4 are used for the Odoo import.
Step 2: Convert to Odoo Format
Script: convert_data.py
This script reads the legacy CSV format (OSPOS-style columns) and outputs Odoo-compatible CSVs.
Input: Legacy CSVs from the Access export (expected in a source directory)
Output: 4 files in data/ directory
Product Conversion
| Legacy Field | Odoo Field | Notes |
|---|---|---|
item_name |
name |
Product name |
barcode |
barcode + default_code |
Used for both fields |
unit_price |
list_price |
Sale price |
cost_price |
standard_price |
Purchase cost |
category |
categ_id |
Mapped as All / {category} |
tax_1_name + tax_1_percent |
taxes_id |
Mapped to ISV 15%, ISV 18%, or Exento |
description |
description |
Product description |
quantity |
qty_available |
Stock quantity (reference only) |
All products are marked: available_in_pos = TRUE, sale_ok = TRUE, purchase_ok = TRUE
Tax Mapping
| Legacy Tax | Rate | Odoo Tax Name |
|---|---|---|
| ISV at 15% | 15% | ISV 15% |
| ISV at 18% | 18% | ISV 18% (alcohol, tobacco) |
| ISV at 0% | 0% | Exento |
| No tax / default | 15% | ISV 15% |
Customer Conversion
| Legacy Field | Odoo Field | Notes |
|---|---|---|
first_name + last_name |
name |
Combined full name |
company_name |
company_name |
Business name |
phone_number |
phone |
Phone |
email |
email |
|
address_1, address_2 |
street, street2 |
Address lines |
city |
city |
City |
country |
country_id |
Mapped to Honduras |
discount_percent |
comment |
Stored in comments (no native discount field in Community) |
comments |
comment |
Appended to comment field |
All customers marked: customer_rank = 1, is_company = FALSE
Supplier Conversion
| Legacy Field | Odoo Field | Notes |
|---|---|---|
company_name |
name |
Supplier name |
phone |
phone |
Phone |
All suppliers marked: supplier_rank = 1, is_company = TRUE
Important: The convert_data.py script has hardcoded paths (lines 20-21). Update these to match the actual file locations before running:
OSPOS_DIR = Path("/path/to/legacy/csv/files")
OUTPUT_DIR = Path("/path/to/CaptainVans-Odoo/data")
Step 3: Import into Odoo
Script: import_data.py
This script uses Odoo’s XML-RPC API to create records directly in the running Odoo instance.
Prerequisites:
- Odoo must be running (
./start-odoo.sh) - Database
captainvansmust exist - Admin user must be authenticated
Configuration (top of script):
URL = 'http://localhost:8069'
DB = 'captainvans'
USERNAME = 'admin'
PASSWORD = 'admin'
DATA_DIR = Path('/path/to/CaptainVans-Odoo/data') # Update this path
Import order (dependencies matter):
- Categories — Creates product categories under “All” parent. Skips existing.
- Taxes — Gets or creates ISV 15%, ISV 18%, Exento. Skips existing.
- Products — 2,505 products in batches of 50. Links to categories and taxes.
- Customers — 1,895 customers in batches of 100. Sets Honduras country.
- Suppliers — 36 suppliers. Marked as company contacts.
Runtime: ~2-5 minutes total
Error handling: Script continues on individual record errors and reports summary. First 5 errors per type are printed.
Step 4: Verify
After import, verify in Odoo:
Products: Point of Sale > Products (should show ~2,505)
Customers: Contacts > filter by "Customers" (should show ~1,895)
Suppliers: Contacts > filter by "Vendors" (should show ~36)
Categories: Inventory > Configuration > Product Categories (should show 15+)
Repeating the Process
When fresh data arrives from the store:
- Export new CSVs from the Access database
- Back up current Odoo database:
docker exec captainvans-odoo-db pg_dump -U odoo captainvans > backup_before_reimport.sql - Decide on strategy:
- Clean reimport: Drop and recreate database, run full setup + import
- Delta import: Modify
import_data.pyto check for existing records and update/skip
- Update paths in
convert_data.pyto point to new CSV files - Run conversion:
python3 convert_data.py - Run import:
python3 import_data.py
Warning: The current import script creates new records without checking for duplicates (except categories and taxes). Running it twice will create duplicate products and customers. For re-imports, either start with a fresh database or modify the script to handle duplicates.
Data Volumes
| Data Type | Count | CSV Size |
|---|---|---|
| Products | 2,505 | 285 KB |
| Customers | 1,895 | 146 KB |
| Suppliers | 36 | ~2 KB |
| Categories | 15 | ~1 KB |
Known Limitations
- Customer discounts are stored in the
commentfield as text (e.g., “Discount: 10%”) because Odoo Community has no native per-customer discount field - Stock quantities from the legacy system are captured in the CSV but not imported as actual inventory movements — only as reference. Proper stock initialization would need a separate inventory adjustment in Odoo
- No transaction history is migrated — only master data (products, customers, suppliers)