Skip to main content

Epicor Kinetic ERP Integration

Comprehensive documentation for the bidirectional integration between TenPoint Crossbows WooCommerce store and Epicor Kinetic ERP.

Overview

Plugin: tenpoint-epicor-integration
Version: 3.0.4
Namespace: Suma\TenpointEpicorIntegration
Author: Rhino Group
ERP Version: Epicor Kinetic 2025.2

Integration Type

Bidirectional synchronization:

  • WooCommerce → Epicor: Orders export as Sales Orders
  • Epicor → WooCommerce: Inventory levels sync from BAQ queries

Architecture

tenpoint-epicor-integration/
├── init.php # Plugin entry, Composer autoload, constants
├── inc/
│ ├── class-plugin.php # Lifecycle orchestrator (activation, cron, dependencies)
│ ├── class-settings.php # Singleton config manager (AES-256 encryption)
│ ├── class-admin.php # Admin control panel (tabs, AJAX handlers)
│ ├── class-orders.php # Order export pipeline (WC → Epicor)
│ ├── class-inventory.php # Inventory sync (Epicor BAQ → WC)
│ ├── class-epicor.php # Guzzle HTTP client for Epicor REST API
│ ├── class-endpoints.php # WP REST API routes
│ ├── class-sync-logger.php # Database-backed structured logging
│ ├── class-notifications.php# HTML error email alerts
│ ├── class-order-generator.php # Test order creation tool
│ ├── class-log.php # File-based rotating logs
│ ├── class-utils.php # Helper functions
│ └── class-frontend.php # Frontend assets stub
├── src/
│ ├── js/Admin.js # Admin panel jQuery
│ └── scss/admin.scss # Admin styles
├── dist/ # Compiled assets (Laravel Mix)
├── vendor/ # Composer dependencies (Guzzle 7.x)
└── Agents.md # Technical documentation

Core Features

1. Order Export (WooCommerce → Epicor)

Trigger: External REST endpoint or admin manual button click

Flow:

  1. Queries WC orders matching criteria:
    • Status: wc-processing
    • Not yet exported (_tenpoint-epicor-integration_exported_to_epicor meta not set)
    • Valid payment method: ebizcharge, ppcp-gateway, paytomorrow (with lender), or $0 orders
  2. Formats line items including:
    • Products: SKU lookup with Epicor part revision validation
    • Shipping: WEBFRT SKU
    • Tax: NSI SKU (with tax-exempt detection)
    • Coupons: Discount line items
    • Gift cards: Misc charge with GiftCardNum_c
    • Warranties: warrantied_bow_sku meta
  3. Creates Sales Order in Epicor via REST API
  4. Uses OData $batch requests to group up to 4 operations per line item (reduces HTTP calls)
  5. On success:
    • Sets order meta: _tenpoint-epicor-integration_exported_to_epicor = 1
    • Sets order meta: _tenpoint-epicor-integration_epicor_order = {OrderNum}
    • Sets order meta: _epicor_order_export_date = {timestamp}
    • Changes order status to wc-shipment-pending or wc-completed
  6. Logs via SyncLogger::logOrderSync()

External Trigger:

curl -X GET "https://tenpointcrossbows.com/wp-json/suma/v1/data/epicor/post-sales-orders" \
-H "Auth-Token: YOUR_TOKEN_HERE"

Supported Payment Methods:

MethodPlugin SlugNotes
EBizChargeebizchargeCredit card/ACH
PayPalppcp-gatewayPayPal Commerce Platform
PayTomorrowpaytomorrowMust have lender metadata
Zero-dollar ordersN/AFree products, gift cards only

2. Inventory Synchronization (Epicor → WooCommerce)

Trigger: External REST endpoint or admin manual button click

Flow:

  1. Queries Epicor BAQ: KP-AVAILINV
    • Returns: Part_PartNum, Calculated_AvailInventory
  2. Builds SKU → quantity map from Epicor response
  3. Resolves SKU → post_id mappings via database query
  4. Filters out products with skip_inventory_update = 'yes' meta
  5. Bulk SQL updates (batches of 100):
    • Updates wp_postmeta: _stock, _stock_status
    • Updates wp_wc_product_meta_lookup table
    • Updates product visibility taxonomy terms
  6. Cache management:
    • Flushes WooCommerce product object caches
    • Clears WooCommerce transients
    • Purges Cloudflare cache for entire zone
  7. Logs via SyncLogger::logInventorySync() with WC-only counts and SKU → quantity map in details

