Files
watersurf-erp/app/templates/documents/order_form.html

239 lines
9.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ title }} — ERP WaterSurf{% endblock %}
{% block content %}
<div class="ws-card">
<h2 class="ws-page-title">{% if object %}Редактировать{% else %}Создать{% endif %} {{ title }}</h2>
<form method="post" class="ws-form-card ws-form-compact">
{% csrf_token %}
<div class="ws-form-row ws-form-row-date-number">
<div class="ws-form-group ws-field-date">
<label for="{{ form.date.id_for_label }}">{{ form.date.label }}</label>
{{ form.date }}
{% if form.date.errors %}<small class="ws-text-danger">{{ form.date.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group ws-field-number">
<label for="{{ form.number.id_for_label }}">{{ form.number.label }}</label>
{{ form.number }}
{% if form.number.errors %}<small class="ws-text-danger">{{ form.number.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.order_kind.id_for_label }}">{{ form.order_kind.label }}</label>
{{ form.order_kind }}
{% if form.order_kind.errors %}<small class="ws-text-danger">{{ form.order_kind.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.organization.id_for_label }}">{{ form.organization.label }}</label>
{{ form.organization }}
{% if form.organization.errors %}<small class="ws-text-danger">{{ form.organization.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.client.id_for_label }}">{{ form.client.label }}</label>
{{ form.client }}
{% if form.client.errors %}<small class="ws-text-danger">{{ form.client.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.status.id_for_label }}">{{ form.status.label }}</label>
{{ form.status }}
{% if form.status.errors %}<small class="ws-text-danger">{{ form.status.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label>Автор</label>
<span class="ws-readonly">{{ author_display|default:"—" }}</span>
</div>
</div>
<p class="ws-order-total-row"><strong>Общая сумма заказа:</strong> <span id="order-total-sum" class="ws-num"></span></p>
<div class="ws-form-section">
<h3 class="ws-form-section-title">Товары</h3>
{{ formset.management_form }}
<div class="ws-table-wrap">
<table class="ws-table ws-table-items" id="order-items">
<colgroup>
<col class="ws-col-product">
<col class="ws-col-price">
<col class="ws-col-currency">
<col class="ws-col-qty">
<col class="ws-col-cost">
<col class="ws-col-del">
</colgroup>
<thead>
<tr>
<th>Товар</th>
<th>Цена</th>
<th>Валюта</th>
<th>Количество</th>
<th class="ws-num">Стоимость</th>
<th>Удалить</th>
</tr>
</thead>
<tbody id="order-items-body">
{% for f in formset %}
<tr class="item-row">
<td class="ws-col-product">{{ f.id }}{{ f.product }}</td>
<td class="ws-col-price">{{ f.price }}</td>
<td class="ws-col-currency">{{ f.currency }}</td>
<td class="ws-col-qty">{{ f.quantity }}</td>
<td class="row-amount ws-col-cost ws-num"></td>
<td class="ws-col-del"><button type="button" class="ws-btn-remove-row" title="Удалить строку" aria-label="Удалить строку">×</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="ws-btn-group" style="margin-top: 0.75rem;">
<button type="button" class="btn btn-ws-secondary" id="add-order-row">+ Добавить строку</button>
</div>
</div>
<div class="ws-btn-group" style="margin-top: 1.25rem;">
<button type="submit" class="btn btn-ws-primary">Сохранить</button>
<a href="{% url 'documents:customer_order_list' %}" class="btn btn-ws-secondary">Отмена</a>
</div>
</form>
</div>
{% endblock %}
{% block extra_js %}
<script>
(function() {
var form = document.querySelector('.ws-form-card');
var tbody = document.getElementById('order-items-body');
var totalInput = form.querySelector('input[name$="-TOTAL_FORMS"]');
if (!totalInput || !tbody) return;
function getPrefix() {
var name = totalInput.getAttribute('name');
return name.replace(/-TOTAL_FORMS$/, '');
}
function formatNum(x) {
var n = parseFloat(x);
if (isNaN(n)) return '—';
var parts = n.toFixed(2).split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '\u202f');
return parts.join(',');
}
function parseNum(s) {
return parseFloat(String(s).replace(/\s/g, '').replace(',', '.')) || 0;
}
function parseQty(s) {
var n = parseInt(String(s).replace(/\s/g, ''), 10);
if (isNaN(n) || n < 0) return 0;
return n > 99 ? 99 : n;
}
function updateRowAmounts() {
document.querySelectorAll('#order-items .item-row').forEach(function(row) {
var priceInput = row.querySelector('input[name$="-price"]');
var qty = parseQty(row.querySelector('input[name$="-quantity"]')?.value);
var price = parseNum(priceInput?.value);
var el = row.querySelector('.row-amount');
if (el) el.textContent = formatNum(price * qty);
});
var totalEl = document.getElementById('order-total-sum');
if (totalEl) {
var sum = 0;
document.querySelectorAll('#order-items .item-row').forEach(function(row) {
var priceInput = row.querySelector('input[name$="-price"]');
var qty = parseQty(row.querySelector('input[name$="-quantity"]')?.value);
var price = parseNum(priceInput?.value);
sum += price * qty;
});
totalEl.textContent = formatNum(sum);
}
}
form.addEventListener('input', updateRowAmounts);
form.addEventListener('submit', function() {
form.querySelectorAll('input[name$="-price"]').forEach(function(input) {
input.value = String(input.value).replace(/\s/g, '').replace(',', '.');
});
});
form.addEventListener('focusin', function(e) {
if (e.target.name && e.target.name.indexOf('-price') !== -1) {
var n = parseNum(e.target.value);
e.target.value = (e.target.value.trim() === '' || isNaN(n)) ? '' : n.toFixed(2);
}
});
form.addEventListener('focusout', function(e) {
if (e.target.name && e.target.name.indexOf('-price') !== -1) {
if (e.target.value.trim() === '') return;
var n = parseNum(e.target.value);
e.target.value = isNaN(n) ? '' : formatNum(n);
}
if (e.target.name && e.target.name.indexOf('-quantity') !== -1) {
var q = parseQty(e.target.value);
e.target.value = String(q);
}
});
updateRowAmounts();
form.querySelectorAll('input[name$="-price"]').forEach(function(input) {
if (input.value && input.value.trim() !== '') input.value = formatNum(parseNum(input.value));
});
function reindexRows() {
var prefix = getPrefix();
var rows = tbody.querySelectorAll('.item-row');
rows.forEach(function(row, i) {
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));
}
var id = el.getAttribute('id');
if (id) {
el.setAttribute('id', id.replace(new RegExp(prefix + '-\\d+'), prefix + '-' + i));
}
});
});
totalInput.value = String(rows.length);
updateRowAmounts();
}
tbody.addEventListener('click', function(e) {
var btn = e.target.closest('.ws-btn-remove-row');
if (!btn) return;
var row = btn.closest('tr.item-row');
if (!row) return;
row.remove();
reindexRows();
});
document.getElementById('add-order-row').addEventListener('click', function() {
var rows = tbody.querySelectorAll('.item-row');
var lastRow = rows[rows.length - 1];
if (!lastRow) return;
var prefix = getPrefix();
var nextIndex = parseInt(totalInput.value, 10);
var clone = lastRow.cloneNode(true);
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));
}
if (el.name && el.name.indexOf('-DELETE') !== -1) {
el.checked = false;
} else if (el.name && el.name.indexOf('-id') !== -1) {
el.value = '';
} else if (el.type !== 'hidden') {
el.value = '';
}
});
var qtyInput = clone.querySelector('input[name$="-quantity"]');
if (qtyInput) qtyInput.value = '1';
var amountCell = clone.querySelector('.row-amount');
if (amountCell) amountCell.textContent = '—';
tbody.appendChild(clone);
reindexRows();
});
})();
</script>
{% endblock %}