From 319f38ea0533ac411cf1cc40d9136f1546ed9232 Mon Sep 17 00:00:00 2001 From: cursor-agent Date: Thu, 26 Feb 2026 13:17:09 +0000 Subject: [PATCH] =?UTF-8?q?Fix:=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=B0=D1=82=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=87=D0=BD=D0=BE=D0=B9=20=D1=87?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=B8=20=D0=B7=D0=B0=D0=BA=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=20(=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=BF=D1=80=D0=B5=D1=84=D0=B8=D0=BA=D1=81=20formset?= =?UTF-8?q?=20items-N)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- HISTORY.md | 12 ++++++++++++ app/documents/views.py | 8 ++++---- app/templates/documents/order_form.html | 8 ++++---- app/templates/documents/supplier_order_form.html | 8 ++++---- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e4a7e7a..ebd230d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,17 @@ # История изменений ERP WaterSurf +## 2025-02-25 23:45 UTC – Сохранение заказа: дата и табличная часть + +**Проблема**: При сохранении заказа клиента не сохранялись поле «Дата» и табличная часть (товары). + +**Причина**: В JavaScript переиндексация строк и клонирование использовали неверный формат имён полей formset: префикс в Django — `items` (related_name), имена имеют вид `items-0-product`, `items-1-price`. В коде искали `items0`, `items1` (без дефиса), из‑за чего переиндексация не срабатывала, клоны получали те же имена `items-0-*`, при отправке дубликаты перезаписывали друг друга и formset обрабатывал только одну строку. При ошибке валидации formset в контекст не передавался обновлённый formset с ошибками. + +**Решение**: В reindexRows и при клонировании строк используется корректный формат: `prefix + '-\\d+'` для индекса (например `items-0`), замена на `prefix + '-' + i`; при клонировании — `prefix + '-\\d+-'` для совпадения с суффиксом поля. При невалидном formset в контекст передаётся `formset=formset`, чтобы на форме отображались ошибки. + +**Изменения**: order_form.html, supplier_order_form.html (регулярные выражения в reindexRows и в обработчике «Добавить строку»), documents/views.py (передача formset в get_context_data при ошибке валидации для заказов клиента и поставщику). + +--- + ## 2025-02-25 23:30 UTC – Поле Цена: разделители числа, без стрелок в Цена и Количество **Проблема**: В поле «Цена» не было разделителей разрядов; стрелки вверх/вниз в полях «Цена» и «Количество» мешали. diff --git a/app/documents/views.py b/app/documents/views.py index 9f2f765..e7ec0ea 100644 --- a/app/documents/views.py +++ b/app/documents/views.py @@ -65,7 +65,7 @@ class CustomerOrderCreate(LoginRequiredMixin, CreateView): logger.info("Создан заказ клиента %s", self.object) messages.success(self.request, "Заказ создан.") return redirect(self.success_url) - return self.render_to_response(self.get_context_data(form=form)) + return self.render_to_response(self.get_context_data(form=form, formset=formset)) def get(self, request, *args, **kwargs): self.object = None @@ -95,7 +95,7 @@ class CustomerOrderUpdate(LoginRequiredMixin, UpdateView): logger.info("Обновлён заказ клиента %s", self.object) messages.success(self.request, "Заказ сохранён.") return redirect(self.success_url) - return self.render_to_response(self.get_context_data(form=form)) + return self.render_to_response(self.get_context_data(form=form, formset=formset)) class CustomerOrderDelete(LoginRequiredMixin, DeleteView): @@ -134,7 +134,7 @@ class SupplierOrderCreate(LoginRequiredMixin, CreateView): logger.info("Создан заказ поставщику %s", self.object) messages.success(self.request, "Заказ создан.") return redirect(self.success_url) - return self.render_to_response(self.get_context_data(form=form)) + return self.render_to_response(self.get_context_data(form=form, formset=formset)) def get(self, request, *args, **kwargs): self.object = None @@ -164,7 +164,7 @@ class SupplierOrderUpdate(LoginRequiredMixin, UpdateView): logger.info("Обновлён заказ поставщику %s", self.object) messages.success(self.request, "Заказ сохранён.") return redirect(self.success_url) - return self.render_to_response(self.get_context_data(form=form)) + return self.render_to_response(self.get_context_data(form=form, formset=formset)) class SupplierOrderDelete(LoginRequiredMixin, DeleteView): diff --git a/app/templates/documents/order_form.html b/app/templates/documents/order_form.html index c23f65a..30e037f 100644 --- a/app/templates/documents/order_form.html +++ b/app/templates/documents/order_form.html @@ -157,11 +157,11 @@ row.querySelectorAll('input, select').forEach(function(el) { var n = el.getAttribute('name'); if (n && n.indexOf(prefix) === 0) { - el.setAttribute('name', n.replace(new RegExp('^' + prefix + '\\d+'), prefix + i)); + el.setAttribute('name', n.replace(new RegExp('^' + prefix + '-\\d+'), prefix + '-' + i)); } var id = el.getAttribute('id'); if (id) { - el.setAttribute('id', id.replace(new RegExp(prefix + '\\d+'), prefix + i)); + el.setAttribute('id', id.replace(new RegExp(prefix + '-\\d+'), prefix + '-' + i)); } }); }); @@ -188,8 +188,8 @@ clone.querySelectorAll('input, select').forEach(function(el) { var name = el.getAttribute('name'); if (name && name.indexOf(prefix) === 0) { - el.setAttribute('name', name.replace(new RegExp('^' + prefix + '\\d+-'), prefix + nextIndex + '-')); - el.setAttribute('id', (el.getAttribute('id') || '').replace(new RegExp(prefix + '\\d+-'), prefix + nextIndex + '-')); + el.setAttribute('name', name.replace(new RegExp('^' + prefix + '-\\d+-'), prefix + '-' + nextIndex + '-')); + el.setAttribute('id', (el.getAttribute('id') || '').replace(new RegExp(prefix + '-\\d+'), prefix + '-' + nextIndex)); } if (el.name && el.name.indexOf('-DELETE') !== -1) { el.checked = false; diff --git a/app/templates/documents/supplier_order_form.html b/app/templates/documents/supplier_order_form.html index e71168e..c0bdec1 100644 --- a/app/templates/documents/supplier_order_form.html +++ b/app/templates/documents/supplier_order_form.html @@ -162,11 +162,11 @@ row.querySelectorAll('input, select').forEach(function(el) { var n = el.getAttribute('name'); if (n && n.indexOf(prefix) === 0) { - el.setAttribute('name', n.replace(new RegExp('^' + prefix + '\\d+'), prefix + i)); + el.setAttribute('name', n.replace(new RegExp('^' + prefix + '-\\d+'), prefix + '-' + i)); } var id = el.getAttribute('id'); if (id) { - el.setAttribute('id', id.replace(new RegExp(prefix + '\\d+'), prefix + i)); + el.setAttribute('id', id.replace(new RegExp(prefix + '-\\d+'), prefix + '-' + i)); } }); }); @@ -193,8 +193,8 @@ clone.querySelectorAll('input, select').forEach(function(el) { var name = el.getAttribute('name'); if (name && name.indexOf(prefix) === 0) { - el.setAttribute('name', name.replace(new RegExp('^' + prefix + '\\d+-'), prefix + nextIndex + '-')); - el.setAttribute('id', (el.getAttribute('id') || '').replace(new RegExp(prefix + '\\d+-'), prefix + nextIndex + '-')); + el.setAttribute('name', name.replace(new RegExp('^' + prefix + '-\\d+-'), prefix + '-' + nextIndex + '-')); + el.setAttribute('id', (el.getAttribute('id') || '').replace(new RegExp(prefix + '-\\d+'), prefix + '-' + nextIndex)); } if (el.name && el.name.indexOf('-id') !== -1) { el.value = '';