External Trigger:

curl -X POST "https://tenpointcrossbows.com/wp-json/suma/v1/data/epicor/sync-inventory" \
-H "Auth-Token: YOUR_TOKEN_HERE"

Performance:

  • Batch size: 100 products per SQL operation
  • Typical sync time: 2-5 seconds for 500 products
  • Database queries: Optimized with CASE/WHEN SQL

3. Admin Control Panel

Location: WP Admin → Epicor Integration
Capability Required: manage_woocommerce

Settings Tab

Multi-environment configuration (live/sandbox):

  • Epicor connection details (URL, instance, API version)
  • Credentials (AES-256 encrypted passwords)
  • Service endpoints (orders, parts, inventory BAQ, revisions BAQ)
  • Company code
  • REST API auth token (with "Generate New Token" button)
  • Feature toggles:
    • Detailed logs
    • Send to Epicor (master kill switch)
    • Change order status
    • Disable SSL verify (dev only)
  • Error email recipients

Order Sync Logs Tab

  • Paginated table (20 entries per page)
  • Searchable (summary, WC order ID, Epicor order number)
  • Filterable by status (success/error/warning)
  • Expandable detail view (full JSON)
  • Manual sync trigger button
  • External cron setup documentation

Inventory Sync Logs Tab

  • Same interface as Order Sync Logs
  • Specific to inventory operations
  • Shows WC-only product counts
  • Details include SKU → quantity mappings

Tools Tab

Generate test WooCommerce orders:

  • Batch size: 1–50 orders
  • Random in-stock products (1–4 per order)
  • Simulated PayPal metadata
  • Optional coupon types: percent discount, free shipping
  • Status: wc-processing (ready for export)

4. Settings Management

Class: Settings (singleton)
Storage: Single wp_options row: tpi_epicor_settings
Access: Dot-notation

$settings = Settings::getInstance();
$url = $settings->get('environments.live.epicor_url');
$username = $settings->get('environments.live.epicor_username');

Configuration Structure:

{
"active_environment": "live",
"environments": {
"live": {
"label": "Production",
"epicor_url": "https://erp.tenpointcrossbows.com",
"epicor_instance": "kineticpilot",
"epicor_api_version": "v1",
"epicor_username": "api_user",
"epicor_password": "ENCRYPTED_AES256_STRING",
"orders_service": "Erp.BO.SalesOrderSvc",
"parts_service": "Erp.BO.PartSvc",
"inventory_baq": "KP-AVAILINV",
"revisions_baq": "KP-REVS",
"company_code": "TENPOINT"
},
"sandbox": { /* ... */ }
},
"auth_token": "your-generated-token-here",
"error_emails": "[email protected],[email protected]",
"detailed_logs": true,
"send_to_epicor": true,
"change_order_status": true,
"disable_ssl_verify": false
}

Password Encryption:

  • Algorithm: AES-256-CBC
  • Key: hash('sha256', AUTH_KEY) from wp-config.php
  • IV: Random, prepended to ciphertext
  • Transparent decryption via Settings::decryptPassword()

Legacy Migration: Automatically migrates v2.x wp_options constants to v3.x single-option structure on first load.

5. Epicor API Client

Class: Epicor (static methods)
HTTP Client: Guzzle 7.x
Timeout: 30 seconds
Authentication: HTTP Basic Auth (Base64-encoded username:password)
Base URL Pattern: {epicor_url}/{epicor_instance}/api/v1

Order Management Methods

MethodHTTP VerbEpicor ServicePurpose
createSalesOrders()POSTErp.BO.SalesOrderSvc/SalesOrdersCreate order header
createSalesOrderDtl()POSTErp.BO.SalesOrderSvc/OrderDtlsCreate detail line
createSalesOrderRel()POSTErp.BO.SalesOrderSvc/OrderRelsCreate release
createSalesOrderMscs()POSTErp.BO.SalesOrderSvc/OrderMscsCreate misc charge
updateSalesOrderHed()PATCHErp.BO.SalesOrderSvc/SalesOrdersUpdate header
updateSalesOrderRel()PATCHErp.BO.SalesOrderSvc/OrderRelsUpdate release

