SortSwift
DocsSign InGet started
InventoryInventory OverviewBulk Lot BuilderMaster SetsChaos Sorting
Syncing
DocumentationSign In
SortSwift Docs
Getting Started
Core Features
Integrations & Tools
Support

Documentation In Progress

Content may be inaccurate or incomplete. For reliable help, contact support or join Discord

Sending Orders to Inventory

How completed buylist orders are sent to inventory with automatic SKU matching, cost tracking, and integration logging.

Overview


When a buylist order is marked as 'complete', store owners can send all or individual items to inventory. The system automatically matches cart items to SKU records, creates or updates inventory documents, tracks costs, and triggers downstream syncs to Shopify, CardTrader, and Manapool.

  • Prerequisites: Order must be in 'complete' status. Order must not already be sent to inventory (sentToInventory !== true).
  • Transaction Safety: Entire process runs in MongoDB transaction - if any step fails, all changes are rolled back
  • SKU Matching: System matches cart items to SKU records using productId, condAbbr, langAbbr, and printing (case-insensitive regex for printing)
  • Cost Tracking: Each inventory item stores cost (what you paid) and price (what you'll sell for, can be re-priced later)
  • Integration Logs: All imports logged in inventory.importLogs array for audit trail

API Endpoints


  • POST /api/buylist/orders/:orderId/send-to-inventory - Sends all non-sent items from order to inventory
  • POST /api/buylist/orders/:orderId/items/send-to-inventory - Sends single item to inventory. Requires body with productId, condAbbr, printing, langAbbr, quantity, remark, comment

Step-by-Step Process (Full Order)


  1. Validation:
    • Authenticates user and verifies store configuration
    • Fetches order by orderId and storeId
    • Validates order.orderStatus === 'complete' (returns 400 if not)
    • Validates order.sentToInventory !== true (returns 400 if already sent)
    • Starts MongoDB session and transaction
  2. Item Processing:
    • Iterates through order.cart array
    • Skips items where cartItem.sentToInventory === true
    • For each item, extracts: productId, categoryId, groupId, condAbbr, printing, langAbbr, paymentType, quantity
    • Performs SKU lookup: SKUModel.findOne with productId, condAbbr (defaults to 'NM'), langAbbr (defaults to 'EN'), printing (case-insensitive regex, defaults to 'Normal')
    • If SKU not found: Logs error and skips item (does not fail transaction)
    • Calculates costToUse based on paymentType:
      • Split: cashPrice + creditPrice
      • Cash: cashPrice
      • Credit: creditPrice
    • Calculates priceToUse: Uses req.body.price if provided and positive, otherwise uses same logic as costToUse
    • Creates newStockItem object with: skuId, quantity, price, cost, comment (from cartItem.notes), remarks (empty), cdn_link, cdn_back_link, condAbbr, langAbbr, printing, requireReprice: true, deleted: false, shopify_variant_id, cardtrader_listing_id, manapool_listing_id
    • Groups items by key = `${productId}-${categoryId}-${groupId}` into inventoryMap
  3. Inventory Upsert:
    • For each group in inventoryMap:
    • Checks if InventoryModel document exists for productId, categoryId, groupId, owner (user._id)
    • If New Inventory:
      • Creates new InventoryModel document
      • Sets stock array with all newStockItems
      • Creates importLogs array with entries for each stock item (includes _id, skuId, quantity, price, cost, comment, remarks)
      • Sets deleted: false, requireReprice: true
      • Saves document within transaction
    • If Existing Inventory:
      • If existingInventory.deleted === true, sets deleted: false and saves
      • For each newStockItem, checks for matching existing stock:
        • Match criteria: skuId matches, cost matches, comment matches, remarks matches
        • If match found: Updates existing stock item quantity (adds if not deleted, replaces if deleted), sets deleted: false, requireReprice: true, updates listing IDs
        • If multiple matches: Updates first, removes duplicates
        • If no match: Adds new stock item to array
      • Creates importLogs entries for all new items
      • Uses MongoDB bulkWrite operations for atomic updates
    • Stores inventoryId in inventoryIds array for later sync
    • Stores fallback price map: {inventoryId: [{skuId, price}, ...]} for sync API
  4. Order Update:
    • Marks all cart items as sentToInventory: true
    • Marks order.sentToInventory: true
    • Saves order document within transaction
  5. Transaction Commit:
    • Commits MongoDB transaction
    • Ends session
    • If any error occurs: Aborts transaction, ends session, returns 500 error
  6. Downstream Syncs:
    • After transaction committed, triggers async syncs (does not block response)
    • Extracts authorization header from request
    • For each inventoryId, sends POST request to sync-api.sortswift-services.com/inventory/sync
    • Payload includes: inventoryIds array, platforms: ['shopify', 'cardtrader', 'manapool'], fallback price map
    • Sync API updates Shopify variants, CardTrader listings, and Manapool listings with new inventory
    • Uses Promise.allSettled so failures don't block other syncs
  7. Response:
    • Returns success: true, message, and updated order object

Single Item Send Process


  1. Endpoint: POST /api/buylist/orders/:orderId/items/send-to-inventory
  2. Body Required: productId, condAbbr, printing, langAbbr, quantity, remark, comment
  3. Process: Similar to full order but processes only one item. Finds matching cart item, performs SKU lookup, creates/updates inventory, marks single item as sentToInventory, triggers syncs
  4. Use Case: Allows sending verified items individually as they're inspected, rather than waiting for entire order to be complete

SKU Matching Logic


The system matches cart items to SKU records using loose matching:

  • productId: Exact match required (number comparison)
  • condAbbr: Exact match, defaults to 'NM' if not provided
  • langAbbr: Exact match, defaults to 'EN' if not provided
  • printing: Case-insensitive regex match (^[printing]$), defaults to 'Normal' if not provided
  • Example Query: SKUModel.findOne({productId: 12345, condAbbr: 'NM', langAbbr: 'EN', printing: /^Foil$/i})
  • If No Match: Item is skipped, error logged, but transaction continues for other items

Inventory Document Grouping


Inventory items are grouped by productId, categoryId, and groupId into single InventoryModel documents:

  • One Document Per Product: All variants (conditions, languages, printings) of same product stored in single document
  • Stock Array: Each variant stored as separate object in stock[] array
  • Matching Logic: When adding to existing inventory, matches by skuId, cost, comment, remarks to determine if quantity should be added or new item created
  • Quantity Merging: If exact match found (same skuId, cost, comment, remarks), adds quantities together. If deleted item matched, replaces quantity instead of adding

Cost vs Price Tracking


  • Cost: What you paid for the item (costToUse). Used for profit calculations and accounting. Based on paymentType:
    • Cash: addedCashPrice
    • Credit: addedCreditPrice
    • Split: addedCashPrice + addedCreditPrice
  • Price: What you'll sell the item for (priceToUse). Can be overridden in req.body.price. If not provided, uses same logic as cost. Can be re-priced later via autoprice system (requireReprice flag enables this)
  • Import Logs: Both cost and price stored in importLogs array for audit trail

Downstream Integration Syncs


After inventory is updated, system triggers syncs to external platforms:

  • Endpoint: POST https://sync-api.sortswift-services.com/inventory/sync
  • Payload: {inventoryIds: [array], platforms: ['shopify', 'cardtrader', 'manapool'], fallback: {inventoryId: [{skuId, price}, ...]}}
  • Authorization: Uses Authorization header from original request (Bearer token)
  • Shopify Sync: Updates product variants with new inventory quantities and prices
  • CardTrader Sync: Updates CardTrader listings with new stock levels
  • Manapool Sync: Updates Manapool listings with new inventory
  • Error Handling: Sync failures are logged but don't fail the transaction (uses Promise.allSettled)
  • Listing IDs: SKU records contain shopify_variant_id, cardtrader_listing_id, manapool_listing_id which are used for updates

Error Handling


  • 400 Bad Request: Order not in 'complete' status, order already sent to inventory, missing required fields
  • 404 Not Found: Store configuration not found, order not found, SKU not found (logged but doesn't fail transaction)
  • 500 Server Error: Database errors, transaction failures - entire operation rolled back
  • SKU Not Found: Item skipped with warning log, transaction continues for other items
  • Transaction Rollback: If any critical error occurs, entire transaction aborted, no partial updates

Best Practices


  • Verify Before Sending: Mark items as 'exported' or 'verified' in order management UI before sending to inventory
  • Check SKU Coverage: Ensure all products have SKU records before sending orders (missing SKUs will be skipped)
  • Review Costs: Verify cost calculations are correct before sending (can't be easily changed after)
  • Send in Batches: For large orders, consider sending items individually as they're verified
  • Monitor Sync Results: Check sync API logs to ensure Shopify/CardTrader/Manapool updates succeeded
  • Backup Strategy: Export order CSV before sending to inventory as backup record
SortSwift

The all-in-one TCG store platform that unifies inventory, POS, scanning, buylist automation, and syncing to modernize your card shop operations.

Product
OverviewFeaturesPricing

© 2025 SortSwift. All rights reserved.Magic: The Gathering and its respective properties are © Wizards of the Coast.Yu-Gi-Oh! and its respective properties are © 2025 Studio Dice/SHUEISHA, TV TOKYO, KONAMI.Cardfight!! Vanguard and Weiß Schwarz are © bushiroad All Rights Reserved.Shadowverse: Evolve is © Cygames, Inc.Godzilla Card Game is TM & © TOHO CO., LTD.hololive OFFICIAL CARD GAME is © COVER©2025 Pokémon. ©1995 - 2025 Nintendo/Creatures Inc./GAME FREAK Inc. TM, ®Nintendo.Disney Lorcana and its respective properties are © Disney

Privacy PolicyTerms of ServiceAccessibility