{% extends "base.html" %} {% import "_bank.html" as bank with context %} {% block title %}{{ order.order_id }} — painfree{% endblock %} {% block body %}

{{ order.order_id }}

{{ order.state.value }} {{ t('state.' ~ order.state.value) }}

{% if replayed %}
{{ t('order.requeued') }} {{ t('order.requeued_why') }}
{% endif %}
{# The identifiers below are identifiers: `MsgId`, the idempotency key, the order id and the IBANs are never translated and never reformatted. The amount and the dates are, and only here -- the stored `pain.001` this row was derived from carries `1234.56` and `2026-09-15` whatever this page shows. #} {# `normal`, `instant` and `instant_or_normal` are the contract's own values and are shown untranslated beside the word for them, for the reason the order states are. #} {# The two documents, kept apart because they are sent in different phases and answer different questions: this is what was built, the one below is what the bank was actually handed and refused. #} {# What the bank refused, kept and checked here. A code like `091113` names no element, so without this an operator has nothing to look at and the only party who can still see the document is the bank. #} {% if order.refused_request %} {% endif %}
{{ t('common.connection') }}{{ order.connection_id }}
{{ t('order.idempotency_key') }}{{ order.idempotency_key }}
MsgId{{ order.msg_id }}
{{ t('order.message_type') }}{{ order.message_type }}
{{ t('order.transactions') }}{{ order.transaction_count | count }}
{{ t('order.control_sum') }}{{ order.control_sum | amount(order.currency) }}
{{ t('order.requested_execution') }}{{ order.requested_execution_date | day }}
{{ t('order.scheme') }} {{ order.scheme.value }} {{ t('scheme.' ~ order.scheme.value) }} {% if order.downgraded %}{{ t('order.downgraded') }}{% endif %}
{{ t('order.requested_scheme') }} {{ order.requested_scheme.value }} {{ t('scheme.' ~ order.requested_scheme.value) }}
{{ t('order.scheme_reason') }} {% if order.scheme_reason %}{{ order.scheme_reason }} {{ t.maybe('scheme_reason.' ~ order.scheme_reason.split(':')[0], '') }} {% else %}—{% endif %}
{{ t('order.bank_order_id') }}{{ order.bank_order_id or '—' }}
{{ t('orders.return_code') }}{% if order.return_code %}{{ order.return_code }}{% else %}—{% endif %}
{{ t('orders.report_text') }}{{ bank.words(order.report_text) }}
{{ t('orders.document') }} {{ t('orders.document_download') }}
{{ t('orders.document_phase') }}
{{ t('orders.refused_request') }} {% if order.refused_request_errors %} {{ t('orders.refused_request_invalid') }}
    {% for failure in order.refused_request_errors %}
  • {{ failure }}
  • {% endfor %}
{% else %} {# A clean result is a finding, not an exoneration: it says the disagreement is semantic rather than structural, which is what points the conversation with the bank at content. #} {{ t('orders.refused_request_clean') }} {% endif %}

{{ t('orders.refused_request_download') }}

{{ t('order.attempts') }}{{ order.attempts | count }}
{{ t('orders.accepted') }}{{ order.accepted_at | moment }}
{{ t('order.last_change') }}{{ order.updated_at | moment }}

{{ t('order.not_shown') }}

{% if may('orders:replay') and replayable %}
{{ t('order.replay') }}
{% endif %}

{{ t('order.attempts_heading') }}

{# One row per message this service built for the order. An order that fell back has two: what the bank refused, and what went instead. The BTF and the `PmtTpInf` sit side by side because reading them together is how an operator checks that the announcement and the document agreed. #} {% for attempt in attempts %} {% endfor %}
{{ t('order.attempt_no') }}{{ t('order.scheme') }} {{ t('common.state') }}MsgId {{ t('order.attempt_btf') }} {{ t('order.attempt_payment_type') }} {{ t('orders.return_code') }} {{ t('order.attempt_reason') }}
{{ attempt.attempt_no | count }} {{ attempt.scheme.value }}
{{ t('scheme.' ~ attempt.scheme.value) }}
{{ attempt.state }}
{{ t('scheme_state.' ~ attempt.state) }}
{{ attempt.msg_id }} {{ attempt.btf_summary }} {% if attempt.payment_type %}{{ attempt.payment_type }} {% else %}{{ t('schemes.none') }}{% endif %} {% if attempt.return_code %}{{ attempt.return_code }}
{{ bank.quiet(attempt.report_text) }}{% else %}—{% endif %}
{% if attempt.reason %}{{ attempt.reason }}{% else %}—{% endif %}

{{ t('order.attempts_hint') }}

{{ t('order.bank_reported') }}

{% if order.bank_status %}
{{ t('order.status') }} {{ order.bank_status }} {% set known = status_codes.get(order.bank_status) %} {% if known %}{{ known.name }}: {{ t.maybe('status_code.' ~ order.bank_status, known.meaning) }}{% endif %}
{{ t('order.reason_code') }}{% if order.status_reason_code %}{{ order.status_reason_code }}{% else %}—{% endif %}
{{ t('order.reason_text') }}{{ bank.words(order.status_reason_text) }}
{{ t('order.reported_at') }}{{ order.status_reported_at | moment }}

{{ t('bank.note') }}

{% else %}

{{ t('order.no_pain002') }}

{% endif %} {% if reports %} {% for report in reports %} {% endfor %}
{{ t('order.status_report') }}{{ t('order.group_status') }} {{ t('order.accepted_count') }}{{ t('order.rejected_count') }} {{ t('order.reason') }}{{ t('order.ingested') }}
{{ report.statement_id }}
{{ report.message_type }}
{% if report.outcome.status %}{{ report.outcome.status }}{% else %}—{% endif %} {% if not report.outcome.decides %}{{ t('order.no_change') }}{% endif %} {{ report.outcome.accepted | count }} {{ report.outcome.rejected | count }} {% if report.outcome.reason_code %}{{ report.outcome.reason_code }} {% endif %}{{ bank.quiet(report.outcome.reason_text) }} {{ report.ingested_at | moment }}

{{ t('order.reports_not_shown') }} {{ t('order.status_code_page') }}.

{% endif %}

{{ t('order.what_happened') }}

{% if events %} {% for event in events %} {% endfor %}
{{ t('common.when') }}{{ t('order.event') }} {{ t('order.who') }}{{ t('error.detail') }}
{{ event.occurred_at | moment }} {{ event.action }} {% if event.outcome != 'success' %}{{ event.outcome }}{% endif %} {{ event.actor_type }} / {{ event.actor_id }}
{{ event.detail | pretty_json }}
{% else %}

{{ t('order.nothing_recorded') }}

{% endif %}
{% endblock %}