Query Methods

MethodHTTP VerbPurpose
validatePartDescription()GETValidate SKU against parts catalog
getOrderByID()POSTRetrieve full sales order
getOrderStatus()GETCheck order open/closed status
getPartBAQ()GETPart revision lookup (KP-REVS BAQ)
getEpicorInventory()GETInventory levels (KP-AVAILINV BAQ)
testConnection()GETConnectivity test
sendBatchRequest()POSTOData $batch multipart/mixed

All methods return: string|false (JSON body or false on failure)

OData Batch Requests

Instead of individual HTTP calls per line item (4 calls × 10 items = 40 requests), the plugin groups operations into a single $batch request:

POST /api/v1/$batch HTTP/1.1
Content-Type: multipart/mixed; boundary=batch_boundary

--batch_boundary
Content-Type: application/http

POST /Erp.BO.SalesOrderSvc/OrderDtls HTTP/1.1
Content-Type: application/json

{"Company":"TENPOINT","OrderNum":12345,"PartNum":"CB123"}

--batch_boundary
Content-Type: application/http

POST /Erp.BO.SalesOrderSvc/OrderRels HTTP/1.1
Content-Type: application/json

{"Company":"TENPOINT","OrderNum":12345,"OrderLine":1}

--batch_boundary--

Benefits:

  • Reduces HTTP round-trips by 75%
  • Faster order export (2-3 seconds vs 10-15 seconds)
  • Lower network overhead

6. REST API Endpoints

Base: /wp-json/suma/v1/data/
Auth: Auth-Token header (timing-safe hash_equals() validation)

EndpointMethodPurposeCron Usage
export/get-ordersGETReturn pending WC ordersManual
export/get-single-orderGETSingle order by IDManual
epicor/post-sales-ordersGETTrigger order export✅ Every 5 min
epicor/sync-inventoryPOSTTrigger inventory sync✅ Hourly
epicor/get-sales-ordersGETRetrieve Epicor ordersManual
epicor/get-part-descriptionGETPart details by SKUManual
epicor/get-order-statusGETCheck Epicor order statusManual
epicor/get-part-revisionGETPart revisions from BAQManual
epicor/get-inventoryPOSTInventory for specific partManual
epicor/public-get-inventoryGETPublic inventory queryManual
epicor/manage-inventory-batchGETEnable manage_stock on all productsAdmin
epicor/update-order-statusGETUpdate WC statuses from EpicorManual/webhook
epicor/clean_logsPOSTDelete logs older than 60 days✅ Daily

7. Logging System

Structured Database Logs

Table: wp_tpi_sync_logs

ColumnTypePurpose
idBIGINTPrimary key
log_typeVARCHAR(20)'order' or 'inventory'
statusVARCHAR(20)'success', 'error', 'warning'
wc_order_idBIGINTWooCommerce order ID (nullable)
epicor_order_numVARCHAR(50)Epicor OrderNum (nullable)
summaryTEXTHuman-readable summary
detailsLONGTEXTJSON with full request/response data
created_atDATETIMETimestamp

Features:

  • Searchable across summary, wc_order_id, epicor_order_num
  • Filterable by log_type and status
  • Auto-cleanup: Entries older than 14 days deleted via daily cron
  • Expandable detail view in admin panel

File-Based Logs

Location: /wp-content/uploads/suma-tenpoint-epicor-integration/

Directories:

  • order/ — Order export logs
  • inventory/ — Inventory sync logs
  • epicor/ — Raw Epicor API request/response logs

Features:

  • Daily rotating files: YYYY-MM-DD.log
  • 90-day retention (old files auto-deleted)
  • Debug-only writes (controlled by detailed_logs setting)

8. Email Notifications

Class: Notifications
Recipients: Configured via error_emails setting (comma-separated)

Email Templates

Order Export Error:

Subject: TenPoint Epicor Integration - Order Export Error

Environment: Live / Sandbox
Order ID: #12345
Epicor Order: (if created)

Error Details:
{formatted error message with stack trace}

View Logs: [Link to Order Sync Logs tab]

Inventory Sync Error:

Subject: TenPoint Epicor Integration - Inventory Sync Error

Environment: Live / Sandbox

Error Details:
{formatted error message}

View Logs: [Link to Inventory Sync Logs tab]

