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:
- Queries WC orders matching criteria:
- Status:
wc-processing - Not yet exported (
_tenpoint-epicor-integration_exported_to_epicormeta not set) - Valid payment method:
ebizcharge,ppcp-gateway,paytomorrow(with lender), or $0 orders
- Status:
- 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_skumeta
- Creates Sales Order in Epicor via REST API
- Uses OData
$batchrequests to group up to 4 operations per line item (reduces HTTP calls) - 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-pendingorwc-completed
- Sets order meta:
- 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:
| Method | Plugin Slug | Notes |
|---|---|---|
| EBizCharge | ebizcharge | Credit card/ACH |
| PayPal | ppcp-gateway | PayPal Commerce Platform |
| PayTomorrow | paytomorrow | Must have lender metadata |
| Zero-dollar orders | N/A | Free products, gift cards only |
2. Inventory Synchronization (Epicor → WooCommerce)
Trigger: External REST endpoint or admin manual button click
Flow:
- Queries Epicor BAQ:
KP-AVAILINV- Returns:
Part_PartNum,Calculated_AvailInventory
- Returns:
- Builds SKU → quantity map from Epicor response
- Resolves SKU →
post_idmappings via database query - Filters out products with
skip_inventory_update = 'yes'meta - Bulk SQL updates (batches of 100):
- Updates
wp_postmeta:_stock,_stock_status - Updates
wp_wc_product_meta_lookuptable - Updates product visibility taxonomy terms
- Updates
- Cache management:
- Flushes WooCommerce product object caches
- Clears WooCommerce transients
- Purges Cloudflare cache for entire zone
- 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/WHENSQL
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
| Method | HTTP Verb | Epicor Service | Purpose |
|---|---|---|---|
createSalesOrders() | POST | Erp.BO.SalesOrderSvc/SalesOrders | Create order header |
createSalesOrderDtl() | POST | Erp.BO.SalesOrderSvc/OrderDtls | Create detail line |
createSalesOrderRel() | POST | Erp.BO.SalesOrderSvc/OrderRels | Create release |
createSalesOrderMscs() | POST | Erp.BO.SalesOrderSvc/OrderMscs | Create misc charge |
updateSalesOrderHed() | PATCH | Erp.BO.SalesOrderSvc/SalesOrders | Update header |
updateSalesOrderRel() | PATCH | Erp.BO.SalesOrderSvc/OrderRels | Update release |
Query Methods
| Method | HTTP Verb | Purpose |
|---|---|---|
validatePartDescription() | GET | Validate SKU against parts catalog |
getOrderByID() | POST | Retrieve full sales order |
getOrderStatus() | GET | Check order open/closed status |
getPartBAQ() | GET | Part revision lookup (KP-REVS BAQ) |
getEpicorInventory() | GET | Inventory levels (KP-AVAILINV BAQ) |
testConnection() | GET | Connectivity test |
sendBatchRequest() | POST | OData $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)
| Endpoint | Method | Purpose | Cron Usage |
|---|---|---|---|
export/get-orders | GET | Return pending WC orders | Manual |
export/get-single-order | GET | Single order by ID | Manual |
epicor/post-sales-orders | GET | Trigger order export | ✅ Every 5 min |
epicor/sync-inventory | POST | Trigger inventory sync | ✅ Hourly |
epicor/get-sales-orders | GET | Retrieve Epicor orders | Manual |
epicor/get-part-description | GET | Part details by SKU | Manual |
epicor/get-order-status | GET | Check Epicor order status | Manual |
epicor/get-part-revision | GET | Part revisions from BAQ | Manual |
epicor/get-inventory | POST | Inventory for specific part | Manual |
epicor/public-get-inventory | GET | Public inventory query | Manual |
epicor/manage-inventory-batch | GET | Enable manage_stock on all products | Admin |
epicor/update-order-status | GET | Update WC statuses from Epicor | Manual/webhook |
epicor/clean_logs | POST | Delete logs older than 60 days | ✅ Daily |
7. Logging System
Structured Database Logs
Table: wp_tpi_sync_logs
| Column | Type | Purpose |
|---|---|---|
id | BIGINT | Primary key |
log_type | VARCHAR(20) | 'order' or 'inventory' |
status | VARCHAR(20) | 'success', 'error', 'warning' |
wc_order_id | BIGINT | WooCommerce order ID (nullable) |
epicor_order_num | VARCHAR(50) | Epicor OrderNum (nullable) |
summary | TEXT | Human-readable summary |
details | LONGTEXT | JSON with full request/response data |
created_at | DATETIME | Timestamp |
Features:
- Searchable across
summary,wc_order_id,epicor_order_num - Filterable by
log_typeandstatus - 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 logsinventory/— Inventory sync logsepicor/— 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_logssetting)
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 Key | Value | Set When |
|---|---|---|
_tenpoint-epicor-integration_exported_to_epicor | 1 | Order exported successfully |
_tenpoint-epicor-integration_epicor_order | Epicor OrderNum | Order exported successfully |
_tenpoint-epicor-integration_epicor_failure | 1 | Order export failed |
_epicor_order_export_date | Datetime string | Order 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
Recommended Setup (External Cron)
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_woocommercecapability 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:
- Check order status is
wc-processing - Verify payment method is supported
- Check "Send to Epicor" toggle is enabled
- Review error logs in admin panel
- Test Epicor connection (Settings tab)
Export fails with "Invalid SKU":
- Verify product SKU matches Epicor part number
- Check part is active in Epicor
- Review part revision in Epicor
- Check
getPartBAQ()response
Export succeeds but order stuck in processing:
- Check "Change Order Status" toggle is enabled
- Verify no other plugins preventing status change
- Review WooCommerce order notes
- Check PHP error logs
Inventory Sync Issues
Inventory not updating:
- Test Epicor connection
- Verify BAQ
KP-AVAILINVexists and accessible - Check product SKUs match Epicor part numbers
- Review inventory sync logs for errors
- Verify products don't have
skip_inventory_update = 'yes'
Partial inventory updates:
- Check for SKU mismatches
- Review batch processing logs
- Verify database query performance
- Check for PHP memory limit issues
Incorrect stock levels:
- Verify Epicor BAQ returns correct data
- Check for unit conversion issues
- Review product "manage stock" setting
- Check for conflicting plugins
Performance Issues
Slow order export:
- Check Epicor API response times
- Verify network connectivity
- Review Guzzle timeout settings
- Check for large order quantities
Slow inventory sync:
- Review SQL query performance
- Check batch size (default: 100)
- Verify database indexes
- Monitor PHP memory usage
High memory usage:
- Reduce batch size in settings
- Increase PHP memory limit
- Review log file sizes
- Check for memory leaks in error logs
API Connection Issues
Connection timeouts:
- Verify Epicor URL is accessible
- Check firewall rules
- Increase Guzzle timeout (default: 30s)
- Review network latency
Authentication failures:
- Verify username/password correct
- Check credentials not expired
- Test with Postman or curl
- Review Epicor user permissions
SSL certificate errors:
- Verify SSL certificate valid
- Check certificate chain
- Temporarily disable SSL verify (dev only)
- Update CA bundle
Development
Local Setup
-
Install PHP dependencies:
cd wp-content/plugins/tenpoint-epicor-integration
composer install --no-dev -
Compile assets:
npm install
npx mix --production -
Configure sandbox environment:
- Use Epicor sandbox credentials
- Set
send_to_epicortotruefor testing - Enable
detailed_logsfor 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:
- Go to: Epicor Integration → Tools
- Set batch size (1-50)
- Choose coupon type (optional)
- Click "Generate Orders"
- Orders created with status
wc-processing
Test Order Export:
- Generate test orders (above)
- Go to: Order Sync Logs tab
- Click "Run Order Sync Now"
- Review log entries for success/errors
- Check Epicor for created sales orders
Test Inventory Sync:
- Go to: Inventory Sync Logs tab
- Click "Run Inventory Sync Now"
- Review log entries
- Check WooCommerce products for updated stock
Debugging
Enable detailed logs:
- Go to: Epicor Integration → Settings
- Enable "Detailed Logs" toggle
- Save settings
- Review file logs:
/wp-content/uploads/suma-tenpoint-epicor-integration/
View raw API requests:
- Enable detailed logs (above)
- Trigger order export or inventory sync
- Review
epicor/{date}.logfiles - 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