Restyle UI to match Mago4 and add invoicing, warehouse, scadenzario

Rework the demo into a faithful Mago4 look-and-feel and expand it with
core ERP modules modelled on the real Mago4 database schema.

UI (ispirata a Mago4):
- Blue header with logo/search/operation date, desktop-style window tabs
- White sidebar with coloured module icons (Anagrafiche, Vendite, Acquisti,
  Amministrazione, Logistica)
- Card-based module landing pages with breadcrumb + tab strip
- Document detail forms with left tabs, line grid and totals box

Fatturazione (InvoiceMng / MA_SaleDoc):
- Document types (Fattura Immediata, DDT, Nota di Credito, ProForma)
- Lines with discount/IVA, automatic taxable/tax/total computation
- Document states (draft -> printed -> issued / posted_to_inventory)

Magazzino (Inventory / MA_InventoryEntries + MA_InventoryReasons):
- Inventory movements with load/unload reasons (DebitCreditSign)
- Posting a movement actually updates item balances
- Causali and stock valuation pages

Amministrazione / Scadenzario (AP_AR / MA_PyblsRcvbls):
- Open items (receivables/payables) with due date, method, state
- Settle action closes the item and recomputes the cash forecast

Dashboard riprogettata (focus su scadenze e to-do):
- Overdue alert banner, KPI for overdue payables/receivables
- Upcoming deadlines table, prioritised "Cose da fare" action list
- 30-day cash-flow forecast and customer credit-limit check

Adds REST endpoints for invoices, movements, schedules and dashboard data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 11:47:32 +02:00
co-authored by Claude Sonnet 4.6
parent 8a9fb5085a
commit b6b6759b9a
26 changed files with 2900 additions and 2125 deletions
+78
View File
@@ -0,0 +1,78 @@
{% extends "base.html" %}
{% block title %}Scadenzario - Mago4 Demo{% endblock %}
{% block window_tabs %}<a href="/scadenzario" class="window-tab active"><i class="material-icons">event_note</i> Scadenzario</a>{% endblock %}
{% set status_badge = {'overdue':'badge-danger','due_soon':'badge-warning','open':'badge-info','paid':'badge-success'} %}
{% set status_label = {'overdue':'Scaduta','due_soon':'In scadenza','open':'Aperta','paid':'Saldata'} %}
{% block content %}
<div class="module-bar">
<div class="module-title">Scadenzario — Partite Aperte</div>
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/amministrazione">Amministrazione</a><span class="sep">»</span><span class="current">Scadenzario</span></div>
</div>
<div class="tab-strip">
<a href="/scadenzario" class="{% if selected_type == 'all' %}active{% endif %}">Tutte</a>
<a href="/scadenzario?type=receivable" class="{% if selected_type == 'receivable' %}active{% endif %}">Incassi (Crediti)</a>
<a href="/scadenzario?type=payable" class="{% if selected_type == 'payable' %}active{% endif %}">Pagamenti (Debiti)</a>
</div>
<div class="doc-toolbar">
<button class="tb-btn" title="Nuova partita" onclick="alert('Nuova partita (demo)')"><i class="material-icons">add_box</i></button>
<button class="tb-btn" title="Cerca"><i class="material-icons">search</i></button>
<div class="tb-sep"></div>
<button class="tb-btn" title="Stampa scadenzario"><i class="material-icons">print</i></button>
<button class="tb-btn" title="Presentazione effetti"><i class="material-icons">request_page</i></button>
</div>
<div class="content-pad">
<div class="panel">
<div class="panel-header"><h3><i class="material-icons">event_note</i> Partite ({{ schedules|length }})</h3></div>
<table class="data-table">
<thead><tr><th>Tipo</th><th>Scadenza</th><th class="text-center">Giorni</th><th>Cliente/Fornitore</th><th>Documento</th><th class="text-center">Modalità</th><th class="text-right">Importo</th><th class="text-center">Stato</th><th></th></tr></thead>
<tbody>
{% for s in schedules %}
{% set st = sched_status(s) %}
{% set days = sched_days(s) %}
<tr {% if st == 'overdue' %}class="low-stock"{% endif %}>
<td>
{% if s.type == RECEIVABLE %}<span class="badge badge-load"><i class="material-icons">south_west</i> Incasso</span>
{% else %}<span class="badge badge-unload"><i class="material-icons">north_east</i> Pagamento</span>{% endif %}
</td>
<td class="text-bold">{{ s.due_date.strftime("%d/%m/%Y") }}</td>
<td class="text-center">
{% if s.closed %}<span class="text-mute"></span>
{% elif days < 0 %}<span class="text-danger text-bold">{{ days }}</span>
{% elif days <= 7 %}<span class="text-warning text-bold">+{{ days }}</span>
{% else %}<span class="text-mute">+{{ days }}</span>{% endif %}
</td>
<td>{{ s.custsupp_name }}</td>
<td><span class="mono">{{ s.doc_no }}</span>{% if s.is_credit_note %} <span class="badge badge-grey">NC</span>{% endif %}</td>
<td class="text-center">{{ s.payment_method }}</td>
<td class="text-right text-bold {% if s.is_credit_note %}text-danger{% endif %}">
{% if s.is_credit_note %}{% endif %}€ {{ "%.2f"|format(s.amount) }}
</td>
<td class="text-center"><span class="badge {{ status_badge[st] }}">{{ status_label[st] }}</span></td>
<td class="text-center">
{% if not s.closed %}
<button class="btn-icon-sm act-cart" title="{{ 'Registra incasso' if s.type == RECEIVABLE else 'Registra pagamento' }}" onclick="settle({{ s.id }})"><i class="material-icons">task_alt</i></button>
{% else %}
<i class="material-icons text-success" style="font-size:17px;" title="Saldata">check_circle</i>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script>
function settle(id) {
if (!confirm('Registrare l\'avvenuto pagamento/incasso della partita?')) return;
fetch(`/scadenzario/${id}/settle`, { method: 'POST' })
.then(r => r.json())
.then(() => { showNotification('Partita saldata'); setTimeout(() => location.reload(), 700); })
.catch(() => showNotification('Errore', 'error'));
}
</script>
{% endblock %}