Реалізація автоматичного розрахунку маржі при дропшипінгу

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація автоматичного розрахунку маржі при дропшипінгу
Середня
~2-3 робочих дні
Часті питання

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

Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація автоматичного розрахунку маржі при дропшиппінгу

Маржа в дропшиппінгу — розниця між розничною ціною магазину та закупочною ціною постачальника за вирахуванням інших витрат: еквайринг, доставка, реклама. Автоматичний розрахунок потрібен, щоб при зміні закупочної ціни рознична ціна пересчитувалась за заданими правилами без участі менеджера.

Що впливає на підсумкову маржу

  • Закупочна ціна — змінюється при кожній синхронізації прайса постачальника
  • Комісія еквайринга — 1.5–3.5% в залежності від платіжної системи
  • Стоимість доставки — може компенсуватися або включатися в ціну
  • Повернення — статистичний відсоток, закладається в наценку
  • Реклама — CAC (cost per acquisition) для розрахунку точки безубиточності

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

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();
});

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

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
    {
        // Приклад table_config:
        // [{"max_price": 500, "percent": 50}, {"max_price": 2000, "percent": 35},
        //  {"max_price": 10000, "percent": 25}, {"max_price": null, "percent": 15}]
        foreach ($table as $tier) {
            if ($tier['max_price'] === null || $price <= $tier['max_price']) {
                return $price * (1 + $tier['percent'] / 100);
            }
        }
        return $price * 1.15; // fallback
    }
}

Resolver правил маржі

Правила застосовуються в порядку специфічності: товар > категорія > постачальник > глобальні.

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();
    }
}

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

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();
    }
}

Терміни

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