HTML Formatting:

  • Red header stripe for errors
  • Pre-formatted code blocks for technical details
  • Direct links to admin panel logs

9. WooCommerce Integration

Custom Order Columns

Exported to Epicor:

  • Green checkmark (✓) if exported
  • "N/E" if not exported
  • Tooltip shows Epicor OrderNum

Date Exported:

  • Timestamp of successful export
  • Formatted as: Y-m-d H:i:s

Column Filtering:

  • Filter orders by export status
  • Filter orders by payment method

Custom Order Meta

Meta KeyValueSet When
_tenpoint-epicor-integration_exported_to_epicor1Order exported successfully
_tenpoint-epicor-integration_epicor_orderEpicor OrderNumOrder exported successfully
_tenpoint-epicor-integration_epicor_failure1Order export failed
_epicor_order_export_dateDatetime stringOrder exported successfully

Order Status Workflow

wc-pending
↓ (payment complete)
wc-processing
↓ (exported to Epicor)
wc-shipment-pending
↓ (Epicor fulfillment)
wc-partially-shipped (if partial)
OR
wc-completed (if fully shipped)

Cron Job Configuration

Disable WP-Cron in wp-config.php:

define('DISABLE_WP_CRON', true);

Add to system crontab:

# Order export every 5 minutes
*/5 * * * * curl -X GET "https://tenpointcrossbows.com/wp-json/suma/v1/data/epicor/post-sales-orders" \
-H "Auth-Token: your-token-here" \
>> /var/log/tenpoint/order-sync.log 2>&1

# Inventory sync every hour (at minute 0)
0 * * * * curl -X POST "https://tenpointcrossbows.com/wp-json/suma/v1/data/epicor/sync-inventory" \
-H "Auth-Token: your-token-here" \
>> /var/log/tenpoint/inventory-sync.log 2>&1

# Clean old logs daily (2 AM)
0 2 * * * curl -X POST "https://tenpointcrossbows.com/wp-json/suma/v1/data/epicor/clean_logs" \
-H "Auth-Token: your-token-here" \
>> /var/log/tenpoint/cleanup.log 2>&1

Why External Cron?

