I am using Sylius just as a housekeeping for my local business. My plan is to create fake online orders for each live offline order and get the sales reports.
While there’s a nice plugin for sales reports, I cannot find a way to factor in seller costs of a product. Say, it costs me $5 to produce and store 1 unit of product, then another $10 on marketing each week. I want to know how much of a profit do I have over week with actual product prices (say, last week I sold 10 units $15 each and this week was 20 units $12 each).
Am I missing some setting in product or warehouse setup?
How do I track changing costs if my supplies get more expensive or cheaper and my seller / purchase cost fluctuates?
There’s a min price in product, but it’s about discounts and minimum profitability, not actual product cost.
Update: There’s a CostPricePlugin for adding Cost Price to products, which is nice, but lacks support update. There’s a SupplierPlugin that adds cost price per supplier per variant, which is very nice, but it also lacks reports support. There’s a SyliusSalesReportsPlugin for sales reports, but it’s only for basic price reports.
Sylius does not support this functionality out-of-the-box, and apart from the plugins you’ve already mentioned, there doesn’t appear to be any other solutions directly addressing your needs. You’ll most likely need to develop a custom solution. Here’s a general approach based on a similar implementation I worked on:
You’ll need to set up a way to manage the various costs associated with your products. These could be product costs, delivery charges, payment fees, warehouse fees, and other operational costs. These costs can be:
For each model, you should create cost calculators. Here's an example of interfaces for cost calculators:
interface PaymentCostCalculatorInterface
{
public function calculate(PaymentInterface $payment): int;
}
interface OrderItemUnitCostCalculatorInterface
{
public function calculate(OrderItemInterface $orderItem): int;
}
This will aggregate the costs for each expense category, like the total cost for all the order items in an order:
interface CalculatorInterface
{
public function calculate(Order $order): int;
}
The aggregated costs calculators will be used in the order processors described in the next step. Each calculator should focus on one type of expense, like item costs, shipment costs, or payment fees.
Implement an order processor for each type of expense to calculate the cost and save it on the corresponding model related to the order, like the order item, the shipment or the payment. Here's an example:
final class OrderPaymentCostProcessor implements OrderProcessorInterface
{
public function __construct(
private readonly PaymentCostCalculatorInterface $calculator,
) {
}
public function process(OrderInterface $order): void
{
foreach ($order->getPayments() as $payment) {
$payment->setCost($this->calculator->calculate($payment));
}
}
}
Aggregate all the individual cost calculators into a composite service using Symfony service tags and the tagged iterator. This service will iterate over the calculators and sum up the costs:
final class CompositeCostCalculator implements CalculatorInterface
{
private iterable $calculators;
public function __construct(iterable $calculators)
{
$this->calculators = $calculators;
}
public function calculate(OrderInterface $order): int
{
$totalCost = 0;
foreach ($this->calculators as $calculator) {
$totalCost += $calculator->calculate($order);
}
return $totalCost;
}
}
Create an Order Processor that calculates and stores the total cost or profit of an order. This ensures the values are immutable and won’t change later.
final class OrderProfitProcessor implements OrderProcessorInterface
{
public function __construct(
private readonly CalculatorInterface $costCalculator,
) {
}
public function process(OrderInterface $order): void
{
$total = $order->getTotal(); // Gross revenue
$taxTotal = $order->getTaxTotal(); // Tax amount
$cost = $this->costCalculator->calculate($order); // Total costs
$profit = $total - $taxTotal - $cost; // Calculate profit
$order->setProfit($profit); // Save profit to the order
}
}
Finally, ensure your sales reports include the new cost (or profit) calculations. You might need to extend or customize existing reporting plugins (like SyliusSalesReportsPlugin) to incorporate this data.