152 lines
7.9 KiB
HTML
152 lines
7.9 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}{% if action=='add' %}Новый заказ{% else %}Редактировать заказ{% endif %} — Стройка{% endblock %}
|
||
{% block page_title %}{% if action=='add' %}Новый заказ{% else %}Редактировать заказ {{ order.number }}{% endif %}{% endblock %}
|
||
{% block topbar_actions %}<a href="{{ url_for('orders_list') }}" class="btn btn-secondary">← Назад</a>{% endblock %}
|
||
{% block content %}
|
||
<div class="form-card" style="max-width:1000px">
|
||
<form method="post" id="orderForm">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label class="form-label">Номер заказа <span class="required">*</span></label>
|
||
<input type="text" name="number" class="form-input" required value="{{ order.number if order else '' }}" placeholder="ЗК-001">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Дата заказа <span class="required">*</span></label>
|
||
<input type="date" name="order_date" class="form-input" required value="{{ today }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Дата доставки</label>
|
||
<input type="date" name="delivery_date" class="form-input" value="{{ order.delivery_date if order else '' }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Статус</label>
|
||
<select name="status" class="form-input">
|
||
{% for s in ['Новый','В обработке','Выполнен','Отменён'] %}
|
||
<option value="{{ s }}" {% if order and order.status==s %}selected{% endif %}>{{ s }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="form-group full-width">
|
||
<label class="form-label">Контрагент <span class="required">*</span></label>
|
||
<select name="counterparty_id" class="form-input" required>
|
||
<option value="">— выбрать —</option>
|
||
<optgroup label="Поставщики (приходная)">
|
||
{% for c in counterparties if c.type==2 %}
|
||
<option value="{{ c.id }}" {% if order and order.counterparty_id==c.id %}selected{% endif %}>{{ c.name }}</option>
|
||
{% endfor %}
|
||
</optgroup>
|
||
<optgroup label="Партнёры (расходная)">
|
||
{% for c in counterparties if c.type==1 %}
|
||
<option value="{{ c.id }}" {% if order and order.counterparty_id==c.id %}selected{% endif %}>{{ c.name }}</option>
|
||
{% endfor %}
|
||
</optgroup>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Позиции заказа -->
|
||
<div style="margin-top:1.5rem">
|
||
<div style="font-size:0.75rem;font-weight:700;color:var(--text-mid);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:0.8rem">
|
||
Позиции заказа
|
||
</div>
|
||
<div class="table-wrapper" style="margin-bottom:1rem">
|
||
<table class="data-table" id="itemsTable">
|
||
<thead><tr><th>Материал</th><th>Ед.</th><th>Количество</th><th>Цена, ₽</th><th class="highlight-col">Сумма, ₽</th><th></th></tr></thead>
|
||
<tbody id="itemsBody">
|
||
{% for item in items %}
|
||
<tr class="item-row">
|
||
<td>
|
||
<select name="material_id[]" class="form-input material-select" required>
|
||
<option value="">— выбрать —</option>
|
||
{% for m in materials %}<option value="{{ m.id }}" data-price="{{ m.current_price }}" data-unit="{{ m.unit }}" {% if item.material_id==m.id %}selected{% endif %}>{{ m.name }}</option>{% endfor %}
|
||
</select>
|
||
</td>
|
||
<td class="unit-cell">{{ item.unit if item else '—' }}</td>
|
||
<td><input type="number" name="quantity[]" class="form-input qty-input" step="0.01" min="0.01" required value="{{ item.quantity }}"></td>
|
||
<td><input type="number" name="unit_price[]" class="form-input price-input" step="0.01" min="0" required value="{{ item.unit_price }}"></td>
|
||
<td class="highlight-col line-total"><strong>{{ "%.2f"|format(item.quantity * item.unit_price) }} ₽</strong></td>
|
||
<td><button type="button" class="btn btn-sm btn-danger" onclick="removeRow(this)">✕</button></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<button type="button" class="btn btn-secondary btn-sm" onclick="addRow()">+ Добавить позицию</button>
|
||
</div>
|
||
|
||
<!-- Итого -->
|
||
<div class="calc-preview" style="margin-top:1rem">
|
||
<div class="calc-title">📊 Итого по заказу</div>
|
||
<div class="calc-row total-row"><span>ИТОГО:</span><strong id="grandTotal">0.00 ₽</strong></div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">💾 Сохранить заказ</button>
|
||
<a href="{{ url_for('orders_list') }}" class="btn btn-secondary">Отмена</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
var materialsData = {
|
||
{% for m in materials %}
|
||
{{ m.id }}: { price: {{ m.current_price }}, unit: '{{ m.unit }}', name: '{{ m.name }}' },
|
||
{% endfor %}
|
||
};
|
||
|
||
function addRow() {
|
||
var tbody = document.getElementById('itemsBody');
|
||
var row = document.createElement('tr');
|
||
row.className = 'item-row';
|
||
row.innerHTML = '<td><select name="material_id[]" class="form-input material-select" required><option value="">— выбрать —</option>' +
|
||
Object.entries(materialsData).map(([id,m]) => '<option value="'+id+'" data-price="'+m.price+'" data-unit="'+m.unit+'">'+m.name+'</option>').join('') +
|
||
'</select></td>' +
|
||
'<td class="unit-cell">—</td>' +
|
||
'<td><input type="number" name="quantity[]" class="form-input qty-input" step="0.01" min="0.01" required value="1"></td>' +
|
||
'<td><input type="number" name="unit_price[]" class="form-input price-input" step="0.01" min="0" required value="0"></td>' +
|
||
'<td class="highlight-col line-total"><strong>0.00 ₽</strong></td>' +
|
||
'<td><button type="button" class="btn btn-sm btn-danger" onclick="removeRow(this)">✕</button></td>';
|
||
tbody.appendChild(row);
|
||
bindRow(row);
|
||
}
|
||
|
||
function removeRow(btn) {
|
||
btn.closest('tr').remove();
|
||
updateTotal();
|
||
}
|
||
|
||
function bindRow(row) {
|
||
var sel = row.querySelector('.material-select');
|
||
var qty = row.querySelector('.qty-input');
|
||
var price = row.querySelector('.price-input');
|
||
sel.addEventListener('change', function() {
|
||
var opt = sel.options[sel.selectedIndex];
|
||
price.value = opt.dataset.price || 0;
|
||
row.querySelector('.unit-cell').textContent = opt.dataset.unit || '—';
|
||
updateLine(row); updateTotal();
|
||
});
|
||
qty.addEventListener('input', function() { updateLine(row); updateTotal(); });
|
||
price.addEventListener('input', function() { updateLine(row); updateTotal(); });
|
||
}
|
||
|
||
function updateLine(row) {
|
||
var q = parseFloat(row.querySelector('.qty-input').value) || 0;
|
||
var p = parseFloat(row.querySelector('.price-input').value) || 0;
|
||
row.querySelector('.line-total').innerHTML = '<strong>' + (q*p).toFixed(2) + ' ₽</strong>';
|
||
}
|
||
|
||
function updateTotal() {
|
||
var total = 0;
|
||
document.querySelectorAll('.item-row').forEach(function(row) {
|
||
var q = parseFloat(row.querySelector('.qty-input').value) || 0;
|
||
var p = parseFloat(row.querySelector('.price-input').value) || 0;
|
||
total += q * p;
|
||
});
|
||
document.getElementById('grandTotal').textContent = total.toFixed(2) + ' ₽';
|
||
}
|
||
|
||
document.querySelectorAll('.item-row').forEach(bindRow);
|
||
updateTotal();
|
||
</script>
|
||
{% endblock %}
|