{# views/product/price/manage.twig #} {% extends 'layouts/base.twig' %} {% block title %}{{ translations.pricing }}{% endblock %} {% block head %} {% endblock %} {% block page_heading %}

{{ translations.product }}

{% if errors is not empty %}
{% endif %} {% endblock %} {% block content %} {# ── Pasek filtrów daty ── #}
{# {{ translations.date_range_info|default('Zakres: 30 dni wstecz') }}#}
{# ── Legenda wskaźnika cenowego ── #}
{{ translations.competitor_price_legend|default('Twoja cena vs konkurencja:') }} {{ translations.cheaper|default('Tańsza') }} {{ translations.in_range|default('W zakresie') }} + {{ translations.pricier|default('Droższa') }} ? {{ translations.no_data|default('Brak danych') }}
{# ── Filtr okresów dostępności ── #}
{{ translations.availability_periods|default('Okresy dostępności') }}:
{# Badge "Wszystkie" #} {# Badge "Bez okresu" #} {# #} {# Badges dla każdego okresu #} {% for period in active_periods %} {% set startDate = period.getStartDate() %} {% set endDate = period.getEndDate() %} {% set periodId = period.getId() %} {% set isActive = period.isActive() %} {% set isRecurring = period.isRecurring() %} {% set fullName = period.getName() %} {# Wyciągnij emoji z początku nazwy (jeśli jest) #} {% set emoji = '' %} {% set displayName = fullName %} {% if fullName matches '/^([\\x{1F300}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}])\\s*(.+)$/u' %} {% set parts = fullName|split(' ', 2) %} {% set emoji = parts[0] %} {% set displayName = parts[1]|default(fullName) %} {% endif %} {# Sprawdź czy ten okres jest aktualny #} {% set today = 'now'|date('Y-m-d') %} {% set isCurrent = false %} {% if isRecurring %} {# Dla recurring: porównuj tylko dzień i miesiąc (ignoruj rok) #} {% set todayMD = 'now'|date('m-d') %} {% set startMD = startDate|date('m-d') %} {% set endMD = endDate|date('m-d') %} {# Obsługa przypadku gdy okres przechodzi przez koniec roku (np. 12-15 do 01-15) #} {% if startMD <= endMD %} {% set isCurrent = (todayMD >= startMD and todayMD <= endMD) %} {% else %} {% set isCurrent = (todayMD >= startMD or todayMD <= endMD) %} {% endif %} {% else %} {# Dla non-recurring: standardowe porównanie z pełną datą #} {% set isCurrent = (today >= startDate and today <= endDate) %} {% endif %} {% endfor %}
{{ translations.price_applies_to_all_periods|default('Uwaga: Jeśli produkt znajduje się w wielu okresach dostępności, ustalona cena dotyczy wszystkich z nich') }}
{# ── Tabela produktów ── #} ↓ {{ translations.cheaper_than_competitors|default('Niższa niż min') }} ↔ {{ translations.within_competitor_range|default('W przedziale') }} ↑ {{ translations.pricier_than_competitors|default('Wyższa niż max') }} — {{ translations.no_competitor_data|default('Brak danych') }} {# ── Karta tabeli ── #}
{% if products is not empty %} {% set last_id_category = null %} {% for product in products %} {# ── Nagłówek kategorii ── #} {% if last_id_category != product.idCategory %} {# Statystyki z PHP (O(1) lookup) #} {% set catStats = categoryCompetitorStats[product.idCategory] ?? {'total': 0, 'with_data': 0, 'avg_marge': null, 'below_expected': 0} %} {% endif %} {# ── Wiersz produktu ── #} {% set cp = competitorPriceIndex[product.id] ?? null %} {% set myPriceVal = product.portionPrice|default(0) * 1 %} {# Wyznacz klasę statusu i wiersza #} {% if cp is null %} {% set rowClass = '' %} {% set iconClass = 'comp-icon-no-data' %} {% set iconSymbol = '—' %} {% elseif myPriceVal <= cp.min %} {% set rowClass = 'row-cheaper' %} {% set iconClass = 'comp-icon-cheaper' %} {% set iconSymbol = '↓' %} {% elseif myPriceVal > cp.max %} {% set rowClass = 'row-pricier' %} {% set iconClass = 'comp-icon-pricier' %} {% set iconSymbol = '↑' %} {% else %} {% set rowClass = 'row-in-range' %} {% set iconClass = 'comp-icon-in-range' %} {% set iconSymbol = '↔' %} {% endif %} {# Nazwa #} {# Koszt produkcji #} {# VAT #} {# Cena sprzedaży – edytowalna #} {# Cena maksymalna #} {# Marża realna + oczekiwana #} {# Cena konkurencji — ikona + dane #} {# Stan magazynowy – edytowalny #} {% set last_id_category = product.idCategory %} {% endfor %} {% else %} {% endif %}
{{ translations.product }} {{ translations.gross_cost|default('Koszt prod.') }} {{ translations.tax_value_perc_short|default('VAT') }} {{ translations.current_sale_price_gross|default('Cena sprzedaży') }} {{ translations.max_sale_price_gross|default('Maks. cena') }} {{ translations.marge_gross|default('Marża') }}
{{ translations.real|default('realna') }} / {{ translations.expected|default('oczekiwana') }}
{{ translations.competitor_price|default('Cena konkurencji') }} {{ translations.in_stock|default('Stan') }}
{{ product.categoryName }} {# .category-warning wstawiany przez JS trafi tutaj (po nazwie) #}
{# Prawa strona: średnia marża + badge z danymi konkurencji #}
{# Produkty poniżej oczekiwanej marży #} {% if catStats.below_expected > 0 %} {{ catStats.below_expected }} {% endif %} {# Średnia marża kategorii #} {% if catStats.avg_marge is not null %} ø {{ catStats.avg_marge }}% {% endif %} {# Badge z danymi konkurencji #} {% if catStats.with_data > 0 %} {{ catStats.with_data }}/{{ catStats.total }} {% else %} {{ translations.no_competitor_data|default('Brak danych') }} {% endif %}
{{ product.name }} {{ product.recipeCostGross }} {{ product.taxValPerc }}%
{{ product.maxPortionPrice }} {% set marge = product.calcMarge %} {% set expMarge = product.expectedMargin %} {% set margeReal = marge * 1 %} {% set margeExp = expMarge is not null ? expMarge * 1 : null %}
{# Marża realna #} {{ margeReal }}% {# Linia oczekiwanej marży + delta — tylko jeśli ustawiona #} {% if margeExp is not null %} {% set delta = margeReal - margeExp %} {% if delta >= 0 %} {% set deltaClass = 'marge-delta-ok' %} {% set deltaIcon = '↑' %} {% elseif delta >= -5 %} {% set deltaClass = 'marge-delta-warn' %} {% set deltaIcon = '→' %} {% else %} {% set deltaClass = 'marge-delta-bad' %} {% set deltaIcon = '↓' %} {% endif %} {{ deltaIcon|raw }} {{ margeExp }}% {% endif %}
{# Ikona statusu po lewej #} {{ iconSymbol|raw }} {% if cp is not null %} {# Dane po prawej: zakres + max 2 obserwacje #} {# Linia z zakresem #} {% if cp.min == cp.max %} {{ cp.min|number_format(2, '.', ',') }} {% else %} {{ cp.min|number_format(2, '.', ',') }} – {{ cp.max|number_format(2, '.', ',') }} {% endif %} {# Max 2 obserwacje: min i max (jeśli różne) #} {# Obserwacja z najniższą ceną #} {% set minEntry = cp.min_entry %} {% set minGap = myPriceVal - (minEntry.price|default(0) * 1) %} {{ minEntry.name }} {{ (minEntry.price|default(0) * 1)|number_format(2, '.', ',') }} {% if minGap > 0 %} +{{ minGap|number_format(2, '.', ',') }} {% elseif minGap < 0 %} {{ minGap|number_format(2, '.', ',') }} {% else %} = {% endif %} {# Obserwacja z najwyższą ceną — tylko jeśli różna od min #} {% if cp.min != cp.max %} {% set maxEntry = cp.max_entry %} {% set maxGap = myPriceVal - (maxEntry.price|default(0) * 1) %} {{ maxEntry.name }} {{ (maxEntry.price|default(0) * 1)|number_format(2, '.', ',') }} {% if maxGap > 0 %} +{{ maxGap|number_format(2, '.', ',') }} {% elseif maxGap < 0 %} {{ maxGap|number_format(2, '.', ',') }} {% else %} = {% endif %} {% endif %} {# Liczba wszystkich obserwacji jeśli > 2 #} {% if cp.count > 2 %} {{ cp.count }} {{ translations.observations|default('obs.') }} {% endif %} {% endif %}
{{ translations.no_data|default('Brak danych') }}
{# ── Modal generatora cennika ── #} {% endblock %} {% block scripts %} {% endblock %}