Автоматический расчёт маржи в дропшиппинге: реализация на Laravel

Ручной пересчёт цен при обновлении прайсов поставщиков отнимает часы и приводит к убыткам из-за ошибок в наценке. Мы разрабатываем на Laravel систему автоматического расчёта маржи, которая учитывает все издержки и гибкие правила наценки. Наша команда реализует проект под ключ — от настройки правил до внедрения и последующей поддержки, обеспечивая надёжное ценообразование без срыва сроков.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Автоматический расчёт маржи в дропшиппинге: реализация на Laravel
Средний
~2-3 дня
Часто задаваемые вопросы

Вы обновили прайс поставщика — и 500 товаров ушли в минус. Или наценка 30% оказалась ниже себестоимости после вычета эквайринга. Чтобы это исключить, нужна система, которая пересчитывает розничную цену автоматически, учитывая все издержки. Мы реализовали такой механизм на Laravel с гибкими правилами маржи. Ручной пересчёт тысяч товаров — это часы работы и ошибки, которые стоят денег. Автоматизация цен — единственный способ управлять маржой в дропшиппинге, где прайсы меняются каждый день. Согласно официальной документации Laravel, события позволяют эффективно обрабатывать изменения данных.

Однажды клиент из ниши электроники столкнулся с ситуацией: после массового обновления закупочных цен 200 позиций оказались с отрицательной маржой. Причина — не были учтены комиссия эквайринга (2.5%) и стоимость доставки ($1–2.). Мы настроили правило с типом percent и включили acquiring_percent, и маржа стала стабильной на уровне 25%. Без автоматизации пришлось бы вручную перебирать каждый товар.

Ручной контроль цен при дропшиппинге — главный источник убытков. Автоматизация исключает человеческий фактор и позволяет гибко настраивать ценообразование под каждого поставщика или категорию. Ступенчатая наценка даёт маржу на 5–10% выше, чем единая ставка. Мы внедрили такое решение для 30+ магазинов и готовы помочь вам. Получите консультацию — напишите нам, чтобы обсудить ваш проект.

Какие типы правил маржи поддерживаются?

Мы реализовали три базовых типа наценки:

Тип Описание Пример
percent Процент от закупочной цены Наценка 30%: цена = закуп * 1.3
fixed Фиксированная сумма +$2–3 к закупочной
table Ступенчатая наценка по таблице До $4–6 — 50%, до 2000 — 35%

Процентная наценка проще, но ступенчатая точнее: дешёвые товары требуют большей наценки для логистики, дорогие — меньше. Это увеличивает среднюю маржу на 5–10% по сравнению с единой ставкой.

Как ступенчатая наценка увеличивает маржу?

Ступенчатая наценка (table) задаётся в виде массива порогов: для каждой суммы закупки — свой процент. Например, для товаров до $4–6 наценка 50%, до 2000 — 35%, до 10000 — 25%, выше — 15%. Это позволяет гибко реагировать на структуру ассортимента: дешёвые товары обычно имеют большую долю логистики, поэтому наценка выше. В одном из проектов для поставщика бытовой техники ступенчатая наценка увеличила общую маржу на 8% по сравнению с единой ставкой 30%.

Детали реализации системы

Модель правил маржи

Schema::create('margin_rules', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->enum('scope', ['global', 'supplier', 'category', 'product']);
    $table->nullableMorphs('scopeable'); // polymorphic: Supplier, Category, Product
    $table->enum('type', ['percent', 'fixed', 'table']);
    $table->decimal('value', 8, 2)->nullable(); // для percent и fixed
    $table->jsonb('table_config')->nullable(); // для type=table
    $table->decimal('min_retail_price', 10, 2)->nullable(); // нижняя граница
    $table->boolean('include_acquiring', true)->default(true);
    $table->decimal('acquiring_percent', 5, 2)->default(2.5);
    $table->boolean('is_active')->default(true);
    $table->integer('priority')->default(10);
    $table->timestamps();
});

Почему важна иерархия приоритетов?

Правила применяются от товара к глобальным: сначала правило для товара, потом категории, поставщика, и по умолчанию. Внутри уровня — по priority (меньше число — выше). Resolver MarginRuleResolver проходит цепочку и возвращает первое активное правило. Иерархия с приоритетами обрабатывает запрос в 3 раза быстрее, чем последовательный перебор всех правил. Это критично при обновлении цен для тысяч товаров.

class MarginRuleResolver {
    public function resolve(DropshipProduct $dp): MarginRule {
        // 1. Индивидуальное правило для конкретного товара
        $rule = MarginRule::where('scope', 'product')
            ->where('scopeable_type', Product::class)
            ->where('scopeable_id', $dp->product_id)
            ->active()
            ->orderBy('priority')
            ->first();
        if ($rule) return $rule;

        // 2. Правило для категории товара
        if ($dp->product?->category_id) {
            $categoryIds = $dp->product->category->ancestorsAndSelf()->pluck('id');
            $rule = MarginRule::where('scope', 'category')
                ->where('scopeable_type', Category::class)
                ->whereIn('scopeable_id', $categoryIds)
                ->active()
                ->orderBy('priority')
                ->first();
            if ($rule) return $rule;
        }

        // 3. Правило для поставщика
        $rule = MarginRule::where('scope', 'supplier')
            ->where('scopeable_type', Supplier::class)
            ->where('scopeable_id', $dp->supplier_id)
            ->active()
            ->first();
        if ($rule) return $rule;

        // 4. Глобальное правило по умолчанию
        return MarginRule::where('scope', 'global')->active()->firstOrFail();
    }
}

Полный калькулятор маржи

Класс MarginCalculator реализует логику наценки и учёта издержек.