Advantages:

  • Guaranteed execution (WP-Cron only runs on page loads)
  • Precise scheduling
  • Better performance (doesn't slow page loads)
  • Separate logging
  • Independent of WordPress traffic

Disadvantages of WP-Cron:

  • Relies on site traffic to trigger
  • Can be delayed or skipped on low-traffic sites
  • Adds overhead to page loads
  • Harder to monitor and debug

Security

Authentication & Authorization

  • WP Admin: manage_woocommerce capability required
  • REST API: Token-based auth with timing-safe comparison
  • AJAX: Nonce verification on all requests
  • Epicor API: HTTP Basic Auth with encrypted credentials

Data Encryption

  • Algorithm: AES-256-CBC
  • Key: Derived from WordPress AUTH_KEY
  • IV: Random, unique per encryption
  • Storage: Encrypted passwords stored in wp_options

Input Validation

// Integer sanitization
$order_id = absint($_POST['order_id']);

// Text sanitization
$summary = sanitize_text_field($_POST['summary']);

// URL sanitization
$url = esc_url_raw($_POST['epicor_url']);

// JSON encoding
$json = wp_json_encode($data, JSON_UNESCAPED_SLASHES);

Database Security

All direct database queries use prepared statements:

$wpdb->prepare(
"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s",
'_sku',
$sku
);

Output Escaping

echo esc_html($order['summary']);
echo esc_url($order['epicor_url']);
echo esc_attr($order['status']);

Troubleshooting

Order Export Issues

Orders not exporting:

  1. Check order status is wc-processing
  2. Verify payment method is supported
  3. Check "Send to Epicor" toggle is enabled
  4. Review error logs in admin panel
  5. Test Epicor connection (Settings tab)

Export fails with "Invalid SKU":

  1. Verify product SKU matches Epicor part number
  2. Check part is active in Epicor
  3. Review part revision in Epicor
  4. Check getPartBAQ() response

Export succeeds but order stuck in processing:

  1. Check "Change Order Status" toggle is enabled
  2. Verify no other plugins preventing status change
  3. Review WooCommerce order notes
  4. Check PHP error logs

Inventory Sync Issues

Inventory not updating:

  1. Test Epicor connection
  2. Verify BAQ KP-AVAILINV exists and accessible
  3. Check product SKUs match Epicor part numbers
  4. Review inventory sync logs for errors
  5. Verify products don't have skip_inventory_update = 'yes'

Partial inventory updates:

  1. Check for SKU mismatches
  2. Review batch processing logs
  3. Verify database query performance
  4. Check for PHP memory limit issues

Incorrect stock levels:

  1. Verify Epicor BAQ returns correct data
  2. Check for unit conversion issues
  3. Review product "manage stock" setting
  4. Check for conflicting plugins

Performance Issues

Slow order export:

  1. Check Epicor API response times
  2. Verify network connectivity
  3. Review Guzzle timeout settings
  4. Check for large order quantities

Slow inventory sync:

  1. Review SQL query performance
  2. Check batch size (default: 100)
  3. Verify database indexes
  4. Monitor PHP memory usage

High memory usage:

  1. Reduce batch size in settings
  2. Increase PHP memory limit
  3. Review log file sizes
  4. Check for memory leaks in error logs

API Connection Issues

Connection timeouts:

  1. Verify Epicor URL is accessible
  2. Check firewall rules
  3. Increase Guzzle timeout (default: 30s)
  4. Review network latency

Authentication failures:

  1. Verify username/password correct
  2. Check credentials not expired
  3. Test with Postman or curl
  4. Review Epicor user permissions

SSL certificate errors:

  1. Verify SSL certificate valid
  2. Check certificate chain
  3. Temporarily disable SSL verify (dev only)
  4. Update CA bundle

Development

Local Setup

  1. Install PHP dependencies:

    cd wp-content/plugins/tenpoint-epicor-integration
    composer install --no-dev
  2. Compile assets:

    npm install
    npx mix --production
  3. Configure sandbox environment:

    • Use Epicor sandbox credentials
    • Set send_to_epicor to true for testing
    • Enable detailed_logs for debugging

Build Process

PHP Dependencies (Composer):

{
"require": {
"guzzlehttp/guzzle": "^7.0"
}
}

JS/CSS Compilation (Laravel Mix):

// webpack.mix.js
mix.js('src/js/Admin.js', 'dist/js')
.sass('src/scss/admin.scss', 'dist/css')
.version();

Build commands:

# Development
npx mix

# Production (minified)
npx mix --production

# Watch mode
npx mix watch

Testing

Generate Test Orders:

  1. Go to: Epicor Integration → Tools
  2. Set batch size (1-50)
  3. Choose coupon type (optional)
  4. Click "Generate Orders"
  5. Orders created with status wc-processing

Test Order Export:

  1. Generate test orders (above)
  2. Go to: Order Sync Logs tab
  3. Click "Run Order Sync Now"
  4. Review log entries for success/errors
  5. Check Epicor for created sales orders

Test Inventory Sync:

  1. Go to: Inventory Sync Logs tab
  2. Click "Run Inventory Sync Now"
  3. Review log entries
  4. Check WooCommerce products for updated stock

Debugging

Enable detailed logs:

  1. Go to: Epicor Integration → Settings
  2. Enable "Detailed Logs" toggle
  3. Save settings
  4. Review file logs: /wp-content/uploads/suma-tenpoint-epicor-integration/

View raw API requests:

  1. Enable detailed logs (above)
  2. Trigger order export or inventory sync
  3. Review epicor/{date}.log files
  4. Check request/response JSON

Database query debugging:

// Add to wp-config.php
define('SAVEQUERIES', true);

// View queries in admin panel
global $wpdb;
print_r($wpdb->queries);

Version History

v3.0.4 (Current)

  • Epicor Kinetic 2025.2 compatibility
  • OData $batch request optimization
  • Enhanced error handling
  • Improved logging system

v3.0.0

  • Settings migrated from constants to wp_options
  • AES-256 password encryption
  • Multi-environment support (live/sandbox)
  • REST API token generation
  • Admin UI redesign

v2.1.0

  • Migrated from Epicor 10 to Kinetic 2025.2
  • Email alert system
  • 60-day log retention

v2.0.0

  • Added inventory synchronization
  • Enhanced order export
  • Structured database logging

v1.0.0

  • Initial release
  • Basic order export functionality

External Resources