Files
Alby96andClaude Sonnet 4.6 b6b6759b9a 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>
2026-06-17 11:47:32 +02:00

181 lines
10 KiB
HTML
Raw Permalink 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 %}Dashboard - Mago4 Demo{% 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">Cruscotto Aziendale</div>
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><span class="current">Dashboard</span></div>
</div>
<div class="content-pad">
<!-- Banner allerta scadenze -->
{% if stats.overdue_pay_count or stats.overdue_recv_count %}
<div class="alert alert-danger">
<i class="material-icons">notification_important</i>
<span>
Attenzione: hai
{% if stats.overdue_pay_count %}<b>{{ stats.overdue_pay_count }} pagamenti scaduti</b> (€ {{ "%.2f"|format(stats.overdue_pay_amount) }}){% endif %}
{% if stats.overdue_pay_count and stats.overdue_recv_count %} e {% endif %}
{% if stats.overdue_recv_count %}<b>{{ stats.overdue_recv_count }} incassi scaduti</b> (€ {{ "%.2f"|format(stats.overdue_recv_amount) }}){% endif %}.
<a href="/scadenzario" style="color:inherit;font-weight:700;text-decoration:underline;">Vai allo scadenzario »</a>
</span>
</div>
{% endif %}
<!-- KPI: focus scadenze -->
<div class="kpi-grid">
<div class="kpi-card" style="border-left-color: var(--c-red)">
<div class="kpi-title">Pagamenti Scaduti</div>
<div class="kpi-row">
<div class="kpi-icon"><i class="material-icons" style="color:var(--c-red)">payments</i></div>
<div><div class="kpi-value">€ {{ "{:,.0f}".format(stats.overdue_pay_amount) }}</div>
<div class="text-mute" style="font-size:11px;">{{ stats.overdue_pay_count }} partite</div></div>
</div>
</div>
<div class="kpi-card" style="border-left-color: var(--c-orange)">
<div class="kpi-title">Incassi Scaduti</div>
<div class="kpi-row">
<div class="kpi-icon"><i class="material-icons" style="color:var(--c-orange)">request_quote</i></div>
<div><div class="kpi-value">€ {{ "{:,.0f}".format(stats.overdue_recv_amount) }}</div>
<div class="text-mute" style="font-size:11px;">{{ stats.overdue_recv_count }} partite</div></div>
</div>
</div>
<div class="kpi-card" style="border-left-color: var(--c-blue)">
<div class="kpi-title">Ordini da Evadere</div>
<div class="kpi-row">
<div class="kpi-icon"><i class="material-icons" style="color:var(--c-blue)">local_shipping</i></div>
<div class="kpi-value">{{ stats.sales_to_fulfill }}</div>
</div>
</div>
<div class="kpi-card" style="border-left-color: var(--c-purple)">
<div class="kpi-title">Articoli da Ordinare</div>
<div class="kpi-row">
<div class="kpi-icon"><i class="material-icons" style="color:var(--c-purple)">production_quantity_limits</i></div>
<div class="kpi-value">{{ stats.low_stock_count }}</div>
</div>
</div>
</div>
<!-- Colonna sinistra: scadenze | destra: cose da fare + cashflow -->
<div class="dash-grid-2">
<!-- Scadenze imminenti -->
<div class="panel">
<div class="panel-header">
<h3><i class="material-icons">event_note</i> Scadenze Imminenti (30 giorni)</h3>
<a href="/scadenzario" class="text-blue" style="font-size:12px;text-decoration:none;">Vedi tutto »</a>
</div>
<table class="data-table">
<thead><tr><th>Scadenza</th><th>Tipo</th><th>Soggetto</th><th class="text-right">Importo</th><th class="text-center">Stato</th></tr></thead>
<tbody>
{% for s in upcoming %}
{% set st = sched_status(s) %}
<tr class="row-link {% if st == 'overdue' %}low-stock{% endif %}" onclick="location.href='/scadenzario?type={{ s.type.value }}'">
<td class="text-bold">{{ s.due_date.strftime("%d/%m") }}</td>
<td>
{% if s.type == RECEIVABLE %}<span class="badge badge-load"><i class="material-icons">south_west</i> Inc.</span>
{% else %}<span class="badge badge-unload"><i class="material-icons">north_east</i> Pag.</span>{% endif %}
</td>
<td>{{ s.custsupp_name }}</td>
<td class="text-right text-bold">€ {{ "%.0f"|format(s.amount) }}</td>
<td class="text-center"><span class="badge {{ status_badge[st] }}">{{ status_label[st] }}</span></td>
</tr>
{% endfor %}
{% if not upcoming %}<tr><td colspan="5" class="text-center text-mute" style="padding:20px;">Nessuna scadenza nei prossimi 30 giorni</td></tr>{% endif %}
</tbody>
</table>
</div>
<div>
<!-- Cose da fare -->
<div class="panel">
<div class="panel-header"><h3><i class="material-icons">checklist</i> Cose da Fare</h3></div>
<div class="todo-list">
{% for t in todos %}
<div class="todo-item">
<div class="todo-icon c-{{ t.color }}"><i class="material-icons">{{ t.icon }}</i></div>
<div class="todo-body">
<div class="todo-text">{{ t.text }}</div>
{% if t.amount %}<div class="todo-amount">€ {{ "%.2f"|format(t.amount) }}</div>{% endif %}
</div>
<a href="{{ t.link }}" class="todo-action">{{ t.action }}</a>
</div>
{% endfor %}
{% if not todos %}
<div class="todo-empty"><i class="material-icons">check_circle</i>Tutto sotto controllo!<br>Nessuna attività urgente.</div>
{% endif %}
</div>
</div>
<!-- Previsione di cassa -->
<div class="panel">
<div class="panel-header"><h3><i class="material-icons">account_balance_wallet</i> Previsione di Cassa (30 gg)</h3></div>
<div class="cashflow-box">
<div class="cashflow-row">
<div class="cf-label"><i class="material-icons text-success">south_west</i> Incassi previsti</div>
<div class="cf-value text-success">€ {{ "{:,.2f}".format(cashflow.inflow) }}</div>
</div>
<div class="cashflow-row">
<div class="cf-label"><i class="material-icons text-danger">north_east</i> Pagamenti previsti</div>
<div class="cf-value text-danger"> € {{ "{:,.2f}".format(cashflow.outflow) }}</div>
</div>
<div class="cashflow-row cf-net">
<div class="cf-label">Saldo previsto</div>
<div class="cf-value {% if cashflow.net >= 0 %}text-success{% else %}text-danger{% endif %}">€ {{ "{:,.2f}".format(cashflow.net) }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Riga inferiore: fido superato + ultimi documenti -->
<div class="dash-grid-2">
<div class="panel">
<div class="panel-header"><h3><i class="material-icons">credit_card</i> Controllo Fido Clienti</h3></div>
<table class="data-table">
<thead><tr><th>Cliente</th><th class="text-right">Fido</th><th class="text-right">Esposizione</th><th class="text-center">Stato</th></tr></thead>
<tbody>
{% for c in credit_exceeded %}
<tr class="low-stock">
<td>{{ c.name }}</td>
<td class="text-right">€ {{ "%.0f"|format(c.credit_limit) }}</td>
<td class="text-right text-danger text-bold">€ {{ "%.0f"|format(c.balance) }}</td>
<td class="text-center"><span class="badge badge-danger"><i class="material-icons">warning</i> Superato</span></td>
</tr>
{% endfor %}
{% if not credit_exceeded %}<tr><td colspan="4" class="text-center text-mute" style="padding:20px;">Nessun cliente ha superato il fido</td></tr>{% endif %}
</tbody>
</table>
</div>
<div class="panel">
<div class="panel-header"><h3><i class="material-icons">description</i> Ultimi Documenti di Vendita</h3></div>
<table class="data-table">
<thead><tr><th>Data</th><th>Numero</th><th>Cliente</th><th class="text-right">Totale</th><th>Stato</th></tr></thead>
<tbody>
{% for i in last_invoices %}
<tr class="row-link" onclick="location.href='/invoices/{{ i.id }}'">
<td>{{ i.doc_date.strftime("%d/%m/%Y") }}</td>
<td class="text-bold mono">{{ i.doc_no }}</td>
<td>{{ i.customer_name }}</td>
<td class="text-right">€ {{ "%.2f"|format(i.total) }}</td>
<td><span class="badge status-{{ i.status.value }}">{{ doc_status_label(i.status) }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- KPI riepilogo -->
<div class="kpi-grid" style="margin-top:16px;">
<div class="kpi-card"><div class="kpi-title">Fatturato Emesso</div><div class="kpi-value text-success">€ {{ "{:,.0f}".format(stats.invoiced_total) }}</div></div>
<div class="kpi-card" style="border-left-color: var(--c-teal)"><div class="kpi-title">Crediti Aperti</div><div class="kpi-value">€ {{ "{:,.0f}".format(stats.open_recv_amount) }}</div></div>
<div class="kpi-card" style="border-left-color: var(--c-orange)"><div class="kpi-title">Debiti Aperti</div><div class="kpi-value">€ {{ "{:,.0f}".format(stats.open_pay_amount) }}</div></div>
<div class="kpi-card" style="border-left-color: var(--c-purple)"><div class="kpi-title">Valore Magazzino</div><div class="kpi-value">€ {{ "{:,.0f}".format(stats.warehouse_value) }}</div></div>
</div>
</div>
{% endblock %}