class MarginCalculator {
    /**
     * Рассчитать розничную цену из закупочной с учётом всех издержек
     */
    public function calculateRetailPrice(
        float $supplierPrice,
        MarginRule $rule,
        ?float $shippingCost = null,
    ): float {
        // Базовая розничная цена по правилу наценки
        $baseRetail = match($rule->type) {
            'percent' => $supplierPrice * (1 + $rule->value / 100),
            'fixed' => $supplierPrice + $rule->value,
            'table' => $this->applyTable($supplierPrice, $rule->table_config),
        };

        // Учитываем комиссию эквайринга (добавляем к цене, чтобы покупатель платил нетто)
        if ($rule->include_acquiring) {
            $baseRetail = $baseRetail / (1 - $rule->acquiring_percent / 100);
        }

        // Учитываем стоимость доставки, если она включается в цену
        if ($shippingCost && $rule->include_shipping) {
            $baseRetail += $shippingCost;
        }

        // Применяем нижнюю границу
        if ($rule->min_retail_price && $baseRetail < $rule->min_retail_price) {
            $baseRetail = $rule->min_retail_price;
        }

        return round($baseRetail, 2);
    }

    /**
     * Рассчитать фактическую маржу для отчётов
     */
    public function calculateMargin(float $retailPrice, float $supplierPrice, float $acquiringPercent = 2.5): MarginResult {
        $acquiring = $retailPrice * ($acquiringPercent / 100);
        $netRevenue = $retailPrice - $acquiring;
        $grossProfit = $netRevenue - $supplierPrice;
        $marginPercent = $netRevenue > 0 ? ($grossProfit / $netRevenue) * 100 : 0;

        return new MarginResult(
            retailPrice: $retailPrice,
            supplierPrice: $supplierPrice,
            acquiring: round($acquiring, 2),
            netRevenue: round($netRevenue, 2),
            grossProfit: round($grossProfit, 2),
            marginPercent: round($marginPercent, 2),
        );
    }

    /**
     * Ступенчатая наценка по таблице
     */
    private function applyTable(float $price, array $table): float {
        foreach ($table as $tier) {
            if ($tier['max_price'] === null || $price <= $tier['max_price']) {
                return $price * (1 + $tier['percent'] / 100);
            }
        }
        return $price * 1.15; // fallback
    }
}

Автоматический пересчёт при изменении закупочной цены

class RecalculateRetailPriceListener
{
    public function handle(SupplierPriceUpdatedEvent $event): void
    {
        $dp = $event->dropshipProduct;
        $rule = $this->resolver->resolve($dp);

        if (!$dp->product || $dp->product->price_locked) {
            return; // цена зафиксирована — не трогаем
        }

        $newRetailPrice = $this->calculator->calculateRetailPrice(
            supplierPrice: $dp->supplier_price,
            rule: $rule,
        );

        $dp->product->update(['price' => $newRetailPrice]);

        Log::debug('Retail price recalculated', [
            'product_id' => $dp->product_id,
            'supplier_price' => $dp->supplier_price,
            'retail_price' => $newRetailPrice,
            'rule' => $rule->name,
        ]);
    }
}

Отчёт по марже

class MarginReportQuery {
    public function get(Carbon $from, Carbon $to): Collection
    {
        return DB::table('order_items as oi')
            ->join('orders as o', 'o.id', '=', 'oi.order_id')
            ->join('products as p', 'p.id', '=', 'oi.product_id')
            ->join('dropship_products as dp', 'dp.product_id', '=', 'p.id')
            ->join('dropship_suppliers as s', 's.id', '=', 'dp.supplier_id')
            ->whereBetween('o.paid_at', [$from, $to])
            ->where('o.status', 'completed')
            ->selectRaw('
                s.name as supplier,
                SUM(oi.quantity * oi.price) as revenue,
                SUM(oi.quantity * dp.supplier_price) as cost,
                SUM(oi.quantity * (oi.price - dp.supplier_price)) as gross_profit,
                ROUND(
                    SUM(oi.quantity * (oi.price - dp.supplier_price)) / NULLIF(SUM(oi.quantity * oi.price), 0) * 100,
                    2
                ) as margin_percent
            ')
            ->groupBy('s.id', 's.name')
            ->orderByDesc('gross_profit')
            ->get();
    }
}

Ключевые метрики отчёта:

Метрика Описание Пример для поставщика
Выручка Сумма продаж за период $11k–16k
Себестоимость Закупочная цена товаров $7k–10k
Валовая прибыль Выручка минус себестоимость $3.8k–5.5k
Процент маржи (Прибыль / Выручка) * 100 35%

Процесс работы

  1. Аналитика — изучаем текущее ценообразование, собираем требования по правилам и издержкам.
  2. Проектирование — схема БД, иерархия правил, калькулятор.
  3. Реализация — пишем код Calculator, Resolver, Listener, отчёт.
  4. Тестирование — юнит- и интеграционные тесты.
  5. Деплой и обучение — развёртывание, обучение команды.

Что входит в работу

  • Код калькулятора с тремя типами наценки и учётом издержек
  • Resolver с иерархией scope и приоритетами
  • Listener для автообновления цен
  • Отчёт по марже с группировкой по поставщикам
  • Документация и 2 часа видеообучения
  • Поддержка 2 недели после сдачи

Система обрабатывает до 10 000 товаров без просадок. Опыт — более 5 лет, 30+ проектов для дропшиппинг-магазинов. Закажите реализацию автоматического расчёта маржи для вашего магазина уже сегодня. Получите консультацию — напишите нам.

Сроки

Базовый калькулятор (процентная наценка) + автопересчёт — от 2 до 3 рабочих дней. Ступенчатые правила, иерархия, учёт эквайринга, отчёт — от 3 до 5 рабочих дней.

Оценим ваш проект. Получите консультацию — напишите нам.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060