{{-- ── STYLES ────────────────────────────────────────────────────────────── --}}
{{-- ── SCHEDULED JOBS ───────────────────────────────────────────────────── --}}
Scheduled jobs (Supervisor · queue worker required)
SyncEbayOrders · every 5 min ProcessStalePrices · every 5 min ScrapeAllmakesPsp · every 6 hours SyncShopifyOrders · ⚠ NOT scheduled — reconciles fulfilled + cancelled on each manual run
{{-- ── LEGEND ────────────────────────────────────────────────────────────── --}}
External system
Job / Service
Confirmed working
Gap / Warning
Decision / Branch
Theme-aware — switches with Filament dark/light toggle
{{-- ── DIAGRAM 1: eBay Order Chain ──────────────────────────────────────── --}}
TRIGGER eBay Order Received SyncEbayOrders · every 5 min
{{-- ── DIAGRAM 2: Shopify Order Chain ──────────────────────────────────── --}}
TRIGGER Shopify Order Received Webhook orders/create · SyncShopifyOrders
{{-- ── DIAGRAM 3: Order Processed Chain ──────────────────────────────────── --}}
EVENT Order Marked as Processed → Stock Deduction → Channel Sync → Fulfillment Dispatch OrderObserver · shared across all order sources
{{-- ── DIAGRAM 4: Stock / Product Changes ──────────────────────────────── --}}
TRIGGER Stock or Product Changed ProductQuantityObserver · ProductObserver
{{-- ── DIAGRAM 5a: Pricing Engine — stale products ─────────────────────── --}}
SCHEDULED Pricing Engine — Individual stale products ProcessStalePrices · every 5 min
{{-- ── DIAGRAM 5b: Pricing Engine — fee profile / rule change ───────────── --}}
EVENT Pricing Engine — Fee profile or rule changed / Force Update All SKUs ChannelFeeProfileObserver · PricingRuleObserver · supersede-safe fan-in
{{-- ── DIAGRAM 6: AllmakesPSP ───────────────────────────────────────────── --}}
SCHEDULED Allmakes PSP Connector ScrapeAllmakesPsp · every 6 hours
{{-- ── SHOPIFY SECTION HEADER ──────────────────────────────────────────── --}}
Shopify listings & pricing — detailed routing
{{-- ── DIAGRAM S1: Listing status lifecycle ────────────────────────────── --}}
STATUS Listing status lifecycle channel_listings.status · drives tab counts in ManageListingsV2
{{-- ── DIAGRAM S2: Create new listings ─────────────────────────────────── --}}
CREATE Create new Shopify listings — ShopifyBatchCreateJob ManageListingsV2 · Not Listed tab → "Create Listings"
{{-- ── DIAGRAM S3: Batch sync phases ───────────────────────────────────── --}}
SYNC Update existing listings — sync paths Bulk: ShopifyBatchSyncJob · Single product: SyncProductToChannels (SEO, description, all listing attrs)
{{-- ── DIAGRAM S4: Price sync routing ──────────────────────────────────── --}}
PRICE Price sync — automatic paths to Shopify Manual tick + Sync = full sync (see SYNC diagram above)
{{-- ── SHOPIFY API FIELD TABLE ───────────────────────────────────────────── --}}
API Shopify API field routing — which mutation handles what
@foreach([ ['Title', 'productUpdate', 'bulk-jsonl', 'BatchSyncJob · Phase 3', ''], ['Description', 'productUpdate', 'bulk-jsonl', 'BatchSyncJob · Phase 3', ''], ['Vendor / Type / Tags', 'productUpdate', 'bulk-jsonl', 'BatchSyncJob · Phase 3', ''], ['Published status', 'productUpdate', 'bulk-jsonl', 'BatchSyncJob · Phase 3', ''], ['URL handle', 'productUpdate', 'per-product', 'BatchSyncJob · Phase 3.6', 'redirectNewHandle:true — bulk JSONL silently ignores handle'], ['Images', 'REST /products/{id}', 'rest', 'BatchSyncJob · Phase 3.5', 'GraphQL bulk ops do not support src image uploads'], ['Price ⚠', 'productVariantsBulkUpdate', 'bulk-jsonl', 'BulkPriceUpdateJob', 'NOT productUpdate — silently ignored on new product model (2024+)'], ['Cost price', 'productVariantsBulkUpdate', 'bulk-jsonl', 'BulkPriceUpdateJob', 'via inventoryItem.cost'], ['Barcode / EAN', 'productVariantsBulkUpdate', 'per-product', 'BatchSyncJob · Phase 3.6b', ''], ['Weight', 'productVariantsBulkUpdate', 'per-product', 'BatchSyncJob · Phase 3.6b', 'via inventoryItem.measurement.weight'], ['Stock quantity', 'inventorySetQuantities', 'batched', 'BatchSyncJob · Phase 4', '250 items per GQL call · per warehouse → Shopify location mapping'], ['Stock quantity', 'REST inventory', 'rest', 'SyncProductToChannels', 'Observer on ProductQuantity change or order fulfilment'], ['SEO Title (title_tag)', 'REST /products/{id}/metafields.json', 'rest', 'SyncProductToChannels', 'namespace=global · GET then PUT/POST upsert · API 2024-01 removed shorthand from product body'], ['SEO Description (description_tag)', 'REST /products/{id}/metafields.json', 'rest', 'SyncProductToChannels', 'namespace=global · same upsert as title_tag · falls back to meta_description then stripped description'], ] as [$field, $mutation, $mode, $phase, $note]) @endforeach
Field Mutation / Endpoint Mode Job / Phase Notes
{{ $field }} {{ $mutation }} @if($mode === 'bulk-jsonl') Bulk JSONL @elseif($mode === 'per-product') Per-product @elseif($mode === 'batched') Batched GQL @else REST @endif {{ $phase }} {{ $note }}
{{-- ── GAPS PANEL ────────────────────────────────────────────────────────── --}}
⚠ Known gaps & issues in automation chains
HIGH
SyncShopifyOrders is not scheduled.
Shopify orders only import if the webhook fires or the job is manually triggered. Fix: add SyncShopifyOrders::dispatch() to the scheduler in bootstrap/app.php every 5–10 min.
HIGH
eBay → Shopify: if the webhook fails, the Cydekick order table stays empty.
The eBay sync audit will show "1 imported" but no order appears until SyncShopifyOrders runs. Scheduling it (above) fixes this automatically.
MED
Products in warehouses with no LocationMapping never trigger channel sync on stock change.
ProductQuantityObserver exits silently if no mapping row exists for that warehouse. Check: Channels → Stock Location Mappings — every active picking location should have a row.
MED
Order lines with no active SkuMapping are not synced to any channel after fulfilment.
Stock is deducted internally but qty is never pushed to Shopify or eBay. Lines appear as [UNLINKED] in Channel Orders. Check: Channels → Sync Reference for unlinked products.
MED
Product name / description changes set needs_update but nothing auto-pushes to Shopify or eBay.
ProductObserver flags the listing but no job is dispatched. Listings go stale silently. Use Listings → Needs Update tab to manually push, or add a dispatch call to ProductObserver.
MED
eBay prices are never auto-pushed by the pricing engine.
RecalculateProductChannelPrice explicitly skips eBay channels when dispatching sync ("Only Shopify channels have SKU mappings to sync"). eBay listing prices are only updated when a listing is manually pushed or created.
INFO
SyncProductToChannels is the active per-product sync path — not deprecated for single-product operations.
Handles all single-product listing pushes: Update button (Product → Channel Listings), Push Now (Channels → Listings), Save & Sync (modal), observer-driven stock sync, and price recalculation. Pushes title, description, SEO title/description, vendor, tags, handle, price, barcode, weight, images, stock, and fitment metafields. ShopifyBatchSyncJob handles bulk/scheduled operations only and does not push SEO.
LIVE
Order fulfillment dispatch — Shopify + eBay (2026-08-26).
When an order transitions open → processed, OrderFulfillmentService::dispatch() automatically pushes tracking + carrier to the channel via FulfillableChannelDriver::fulfillOrder(). ShopifyClient and EbayClient both implement the interface. API errors are logged but do not block the order save — use the Resend Dispatch button on ViewOrder to retry. The dispatched_at column on orders records when dispatch succeeded.
{{-- ── MERMAID ───────────────────────────────────────────────────────────────── --}}