perkun.eu Services Portfolio Blog About Contact PL
← Blog

9/3/2024

WooCommerce dropshipping — automatic margin sync with your supplier

TL;DR: Per-category margin multiplier + automatic calculation at XML import = zero manual price setting. Changing the margin for the “IP Cameras” category from 1.3x to 1.4x = 1 config entry, and the next import recalculates everything else.

Dropshipping is a model where supplier prices change regularly — new stock, exchange rates, seasonality. If your store has 2,700 products, manually updating prices after every supplier change takes several hours. Automatic sync reduces this to running a cron job.

The problem with manual price management

With a catalog of 2,700 products, changing a pricing strategy — for example raising the margin for the entire “DVR Recorders” category from 30% to 40% — means (in the manual approach): exporting a CSV from WooCommerce, opening it in Excel, filtering by category, applying a formula to the price column, saving, and importing back. The procedure takes 2 hours and is prone to errors: it’s easy to overwrite the wrong column, import an old file, or miss products in subcategories.

The worse scenario: the supplier changes prices weekly. With every change you need to recalculate all products, because your price should be: supplier_price × margin. If you don’t update regularly, you’re selling below margin.

The solution: supplier price is always the source of truth. Margins live in configuration. XML import calculates automatically.

Margin configuration structure

A simple PHP array as a config file:

// config/margins.php
return [
    'ip-cameras'         => 1.35,
    'dome-cameras'       => 1.35,
    'bullet-cameras'     => 1.30,
    'dvr-recorders'      => 1.40,
    'nvr-recorders'      => 1.38,
    'cctv-accessories'   => 1.45,
    'power-supplies'     => 1.50,
    'cabling'            => 1.55,
    '_default'           => 1.30,  // fallback for unknown categories
];

Price calculation function:

function calculatePrice(float $supplierPrice, string $categorySlug): float {
    $margins = config('margins');
    $multiplier = $margins[$categorySlug] ?? $margins['_default'];
    return round($supplierPrice * $multiplier, 2);
}

round() to 2 decimal places prevents prices like 149.99999 in the database.

Integration with the XML importer

When parsing the supplier’s XML feed (e.g. Hurton format), for each product:

function importProduct(SimpleXMLElement $node): void {
    $supplierPrice = (float) $node->cena_netto;
    $categorySlug  = sanitizeSlug((string) $node->kategoria);

    $finalPrice    = calculatePrice($supplierPrice, $categorySlug);
    $sku           = (string) $node->symbol;

    // Check if the product exists
    $productId = wc_get_product_id_by_sku($sku);

    if ($productId) {
        // Update existing
        $product = wc_get_product($productId);
        $product->set_regular_price($finalPrice);
        $product->save();
    } else {
        // New product
        $product = new WC_Product_Simple();
        $product->set_sku($sku);
        $product->set_name((string) $node->nazwa);
        $product->set_regular_price($finalPrice);
        // ... other fields
        $product->save();
    }
}

Importing 2,700 products with proper use of wc_get_product_id_by_sku() (a query on meta_value) takes 3–8 minutes depending on the server. Optimization: batch updates via direct SQL queries on the _regular_price field instead of using the WooCommerce API.

Dynamic margin

A more advanced approach — margin depending on the base product price. Cheap products have low absolute profit at a small margin, so a higher multiplier makes sense:

function calculateDynamicPrice(float $supplierPrice, string $categorySlug): float {
    $margins = config('margins');
    $baseMultiplier = $margins[$categorySlug] ?? $margins['_default'];

    // Correction based on price
    if ($supplierPrice < 50) {
        $correction = 1.15;  // +15% for cheap products
    } elseif ($supplierPrice < 200) {
        $correction = 1.05;  // +5% for mid-range
    } else {
        $correction = 1.00;  // no correction for expensive items
    }

    return round($supplierPrice * $baseMultiplier * $correction, 2);
}

Effect: a product costing 20 PLN from the supplier with margin 1.35 × 1.15 = 1.5525 = price 31 PLN (absolute margin 11 PLN). The same multiplier for a 1000 PLN product gives a 1350 PLN price (margin 350 PLN) — no correction needed there.

Margin monitoring

WooCommerce hook to detect sales below the margin threshold:

add_action('woocommerce_order_item_meta_end', function($item_id, $item, $order) {
    $product      = $item->get_product();
    $salePrice    = (float) $item->get_total() / $item->get_quantity();
    $costMeta     = get_post_meta($product->get_id(), '_supplier_price', true);
    $supplierCost = (float) $costMeta;

    if ($supplierCost > 0) {
        $margin = ($salePrice - $supplierCost) / $salePrice;
        if ($margin < 0.15) {
            // Alert: margin below 15%
            wp_mail(
                'admin@yourshop.com',
                'Alert: low margin',
                "Product {$product->get_name()} sold with margin " . round($margin * 100) . "%"
            );
        }
    }
}, 10, 3);

It’s also worth saving the supplier price as a _supplier_price meta field at every import. That gives you a history of supplier price changes and lets you analyze trends.

Summary

Automatic margin sync is essential for dropshipping with more than a few hundred products. Configuration as a PHP array, calculation at import, alert monitoring — a system that takes a week to build once and saves hours every week. With a store of 2,700 products and weekly supplier price changes — it’s a necessity.