Feature: единое отображение чисел с разделителями разрядов (фильтр ws_num)
Made-with: Cursor
This commit is contained in:
10
HISTORY.md
10
HISTORY.md
@@ -1,5 +1,15 @@
|
|||||||
# История изменений ERP WaterSurf
|
# История изменений ERP WaterSurf
|
||||||
|
|
||||||
|
## 2025-02-26 00:25 UTC – Единое отображение чисел с разделителями разрядов
|
||||||
|
|
||||||
|
**Задача**: Везде, где выводятся числовые значения, показывать их с разделителем разрядов и запятой как десятичным разделителем.
|
||||||
|
|
||||||
|
**Решение**: Добавлен шаблонный фильтр **ws_num** (documents/templatetags/document_filters.py): формат «1 851 635,50», неразрывный пробел между разрядами. Фильтр подключён во всех списках документов и на странице экономики заказа (суммы, маржа, проценты).
|
||||||
|
|
||||||
|
**Изменения**: document_filters.py, шаблоны списков документов и customer_order_detail.html (load document_filters, вывод через |ws_num:2 или |ws_num:1).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2025-02-26 00:15 UTC – Экономика заказа клиента: связи и отчёт
|
## 2025-02-26 00:15 UTC – Экономика заказа клиента: связи и отчёт
|
||||||
|
|
||||||
**Задача**: Цепочка «Заказ клиента → Поступления денежных средств (несколько) → Заказ поставщику по заказу → Расход на оплату поставщику; плюс прочие расходы по заказу (логистика)». По заказу клиента видеть: сколько поступило, сколько потрачено, маржа, маржинальность, рентабельность.
|
**Задача**: Цепочка «Заказ клиента → Поступления денежных средств (несколько) → Заказ поставщику по заказу → Расход на оплату поставщику; плюс прочие расходы по заказу (логистика)». По заказу клиента видеть: сколько поступило, сколько потрачено, маржа, маржинальность, рентабельность.
|
||||||
|
|||||||
1
app/documents/templatetags/__init__.py
Normal file
1
app/documents/templatetags/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Пакет тегов и фильтров для шаблонов документов
|
||||||
53
app/documents/templatetags/document_filters.py
Normal file
53
app/documents/templatetags/document_filters.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
"""Фильтры для отображения чисел в шаблонах документов."""
|
||||||
|
from django import template
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def ws_num(value, decimal_places=2):
|
||||||
|
"""
|
||||||
|
Форматирование числа с разделителем разрядов (неразрывный пробел)
|
||||||
|
и запятой как десятичным разделителем.
|
||||||
|
Пример: 1851635.5 -> "1 851 635,50"
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return "—"
|
||||||
|
try:
|
||||||
|
places = int(decimal_places)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
places = 2
|
||||||
|
try:
|
||||||
|
if isinstance(value, (Decimal, float)):
|
||||||
|
n = float(value)
|
||||||
|
else:
|
||||||
|
n = float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return "—"
|
||||||
|
if places == 0:
|
||||||
|
s = f"{int(round(n))}"
|
||||||
|
else:
|
||||||
|
s = f"{n:.{decimal_places}f}"
|
||||||
|
if "." in s:
|
||||||
|
int_part, dec_part = s.split(".", 1)
|
||||||
|
else:
|
||||||
|
int_part, dec_part = s, "0" * places
|
||||||
|
# Разделитель тысяч — неразрывный пробел
|
||||||
|
if int_part.startswith("-"):
|
||||||
|
rest = int_part[1:]
|
||||||
|
formatted = ""
|
||||||
|
while len(rest) > 3:
|
||||||
|
formatted = "\u202f" + rest[-3:] + formatted
|
||||||
|
rest = rest[:-3]
|
||||||
|
int_part = "-" + rest + formatted if rest else "-" + formatted.lstrip("\u202f")
|
||||||
|
else:
|
||||||
|
formatted = ""
|
||||||
|
rest = int_part
|
||||||
|
while len(rest) > 3:
|
||||||
|
formatted = "\u202f" + rest[-3:] + formatted
|
||||||
|
rest = rest[:-3]
|
||||||
|
int_part = (rest + formatted) if rest else formatted.lstrip("\u202f")
|
||||||
|
if places == 0:
|
||||||
|
return int_part
|
||||||
|
return f"{int_part},{dec_part}"
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Расходы денежных средств — ERP WaterSurf{% endblock %}
|
{% block title %}Расходы денежных средств — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
<td>{{ obj.date }}</td>
|
<td>{{ obj.date }}</td>
|
||||||
<td>{{ obj.number }}</td>
|
<td>{{ obj.number }}</td>
|
||||||
<td>{{ obj.sender }}</td>
|
<td>{{ obj.sender }}</td>
|
||||||
<td class="ws-num">{{ obj.amount }}</td>
|
<td class="ws-num">{{ obj.amount|ws_num:2 }}</td>
|
||||||
<td>{{ obj.supplier_order|default:"—" }}</td>
|
<td>{{ obj.supplier_order|default:"—" }}</td>
|
||||||
<td class="ws-actions">
|
<td class="ws-actions">
|
||||||
<a href="{% url 'documents:cash_expense_edit' obj.pk %}" class="ws-link">Изменить</a>
|
<a href="{% url 'documents:cash_expense_edit' obj.pk %}" class="ws-link">Изменить</a>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Поступления денежных средств — ERP WaterSurf{% endblock %}
|
{% block title %}Поступления денежных средств — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
<td>{{ obj.date }}</td>
|
<td>{{ obj.date }}</td>
|
||||||
<td>{{ obj.number }}</td>
|
<td>{{ obj.number }}</td>
|
||||||
<td>{{ obj.recipient }}</td>
|
<td>{{ obj.recipient }}</td>
|
||||||
<td class="ws-num">{{ obj.amount }}</td>
|
<td class="ws-num">{{ obj.amount|ws_num:2 }}</td>
|
||||||
<td>{{ obj.customer_order|default:"—" }}</td>
|
<td>{{ obj.customer_order|default:"—" }}</td>
|
||||||
<td class="ws-actions">
|
<td class="ws-actions">
|
||||||
<a href="{% url 'documents:cash_inflow_edit' obj.pk %}" class="ws-link">Изменить</a>
|
<a href="{% url 'documents:cash_inflow_edit' obj.pk %}" class="ws-link">Изменить</a>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Перемещения денежных средств — ERP WaterSurf{% endblock %}
|
{% block title %}Перемещения денежных средств — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
<td>{{ obj.number }}</td>
|
<td>{{ obj.number }}</td>
|
||||||
<td>{{ obj.sender }}</td>
|
<td>{{ obj.sender }}</td>
|
||||||
<td>{{ obj.recipient }}</td>
|
<td>{{ obj.recipient }}</td>
|
||||||
<td class="ws-num">{{ obj.amount }}</td>
|
<td class="ws-num">{{ obj.amount|ws_num:2 }}</td>
|
||||||
<td class="ws-actions">
|
<td class="ws-actions">
|
||||||
<a href="{% url 'documents:cash_transfer_edit' obj.pk %}" class="ws-link">Изменить</a>
|
<a href="{% url 'documents:cash_transfer_edit' obj.pk %}" class="ws-link">Изменить</a>
|
||||||
<span class="ws-text-muted"> · </span>
|
<span class="ws-text-muted"> · </span>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Заказ {{ object.number }} — ERP WaterSurf{% endblock %}
|
{% block title %}Заказ {{ object.number }} — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
<p class="mb-1"><strong>Организация:</strong> {{ object.organization }}</p>
|
<p class="mb-1"><strong>Организация:</strong> {{ object.organization }}</p>
|
||||||
<p class="mb-1"><strong>Клиент:</strong> {{ object.client }}</p>
|
<p class="mb-1"><strong>Клиент:</strong> {{ object.client }}</p>
|
||||||
<p class="mb-1"><strong>Вид заказа:</strong> {{ object.order_kind }}</p>
|
<p class="mb-1"><strong>Вид заказа:</strong> {{ object.order_kind }}</p>
|
||||||
<p class="mb-1"><strong>Стоимость заказа:</strong> {{ object.total_amount|floatformat:2 }}</p>
|
<p class="mb-1"><strong>Стоимость заказа:</strong> {{ object.total_amount|ws_num:2 }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ws-form-section" style="margin-top: 1.5rem;">
|
<div class="ws-form-section" style="margin-top: 1.5rem;">
|
||||||
@@ -25,31 +26,31 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Поступило от клиента (всего поступлений по заказу)</td>
|
<td>Поступило от клиента (всего поступлений по заказу)</td>
|
||||||
<td class="ws-num">{{ economics.total_inflows|floatformat:2 }}</td>
|
<td class="ws-num">{{ economics.total_inflows|ws_num:2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Расходы: оплата поставщикам (по заказам поставщику по этому заказу)</td>
|
<td>Расходы: оплата поставщикам (по заказам поставщику по этому заказу)</td>
|
||||||
<td class="ws-num">−{{ economics.expenses_via_supplier|floatformat:2 }}</td>
|
<td class="ws-num">−{{ economics.expenses_via_supplier|ws_num:2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Расходы: прочие по заказу (логистика и т.п.)</td>
|
<td>Расходы: прочие по заказу (логистика и т.п.)</td>
|
||||||
<td class="ws-num">−{{ economics.expenses_direct|floatformat:2 }}</td>
|
<td class="ws-num">−{{ economics.expenses_direct|ws_num:2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Всего расходов</strong></td>
|
<td><strong>Всего расходов</strong></td>
|
||||||
<td class="ws-num"><strong>−{{ economics.total_expenses|floatformat:2 }}</strong></td>
|
<td class="ws-num"><strong>−{{ economics.total_expenses|ws_num:2 }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Маржа (поступления − расходы)</strong></td>
|
<td><strong>Маржа (поступления − расходы)</strong></td>
|
||||||
<td class="ws-num"><strong>{{ economics.margin|floatformat:2 }}</strong></td>
|
<td class="ws-num"><strong>{{ economics.margin|ws_num:2 }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Маржинальность (маржа / поступления)</td>
|
<td>Маржинальность (маржа / поступления)</td>
|
||||||
<td class="ws-num">{% if economics.margin_pct is not None %}{{ economics.margin_pct|floatformat:1 }}%{% else %}—{% endif %}</td>
|
<td class="ws-num">{% if economics.margin_pct is not None %}{{ economics.margin_pct|ws_num:1 }}%{% else %}—{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Рентабельность (маржа / расходы)</td>
|
<td>Рентабельность (маржа / расходы)</td>
|
||||||
<td class="ws-num">{% if economics.profitability_pct is not None %}{{ economics.profitability_pct|floatformat:1 }}%{% else %}—{% endif %}</td>
|
<td class="ws-num">{% if economics.profitability_pct is not None %}{{ economics.profitability_pct|ws_num:1 }}%{% else %}—{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
<td>{{ inv.date }}</td>
|
<td>{{ inv.date }}</td>
|
||||||
<td>{{ inv.number }}</td>
|
<td>{{ inv.number }}</td>
|
||||||
<td>{{ inv.recipient }}</td>
|
<td>{{ inv.recipient }}</td>
|
||||||
<td class="ws-num">{{ inv.amount|floatformat:2 }}</td>
|
<td class="ws-num">{{ inv.amount|ws_num:2 }}</td>
|
||||||
<td><a href="{% url 'documents:cash_inflow_edit' inv.pk %}" class="ws-link">Изменить</a></td>
|
<td><a href="{% url 'documents:cash_inflow_edit' inv.pk %}" class="ws-link">Изменить</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -108,7 +109,7 @@
|
|||||||
<td>{{ so.date }}</td>
|
<td>{{ so.date }}</td>
|
||||||
<td>{{ so.number }}</td>
|
<td>{{ so.number }}</td>
|
||||||
<td>{{ so.supplier }}</td>
|
<td>{{ so.supplier }}</td>
|
||||||
<td class="ws-num">{{ so.total_amount|floatformat:2 }}</td>
|
<td class="ws-num">{{ so.total_amount|ws_num:2 }}</td>
|
||||||
<td><a href="{% url 'documents:supplier_order_edit' so.pk %}" class="ws-link">Изменить</a></td>
|
<td><a href="{% url 'documents:supplier_order_edit' so.pk %}" class="ws-link">Изменить</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -141,7 +142,7 @@
|
|||||||
<td>{{ ex.date }}</td>
|
<td>{{ ex.date }}</td>
|
||||||
<td>{{ ex.number }}</td>
|
<td>{{ ex.number }}</td>
|
||||||
<td>{{ ex.sender }}</td>
|
<td>{{ ex.sender }}</td>
|
||||||
<td class="ws-num">{{ ex.amount|floatformat:2 }}</td>
|
<td class="ws-num">{{ ex.amount|ws_num:2 }}</td>
|
||||||
<td><a href="{% url 'documents:cash_expense_edit' ex.pk %}" class="ws-link">Изменить</a></td>
|
<td><a href="{% url 'documents:cash_expense_edit' ex.pk %}" class="ws-link">Изменить</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Заказы клиентов — ERP WaterSurf{% endblock %}
|
{% block title %}Заказы клиентов — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -27,7 +28,7 @@
|
|||||||
<td>{{ obj.order_kind }}</td>
|
<td>{{ obj.order_kind }}</td>
|
||||||
<td>{{ obj.organization }}</td>
|
<td>{{ obj.organization }}</td>
|
||||||
<td>{{ obj.client }}</td>
|
<td>{{ obj.client }}</td>
|
||||||
<td class="ws-num">{{ obj.total_amount }}</td>
|
<td class="ws-num">{{ obj.total_amount|ws_num:2 }}</td>
|
||||||
<td class="ws-actions">
|
<td class="ws-actions">
|
||||||
<a href="{% url 'documents:customer_order_edit' obj.pk %}" class="ws-link">Изменить</a>
|
<a href="{% url 'documents:customer_order_edit' obj.pk %}" class="ws-link">Изменить</a>
|
||||||
<span class="ws-text-muted"> · </span>
|
<span class="ws-text-muted"> · </span>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load document_filters %}
|
||||||
{% block title %}Заказы поставщику — ERP WaterSurf{% endblock %}
|
{% block title %}Заказы поставщику — ERP WaterSurf{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ws-card">
|
<div class="ws-card">
|
||||||
@@ -26,8 +27,8 @@
|
|||||||
<td>{{ obj.number }}</td>
|
<td>{{ obj.number }}</td>
|
||||||
<td>{{ obj.organization }}</td>
|
<td>{{ obj.organization }}</td>
|
||||||
<td>{{ obj.supplier }}</td>
|
<td>{{ obj.supplier }}</td>
|
||||||
<td class="ws-num">{{ obj.total_in_currency }}</td>
|
<td class="ws-num">{{ obj.total_in_currency|ws_num:2 }}</td>
|
||||||
<td class="ws-num">{{ obj.total_amount }}</td>
|
<td class="ws-num">{{ obj.total_amount|ws_num:2 }}</td>
|
||||||
<td class="ws-actions">
|
<td class="ws-actions">
|
||||||
<a href="{% url 'documents:supplier_order_edit' obj.pk %}" class="ws-link">Изменить</a>
|
<a href="{% url 'documents:supplier_order_edit' obj.pk %}" class="ws-link">Изменить</a>
|
||||||
<span class="ws-text-muted"> · </span>
|
<span class="ws-text-muted"> · </span>
|
||||||
|
|||||||
Reference in New Issue
Block a user