From 4826c4932a0b453efe8a8292ae9841b3ca1ca235 Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Mon, 9 Nov 2026 23:16:47 -0300 Subject: [PATCH 1/2] fix(transactions): show exchanged amount for both sides of combined transfers --- app/templates/cotton/transaction/item.html | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/templates/cotton/transaction/item.html b/app/templates/cotton/transaction/item.html index 6b518db4..7c43c2aa 100644 --- a/app/templates/cotton/transaction/item.html +++ b/app/templates/cotton/transaction/item.html @@ -157,6 +157,18 @@ :suffix="transaction.account.currency.suffix" :decimal_places="transaction.account.currency.decimal_places" color="red"> + {% if not dummy %} + {% with exchanged=transaction.exchanged_amount %} + {% if exchanged %} + + {% endif %} + {% endwith %} + {% endif %}
{% if transaction.account.group %}{{ transaction.account.group.name }} • {% endif %}{{ transaction.account.name }}
@@ -167,6 +179,18 @@ :suffix="transfer_to.account.currency.suffix" :decimal_places="transfer_to.account.currency.decimal_places" color="green"> + {% if not dummy %} + {% with exchanged=transfer_to.exchanged_amount %} + {% if exchanged %} + + {% endif %} + {% endwith %} + {% endif %}
{% if transfer_to.account.group %}{{ transfer_to.account.group.name }} • {% endif %}{{ transfer_to.account.name }}
@@ -181,7 +205,7 @@ {% endif %} {# Exchange Rate#} - {% if not dummy %} + {% if not dummy and not is_transfer %} {% with exchanged=transaction.exchanged_amount %} {% if exchanged %}
From 085480563038d7b3807c1be0c3408829b117d81d Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Mon, 9 Nov 2026 23:35:56 -0300 Subject: [PATCH 2/2] feat(transactions): show combined transfers as two legs on a timeline - Shared header (date, description, badges); each side gets its own row: account with its category, tags and entities, amount + exchanged amount - Timeline rail from the transfer icon branching into the out/in icons - Amounts drop below on small screens and sit bottom-right on lg, like regular items; spacing matches regular item metadata rows - Notes move below the legs on transfers so they don't sit in the rail --- .../cotton/transaction/_transfer_leg.html | 50 ++++++++ app/templates/cotton/transaction/item.html | 115 ++++-------------- 2 files changed, 75 insertions(+), 90 deletions(-) create mode 100644 app/templates/cotton/transaction/_transfer_leg.html diff --git a/app/templates/cotton/transaction/_transfer_leg.html b/app/templates/cotton/transaction/_transfer_leg.html new file mode 100644 index 00000000..a3cd8e50 --- /dev/null +++ b/app/templates/cotton/transaction/_transfer_leg.html @@ -0,0 +1,50 @@ +{% load i18n %} +{# One side of a combined transfer, laid out like a mini transaction: account + metadata | amount #} +{# Only the divider between legs gets the extra space; the outer edges stay flush #} +
+ {# Timeline rail: x sits under the transfer icon's center (7.5px into the column, minus the container's ps-5 = 20px). #} + {# The first leg's rail reaches up past the description's mb + container mt to the icon; the last one stops at its icon, making └ #} + + {# Stub from the rail into this leg's icon, at the icon's vertical center #} + + {# h-5 matches the account line's height so the icon centers on it #} +
+ +
+ {# Like a regular item: amount drops below on small screens, bottom-right on lg #} +
+
+
{% if leg.account.group %}{{ leg.account.group.name }} • {% endif %}{{ leg.account.name }}
+ {# Same rhythm as a regular item's metadata rows: mb-2 lg:mb-1 between rows, pe-1 + mr-1 after the icon #} +
+ {# h-4 = one text-xs line, so each icon centers on its row's first line #} + {% if leg.category %}
{{ leg.category.name }}
{% endif %} + {% if leg.tags.all %}
{{ leg.tags.all|join:", " }}
{% endif %} + {% if leg.entities.all %}
{{ leg.entities.all|join:", " }}
{% endif %} +
+
+ {# Mobile gaps match a regular item's amount rows (mb-2 lg:mb-0) #} +
+ + {% if not dummy %} + {% with exchanged=leg.exchanged_amount %} + {% if exchanged %} + + {% endif %} + {% endwith %} + {% endif %} +
+
+
diff --git a/app/templates/cotton/transaction/item.html b/app/templates/cotton/transaction/item.html index 7c43c2aa..831d996a 100644 --- a/app/templates/cotton/transaction/item.html +++ b/app/templates/cotton/transaction/item.html @@ -76,42 +76,22 @@
{{ overriden_entities|join:", " }}
- {% elif is_transfer %} - {% if transaction.entities.all or transfer_to.entities.all %} -
-
-
{{ transaction.entities.all|join:", "|default:"—" }}
-
-
{{ transfer_to.entities.all|join:", "|default:"—" }}
-
- {% endif %} - {% comment %} If no override, fall back to transaction entities, but ONLY if the transaction has an ID. {% endcomment %} - {% elif transaction.id and transaction.entities.all %} + {% elif transaction.id and transaction.entities.all and not is_transfer %}
{{ transaction.entities.all|join:", " }}
{% endif %} {# Notes#} - {% if transaction.notes %} + {% if transaction.notes and not is_transfer %}
{{ transaction.notes | limited_markdown | linebreaksbr }}
{% endif %} {# Category#} - {% if is_transfer %} - {# Each side keeps its own category: "from" line, then "to" line with the arrow under the icon #} - {% if transaction.category or transfer_to.category %} -
-
-
{{ transaction.category.name|default:"—" }}
-
-
{{ transfer_to.category.name|default:"—" }}
-
- {% endif %} - {% elif transaction.category %} + {% if transaction.category and not is_transfer %}
{{ transaction.category.name }}
@@ -125,76 +105,33 @@
{{ overriden_tags|join:", " }}
- {% elif is_transfer %} - {% if transaction.tags.all or transfer_to.tags.all %} -
-
-
{{ transaction.tags.all|join:", "|default:"—" }}
-
-
{{ transfer_to.tags.all|join:", "|default:"—" }}
-
- {% endif %} - {% comment %} If no override, fall back to transaction tags, but ONLY if the transaction has an ID. {% endcomment %} - {% elif transaction.id and transaction.tags.all %} + {% elif transaction.id and transaction.tags.all and not is_transfer %}
{{ transaction.tags.all|join:", " }}
{% endif %} + {% if is_transfer %} + {# ps-5 leaves room for the timeline rail that drops from the transfer icon (see _transfer_leg) #} +
+ {% translate 'From' as from_label %}{% translate 'To' as to_label %} + {% include "cotton/transaction/_transfer_leg.html" with leg=transaction color="red" icon="fa-arrow-right-from-bracket" label=from_label first=True %} + {% include "cotton/transaction/_transfer_leg.html" with leg=transfer_to color="green" icon="fa-arrow-right-to-bracket" label=to_label first=False %} +
+ {# Notes go below the legs so they don't sit in the rail's path #} + {% if transaction.notes %} +
+
+
{{ transaction.notes | limited_markdown | linebreaksbr }}
+
+ {% endif %} + {% endif %} + {% if not is_transfer %}
- {% if is_transfer %} - {# Each side: amount, with its account below, like a regular item #} -
- -
- - {% if not dummy %} - {% with exchanged=transaction.exchanged_amount %} - {% if exchanged %} - - {% endif %} - {% endwith %} - {% endif %} -
{% if transaction.account.group %}{{ transaction.account.group.name }} • {% endif %}{{ transaction.account.name }}
-
- -
- - {% if not dummy %} - {% with exchanged=transfer_to.exchanged_amount %} - {% if exchanged %} - - {% endif %} - {% endwith %} - {% endif %} -
{% if transfer_to.account.group %}{{ transfer_to.account.group.name }} • {% endif %}{{ transfer_to.account.name }}
-
-
- {% else %}
- {% endif %} {# Exchange Rate#} - {% if not dummy and not is_transfer %} + {% if not dummy %} {% with exchanged=transaction.exchanged_amount %} {% if exchanged %}
@@ -219,12 +155,11 @@ {% endif %} {% endwith %} {% endif %} - {% if not is_transfer %} -
- {% if transaction.account.group %}{{ transaction.account.group.name }} • {% endif %}{{ transaction.account.name }} -
- {% endif %} +
+ {% if transaction.account.group %}{{ transaction.account.group.name }} • {% endif %}{{ transaction.account.name }} +
+ {% endif %} {% if not dummy %}
{# Item actions#}