{% extends "layouts/base.twig" %} {% block title %}{{ recipe.name }} — {{ translations.calculator_subtitle }}{% endblock %} {% block head %} {% endblock %} {% block page_heading %}
{{ translations.back_to_recipes }}

{{ recipe.name }} {% if recipe.is_archived %} {{ translations.archived }} {% endif %}

{{ translations.calculator_subtitle }}

{% endblock %} {% block content %}
{# ===== TILE 1: PRODUKT ===== #}
1 {{ translations.product_tile_title }}
{% if recipe.product_id %} {{ translations.open_in_catalog }} {% endif %}
{{ translations.recipe_product_hint }}
{# Karta podglądu produktu #}
{{ recipe.product_name ? (recipe.product_name|slice(0, 2)|upper) : '--' }}
{{ recipe.product_name ?? '' }}
{{ translations.product_preview_sku }}: {{ recipe.product_sku ?? translations.product_no_sku }}
{{ recipe.product_is_active ? translations.product_preview_status_active : translations.product_preview_status_inactive }}
{# #}
{# ===== TILE 2: SUROWCE ===== #}
2 {{ translations.raw_materials_tile_title }}
{# Formularz dodawania składnika #} {# Tabela składników #}
{% if ingredients is empty %} {% else %} {% set tw = summary.total_weight ?? 0 %} {% for ing in ingredients %} {% endfor %} {% endif %} {% if ingredients is not empty %} {% endif %}
{{ translations.ingredient_name }} {{ translations.ingredients_qty_header }} {{ translations.ingredients_price_header }} % {{ translations.ingredients_cost_header }}
{{ translations.no_results }}
{{ ing.raw_material_name_snapshot }} {% if ing.raw_material_is_archived %} {% endif %} {% set _noDecUnits = ['g', 'ml', 'szt'] %} {% if ing.unit_symbol_snapshot in _noDecUnits %} {{ ing.quantity|number_format(0, '.', '') }} {% else %} {{ ing.quantity|number_format(2, '.', '') }} {% endif %} {{ ing.unit_symbol_snapshot }} {% if ing.price_net_snapshot is not null %} {{ ing.price_net_snapshot|number_format(2, '.', '') }} {% else %} {% endif %} {% if tw > 0 %} {{ (ing.quantity / tw * 100)|number_format(2, '.', '') }}% {% else %} — {% endif %} {% if ing.price_net_snapshot is not null %} {{ (ing.quantity * ing.price_net_snapshot)|number_format(2, '.', '') }} {% else %} {% endif %}
{{ ingredients|length }} {{ translations.summary_ingredients }} · {% set _twUnit = ingredients[0].unit_symbol_snapshot ?? '' %}{% if _twUnit in ['g','ml','szt'] %}{{ (summary.total_weight ?? 0)|number_format(0, '.', '') }}{% else %}{{ (summary.total_weight ?? 0)|number_format(2, '.', '') }}{% endif %} {{ _twUnit }} {{ translations.summary_total_weight|lower }} 100% {% if summary.total_cost is not null %} {{ summary.total_cost|number_format(2, '.', '') }} {{ currency }} {% else %} {% endif %}
{# Totals row (mockup-style) #} {% if ingredients is not empty %}
{{ ingredients|length }} {{ translations.summary_ingredients }} · {% set _twUnit2 = ingredients[0].unit_symbol_snapshot ?? '' %}{% if _twUnit2 in ['g','ml','szt'] %}{{ (summary.total_weight ?? 0)|number_format(0, '.', '') }}{% else %}{{ (summary.total_weight ?? 0)|number_format(2, '.', '') }}{% endif %} {{ _twUnit2 }} {{ translations.total_raw_materials }} {% if summary.total_cost is not null %} {{ summary.total_cost|number_format(2, '.', '') }} {{ currency }} {% else %} {% endif %}
{% endif %} {# Alergeny #}
{{ translations.allergens_section }} — {{ translations.allergens_hint }}
{% if summary.allergens is empty %} {{ translations.no_allergens }} {% else %} {% set _seenAllergens = [] %} {% for a in summary.allergens %} {% set _allergenKey = a.code ?? a.name ?? '' %} {% if _allergenKey not in _seenAllergens %} {% set _seenAllergens = _seenAllergens|merge([_allergenKey]) %} {% set _allergenLabel = translations['allergen_' ~ _allergenKey] ?? a.name ?? _allergenKey %} {{ _allergenLabel }} {% endif %} {% endfor %} {% endif %}
{# Notatki technologiczne #}
{{ translations.technical_notes }}
{{ translations.technical_notes_hint }}
{# ===== SUMMARY AFTER TILE 2 ===== #} {% if ingredients is not empty %} {% set _sum_g = 0 %} {% set _sum_ml = 0 %} {% set _sum_szt = 0 %} {% for _si in ingredients %} {% if _si.unit_symbol_snapshot == 'g' %} {% set _sum_g = _sum_g + _si.quantity %} {% elseif _si.unit_symbol_snapshot == 'ml' %} {% set _sum_ml = _sum_ml + _si.quantity %} {% elseif _si.unit_symbol_snapshot == 'szt' %} {% set _sum_szt = _sum_szt + _si.quantity %} {% endif %} {% endfor %}
{{ translations.summary_step2_title }}
{{ translations.summary_recipe_intro }} {% set _sum_parts = [] %} {% if _sum_g > 0 %} {% set _sum_parts = _sum_parts|merge(['' ~ _sum_g|number_format(0, '.', '') ~ ' g']) %} {% endif %} {% if _sum_ml > 0 %} {% set _sum_parts = _sum_parts|merge(['' ~ _sum_ml|number_format(0, '.', '') ~ ' ml']) %} {% endif %} {% if _sum_szt > 0 %} {% set _sum_parts = _sum_parts|merge(['' ~ _sum_szt|number_format(0, '.', '') ~ ' szt']) %} {% endif %} {% if _sum_parts is empty %} {{ (summary.total_weight ?? 0)|number_format(2, '.', '') }} {{ ingredients[0].unit_symbol_snapshot ?? '' }} {% else %} {% for _part in _sum_parts %} {{ _part|raw }}{% if not loop.last %} {{ translations.summary_and }} {% endif %} {% endfor %} {% endif %} {% if summary.total_cost is not null %} — {{ translations.summary_cost_intro }} {{ summary.total_cost|number_format(2, '.', '') }} {{ currency }}. {% endif %}
{% endif %} {# ===== TILE 3: SALES UNIT ===== #} {% set su = recipe.sales_unit %} {# Default portion_unit is resolved in PHP (RecipeController::resolveDefaultPortionUnit) #} {% set _portionUnit = defaultPortionUnit %}
3 {{ translations.sales_unit_tile_title }}
{{ translations.sales_unit_portion_size_hint }}
{{ translations.sales_unit_portions_per_packaging_hint }}
{{ translations.sales_unit_packagings_per_carton_hint }}
{{ translations.sales_unit_portions_per_carton }}
{% if su.portions_per_carton is not null %}{{ su.portions_per_carton }}{% else %}{% endif %}
{{ translations.sales_unit_packaging_weight }}
{% if su.packaging_size is not null %} {% set _puUnit = su.portion_unit ?? _portionUnit %} {% if _puUnit in ['g','ml','szt'] %}{{ su.packaging_size|number_format(0, '.', '') }}{% else %}{{ su.packaging_size|number_format(2, '.', '') }}{% endif %} {{ _puUnit }} {% else %}{% endif %}
{{ translations.sales_unit_carton_weight }}
{% if su.carton_weight_kg is not null %} {{ su.carton_weight_kg|number_format(2, '.', '') }} kg {% elseif su.carton_size is not null %} {% set _puUnit2 = su.portion_unit ?? _portionUnit %} {% if _puUnit2 in ['g','ml','szt'] %}{{ su.carton_size|number_format(0, '.', '') }}{% else %}{{ su.carton_size|number_format(2, '.', '') }}{% endif %} {{ _puUnit2 }} {% else %}{% endif %}
{{ translations.sales_unit_hierarchy_hint }}
{# ===== TILE 4: LABOR ===== #}
4 {{ translations.labor_tile_title }}
{% set _hasNoLaborValues = recipe.labor_time_per_unit_min is null and recipe.labor_hourly_rate is null %} {% set _hasLaborDefaults = laborDefaults is defined and (laborDefaults.labor_time_default is not null or laborDefaults.labor_rate_default is not null) %} {% if _hasNoLaborValues and _hasLaborDefaults %} {% endif %}
{{ translations.labor_unit_min }}
{{ translations.labor_time_hint }}
{{ currency }}/h
{{ translations.labor_rate_hint }}
{% if recipe.labor_cost_per_unit is not null %} {{ recipe.labor_cost_per_unit|number_format(2, '.', '') }} {{ currency }} {% else %} {% endif %}
{{ translations.labor_cost_hint }}
{# ===== LABOR SUMMARY ===== #} {% if recipe.labor_time_per_unit_min is not null or recipe.labor_cost_per_unit is not null %}
{{ translations.labor_summary_title }}
{{ translations.labor_summary_intro }} {% if recipe.labor_time_per_unit_min is not null %} {{ recipe.labor_time_per_unit_min|number_format(1, '.', '') }} {{ translations.labor_unit_min }} {% else %} {% endif %} {{ translations.labor_summary_cost_intro }} {% if recipe.labor_cost_per_unit is not null %} {{ recipe.labor_cost_per_unit|number_format(2, '.', '') }} {{ currency }}. {% else %} . {% endif %}
{% endif %} {# ===== TILE 5: OVERHEAD ===== #} {% set oh = recipe.overhead %}
5 {{ translations.overhead_tile_title }}
/ {{ translations.month_short }}
{{ translations.overhead_forecast_hint }}
{{ currency }}
{{ translations.overhead_pool_hint }}
{% if oh.overhead_per_portion is not null %} {{ oh.overhead_per_portion|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.overhead_per_portion_hint }}
{% if recipe.sales_unit.portions_per_carton is not null %} {{ translations.overhead_based_on|replace({'{n}': recipe.sales_unit.portions_per_carton}) }} {% else %} {{ translations.overhead_missing_sales_unit }} {% endif %}
{# ===== TILE 6: PACKAGING COST ===== #} {% set pack = recipe.packaging %}
6 {{ translations.packaging_tile_title }}
{{ translations.packaging_description_hint }}
{{ currency }}
{{ translations.packaging_cost_hint }}
{% if pack.cost_per_packaging is not null and recipe.sales_unit.portions_per_packaging is not null and recipe.sales_unit.portions_per_packaging > 0 %} {{ (pack.cost_per_packaging / recipe.sales_unit.portions_per_packaging)|number_format(2, '.', '') }} {{ currency }} {% else %} {% endif %}
{{ translations.packaging_cost_per_portion_hint }}
{# ===== TILE 7: VIDANGES ===== #} {% set vid = recipe.vidanges %}
7 {{ translations.vidanges_tile_title }}
{{ translations.vidanges_carton_type_hint }}
{{ currency }}
{{ translations.vidanges_deposit_hint }}
{{ translations.vidanges_deposit_per_packaging }}
{% if vid.deposit_per_packaging is not null %} {{ vid.deposit_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.vidanges_deposit_per_packaging_hint }}
{{ translations.vidanges_deposit_per_portion }}
{% if vid.deposit_per_portion is not null %} {{ vid.deposit_per_portion|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.vidanges_deposit_per_portion_hint }}
{{ translations.vidanges_passthrough_note }}
{% if recipe.sales_unit.packagings_per_carton is null or recipe.sales_unit.portions_per_packaging is null %}
{{ translations.vidanges_missing_sales_unit }}
{% endif %}
{# ===== TILE 8: P&L SUMMARY ===== #} {% set pnl = recipe.pnl %} {% set _pnl_ppp = pnl.portions_per_packaging %}
8 {{ translations.pnl_tile_title }}

{{ translations.pnl_computed_per_packaging }}: {% if pnl.portions_per_packaging is not null %} {{ pnl.portions_per_packaging }} {{ translations.pnl_portions }} {% else %} {{ translations.pnl_missing_portions }} {% endif %}

{# ===== PANEL: PER PACKAGING ===== #}
{{ translations.pnl_raw_materials }} {% if pnl.raw_materials_per_packaging is not null %} {{ pnl.raw_materials_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_labor }} {% if pnl.labor_per_packaging is not null %} {{ pnl.labor_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_overhead }} {% if pnl.overhead_per_packaging is not null %} {{ pnl.overhead_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_packaging }} {% if pnl.packaging_per_packaging is not null %} {{ pnl.packaging_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{{ translations.pnl_packaging_not_set }}{% endif %}
{{ translations.pnl_total_cost }} {% if pnl.total_cost_per_packaging is not null %} {{ pnl.total_cost_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
+ {% if pnl.vidanges_per_packaging is not null %} {{ pnl.vidanges_per_packaging|number_format(2, '.', '') }} {{ currency }} {% else %}—{% endif %} {{ translations.pnl_vidanges_passthrough }}
{{ translations.pnl_recommended_price }} {% if pnl.recommended_packaging_price is not null %} {{ pnl.recommended_packaging_price|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_per_portion_label }} {% if pnl.target_margin is not null %}({{ translations.pnl_margin }} {{ pnl.target_margin|number_format(2, '.', '') }}%){% endif %} {% if pnl.per_portion_price is not null %} {{ pnl.per_portion_price|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{# ===== PANEL: PER PORTION ===== #}
{{ translations.pnl_raw_materials }} {% if _pnl_ppp and pnl.raw_materials_per_packaging is not null %} {{ (pnl.raw_materials_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_labor }} {% if _pnl_ppp and pnl.labor_per_packaging is not null %} {{ (pnl.labor_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_overhead }} {% if _pnl_ppp and pnl.overhead_per_packaging is not null %} {{ (pnl.overhead_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{{ translations.pnl_packaging }} {% if _pnl_ppp and pnl.packaging_per_packaging is not null %} {{ (pnl.packaging_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}{{ translations.pnl_packaging_not_set }}{% endif %}
{{ translations.pnl_total_cost }} {% if _pnl_ppp and pnl.total_cost_per_packaging is not null %} {{ (pnl.total_cost_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
+ {% if _pnl_ppp and pnl.vidanges_per_packaging is not null %} {{ (pnl.vidanges_per_packaging / _pnl_ppp)|number_format(2, '.', '') }} {{ currency }} {% else %}—{% endif %} {{ translations.pnl_vidanges_passthrough }}
{{ translations.pnl_recommended_price }} {% if pnl.per_portion_price is not null %} {{ pnl.per_portion_price|number_format(2, '.', '') }} {{ currency }} {% else %}{% endif %}
{# Margin slider — wspólny dla obu widoków #}
0%20%40%60%80%
{# ===== MODAL: Quick-add raw material ===== #} {# ===== MODAL: Add raw material (full) ===== #} {# ===== MODAL: Manage raw material categories (from quick-add) ===== #} {# ===== MODAL: Labor defaults ===== #} {# ===== AKCJE RECEPTURY ===== #}
{# {% if recipe.is_archived %} {% else %} {% endif %} #}
{{ translations.back_to_recipes }}
{# /.calc-page-wrap #} {% endblock %} {% block scripts %} {% endblock %}