Compare commits
No commits in common. "013542165590f5f46050b89755c294e22ec1a861" and "07b9b7c878dc8822a54ff70921f8859c2597f0a5" have entirely different histories.
0135421655
...
07b9b7c878
@ -1,12 +1,14 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Categorías{% endblock %}
|
{% block title %}
|
||||||
|
Categorías
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar categoría{% endblock %}
|
{% block content %}
|
||||||
|
<h2>Eliminar categoría</h2>
|
||||||
|
|
||||||
{% block delete_question %}¿Seguro que quieres eliminar la categoría <strong>{{ category.name }}</strong>?{% endblock %}
|
<p>¿Seguro que quieres eliminar la categoría <strong>{{ category.name }}</strong>?</p>
|
||||||
|
|
||||||
{% block delete_warnings %}
|
|
||||||
{% if expense_count %}
|
{% if expense_count %}
|
||||||
<p class="form-errors">
|
<p class="form-errors">
|
||||||
Esta categoría tiene {{ expense_count }} gasto{{ expense_count|pluralize }} asociado{{ expense_count|pluralize }}
|
Esta categoría tiene {{ expense_count }} gasto{{ expense_count|pluralize }} asociado{{ expense_count|pluralize }}
|
||||||
@ -16,7 +18,7 @@
|
|||||||
{% if children %}
|
{% if children %}
|
||||||
<p class="form-errors">
|
<p class="form-errors">
|
||||||
Atención: también se eliminarán sus subcategorías:
|
Atención: también se eliminarán sus subcategorías:
|
||||||
{% for child in children %}<strong>{{ child.name }}</strong>{% if not forloop.last %}, {% endif %}{% endfor %}.
|
{% for child in children %}<strong>{{child.name}}</strong>{% if not forloop.last %}, {% endif %}{% endfor %}.
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -26,6 +28,11 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'category_list' %}{% endblock %}
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button class="btn btn-danger">Eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'category_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@ -10,9 +10,7 @@
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'category_list' %}">Cancelar</a>
|
<a class="btn btn-secondary" href="{% url 'category_list' %}">Cancelar</a>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{% extends "expenses/base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Eliminar{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% block delete_heading %}Eliminar{% endblock %}</h1>
|
|
||||||
|
|
||||||
<p>{% block delete_question %}¿Seguro que quieres eliminar este elemento?{% endblock %}</p>
|
|
||||||
|
|
||||||
{% block delete_warnings %}{% endblock %}
|
|
||||||
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% block delete_extra_fields %}{% endblock %}
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-danger">{% block delete_button %}Eliminar{% endblock %}</button>
|
|
||||||
<a class="btn btn-secondary" href="{% block cancel_url %}{% endblock %}">Cancelar</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,11 +1,18 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Eliminar{% endblock %}
|
{% block title %}Eliminar{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar cuenta{% endblock %}
|
<h1>Eliminar cuenta</h1>
|
||||||
|
|
||||||
{% block delete_question %}¿Seguro que quieres eliminar la cuenta <strong>{{ account.name }}</strong>?{% endblock %}
|
<p>
|
||||||
|
¿Seguro que quieres eliminar la cuenta
|
||||||
|
<strong>{{ account.name }}</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% block delete_button %}Sí, eliminar{% endblock %}
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-danger">Sí, eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'account_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'account_list' %}{% endblock %}
|
{% endblock %}
|
||||||
@ -14,10 +14,8 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'account_list' %}">Cancelar</a>
|
<a class="btn btn-secondary" href="{% url 'account_list' %}">Cancelar</a>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,16 +1,21 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Eliminar gasto{% endblock %}
|
{% block title %}Eliminar gasto{% endblock %}
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar gasto{% endblock %}
|
{% block content %}
|
||||||
|
<h1>Eliminar gasto</h1>
|
||||||
|
|
||||||
{% block delete_question %}
|
<p>
|
||||||
¿Seguro que quieres eliminar el gasto de
|
¿Seguro que quieres eliminar el gasto de
|
||||||
<strong>{{ expense.amount }}€</strong>
|
<strong>{{ expense.amount }}€</strong>
|
||||||
del {{ expense.date }} perteneciente a
|
del {{ expense.date }} perteneciente a
|
||||||
<strong>{{ expense.account }}</strong>?
|
<strong>{{ expense.account }}</strong>?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-danger">Sí, eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'expense_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block delete_button %}Sí, eliminar{% endblock %}
|
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'expense_list' %}{% endblock %}
|
|
||||||
|
|||||||
@ -46,7 +46,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
{% if form.instance.pk %}
|
{% if form.instance.pk %}
|
||||||
Guardar gasto
|
Guardar gasto
|
||||||
@ -54,7 +53,7 @@
|
|||||||
Crear gasto
|
Crear gasto
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'expense_list' %}">Volver</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<a class="btn btn-secondary" href="{% url 'expense_list' %}">Volver</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Eliminar ingreso{% endblock %}
|
{% block title %}Eliminar ingreso{% endblock %}
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar ingreso{% endblock %}
|
{% block content %}
|
||||||
|
<h1>Eliminar ingreso</h1>
|
||||||
|
|
||||||
{% block delete_question %}
|
<p>
|
||||||
¿Seguro que quieres eliminar el ingreso de
|
¿Seguro que quieres eliminar el ingreso de
|
||||||
<strong>{{ income.amount }}€</strong>
|
<strong>{{ income.amount }}€</strong>
|
||||||
del {{ income.date }} perteneciente a
|
del {{ income.date }} perteneciente a
|
||||||
<strong>{{ income.account }}</strong>?
|
<strong>{{ income.account }}</strong>?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-danger">Sí, eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'income_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block delete_button %}Sí, eliminar{% endblock %}
|
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'income_list' %}{% endblock %}
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
{% if form.instance.pk %}
|
{% if form.instance.pk %}
|
||||||
Guardar ingreso
|
Guardar ingreso
|
||||||
@ -27,7 +26,7 @@
|
|||||||
Crear ingreso
|
Crear ingreso
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'income_list' %}">Volver</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<a class="btn btn-secondary" href="{% url 'income_list' %}">Volver</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,9 +1,17 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Etiquetas{% endblock %}
|
{% block title %}Etiquetas{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>Eliminar etiqueta</h1>
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar etiqueta{% endblock %}
|
<p>
|
||||||
|
¿Seguro que quieres eliminar la etiqueta
|
||||||
|
<strong>{{ tag.name }}€</strong>?
|
||||||
|
</p>
|
||||||
|
|
||||||
{% block delete_question %}¿Seguro que quieres eliminar la etiqueta <strong>{{ tag.name }}</strong>?{% endblock %}
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-danger">Eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'tag_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'tag_list' %}{% endblock %}
|
{% endblock %}
|
||||||
@ -6,9 +6,8 @@
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'tag_list' %}">Volver</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<a class="btn btn-secondary" href="{% url 'tag_list' %}">Volver</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,23 +1,26 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Repostajes{% endblock %}
|
{% block title %}
|
||||||
|
Repostajes
|
||||||
{% block delete_heading %}Eliminar repostaje.{% endblock %}
|
|
||||||
|
|
||||||
{% block delete_question %}
|
|
||||||
¿Seguro que quieres eliminar el repostaje del
|
|
||||||
<strong>{{ fuel.expense.date }}</strong>
|
|
||||||
({{ fuel.liters }}L por {{ fuel.expense.amount }}€)?
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block delete_warnings %}
|
{% block content %}
|
||||||
|
<h2>Eliminar repostaje.</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
¿Seguro que quieres eliminar el repostaje del
|
||||||
|
<strong>{{ fuel.expense.date}}</strong>
|
||||||
|
({{ fuel.liters }}L por {{fuel.expense.amount}}€)?
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="form-errors">
|
<p class="form-errors">
|
||||||
Se eliminará también el gasto asociado en el listado de gastos.
|
Se eliminará también el gasto asociado en el listado de gastos.
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block delete_extra_fields %}
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
{% if next %}<input type="hidden" name="next" value="{{ next }}">{% endif %}
|
{% if next %}<input type="hidden" name="next" value="{{ next }}">{% endif %}
|
||||||
|
<button class="btn btn-danger">Eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'fuel_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block cancel_url %}{% if next %}{{ next }}{% else %}{% url 'fuel_list' %}{% endif %}{% endblock %}
|
|
||||||
|
|||||||
@ -21,7 +21,6 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if next %}<input type="hidden" name="next" value="{{ next }}">{% endif %}
|
{% if next %}<input type="hidden" name="next" value="{{ next }}">{% endif %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
{% if editing %}
|
{% if editing %}
|
||||||
Guardar repostaje
|
Guardar repostaje
|
||||||
@ -29,6 +28,7 @@
|
|||||||
Crear repostaje
|
Crear repostaje
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<a class="btn btn-secondary" href="{{ next }}">Volver</a>
|
<a class="btn btn-secondary" href="{{ next }}">Volver</a>
|
||||||
@ -37,6 +37,4 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-secondary" href="{% url 'fuel_list' %}">Volver</a>
|
<a class="btn btn-secondary" href="{% url 'fuel_list' %}">Volver</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,9 +1,18 @@
|
|||||||
{% extends "expenses/_confirm_delete.html" %}
|
{% extends "expenses/base.html" %}
|
||||||
|
|
||||||
{% block title %}Objetivos{% endblock %}
|
{% block title %}Objetivos{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
{% block delete_heading %}Eliminar objetivo{% endblock %}
|
<h1>Eliminar objetivo</h1>
|
||||||
|
|
||||||
{% block delete_question %}¿Seguro que quieres eliminar el objetivo <strong>{{ goal.name }}</strong>?{% endblock %}
|
<p>
|
||||||
|
¿Seguro que quieres eliminar el objetivo
|
||||||
|
<strong>{{ goal.name }}</strong>?
|
||||||
|
</p>
|
||||||
|
|
||||||
{% block cancel_url %}{% url 'goal_list' %}{% endblock %}
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-danger">Eliminar</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'goal_list' %}">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@ -11,11 +11,10 @@
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
Guardar
|
Guardar
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-secondary" href="{% url 'goal_list' %}">Volver</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<a class="btn btn-secondary" href="{% url 'goal_list' %}">Volver</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -67,165 +67,3 @@ def test_fuel_delete_post_cascades_to_fuelentry(auth_client, user, account, cate
|
|||||||
|
|
||||||
assert not Expense.objects.filter(pk=expense.pk).exists()
|
assert not Expense.objects.filter(pk=expense.pk).exists()
|
||||||
assert not FuelEntry.objects.filter(pk=entry.pk).exists()
|
assert not FuelEntry.objects.filter(pk=entry.pk).exists()
|
||||||
|
|
||||||
|
|
||||||
def _edit_payload(account, edit_date, amount, odometer, liters, next_url=None):
|
|
||||||
payload = {
|
|
||||||
"date": edit_date.strftime("%Y-%m-%d"),
|
|
||||||
"amount": str(amount),
|
|
||||||
"account": account.pk,
|
|
||||||
"odometer": str(odometer),
|
|
||||||
"liters": str(liters),
|
|
||||||
}
|
|
||||||
if next_url is not None:
|
|
||||||
payload["next"] = next_url
|
|
||||||
return payload
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_edit_post_updates_expense_and_fuelentry(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"), amount=Decimal("50"))
|
|
||||||
expense = entry.expense
|
|
||||||
new_date = date(2024, 3, 1)
|
|
||||||
|
|
||||||
response = auth_client.post(
|
|
||||||
reverse('fuel_edit', args=[expense.pk]),
|
|
||||||
_edit_payload(account, new_date, Decimal("45.50"), 1200, Decimal("22.5")),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
|
|
||||||
expense.refresh_from_db()
|
|
||||||
entry.refresh_from_db()
|
|
||||||
|
|
||||||
assert expense.amount == Decimal("45.50")
|
|
||||||
assert expense.date == new_date
|
|
||||||
assert expense.account_id == account.pk
|
|
||||||
assert entry.odometer == 1200
|
|
||||||
assert entry.liters == Decimal("22.5")
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_edit_invalid_post_returns_200_and_does_not_modify(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"), amount=Decimal("50"))
|
|
||||||
expense = entry.expense
|
|
||||||
original_date = expense.date
|
|
||||||
|
|
||||||
payload = _edit_payload(account, date(2024, 3, 1), Decimal("45.50"), 1200, Decimal("22.5"))
|
|
||||||
payload["liters"] = ""
|
|
||||||
|
|
||||||
response = auth_client.post(reverse('fuel_edit', args=[expense.pk]), payload)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
expense.refresh_from_db()
|
|
||||||
entry.refresh_from_db()
|
|
||||||
|
|
||||||
assert expense.amount == Decimal("50")
|
|
||||||
assert expense.date == original_date
|
|
||||||
assert entry.odometer == 1000
|
|
||||||
assert entry.liters == Decimal("20")
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_edit_redirects_to_next_when_relative(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
next_url = reverse('expense_list')
|
|
||||||
|
|
||||||
response = auth_client.post(
|
|
||||||
reverse('fuel_edit', args=[expense.pk]),
|
|
||||||
_edit_payload(account, date(2024, 3, 1), Decimal("45.50"), 1200, Decimal("22.5"), next_url=next_url),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == next_url
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_edit_ignores_external_next(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
|
|
||||||
response = auth_client.post(
|
|
||||||
reverse('fuel_edit', args=[expense.pk]),
|
|
||||||
_edit_payload(
|
|
||||||
account, date(2024, 3, 1), Decimal("45.50"), 1200, Decimal("22.5"),
|
|
||||||
next_url="https://evil.com",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == reverse('expense_list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_get_does_not_delete(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
|
|
||||||
response = auth_client.get(reverse('fuel_delete', args=[expense.pk]))
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert Expense.objects.filter(pk=expense.pk).exists()
|
|
||||||
assert FuelEntry.objects.filter(pk=entry.pk).exists()
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_get_cancel_link_uses_next_when_provided(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
next_url = reverse('expense_list')
|
|
||||||
|
|
||||||
response = auth_client.get(reverse('fuel_delete', args=[expense.pk]), {"next": next_url})
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert f'<a class="btn btn-secondary" href="{next_url}">Cancelar</a>'.encode() in response.content
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_get_cancel_link_falls_back_to_fuel_list_without_next(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
fuel_list_url = reverse('fuel_list')
|
|
||||||
|
|
||||||
response = auth_client.get(reverse('fuel_delete', args=[expense.pk]))
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert f'<a class="btn btn-secondary" href="{fuel_list_url}">Cancelar</a>'.encode() in response.content
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_post_redirects_to_next_when_relative(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
next_url = reverse('expense_list')
|
|
||||||
|
|
||||||
response = auth_client.post(reverse('fuel_delete', args=[expense.pk]), {"next": next_url})
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == next_url
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_post_ignores_external_next(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
|
|
||||||
response = auth_client.post(reverse('fuel_delete', args=[expense.pk]), {"next": "https://evil.com"})
|
|
||||||
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url == reverse('fuel_list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_delete_get_ignores_unsafe_next_in_cancel_link(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
fuel_list_url = reverse('fuel_list')
|
|
||||||
|
|
||||||
response = auth_client.get(reverse('fuel_delete', args=[expense.pk]), {"next": "javascript:alert(1)"})
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert b"javascript:" not in response.content
|
|
||||||
assert f'<a class="btn btn-secondary" href="{fuel_list_url}">Cancelar</a>'.encode() in response.content
|
|
||||||
|
|
||||||
|
|
||||||
def test_fuel_edit_get_ignores_unsafe_next_in_cancel_link(auth_client, user, account, category):
|
|
||||||
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
|
|
||||||
expense = entry.expense
|
|
||||||
|
|
||||||
response = auth_client.get(reverse('fuel_edit', args=[expense.pk]), {"next": "javascript:alert(1)"})
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert b"javascript:" not in response.content
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ def _get_int(value):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _safe_next(request):
|
def _redirect_back(request, fallback):
|
||||||
next_url = request.POST.get("next") or request.GET.get("next")
|
next_url = request.POST.get("next") or request.GET.get("next")
|
||||||
|
|
||||||
if next_url and url_has_allowed_host_and_scheme(
|
if next_url and url_has_allowed_host_and_scheme(
|
||||||
@ -38,14 +38,9 @@ def _safe_next(request):
|
|||||||
allowed_hosts={request.get_host()},
|
allowed_hosts={request.get_host()},
|
||||||
require_https=request.is_secure(),
|
require_https=request.is_secure(),
|
||||||
):
|
):
|
||||||
return next_url
|
return redirect(next_url)
|
||||||
|
|
||||||
return ""
|
return redirect(fallback)
|
||||||
|
|
||||||
|
|
||||||
def _redirect_back(request, fallback):
|
|
||||||
next_url = _safe_next(request)
|
|
||||||
return redirect(next_url or fallback)
|
|
||||||
|
|
||||||
|
|
||||||
def sub_months(year, month, n):
|
def sub_months(year, month, n):
|
||||||
@ -809,16 +804,20 @@ def fuel_list(request):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def fuel_edit(request, pk):
|
def fuel_edit(request, pk):
|
||||||
expense = get_object_or_404(Expense, pk=pk, owner=request.user)
|
expense = get_object_or_404(
|
||||||
|
Expense,
|
||||||
|
pk=pk,
|
||||||
|
owner=request.user,
|
||||||
|
)
|
||||||
|
|
||||||
fuel = get_object_or_404(FuelEntry, expense=expense)
|
fuel = get_object_or_404(FuelEntry, expense=expense)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = FuelEntryForm(request.POST, instance=expense, user=request.user)
|
form = FuelEntryForm(request.POST, user=request.user)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# Update expense
|
# Update expense
|
||||||
expense = form.save(commit=False)
|
expense.date = form.save(commit=False)
|
||||||
expense.description = "Repostaje"
|
expense.description = "Repostaje"
|
||||||
expense.save()
|
expense.save()
|
||||||
|
|
||||||
@ -831,7 +830,7 @@ def fuel_edit(request, pk):
|
|||||||
else:
|
else:
|
||||||
form = FuelEntryForm(instance=expense, user=request.user)
|
form = FuelEntryForm(instance=expense, user=request.user)
|
||||||
|
|
||||||
next_url = _safe_next(request)
|
next_url = request.POST.get("next") or request.GET.get("next", "")
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@ -855,7 +854,7 @@ def fuel_delete(request, pk):
|
|||||||
messages.success(request, "Repostaje eliminado.")
|
messages.success(request, "Repostaje eliminado.")
|
||||||
return _redirect_back(request, "fuel_list")
|
return _redirect_back(request, "fuel_list")
|
||||||
|
|
||||||
next_url = _safe_next(request)
|
next_url = request.POST.get("next") or request.GET.get("next", "")
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user