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:
@@ -0,0 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Acquisti - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/acquisti" class="window-tab active"><i class="material-icons">shopping_cart</i> Acquisti</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Acquisti</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><span class="current">Acquisti</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/purchase-orders">Ordini Fornitore</a>
|
||||
<a href="/acquisti" class="active">Documenti Acquisto</a>
|
||||
<a href="#">Politiche Acquisto</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid">
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Ordini Fornitore</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/purchase-orders" class="menu-link"><i class="material-icons ml-icon mtype-form">shopping_cart</i><span class="ml-text">Ordini Fornitore</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/purchase-orders" class="menu-link"><i class="material-icons ml-icon mtype-list">list_alt</i><span class="ml-text">Lista Ordini Fornitore</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/purchase-orders" class="menu-link"><i class="material-icons ml-icon mtype-print">print</i><span class="ml-text">Portafoglio Fornitori</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Documenti di Acquisto</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-form">receipt_long</i><span class="ml-text">Fatture di Acquisto</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-form">inventory</i><span class="ml-text">Documenti di Carico</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Procedure</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/magazzino" class="menu-link"><i class="material-icons ml-icon mtype-proc">bolt</i><span class="ml-text">Generazione Ordini da Sottoscorta</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-print">bar_chart</i><span class="ml-text">Statistiche Acquisti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Amministrazione - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/amministrazione" class="window-tab active"><i class="material-icons">account_balance</i> Amministrazione</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Amministrazione</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><span class="current">Amministrazione</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/amministrazione" class="active">Amministrazione</a>
|
||||
<a href="/scadenzario">Scadenzario</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid">
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Pagamenti / Incassi</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/scadenzario" class="menu-link"><i class="material-icons ml-icon mtype-form">event_note</i><span class="ml-text">Scadenzario</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/scadenzario?type=receivable" class="menu-link"><i class="material-icons ml-icon mtype-list">request_quote</i><span class="ml-text">Partite a Credito (Incassi)</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/scadenzario?type=payable" class="menu-link"><i class="material-icons ml-icon mtype-list">payments</i><span class="ml-text">Partite a Debito (Pagamenti)</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/scadenzario" class="menu-link"><i class="material-icons ml-icon mtype-print">print</i><span class="ml-text">Stampa Scadenzario</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Contabilità</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-form">menu_book</i><span class="ml-text">Prima Nota</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">account_tree</i><span class="ml-text">Piano dei Conti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-print">summarize</i><span class="ml-text">Bilancio</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>IVA</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-print">receipt_long</i><span class="ml-text">Registri IVA</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">calculate</i><span class="ml-text">Liquidazione IVA</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Banche</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">account_balance</i><span class="ml-text">Banche Aziendali</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">sync_alt</i><span class="ml-text">Riconciliazione Bancaria</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">request_page</i><span class="ml-text">Presentazione Effetti (RiBa)</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Anagrafiche - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/anagrafiche" class="window-tab active"><i class="material-icons">contacts</i> Anagrafiche</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Anagrafiche</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><span class="current">Anagrafiche</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/anagrafiche" class="active">Anagrafiche</a>
|
||||
<a href="/customers">Clienti</a>
|
||||
<a href="/suppliers">Fornitori</a>
|
||||
<a href="/products">Articoli</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid">
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Clienti</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/customers" class="menu-link"><i class="material-icons ml-icon mtype-form">people</i><span class="ml-text">Clienti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/customers" class="menu-link"><i class="material-icons ml-icon mtype-print">badge</i><span class="ml-text">Schede Cliente</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/customers" class="menu-link"><i class="material-icons ml-icon mtype-list">account_balance_wallet</i><span class="ml-text">Fido Cliente</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Fornitori</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/suppliers" class="menu-link"><i class="material-icons ml-icon mtype-form">local_shipping</i><span class="ml-text">Fornitori</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/suppliers" class="menu-link"><i class="material-icons ml-icon mtype-print">badge</i><span class="ml-text">Schede Fornitore</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Articoli</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/products" class="menu-link"><i class="material-icons ml-icon mtype-form">inventory_2</i><span class="ml-text">Articoli</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/warehouse" class="menu-link"><i class="material-icons ml-icon mtype-list">inventory</i><span class="ml-text">Giacenze Articoli</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/products" class="menu-link"><i class="material-icons ml-icon mtype-print">sell</i><span class="ml-text">Listini</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Tabelle</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">category</i><span class="ml-text">Categorie Clienti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">payments</i><span class="ml-text">Condizioni di Pagamento</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">public</i><span class="ml-text">Paesi ISO</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
+80
-63
@@ -3,81 +3,98 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Mago4 Demo - ERP{% endblock %}</title>
|
||||
<title>{% block title %}Mago4 Demo{% endblock %}</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap">
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='/css/style.css') }}">
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container">
|
||||
<!-- Sidebar Navigation -->
|
||||
<!-- Window tabs (stile desktop Mago4) -->
|
||||
<div class="window-tabs">
|
||||
<a href="/" class="window-tab {% if active_module == 'dashboard' %}active{% endif %}">
|
||||
<i class="material-icons">home</i> Home
|
||||
</a>
|
||||
{% block window_tabs %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<header class="app-header">
|
||||
<button class="header-burger"><i class="material-icons">menu</i></button>
|
||||
<a href="/" class="logo">
|
||||
<span class="logo-mark">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 20 L8 4 L12 14 L16 4 L22 20 L18 20 L15 11 L12 18 L9 11 L6 20 Z" fill="#1577be"/>
|
||||
<path d="M16 14 L20 14 L20 9 L22 9 L22 20 L18 20 Z" fill="#5aa632"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="logo-text">MAGO</span>
|
||||
</a>
|
||||
|
||||
<div class="header-search">
|
||||
<i class="material-icons">search</i>
|
||||
<input type="text" placeholder="Cerca..." onkeydown="if(event.key==='Enter')globalSearch(this.value)">
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<button class="header-icon-btn" title="Lingua"><i class="material-icons">language</i></button>
|
||||
<div class="header-date">
|
||||
<span class="label">Data delle operazioni</span>
|
||||
<span class="value">{{ operation_date }}</span>
|
||||
</div>
|
||||
<button class="header-icon-btn" title="Calendario"><i class="material-icons">event</i></button>
|
||||
<button class="header-icon-btn" title="Profilo"><i class="material-icons">account_circle</i></button>
|
||||
<button class="header-icon-btn" title="Aggiorna" onclick="location.reload()"><i class="material-icons">refresh</i></button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="app-body">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="logo">
|
||||
<span class="logo-text">Mago4</span>
|
||||
<span class="logo-version">Demo</span>
|
||||
</div>
|
||||
<div class="sidebar-group-title">
|
||||
<span class="gt-left">
|
||||
<span class="gt-mark"><i class="material-icons">apps</i></span>
|
||||
<span class="gt-text">Mago4</span>
|
||||
</span>
|
||||
<i class="material-icons chev">expand_less</i>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a href="/" class="nav-item {% if request.url.path == '/' %}active{% endif %}">
|
||||
<i class="material-icons">dashboard</i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="/customers" class="nav-item {% if 'customers' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">people</i>
|
||||
<span>Clienti</span>
|
||||
</a>
|
||||
<a href="/suppliers" class="nav-item {% if 'suppliers' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">local_shipping</i>
|
||||
<span>Fornitori</span>
|
||||
</a>
|
||||
<a href="/products" class="nav-item {% if 'products' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">inventory_2</i>
|
||||
<span>Articoli</span>
|
||||
</a>
|
||||
<a href="/sales-orders" class="nav-item {% if 'sales' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">receipt</i>
|
||||
<span>Ordini Vendita</span>
|
||||
</a>
|
||||
<a href="/purchase-orders" class="nav-item {% if 'purchase' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">shopping_cart</i>
|
||||
<span>Ordini Acquisto</span>
|
||||
</a>
|
||||
<a href="/warehouse" class="nav-item {% if 'warehouse' in request.url.path %}active{% endif %}">
|
||||
<i class="material-icons">warehouse</i>
|
||||
<span>Magazzino</span>
|
||||
</a>
|
||||
</nav>
|
||||
<a href="/" class="nav-item {% if active_module == 'dashboard' %}active{% endif %}">
|
||||
<i class="material-icons ic-anagrafiche">dashboard</i> Dashboard
|
||||
</a>
|
||||
<a href="/anagrafiche" class="nav-item {% if active_module == 'anagrafiche' %}active{% endif %}">
|
||||
<i class="material-icons ic-anagrafiche">contacts</i> Anagrafiche
|
||||
</a>
|
||||
<a href="/amministrazione" class="nav-item {% if active_module == 'amministrazione' %}active{% endif %}">
|
||||
<i class="material-icons ic-amministrazione">account_balance</i> Amministrazione
|
||||
</a>
|
||||
<a href="/vendite" class="nav-item {% if active_module == 'vendite' %}active{% endif %}">
|
||||
<i class="material-icons ic-vendite">point_of_sale</i> Vendite
|
||||
</a>
|
||||
<a href="/acquisti" class="nav-item {% if active_module == 'acquisti' %}active{% endif %}">
|
||||
<i class="material-icons ic-acquisti">shopping_cart</i> Acquisti
|
||||
</a>
|
||||
<a href="/magazzino" class="nav-item {% if active_module == 'magazzino' %}active{% endif %}">
|
||||
<i class="material-icons ic-logistica">warehouse</i> Logistica
|
||||
</a>
|
||||
<a href="#" class="nav-item disabled">
|
||||
<i class="material-icons ic-produzione">precision_manufacturing</i> Produzione
|
||||
</a>
|
||||
<a href="#" class="nav-item disabled">
|
||||
<i class="material-icons ic-servizi">build</i> Servizi
|
||||
</a>
|
||||
<a href="#" class="nav-item disabled">
|
||||
<i class="material-icons ic-preferenze">settings</i> Preferenze
|
||||
</a>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="version-info">
|
||||
<span>v1.0.0</span>
|
||||
<span>In-Memory DB</span>
|
||||
</div>
|
||||
<div class="sidebar-sub-group">
|
||||
<span>TaskBuilder Studio</span>
|
||||
<i class="material-icons chev" style="font-size:16px;">expand_more</i>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<header class="top-bar">
|
||||
<div class="top-bar-content">
|
||||
<h1 class="page-title">{% block page_title %}{% endblock %}</h1>
|
||||
<div class="top-bar-actions">
|
||||
<button class="btn-icon" title="Notifiche">
|
||||
<i class="material-icons">notifications</i>
|
||||
</button>
|
||||
<button class="btn-icon" title="Profilo">
|
||||
<i class="material-icons">account_circle</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="content-area">
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
<!-- Main content -->
|
||||
<main class="main">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
+60
-111
@@ -1,53 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Clienti - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Clienti{% endblock %}
|
||||
{% block window_tabs %}<a href="/customers" class="window-tab active"><i class="material-icons">people</i> Clienti</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<button class="btn btn-primary" onclick="openCreateCustomerModal()">
|
||||
<i class="material-icons">add</i> Nuovo Cliente
|
||||
</button>
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Clienti</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/anagrafiche">Anagrafiche</a><span class="sep">»</span><span class="current">Clienti</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/anagrafiche">Anagrafiche</a>
|
||||
<a href="/customers" class="active">Clienti</a>
|
||||
<a href="/suppliers">Fornitori</a>
|
||||
<a href="/products">Articoli</a>
|
||||
</div>
|
||||
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Elenco Clienti</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo" onclick="alert('Nuovo cliente (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"><i class="material-icons">print</i></button>
|
||||
<button class="tb-btn" title="Esporta"><i class="material-icons">file_download</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">people</i> Elenco Clienti</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Ragione Sociale</th>
|
||||
<th>Città</th>
|
||||
<th>Paese</th>
|
||||
<th>Telefono</th>
|
||||
<th>Limite Credito</th>
|
||||
<th>Saldo</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
<tr><th>Codice</th><th>Ragione Sociale</th><th>Partita IVA</th><th>Città</th><th>Paese</th><th>Pagamento</th><th class="text-right">Fido</th><th class="text-right">Saldo</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for customer in customers %}
|
||||
{% for c in customers %}
|
||||
<tr>
|
||||
<td><strong>{{ customer.code }}</strong></td>
|
||||
<td>{{ customer.name }}</td>
|
||||
<td>{{ customer.city }}</td>
|
||||
<td>{{ customer.country }}</td>
|
||||
<td>{{ customer.phone }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(customer.credit_limit) }}</td>
|
||||
<td class="text-right {% if customer.balance > 0 %}text-danger{% endif %}">
|
||||
€ {{ "%.2f"|format(customer.balance) }}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-info" onclick="viewCustomer({{ customer.id }})">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
<button class="btn-small btn-warning" onclick="editCustomer({{ customer.id }})">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-bold mono">{{ c.code }}</td>
|
||||
<td>{{ c.name }}</td>
|
||||
<td class="mono">{{ c.vat_number or '—' }}</td>
|
||||
<td>{{ c.city }}</td>
|
||||
<td>{{ c.country }}</td>
|
||||
<td>{{ c.payment_terms }}</td>
|
||||
<td class="text-right">€ {{ "%.0f"|format(c.credit_limit) }}</td>
|
||||
<td class="text-right {% if c.balance > 0 %}text-danger{% endif %}">€ {{ "%.2f"|format(c.balance) }}</td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-view" onclick="viewCustomer({{ c.id }})"><i class="material-icons">visibility</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -55,82 +48,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Detail -->
|
||||
<div id="customerModal" class="modal" onclick="closeModal(event)">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Dettagli Cliente</h2>
|
||||
<button class="btn-close" onclick="closeModal()">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
<!-- Content loaded via JS -->
|
||||
</div>
|
||||
<div id="genericModal" class="modal" onclick="if(event.target.id==='genericModal')closeGenericModal()">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2 id="gmTitle">Dettagli</h2><button class="modal-close" onclick="closeGenericModal()"><i class="material-icons">close</i></button></div>
|
||||
<div class="modal-body" id="gmBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewCustomer(customerId) {
|
||||
fetch(`/api/customers/${customerId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('modalTitle').textContent = `Cliente: ${data.name}`;
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Codice</label>
|
||||
<span>${data.code}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Ragione Sociale</label>
|
||||
<span>${data.name}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Email</label>
|
||||
<span>${data.email}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Telefono</label>
|
||||
<span>${data.phone}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Indirizzo</label>
|
||||
<span>${data.address}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Città</label>
|
||||
<span>${data.city}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Paese</label>
|
||||
<span>${data.country}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Limite Credito</label>
|
||||
<span>€ ${data.credit_limit.toFixed(2)}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Saldo</label>
|
||||
<span class="${data.balance > 0 ? 'text-danger' : ''}">${data.balance > 0 ? '€ ' : ''}${Math.abs(data.balance).toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('customerModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
|
||||
function editCustomer(customerId) {
|
||||
alert('Modifica cliente: ' + customerId + ' (da implementare)');
|
||||
}
|
||||
|
||||
function openCreateCustomerModal() {
|
||||
alert('Creazione nuovo cliente (da implementare)');
|
||||
}
|
||||
|
||||
function closeModal(event) {
|
||||
if (event && event.target.id !== 'customerModal') return;
|
||||
document.getElementById('customerModal').classList.remove('show');
|
||||
function viewCustomer(id) {
|
||||
fetch(`/api/customers/${id}`).then(r=>r.json()).then(d=>{
|
||||
document.getElementById('gmTitle').textContent = 'Cliente: ' + d.name;
|
||||
document.getElementById('gmBody').innerHTML = `
|
||||
<div style="padding:16px">
|
||||
<div class="form-section-title">Dati Principali</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Codice', d.code)}${field('Ragione Sociale', d.name)}
|
||||
${field('Partita IVA', d.vat_number || '—')}${field('Codice Fiscale', d.fiscal_code || '—')}
|
||||
${field('Email', d.email)}${field('Telefono', d.phone)}
|
||||
</div>
|
||||
<div class="form-section-title">Indirizzo</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Indirizzo', d.address)}${field('Città', d.city)}
|
||||
${field('Paese', d.country)}${field('Pagamento', d.payment_terms)}
|
||||
</div>
|
||||
<div class="form-section-title">Dati Commerciali</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Limite Credito', '€ ' + d.credit_limit.toFixed(2))}
|
||||
${field('Saldo', '€ ' + d.balance.toFixed(2))}
|
||||
</div>
|
||||
</div>`;
|
||||
document.getElementById('genericModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
+173
-99
@@ -1,106 +1,180 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Dashboard{% 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="dashboard-container">
|
||||
<!-- First Row - KPI Cards -->
|
||||
<div class="widget-row">
|
||||
{% for widget in widgets[0] %}
|
||||
<div class="widget-card stat-card" style="border-left-color: var(--color-{{ widget.color }})">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">{{ widget.title }}</h3>
|
||||
</div>
|
||||
<div class="widget-body stat-body">
|
||||
<div class="stat-icon" style="color: var(--color-{{ widget.color }})">
|
||||
<i class="material-icons">{{ widget.icon }}</i>
|
||||
</div>
|
||||
<div class="stat-value">{{ widget.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Second Row - Data Tables -->
|
||||
<div class="widget-row">
|
||||
{% for widget in widgets[1] %}
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">{{ widget.title }}</h3>
|
||||
{% if widget.subtitle %}<p class="widget-subtitle">{{ widget.subtitle }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="widget-body grid-body">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if "Vendita" in widget.title %}
|
||||
<th>Data</th>
|
||||
<th>Numero Ordine</th>
|
||||
<th>Cliente</th>
|
||||
<th>Importo</th>
|
||||
<th>Stato</th>
|
||||
{% else %}
|
||||
<th>Data</th>
|
||||
<th>Numero Ordine</th>
|
||||
<th>Fornitore</th>
|
||||
<th>Importo</th>
|
||||
<th>Stato</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if "Vendita" in widget.title %}
|
||||
{% for order in widget.data.orders %}
|
||||
<tr>
|
||||
<td>{{ order.order_date }}</td>
|
||||
<td><strong>{{ order.order_number }}</strong></td>
|
||||
<td>{{ order.customer_name }}</td>
|
||||
<td class="text-right">{{ order.total_amount }}</td>
|
||||
<td>
|
||||
<span class="badge status-{{ order.status }}">{{ order.status }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for order in widget.data.orders %}
|
||||
<tr>
|
||||
<td>{{ order.order_date }}</td>
|
||||
<td><strong>{{ order.order_number }}</strong></td>
|
||||
<td>{{ order.supplier_name }}</td>
|
||||
<td class="text-right">{{ order.total_amount }}</td>
|
||||
<td>
|
||||
<span class="badge status-{{ order.status }}">{{ order.status }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="summary-stats">
|
||||
<div class="stat-summary">
|
||||
<div class="stat-label">Totale Clienti</div>
|
||||
<div class="stat-number">{{ stats.total_customers }}</div>
|
||||
</div>
|
||||
<div class="stat-summary">
|
||||
<div class="stat-label">Totale Fornitori</div>
|
||||
<div class="stat-number">{{ stats.total_suppliers }}</div>
|
||||
</div>
|
||||
<div class="stat-summary">
|
||||
<div class="stat-label">Articoli in Catalogo</div>
|
||||
<div class="stat-number">{{ stats.total_products }}</div>
|
||||
</div>
|
||||
<div class="stat-summary">
|
||||
<div class="stat-label">Debiti Fornitori</div>
|
||||
<div class="stat-number text-orange">€ {{ "%.2f"|format(stats.total_payable) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<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 %}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Movimenti di Magazzino - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/inventory-movements" class="window-tab active"><i class="material-icons">swap_vert</i> Movimenti</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Movimenti di Magazzino</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/magazzino">Logistica</a><span class="sep">»</span><span class="current">Movimenti</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/magazzino">Magazzino</a>
|
||||
<a href="/inventory-movements" class="active">Movimenti</a>
|
||||
<a href="/warehouse">Giacenze</a>
|
||||
<a href="/inventory-reasons">Causali</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo movimento" onclick="alert('Nuovo movimento (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 giornale"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="alert alert-info"><i class="material-icons">info</i> I movimenti <b>confermati</b> aggiornano le giacenze secondo il segno della causale (carico +, scarico −).</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">swap_vert</i> Elenco Movimenti</h3></div>
|
||||
<table class="data-table">
|
||||
<thead><tr><th>Data Reg.</th><th>Numero</th><th>Causale</th><th class="text-center">Segno</th><th>Cliente/Fornitore</th><th class="text-center">Deposito</th><th class="text-right">Valore</th><th class="text-center">Stato</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for m in movements %}
|
||||
<tr class="row-link" onclick="location.href='/inventory-movements/{{ m.id }}'">
|
||||
<td>{{ m.posting_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td class="text-bold mono">{{ m.doc_no }}</td>
|
||||
<td><span class="mono">{{ m.reason_code }}</span> — {{ m.reason_description }}</td>
|
||||
<td class="text-center">
|
||||
{% if m.sign.value == 'load' %}<span class="badge badge-load"><i class="material-icons">add</i> Carico</span>
|
||||
{% else %}<span class="badge badge-unload"><i class="material-icons">remove</i> Scarico</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ m.custsupp_name or '—' }}</td>
|
||||
<td class="text-center">{{ m.storage }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(m.total_amount) }}</td>
|
||||
<td class="text-center">
|
||||
{% if m.posted %}<span class="badge badge-success"><i class="material-icons">check</i> Confermato</span>
|
||||
{% else %}<span class="badge badge-grey">Bozza</span>{% endif %}
|
||||
</td>
|
||||
<td class="text-center"><i class="material-icons text-blue" style="font-size:17px;">chevron_right</i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Causali di Magazzino - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/inventory-reasons" class="window-tab active"><i class="material-icons">rule</i> Causali</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Causali di Magazzino</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/magazzino">Logistica</a><span class="sep">»</span><span class="current">Causali</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/magazzino">Magazzino</a>
|
||||
<a href="/inventory-movements">Movimenti</a>
|
||||
<a href="/warehouse">Giacenze</a>
|
||||
<a href="/inventory-reasons" class="active">Causali</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuova causale" onclick="alert('Nuova causale (demo)')"><i class="material-icons">add_box</i></button>
|
||||
<button class="tb-btn" title="Stampa"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="alert alert-info"><i class="material-icons">info</i> Le causali determinano se un movimento <b>carica</b> o <b>scarica</b> la giacenza (campo <span class="mono">DebitCreditSign</span> in Mago4).</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">rule</i> Elenco Causali</h3></div>
|
||||
<table class="data-table">
|
||||
<thead><tr><th>Codice</th><th>Descrizione</th><th class="text-center">Segno</th><th class="text-center">Numerazione Fiscale</th><th class="text-center">Aggiorna Giacenze</th></tr></thead>
|
||||
<tbody>
|
||||
{% for r in reasons %}
|
||||
<tr>
|
||||
<td class="text-bold mono">{{ r.code }}</td>
|
||||
<td>{{ r.description }}</td>
|
||||
<td class="text-center">
|
||||
{% if r.sign.value == 'load' %}<span class="badge badge-load"><i class="material-icons">add</i> Carico (+)</span>
|
||||
{% else %}<span class="badge badge-unload"><i class="material-icons">remove</i> Scarico (−)</span>{% endif %}
|
||||
</td>
|
||||
<td class="text-center">{% if r.fiscal %}<i class="material-icons text-success">check_circle</i>{% else %}<i class="material-icons text-mute">remove</i>{% endif %}</td>
|
||||
<td class="text-center">{% if r.updates_balance %}<i class="material-icons text-success">check_circle</i>{% else %}<i class="material-icons text-mute">remove</i>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,141 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ invoice.doc_no }} - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}
|
||||
<a href="/invoices" class="window-tab"><i class="material-icons">description</i> Documenti Vendita</a>
|
||||
<a href="/invoices/{{ invoice.id }}" class="window-tab active"><i class="material-icons">request_quote</i> {{ invoice.doc_no }}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">{{ doc_type_label(invoice.doc_type) }} — {{ invoice.doc_no }}</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/vendite">Vendite</a><span class="sep">»</span><a href="/invoices">Documenti Vendita</a><span class="sep">»</span><span class="current">{{ invoice.doc_no }}</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar documento -->
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Primo"><i class="material-icons">first_page</i></button>
|
||||
<button class="tb-btn" title="Precedente"><i class="material-icons">chevron_left</i></button>
|
||||
<button class="tb-btn" title="Successivo"><i class="material-icons">chevron_right</i></button>
|
||||
<button class="tb-btn" title="Ultimo"><i class="material-icons">last_page</i></button>
|
||||
<div class="tb-sep"></div>
|
||||
<button class="tb-btn" title="Nuovo"><i class="material-icons">note_add</i></button>
|
||||
<button class="tb-btn" title="Modifica"><i class="material-icons">edit</i></button>
|
||||
<button class="tb-btn" title="Salva"><i class="material-icons">save</i></button>
|
||||
<button class="tb-btn" title="Elimina"><i class="material-icons">delete</i></button>
|
||||
<div class="tb-sep"></div>
|
||||
<button class="tb-btn" title="Stampa"><i class="material-icons">print</i></button>
|
||||
<button class="tb-btn" title="Invia email"><i class="material-icons">email</i></button>
|
||||
<button class="tb-btn" title="Esporta XML (FatturaPA)"><i class="material-icons">file_download</i></button>
|
||||
<div class="tb-sep"></div>
|
||||
<a class="tb-btn" href="/invoices" title="Chiudi"><i class="material-icons">logout</i></a>
|
||||
</div>
|
||||
|
||||
<!-- Intestazione documento -->
|
||||
<div style="background:#fff;border-bottom:1px solid var(--border);padding:14px 20px;">
|
||||
<div class="form-grid-2" style="margin-bottom:0;">
|
||||
<div>
|
||||
<div class="field-row"><label>Numero</label><div class="field-value text-bold">{{ invoice.doc_no }}</div></div>
|
||||
<div class="field-row"><label>Del</label><div class="field-value">{{ invoice.doc_date.strftime("%d/%m/%Y") }}</div></div>
|
||||
<div class="field-row"><label>Registrato il</label><div class="field-value">{{ invoice.posting_date.strftime("%d/%m/%Y") }}</div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field-row"><label>Cliente</label><div class="field-value">{{ invoice.customer_name }}</div></div>
|
||||
<div class="field-row"><label>Partita IVA</label><div class="field-value mono">{{ invoice.customer_vat or '—' }}</div></div>
|
||||
<div class="field-row"><label>Stato</label><div class="field-value"><span class="badge status-{{ invoice.status.value }}">{{ doc_status_label(invoice.status) }}</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form con tab laterali -->
|
||||
<div class="doc-form">
|
||||
<div class="doc-form-tabs">
|
||||
<div class="doc-form-tab active" onclick="showTab(this,'tab-main')"><i class="material-icons">article</i> Dati Principali</div>
|
||||
<div class="doc-form-tab" onclick="showTab(this,'tab-lines')"><i class="material-icons">list</i> Righe Documento</div>
|
||||
<div class="doc-form-tab" onclick="showTab(this,'tab-pay')"><i class="material-icons">payments</i> Dati Pagamento</div>
|
||||
<div class="doc-form-tab" onclick="showTab(this,'tab-notes')"><i class="material-icons">notes</i> Note</div>
|
||||
</div>
|
||||
|
||||
<div class="doc-form-content">
|
||||
<!-- TAB: Dati principali -->
|
||||
<div id="tab-main">
|
||||
<div class="form-section-title">Dati Principali</div>
|
||||
<div class="form-grid-2">
|
||||
<div class="field-row"><label>Tipo Documento</label><div class="field-value">{{ doc_type_label(invoice.doc_type) }}</div></div>
|
||||
<div class="field-row"><label>Valuta</label><div class="field-value">{{ invoice.currency }}</div></div>
|
||||
<div class="field-row"><label>Listino</label><div class="field-value">{{ invoice.price_list }}</div></div>
|
||||
<div class="field-row"><label>Agente</label><div class="field-value">{{ invoice.salesperson or '—' }}</div></div>
|
||||
<div class="field-row"><label>Nostro Rif.</label><div class="field-value">{{ invoice.our_reference or '—' }}</div></div>
|
||||
<div class="field-row"><label>Vostro Rif.</label><div class="field-value">{{ invoice.your_reference or '—' }}</div></div>
|
||||
</div>
|
||||
<div class="form-section-title">Stato Elaborazione</div>
|
||||
<div class="form-grid-2">
|
||||
<div class="field-row"><label>Stampato</label><div class="field-value">{{ 'Sì' if invoice.printed else 'No' }}</div></div>
|
||||
<div class="field-row"><label>Emesso</label><div class="field-value">{{ 'Sì' if invoice.issued else 'No' }}</div></div>
|
||||
<div class="field-row"><label>Scaricato a Magazzino</label><div class="field-value">{{ 'Sì' if invoice.posted_to_inventory else 'No' }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Righe -->
|
||||
<div id="tab-lines" style="display:none;">
|
||||
<div class="form-section-title">Righe Documento</div>
|
||||
<table class="data-table-nested">
|
||||
<thead><tr><th>#</th><th>Articolo</th><th>Descrizione</th><th class="text-center">UM</th><th class="text-right">Q.tà</th><th class="text-right">Val. Unit.</th><th class="text-right">Sconto</th><th class="text-right">Prezzo Netto</th><th class="text-center">IVA</th><th class="text-right">Imponibile</th></tr></thead>
|
||||
<tbody>
|
||||
{% for l in invoice.lines %}
|
||||
<tr>
|
||||
<td>{{ l.line_no }}</td>
|
||||
<td class="mono text-bold">{{ l.item_code }}</td>
|
||||
<td>{{ l.description }}</td>
|
||||
<td class="text-center">{{ l.uom }}</td>
|
||||
<td class="text-right">{{ "%.2f"|format(l.quantity) }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(l.unit_value) }}</td>
|
||||
<td class="text-right">{{ "%.0f"|format(l.discount_pct) }}%</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(l.net_price) }}</td>
|
||||
<td class="text-center">{{ "%.0f"|format(l.tax_rate) }}%</td>
|
||||
<td class="text-right text-bold">€ {{ "%.2f"|format(l.taxable_amount) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Pagamento -->
|
||||
<div id="tab-pay" style="display:none;">
|
||||
<div class="form-section-title">Dati di Pagamento</div>
|
||||
<div class="form-grid-2">
|
||||
<div class="field-row"><label>Condizione</label><div class="field-value">{{ invoice.payment_terms }}</div></div>
|
||||
<div class="field-row"><label>Totale Documento</label><div class="field-value text-bold">€ {{ "%.2f"|format(invoice.total) }}</div></div>
|
||||
</div>
|
||||
<div class="form-section-title">Riepilogo IVA</div>
|
||||
<table class="data-table-nested">
|
||||
<thead><tr><th>Aliquota</th><th class="text-right">Imponibile</th><th class="text-right">Imposta</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>22%</td><td class="text-right">€ {{ "%.2f"|format(invoice.taxable_total) }}</td><td class="text-right">€ {{ "%.2f"|format(invoice.tax_total) }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Note -->
|
||||
<div id="tab-notes" style="display:none;">
|
||||
<div class="form-section-title">Note</div>
|
||||
<div class="field-value" style="min-height:80px;align-items:flex-start;">{{ invoice.notes or 'Nessuna nota.' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Totali -->
|
||||
<div class="totals-box">
|
||||
<div class="t-item"><div class="t-label">Totale Imponibile</div><div class="t-value">€ {{ "%.2f"|format(invoice.taxable_total) }}</div></div>
|
||||
<div class="t-item"><div class="t-label">Totale IVA</div><div class="t-value">€ {{ "%.2f"|format(invoice.tax_total) }}</div></div>
|
||||
<div class="t-item grand"><div class="t-label">TOTALE DOCUMENTO</div><div class="t-value">€ {{ "%.2f"|format(invoice.total) }}</div></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showTab(el, id) {
|
||||
document.querySelectorAll('.doc-form-tab').forEach(t => t.classList.remove('active'));
|
||||
el.classList.add('active');
|
||||
['tab-main','tab-lines','tab-pay','tab-notes'].forEach(t =>
|
||||
document.getElementById(t).style.display = (t === id ? 'block' : 'none'));
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Documenti Vendita - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/invoices" class="window-tab active"><i class="material-icons">description</i> Documenti Vendita</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Documenti di Vendita</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/vendite">Vendite</a><span class="sep">»</span><span class="current">Documenti Vendita</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/invoices" class="{% if selected_type == 'all' %}active{% endif %}">Tutti</a>
|
||||
<a href="/invoices?type=immediate_invoice" class="{% if selected_type == 'immediate_invoice' %}active{% endif %}">Fatture Immediate</a>
|
||||
<a href="/invoices?type=delivery_note" class="{% if selected_type == 'delivery_note' %}active{% endif %}">DDT</a>
|
||||
<a href="/invoices?type=credit_note" class="{% if selected_type == 'credit_note' %}active{% endif %}">Note di Credito</a>
|
||||
<a href="/invoices?type=proforma_invoice" class="{% if selected_type == 'proforma_invoice' %}active{% endif %}">ProForma</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo documento" onclick="alert('Nuovo documento (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"><i class="material-icons">print</i></button>
|
||||
<button class="tb-btn" title="Invia email"><i class="material-icons">email</i></button>
|
||||
<button class="tb-btn" title="Esporta XML"><i class="material-icons">file_download</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">description</i> Elenco Documenti ({{ invoices|length }})</h3></div>
|
||||
<table class="data-table">
|
||||
<thead><tr><th>Tipo</th><th>Numero</th><th>Data</th><th>Cliente</th><th class="text-right">Imponibile</th><th class="text-right">IVA</th><th class="text-right">Totale</th><th>Stato</th><th class="text-center">Fatt.</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for i in invoices %}
|
||||
<tr class="row-link" onclick="location.href='/invoices/{{ i.id }}'">
|
||||
<td>{{ doc_type_label(i.doc_type) }}</td>
|
||||
<td class="text-bold mono">{{ i.doc_no }}</td>
|
||||
<td>{{ i.doc_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td>{{ i.customer_name }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(i.taxable_total) }}</td>
|
||||
<td class="text-right text-mute">€ {{ "%.2f"|format(i.tax_total) }}</td>
|
||||
<td class="text-right text-bold">€ {{ "%.2f"|format(i.total) }}</td>
|
||||
<td><span class="badge status-{{ i.status.value }}">{{ doc_status_label(i.status) }}</span></td>
|
||||
<td class="text-center">
|
||||
{% if i.issued %}<i class="material-icons text-success" title="Emesso" style="font-size:17px;">check_circle</i>
|
||||
{% elif i.printed %}<i class="material-icons text-warning" title="Stampato" style="font-size:17px;">print</i>
|
||||
{% else %}<i class="material-icons text-mute" title="Bozza" style="font-size:17px;">edit</i>{% endif %}
|
||||
</td>
|
||||
<td class="text-center"><i class="material-icons text-blue" style="font-size:17px;">chevron_right</i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Logistica - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/magazzino" class="window-tab active"><i class="material-icons">warehouse</i> Logistica</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Logistica & Magazzino</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><span class="current">Logistica</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/magazzino" class="active">Magazzino</a>
|
||||
<a href="/inventory-movements">Movimenti</a>
|
||||
<a href="/warehouse">Giacenze</a>
|
||||
<a href="/inventory-reasons">Tabelle</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid">
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Movimenti di Magazzino</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/inventory-movements" class="menu-link"><i class="material-icons ml-icon mtype-form">swap_vert</i><span class="ml-text">Movimenti di Magazzino</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/inventory-movements" class="menu-link"><i class="material-icons ml-icon mtype-list">list_alt</i><span class="ml-text">Lista Movimenti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/inventory-movements" class="menu-link"><i class="material-icons ml-icon mtype-print">print</i><span class="ml-text">Giornale di Magazzino</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Giacenze</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/warehouse" class="menu-link"><i class="material-icons ml-icon mtype-list">inventory</i><span class="ml-text">Giacenze Articoli</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/warehouse" class="menu-link"><i class="material-icons ml-icon mtype-print">warning</i><span class="ml-text">Articoli Sotto Scorta</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/warehouse" class="menu-link"><i class="material-icons ml-icon mtype-list">euro</i><span class="ml-text">Valorizzazione Magazzino</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Tabelle</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/inventory-reasons" class="menu-link"><i class="material-icons ml-icon mtype-list">rule</i><span class="ml-text">Causali di Magazzino</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">warehouse</i><span class="ml-text">Depositi</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-list">place</i><span class="ml-text">Ubicazioni</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Procedure</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">bolt</i><span class="ml-text">Inventario Fisico</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">bolt</i><span class="ml-text">Ricalcolo Giacenze</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,100 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ movement.doc_no }} - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}
|
||||
<a href="/inventory-movements" class="window-tab"><i class="material-icons">swap_vert</i> Movimenti</a>
|
||||
<a href="/inventory-movements/{{ movement.id }}" class="window-tab active"><i class="material-icons">inventory</i> {{ movement.doc_no }}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Movimento di Magazzino — {{ movement.doc_no }}</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/magazzino">Logistica</a><span class="sep">»</span><a href="/inventory-movements">Movimenti</a><span class="sep">»</span><span class="current">{{ movement.doc_no }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Precedente"><i class="material-icons">chevron_left</i></button>
|
||||
<button class="tb-btn" title="Successivo"><i class="material-icons">chevron_right</i></button>
|
||||
<div class="tb-sep"></div>
|
||||
<button class="tb-btn" title="Modifica"><i class="material-icons">edit</i></button>
|
||||
<button class="tb-btn" title="Salva"><i class="material-icons">save</i></button>
|
||||
<button class="tb-btn" title="Stampa"><i class="material-icons">print</i></button>
|
||||
<div class="tb-sep"></div>
|
||||
{% if not movement.posted %}
|
||||
<button class="btn btn-primary" style="margin-left:6px;" onclick="postMovement({{ movement.id }})"><i class="material-icons">check</i> Conferma e aggiorna giacenze</button>
|
||||
{% endif %}
|
||||
<a class="tb-btn" href="/inventory-movements" title="Chiudi" style="margin-left:auto;"><i class="material-icons">logout</i></a>
|
||||
</div>
|
||||
|
||||
<div style="background:#fff;border-bottom:1px solid var(--border);padding:14px 20px;">
|
||||
<div class="form-grid-2" style="margin-bottom:0;">
|
||||
<div>
|
||||
<div class="field-row"><label>Numero</label><div class="field-value text-bold">{{ movement.doc_no }}</div></div>
|
||||
<div class="field-row"><label>Data Documento</label><div class="field-value">{{ movement.doc_date.strftime("%d/%m/%Y") }}</div></div>
|
||||
<div class="field-row"><label>Data Registrazione</label><div class="field-value">{{ movement.posting_date.strftime("%d/%m/%Y") }}</div></div>
|
||||
<div class="field-row"><label>Deposito</label><div class="field-value">{{ movement.storage }}</div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="field-row"><label>Causale</label><div class="field-value"><span class="mono">{{ movement.reason_code }}</span> — {{ movement.reason_description }}</div></div>
|
||||
<div class="field-row"><label>Segno</label><div class="field-value">
|
||||
{% if movement.sign.value == 'load' %}<span class="badge badge-load"><i class="material-icons">add</i> Carico (+)</span>
|
||||
{% else %}<span class="badge badge-unload"><i class="material-icons">remove</i> Scarico (−)</span>{% endif %}
|
||||
</div></div>
|
||||
<div class="field-row"><label>Cliente/Fornitore</label><div class="field-value">{{ movement.custsupp_name or '—' }}</div></div>
|
||||
<div class="field-row"><label>Stato</label><div class="field-value" id="statusField">
|
||||
{% if movement.posted %}<span class="badge badge-success"><i class="material-icons">check</i> Confermato</span>
|
||||
{% else %}<span class="badge badge-grey">Bozza</span>{% endif %}
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">list</i> Righe Movimento</h3></div>
|
||||
<table class="data-table">
|
||||
<thead><tr><th>#</th><th>Articolo</th><th>Descrizione</th><th class="text-center">UM</th><th class="text-right">Q.tà</th><th class="text-right">Valore Unit.</th><th class="text-right">Importo</th><th>Lotto</th><th>Ubicazione</th></tr></thead>
|
||||
<tbody>
|
||||
{% for l in movement.lines %}
|
||||
<tr>
|
||||
<td>{{ l.line_no }}</td>
|
||||
<td class="mono text-bold">{{ l.item_code }}</td>
|
||||
<td>{{ l.description }}</td>
|
||||
<td class="text-center">{{ l.uom }}</td>
|
||||
<td class="text-right {% if movement.sign.value == 'load' %}text-success{% else %}text-danger{% endif %} text-bold">
|
||||
{{ '+' if movement.sign.value == 'load' else '−' }}{{ "%.2f"|format(l.quantity) }}
|
||||
</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(l.unit_value) }}</td>
|
||||
<td class="text-right text-bold">€ {{ "%.2f"|format(l.line_amount) }}</td>
|
||||
<td>{{ l.lot or '—' }}</td>
|
||||
<td>{{ l.location or '—' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if movement.notes %}
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">notes</i> Note</h3></div>
|
||||
<div class="panel-body padded">{{ movement.notes }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="totals-box">
|
||||
<div class="t-item grand"><div class="t-label">VALORE TOTALE MOVIMENTO</div><div class="t-value">€ {{ "%.2f"|format(movement.total_amount) }}</div></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function postMovement(id) {
|
||||
if (!confirm('Confermare il movimento? Le giacenze degli articoli verranno aggiornate.')) return;
|
||||
fetch(`/inventory-movements/${id}/post`, { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
showNotification('Movimento confermato — giacenze aggiornate');
|
||||
setTimeout(() => location.reload(), 800);
|
||||
})
|
||||
.catch(() => showNotification('Errore nella conferma', 'error'));
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
+67
-121
@@ -1,68 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Articoli - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Articoli{% endblock %}
|
||||
{% block window_tabs %}<a href="/products" class="window-tab active"><i class="material-icons">inventory_2</i> Articoli</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<button class="btn btn-primary" onclick="openCreateProductModal()">
|
||||
<i class="material-icons">add</i> Nuovo Articolo
|
||||
</button>
|
||||
{% if low_stock %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="material-icons">warning</i>
|
||||
{{ low_stock|length }} articolo/i sotto la soglia di riordino
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Articoli</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/anagrafiche">Anagrafiche</a><span class="sep">»</span><span class="current">Articoli</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/anagrafiche">Anagrafiche</a>
|
||||
<a href="/customers">Clienti</a>
|
||||
<a href="/suppliers">Fornitori</a>
|
||||
<a href="/products" class="active">Articoli</a>
|
||||
</div>
|
||||
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Catalogo Articoli</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo" onclick="alert('Nuovo articolo (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"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
{% if low_stock %}
|
||||
<div class="alert alert-warning"><i class="material-icons">warning</i> {{ low_stock|length }} articolo/i sotto la scorta minima</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">inventory_2</i> Catalogo Articoli</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Descrizione</th>
|
||||
<th>Categoria</th>
|
||||
<th>Prezzo Unitario</th>
|
||||
<th>Giacenza</th>
|
||||
<th>Soglia Riordino</th>
|
||||
<th>Fornitore</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Codice</th><th>Descrizione</th><th>Categoria</th><th class="text-center">UM</th><th class="text-right">Prezzo Vend.</th><th class="text-right">Costo</th><th class="text-center">IVA</th><th class="text-right">Giacenza</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for product in products %}
|
||||
<tr class="{% if product.quantity_in_stock <= product.reorder_level %}low-stock{% endif %}">
|
||||
<td><strong>{{ product.code }}</strong></td>
|
||||
<td>{{ product.description }}</td>
|
||||
<td>{{ product.category }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(product.unit_price) }}</td>
|
||||
<td class="text-right {% if product.quantity_in_stock <= product.reorder_level %}text-danger{% endif %}">
|
||||
{{ product.quantity_in_stock }}
|
||||
{% if product.quantity_in_stock <= product.reorder_level %}
|
||||
<i class="material-icons small-icon">warning</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">{{ product.reorder_level }}</td>
|
||||
<td>
|
||||
{% if product.supplier_id %}
|
||||
Fornitore #{{ product.supplier_id }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-info" onclick="viewProduct({{ product.id }})">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
<button class="btn-small btn-warning" onclick="editProduct({{ product.id }})">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
{% for p in products %}
|
||||
<tr class="{% if p.quantity_in_stock <= p.reorder_level %}low-stock{% endif %}">
|
||||
<td class="text-bold mono">{{ p.code }}</td>
|
||||
<td>{{ p.description }}</td>
|
||||
<td>{{ p.category }}</td>
|
||||
<td class="text-center">{{ p.uom }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(p.unit_price) }}</td>
|
||||
<td class="text-right text-mute">€ {{ "%.2f"|format(p.purchase_price) }}</td>
|
||||
<td class="text-center">{{ "%.0f"|format(p.tax_rate) }}%</td>
|
||||
<td class="text-right {% if p.quantity_in_stock <= p.reorder_level %}text-danger text-bold{% endif %}">
|
||||
{{ p.quantity_in_stock }} {{ p.uom }}
|
||||
{% if p.quantity_in_stock <= p.reorder_level %}<i class="material-icons" style="font-size:14px;vertical-align:middle;">warning</i>{% endif %}
|
||||
</td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-view" onclick="viewProduct({{ p.id }})"><i class="material-icons">visibility</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -70,75 +52,39 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="productModal" class="modal" onclick="closeModal(event)">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Dettagli Articolo</h2>
|
||||
<button class="btn-close" onclick="closeModal()">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
</div>
|
||||
<div id="genericModal" class="modal" onclick="if(event.target.id==='genericModal')closeGenericModal()">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2 id="gmTitle">Dettagli</h2><button class="modal-close" onclick="closeGenericModal()"><i class="material-icons">close</i></button></div>
|
||||
<div class="modal-body" id="gmBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewProduct(productId) {
|
||||
fetch(`/api/products/${productId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const isLowStock = data.quantity_in_stock <= data.reorder_level;
|
||||
document.getElementById('modalTitle').textContent = `Articolo: ${data.code}`;
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Codice</label>
|
||||
<span>${data.code}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Descrizione</label>
|
||||
<span>${data.description}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Categoria</label>
|
||||
<span>${data.category}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Prezzo Unitario</label>
|
||||
<span>€ ${data.unit_price.toFixed(2)}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Giacenza Attuale</label>
|
||||
<span class="${isLowStock ? 'text-danger' : ''}">
|
||||
${data.quantity_in_stock} ${isLowStock ? '⚠️ SOTTOSORTA' : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Soglia Riordino</label>
|
||||
<span>${data.reorder_level}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Fornitore</label>
|
||||
<span>${data.supplier_id ? 'Fornitore #' + data.supplier_id : '—'}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('productModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
|
||||
function editProduct(productId) {
|
||||
alert('Modifica articolo: ' + productId + ' (da implementare)');
|
||||
}
|
||||
|
||||
function openCreateProductModal() {
|
||||
alert('Creazione nuovo articolo (da implementare)');
|
||||
}
|
||||
|
||||
function closeModal(event) {
|
||||
if (event && event.target.id !== 'productModal') return;
|
||||
document.getElementById('productModal').classList.remove('show');
|
||||
function viewProduct(id) {
|
||||
fetch(`/api/products/${id}`).then(r=>r.json()).then(d=>{
|
||||
const low = d.quantity_in_stock <= d.reorder_level;
|
||||
openGenericModal('Articolo: ' + d.code, `
|
||||
<div style="padding:16px">
|
||||
<div class="form-section-title">Dati Principali</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Codice', d.code)}${field('Descrizione', d.description)}
|
||||
${field('Categoria', d.category)}${field('Unità di Misura', d.uom)}
|
||||
</div>
|
||||
<div class="form-section-title">Dati Commerciali</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Prezzo di Vendita', '€ ' + d.unit_price.toFixed(2))}
|
||||
${field('Costo di Acquisto', '€ ' + d.purchase_price.toFixed(2))}
|
||||
${field('Aliquota IVA', d.tax_rate.toFixed(0) + '%')}
|
||||
</div>
|
||||
<div class="form-section-title">Dati Magazzino</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Giacenza', '<span class="' + (low?'text-danger text-bold':'') + '">' + d.quantity_in_stock + ' ' + d.uom + (low?' ⚠ sottoscorta':'') + '</span>')}
|
||||
${field('Scorta Minima', d.reorder_level + ' ' + d.uom)}
|
||||
${field('Scorta Massima', d.max_stock + ' ' + d.uom)}
|
||||
${field('Valore Giacenza', '€ ' + (d.quantity_in_stock * d.purchase_price).toFixed(2))}
|
||||
</div>
|
||||
</div>`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
+49
-123
@@ -1,60 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Ordini Acquisto - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Ordini Acquisto{% endblock %}
|
||||
{% block title %}Ordini Fornitore - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/purchase-orders" class="window-tab active"><i class="material-icons">shopping_cart</i> Ordini Fornitore</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<button class="btn btn-primary" onclick="openCreateOrderModal()">
|
||||
<i class="material-icons">add</i> Nuovo Ordine
|
||||
</button>
|
||||
{% if pending %}
|
||||
<div class="alert alert-info">
|
||||
<i class="material-icons">info</i>
|
||||
{{ pending|length }} ordine/i in sospeso
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Ordini Fornitore</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/acquisti">Acquisti</a><span class="sep">»</span><span class="current">Ordini Fornitore</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/purchase-orders" class="active">Ordini Fornitore</a>
|
||||
<a href="/acquisti">Documenti Acquisto</a>
|
||||
</div>
|
||||
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Elenco Ordini Acquisto</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo" onclick="alert('Nuovo ordine (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"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
{% if pending %}<div class="alert alert-info"><i class="material-icons">info</i> {{ pending|length }} ordine/i in sospeso</div>{% endif %}
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">shopping_cart</i> Elenco Ordini Fornitore</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
<th>Numero Ordine</th>
|
||||
<th>Fornitore</th>
|
||||
<th>Importo Totale</th>
|
||||
<th>Data Consegna Prevista</th>
|
||||
<th>Stato</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Data</th><th>Numero</th><th>Fornitore</th><th class="text-right">Importo</th><th>Consegna Prevista</th><th>Stato</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for order in orders %}
|
||||
{% for o in orders %}
|
||||
<tr>
|
||||
<td>{{ order.order_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td><strong>{{ order.order_number }}</strong></td>
|
||||
<td>{{ order.supplier_name }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(order.total_amount) }}</td>
|
||||
<td>
|
||||
{% if order.expected_delivery_date %}
|
||||
{{ order.expected_delivery_date.strftime("%d/%m/%Y") }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge status-{{ order.status.value }}">{{ order.status.value }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-info" onclick="viewOrder({{ order.id }})">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
</td>
|
||||
<td>{{ o.order_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td class="text-bold mono">{{ o.order_number }}</td>
|
||||
<td>{{ o.supplier_name }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(o.total_amount) }}</td>
|
||||
<td>{{ o.expected_delivery_date.strftime("%d/%m/%Y") if o.expected_delivery_date else '—' }}</td>
|
||||
<td><span class="badge status-{{ o.status.value }}">{{ o.status.value }}</span></td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-view" onclick="viewOrder({{ o.id }})"><i class="material-icons">visibility</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -62,84 +42,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="orderModal" class="modal" onclick="closeModal(event)">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Dettagli Ordine Acquisto</h2>
|
||||
<button class="btn-close" onclick="closeModal()">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
</div>
|
||||
<div id="genericModal" class="modal" onclick="if(event.target.id==='genericModal')closeGenericModal()">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2 id="gmTitle">Ordine</h2><button class="modal-close" onclick="closeGenericModal()"><i class="material-icons">close</i></button></div>
|
||||
<div class="modal-body" id="gmBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewOrder(orderId) {
|
||||
fetch(`/api/purchase-orders/${orderId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const deliveryDate = data.expected_delivery_date ? new Date(data.expected_delivery_date).toLocaleDateString('it-IT') : '—';
|
||||
const orderDate = new Date(data.order_date).toLocaleDateString('it-IT');
|
||||
|
||||
let linesHtml = '<table class="data-table-nested"><thead><tr><th>Articolo</th><th>Quantità</th><th>Prezzo Unit.</th><th>Totale</th></tr></thead><tbody>';
|
||||
data.lines.forEach(line => {
|
||||
linesHtml += `<tr>
|
||||
<td>${line.product_description} (${line.product_code})</td>
|
||||
<td class="text-right">${line.quantity}</td>
|
||||
<td class="text-right">€ ${line.unit_price.toFixed(2)}</td>
|
||||
<td class="text-right">€ ${line.total_amount.toFixed(2)}</td>
|
||||
</tr>`;
|
||||
});
|
||||
linesHtml += '</tbody></table>';
|
||||
|
||||
document.getElementById('modalTitle').textContent = `Ordine: ${data.order_number}`;
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Numero Ordine</label>
|
||||
<span>${data.order_number}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Data Ordine</label>
|
||||
<span>${orderDate}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Fornitore</label>
|
||||
<span>${data.supplier_name}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Stato</label>
|
||||
<span class="badge status-${data.status}">
|
||||
${data.status.replace('_', ' ').toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Data Consegna Prevista</label>
|
||||
<span>${deliveryDate}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Importo Totale</label>
|
||||
<span class="text-orange" style="font-size: 1.2em; font-weight: bold;">€ ${data.total_amount.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>Righe Ordine</h4>
|
||||
${linesHtml}
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('orderModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
|
||||
function openCreateOrderModal() {
|
||||
alert('Creazione nuovo ordine (da implementare)');
|
||||
}
|
||||
|
||||
function closeModal(event) {
|
||||
if (event && event.target.id !== 'orderModal') return;
|
||||
document.getElementById('orderModal').classList.remove('show');
|
||||
function viewOrder(id) {
|
||||
fetch(`/api/purchase-orders/${id}`).then(r=>r.json()).then(d=>{
|
||||
let rows = d.lines.map(l=>`<tr><td>${l.product_description} <span class="text-mute">(${l.product_code})</span></td>
|
||||
<td class="text-right">${l.quantity}</td><td class="text-right">€ ${l.unit_price.toFixed(2)}</td>
|
||||
<td class="text-right">€ ${l.total_amount.toFixed(2)}</td></tr>`).join('');
|
||||
openGenericModal('Ordine: ' + d.order_number, `
|
||||
<div style="padding:16px">
|
||||
<div class="form-grid-2">
|
||||
${field('Numero Ordine', d.order_number)}${field('Data', fmtDate(d.order_date))}
|
||||
${field('Fornitore', d.supplier_name)}${field('Stato', d.status)}
|
||||
${field('Consegna Prevista', fmtDate(d.expected_delivery_date))}${field('Totale', '€ ' + d.total_amount.toFixed(2))}
|
||||
</div>
|
||||
<div class="form-section-title">Righe Ordine</div>
|
||||
<table class="data-table-nested"><thead><tr><th>Articolo</th><th class="text-right">Q.tà</th><th class="text-right">Prezzo</th><th class="text-right">Totale</th></tr></thead><tbody>${rows}</tbody></table>
|
||||
</div>`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
+49
-123
@@ -1,60 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Ordini Vendita - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Ordini Vendita{% endblock %}
|
||||
{% block title %}Ordini Cliente - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/sales-orders" class="window-tab active"><i class="material-icons">receipt_long</i> Ordini Cliente</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<button class="btn btn-primary" onclick="openCreateOrderModal()">
|
||||
<i class="material-icons">add</i> Nuovo Ordine
|
||||
</button>
|
||||
{% if pending %}
|
||||
<div class="alert alert-info">
|
||||
<i class="material-icons">info</i>
|
||||
{{ pending|length }} ordine/i in sospeso
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Ordini Cliente</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/vendite">Vendite</a><span class="sep">»</span><span class="current">Ordini Cliente</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/sales-orders" class="active">Ordini Cliente</a>
|
||||
<a href="/vendite">Documenti Vendita</a>
|
||||
</div>
|
||||
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Elenco Ordini Vendita</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo" onclick="alert('Nuovo ordine (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"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
{% if pending %}<div class="alert alert-info"><i class="material-icons">info</i> {{ pending|length }} ordine/i in sospeso</div>{% endif %}
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">receipt_long</i> Elenco Ordini Cliente</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data</th>
|
||||
<th>Numero Ordine</th>
|
||||
<th>Cliente</th>
|
||||
<th>Importo Totale</th>
|
||||
<th>Data Consegna</th>
|
||||
<th>Stato</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Data</th><th>Numero</th><th>Cliente</th><th class="text-right">Importo</th><th>Consegna</th><th>Stato</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for order in orders %}
|
||||
{% for o in orders %}
|
||||
<tr>
|
||||
<td>{{ order.order_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td><strong>{{ order.order_number }}</strong></td>
|
||||
<td>{{ order.customer_name }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(order.total_amount) }}</td>
|
||||
<td>
|
||||
{% if order.delivery_date %}
|
||||
{{ order.delivery_date.strftime("%d/%m/%Y") }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge status-{{ order.status.value }}">{{ order.status.value }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-info" onclick="viewOrder({{ order.id }})">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
</td>
|
||||
<td>{{ o.order_date.strftime("%d/%m/%Y") }}</td>
|
||||
<td class="text-bold mono">{{ o.order_number }}</td>
|
||||
<td>{{ o.customer_name }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(o.total_amount) }}</td>
|
||||
<td>{{ o.delivery_date.strftime("%d/%m/%Y") if o.delivery_date else '—' }}</td>
|
||||
<td><span class="badge status-{{ o.status.value }}">{{ o.status.value }}</span></td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-view" onclick="viewOrder({{ o.id }})"><i class="material-icons">visibility</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -62,84 +42,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="orderModal" class="modal" onclick="closeModal(event)">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Dettagli Ordine Vendita</h2>
|
||||
<button class="btn-close" onclick="closeModal()">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
</div>
|
||||
<div id="genericModal" class="modal" onclick="if(event.target.id==='genericModal')closeGenericModal()">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2 id="gmTitle">Ordine</h2><button class="modal-close" onclick="closeGenericModal()"><i class="material-icons">close</i></button></div>
|
||||
<div class="modal-body" id="gmBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewOrder(orderId) {
|
||||
fetch(`/api/sales-orders/${orderId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const deliveryDate = data.delivery_date ? new Date(data.delivery_date).toLocaleDateString('it-IT') : '—';
|
||||
const orderDate = new Date(data.order_date).toLocaleDateString('it-IT');
|
||||
|
||||
let linesHtml = '<table class="data-table-nested"><thead><tr><th>Articolo</th><th>Quantità</th><th>Prezzo Unit.</th><th>Totale</th></tr></thead><tbody>';
|
||||
data.lines.forEach(line => {
|
||||
linesHtml += `<tr>
|
||||
<td>${line.product_description} (${line.product_code})</td>
|
||||
<td class="text-right">${line.quantity}</td>
|
||||
<td class="text-right">€ ${line.unit_price.toFixed(2)}</td>
|
||||
<td class="text-right">€ ${line.total_amount.toFixed(2)}</td>
|
||||
</tr>`;
|
||||
});
|
||||
linesHtml += '</tbody></table>';
|
||||
|
||||
document.getElementById('modalTitle').textContent = `Ordine: ${data.order_number}`;
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Numero Ordine</label>
|
||||
<span>${data.order_number}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Data Ordine</label>
|
||||
<span>${orderDate}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Cliente</label>
|
||||
<span>${data.customer_name}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Stato</label>
|
||||
<span class="badge status-${data.status}">
|
||||
${data.status.replace('_', ' ').toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Data Consegna Prevista</label>
|
||||
<span>${deliveryDate}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Importo Totale</label>
|
||||
<span class="text-success" style="font-size: 1.2em; font-weight: bold;">€ ${data.total_amount.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>Righe Ordine</h4>
|
||||
${linesHtml}
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('orderModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
|
||||
function openCreateOrderModal() {
|
||||
alert('Creazione nuovo ordine (da implementare)');
|
||||
}
|
||||
|
||||
function closeModal(event) {
|
||||
if (event && event.target.id !== 'orderModal') return;
|
||||
document.getElementById('orderModal').classList.remove('show');
|
||||
function viewOrder(id) {
|
||||
fetch(`/api/sales-orders/${id}`).then(r=>r.json()).then(d=>{
|
||||
let rows = d.lines.map(l=>`<tr><td>${l.product_description} <span class="text-mute">(${l.product_code})</span></td>
|
||||
<td class="text-right">${l.quantity}</td><td class="text-right">€ ${l.unit_price.toFixed(2)}</td>
|
||||
<td class="text-right">€ ${l.total_amount.toFixed(2)}</td></tr>`).join('');
|
||||
openGenericModal('Ordine: ' + d.order_number, `
|
||||
<div style="padding:16px">
|
||||
<div class="form-grid-2">
|
||||
${field('Numero Ordine', d.order_number)}${field('Data', fmtDate(d.order_date))}
|
||||
${field('Cliente', d.customer_name)}${field('Stato', d.status)}
|
||||
${field('Consegna Prevista', fmtDate(d.delivery_date))}${field('Totale', '€ ' + d.total_amount.toFixed(2))}
|
||||
</div>
|
||||
<div class="form-section-title">Righe Ordine</div>
|
||||
<table class="data-table-nested"><thead><tr><th>Articolo</th><th class="text-right">Q.tà</th><th class="text-right">Prezzo</th><th class="text-right">Totale</th></tr></thead><tbody>${rows}</tbody></table>
|
||||
</div>`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -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 %}
|
||||
+51
-111
@@ -1,53 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Fornitori - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Fornitori{% endblock %}
|
||||
{% block window_tabs %}<a href="/suppliers" class="window-tab active"><i class="material-icons">local_shipping</i> Fornitori</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<button class="btn btn-primary" onclick="openCreateSupplierModal()">
|
||||
<i class="material-icons">add</i> Nuovo Fornitore
|
||||
</button>
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Fornitori</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/anagrafiche">Anagrafiche</a><span class="sep">»</span><span class="current">Fornitori</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/anagrafiche">Anagrafiche</a>
|
||||
<a href="/customers">Clienti</a>
|
||||
<a href="/suppliers" class="active">Fornitori</a>
|
||||
<a href="/products">Articoli</a>
|
||||
</div>
|
||||
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Elenco Fornitori</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="doc-toolbar">
|
||||
<button class="tb-btn" title="Nuovo" onclick="alert('Nuovo fornitore (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"><i class="material-icons">print</i></button>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">local_shipping</i> Elenco Fornitori</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Ragione Sociale</th>
|
||||
<th>Città</th>
|
||||
<th>Paese</th>
|
||||
<th>Telefono</th>
|
||||
<th>Termini Pagamento</th>
|
||||
<th>Saldo</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Codice</th><th>Ragione Sociale</th><th>Partita IVA</th><th>Città</th><th>Paese</th><th class="text-center">Pagamento</th><th class="text-right">Saldo</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for supplier in suppliers %}
|
||||
{% for s in suppliers %}
|
||||
<tr>
|
||||
<td><strong>{{ supplier.code }}</strong></td>
|
||||
<td>{{ supplier.name }}</td>
|
||||
<td>{{ supplier.city }}</td>
|
||||
<td>{{ supplier.country }}</td>
|
||||
<td>{{ supplier.phone }}</td>
|
||||
<td class="text-center">{{ supplier.payment_terms_days }} gg</td>
|
||||
<td class="text-right {% if supplier.balance > 0 %}text-warning{% endif %}">
|
||||
€ {{ "%.2f"|format(supplier.balance) }}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-info" onclick="viewSupplier({{ supplier.id }})">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
<button class="btn-small btn-warning" onclick="editSupplier({{ supplier.id }})">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-bold mono">{{ s.code }}</td>
|
||||
<td>{{ s.name }}</td>
|
||||
<td class="mono">{{ s.vat_number or '—' }}</td>
|
||||
<td>{{ s.city }}</td>
|
||||
<td>{{ s.country }}</td>
|
||||
<td class="text-center">{{ s.payment_terms_days }} gg</td>
|
||||
<td class="text-right {% if s.balance > 0 %}text-warning{% endif %}">€ {{ "%.2f"|format(s.balance) }}</td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-view" onclick="viewSupplier({{ s.id }})"><i class="material-icons">visibility</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -55,80 +44,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="supplierModal" class="modal" onclick="closeModal(event)">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Dettagli Fornitore</h2>
|
||||
<button class="btn-close" onclick="closeModal()">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
</div>
|
||||
<div id="genericModal" class="modal" onclick="if(event.target.id==='genericModal')closeGenericModal()">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2 id="gmTitle">Dettagli</h2><button class="modal-close" onclick="closeGenericModal()"><i class="material-icons">close</i></button></div>
|
||||
<div class="modal-body" id="gmBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewSupplier(supplierId) {
|
||||
fetch(`/api/suppliers/${supplierId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('modalTitle').textContent = `Fornitore: ${data.name}`;
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<label>Codice</label>
|
||||
<span>${data.code}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Ragione Sociale</label>
|
||||
<span>${data.name}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Email</label>
|
||||
<span>${data.email}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Telefono</label>
|
||||
<span>${data.phone}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Indirizzo</label>
|
||||
<span>${data.address}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Città</label>
|
||||
<span>${data.city}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Paese</label>
|
||||
<span>${data.country}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Termini Pagamento</label>
|
||||
<span>${data.payment_terms_days} giorni</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<label>Saldo Debito</label>
|
||||
<span class="${data.balance > 0 ? 'text-warning' : ''}">€ ${data.balance.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('supplierModal').classList.add('show');
|
||||
});
|
||||
}
|
||||
|
||||
function editSupplier(supplierId) {
|
||||
alert('Modifica fornitore: ' + supplierId + ' (da implementare)');
|
||||
}
|
||||
|
||||
function openCreateSupplierModal() {
|
||||
alert('Creazione nuovo fornitore (da implementare)');
|
||||
}
|
||||
|
||||
function closeModal(event) {
|
||||
if (event && event.target.id !== 'supplierModal') return;
|
||||
document.getElementById('supplierModal').classList.remove('show');
|
||||
function viewSupplier(id) {
|
||||
fetch(`/api/suppliers/${id}`).then(r=>r.json()).then(d=>{
|
||||
openGenericModal('Fornitore: ' + d.name, `
|
||||
<div style="padding:16px">
|
||||
<div class="form-section-title">Dati Principali</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Codice', d.code)}${field('Ragione Sociale', d.name)}
|
||||
${field('Partita IVA', d.vat_number || '—')}${field('Email', d.email)}
|
||||
${field('Telefono', d.phone)}${field('Termini Pagamento', d.payment_terms_days + ' giorni')}
|
||||
</div>
|
||||
<div class="form-section-title">Indirizzo</div>
|
||||
<div class="form-grid-2">
|
||||
${field('Indirizzo', d.address)}${field('Città', d.city)}
|
||||
${field('Paese', d.country)}${field('Saldo Debito', '€ ' + d.balance.toFixed(2))}
|
||||
</div>
|
||||
</div>`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Vendite - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/vendite" class="window-tab active"><i class="material-icons">point_of_sale</i> Vendite</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Documenti Vendita</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/vendite">Vendite</a><span class="sep">»</span><span class="current">Documenti Vendita</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/sales-orders">Ordini Cliente</a>
|
||||
<a href="/vendite" class="active">Documenti Vendita</a>
|
||||
<a href="#">Politiche Vendita</a>
|
||||
<a href="#">Statistiche</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-grid">
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Fatture</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/invoices?type=immediate_invoice" class="menu-link"><i class="material-icons ml-icon mtype-form">request_quote</i><span class="ml-text">Fatture Immediate</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/invoices?type=accompanying_invoice" class="menu-link"><i class="material-icons ml-icon mtype-form">receipt</i><span class="ml-text">Fatture Accompagnatorie</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/invoices?type=proforma_invoice" class="menu-link"><i class="material-icons ml-icon mtype-list">description</i><span class="ml-text">Fatture ProForma</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/invoices" class="menu-link"><i class="material-icons ml-icon mtype-print">print</i><span class="ml-text">Portafoglio Clienti</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Documenti di Trasporto</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/invoices?type=delivery_note" class="menu-link"><i class="material-icons ml-icon mtype-form">local_shipping</i><span class="ml-text">Documenti di Trasporto</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/invoices?type=delivery_note" class="menu-link"><i class="material-icons ml-icon mtype-list">list_alt</i><span class="ml-text">Lista Documenti di Trasporto</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Note di Credito / Debito</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="/invoices?type=credit_note" class="menu-link"><i class="material-icons ml-icon mtype-form">undo</i><span class="ml-text">Note di Credito</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="/invoices?type=debit_note" class="menu-link"><i class="material-icons ml-icon mtype-form">redo</i><span class="ml-text">Note di Debito</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-card">
|
||||
<div class="menu-card-header"><h3>Procedure</h3><span class="collapse-dot">○—</span></div>
|
||||
<div class="menu-card-body">
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">bolt</i><span class="ml-text">Fatturazione Differita DDT</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-proc">bolt</i><span class="ml-text">Evasione Picking List</span><i class="material-icons ml-star">star_border</i></a>
|
||||
<a href="#" class="menu-link"><i class="material-icons ml-icon mtype-print">bar_chart</i><span class="ml-text">Grafici di Fatturato</span><i class="material-icons ml-star">star_border</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
+59
-119
@@ -1,131 +1,77 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Magazzino - Mago4 Demo{% endblock %}
|
||||
{% block page_title %}Magazzino{% endblock %}
|
||||
{% block title %}Giacenze - Mago4 Demo{% endblock %}
|
||||
{% block window_tabs %}<a href="/warehouse" class="window-tab active"><i class="material-icons">inventory</i> Giacenze</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<div class="module-bar">
|
||||
<div class="module-title">Giacenze Articoli</div>
|
||||
<div class="breadcrumb"><a href="/">Mago4</a><span class="sep">»</span><a href="/magazzino">Logistica</a><span class="sep">»</span><span class="current">Giacenze</span></div>
|
||||
</div>
|
||||
<div class="tab-strip">
|
||||
<a href="/magazzino">Magazzino</a>
|
||||
<a href="/inventory-movements">Movimenti</a>
|
||||
<a href="/warehouse" class="active">Giacenze</a>
|
||||
<a href="/inventory-reasons">Causali</a>
|
||||
</div>
|
||||
|
||||
<div class="content-pad">
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi-card" style="border-left-color: var(--c-blue)">
|
||||
<div class="kpi-title">Articoli in Catalogo</div>
|
||||
<div class="kpi-row"><div class="kpi-icon"><i class="material-icons" style="color:var(--c-blue)">inventory_2</i></div><div class="kpi-value">{{ products|length }}</div></div>
|
||||
</div>
|
||||
<div class="kpi-card" style="border-left-color: var(--c-red)">
|
||||
<div class="kpi-title">Articoli Sotto Scorta</div>
|
||||
<div class="kpi-row"><div class="kpi-icon"><i class="material-icons" style="color:var(--c-red)">warning</i></div><div class="kpi-value">{{ low_stock|length }}</div></div>
|
||||
</div>
|
||||
<div class="kpi-card" style="border-left-color: var(--c-purple)">
|
||||
<div class="kpi-title">Valore Magazzino (a costo)</div>
|
||||
<div class="kpi-row"><div class="kpi-icon"><i class="material-icons" style="color:var(--c-purple)">euro_symbol</i></div><div class="kpi-value">€ {{ "{:,.0f}".format(warehouse_value) }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if low_stock %}
|
||||
<div class="alert alert-danger" style="flex: 1;">
|
||||
<i class="material-icons">error</i>
|
||||
<strong>Attenzione!</strong> {{ low_stock|length }} articolo/i in sottosorta
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="widget-row" style="margin-bottom: 20px;">
|
||||
<div class="widget-card stat-card" style="border-left-color: var(--color-blue)">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Articoli in Catalogo</h3>
|
||||
</div>
|
||||
<div class="widget-body stat-body">
|
||||
<div class="stat-value">{{ products|length }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget-card stat-card" style="border-left-color: var(--color-red)">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Articoli in Sottosorta</h3>
|
||||
</div>
|
||||
<div class="widget-body stat-body">
|
||||
<div class="stat-icon" style="color: var(--color-red)">
|
||||
<i class="material-icons">warning</i>
|
||||
</div>
|
||||
<div class="stat-value">{{ low_stock|length }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Low Stock Alert -->
|
||||
{% if low_stock %}
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">
|
||||
<i class="material-icons">warning</i> Articoli Sotto Soglia Riordino
|
||||
</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons" style="color:var(--c-red)">warning</i> Articoli Sotto Scorta Minima</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Descrizione</th>
|
||||
<th>Giacenza</th>
|
||||
<th>Soglia Riordino</th>
|
||||
<th>Scostamento</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Codice</th><th>Descrizione</th><th class="text-right">Giacenza</th><th class="text-right">Scorta Min.</th><th class="text-right">Da Riordinare</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for product in low_stock %}
|
||||
<tr class="highlight-danger">
|
||||
<td><strong>{{ product.code }}</strong></td>
|
||||
<td>{{ product.description }}</td>
|
||||
<td class="text-danger text-bold">{{ product.quantity_in_stock }}</td>
|
||||
<td class="text-right">{{ product.reorder_level }}</td>
|
||||
<td class="text-right text-danger">
|
||||
-{{ product.reorder_level - product.quantity_in_stock }} unità
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-small btn-primary" onclick="createPOForProduct({{ product.id }})">
|
||||
<i class="material-icons">add_shopping_cart</i>
|
||||
</button>
|
||||
</td>
|
||||
{% for p in low_stock %}
|
||||
<tr class="low-stock">
|
||||
<td class="text-bold mono">{{ p.code }}</td>
|
||||
<td>{{ p.description }}</td>
|
||||
<td class="text-right text-danger text-bold">{{ p.quantity_in_stock }} {{ p.uom }}</td>
|
||||
<td class="text-right">{{ p.reorder_level }} {{ p.uom }}</td>
|
||||
<td class="text-right text-danger">{{ p.max_stock - p.quantity_in_stock }} {{ p.uom }}</td>
|
||||
<td class="text-center"><button class="btn-icon-sm act-cart" title="Genera ordine" onclick="alert('Genera ordine di acquisto (demo)')"><i class="material-icons">add_shopping_cart</i></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- All Products -->
|
||||
<div class="widget-card">
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">Inventario Completo</h3>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="panel">
|
||||
<div class="panel-header"><h3><i class="material-icons">inventory</i> Inventario Completo — Deposito MAG01</h3></div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Descrizione</th>
|
||||
<th>Categoria</th>
|
||||
<th>Giacenza</th>
|
||||
<th>Soglia Riordino</th>
|
||||
<th>Prezzo Unit.</th>
|
||||
<th>Valore Magazzino</th>
|
||||
<th>Stato</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead><tr><th>Codice</th><th>Descrizione</th><th>Categoria</th><th class="text-right">Giacenza</th><th class="text-right">Scorta Min.</th><th class="text-right">Costo Unit.</th><th class="text-right">Valore</th><th class="text-center">Stato</th></tr></thead>
|
||||
<tbody>
|
||||
{% for product in products %}
|
||||
<tr class="{% if product.quantity_in_stock <= product.reorder_level %}low-stock{% endif %}">
|
||||
<td><strong>{{ product.code }}</strong></td>
|
||||
<td>{{ product.description }}</td>
|
||||
<td>{{ product.category }}</td>
|
||||
<td class="text-right {% if product.quantity_in_stock <= product.reorder_level %}text-danger text-bold{% endif %}">
|
||||
{{ product.quantity_in_stock }}
|
||||
</td>
|
||||
<td class="text-right">{{ product.reorder_level }}</td>
|
||||
<td class="text-right">€ {{ "%.2f"|format(product.unit_price) }}</td>
|
||||
<td class="text-right text-bold">
|
||||
€ {{ "%.2f"|format(product.quantity_in_stock * product.unit_price) }}
|
||||
</td>
|
||||
<td>
|
||||
{% if product.quantity_in_stock <= product.reorder_level %}
|
||||
<span class="badge badge-danger">
|
||||
<i class="material-icons">warning</i> SOTTOSORTA
|
||||
</span>
|
||||
{% elif product.quantity_in_stock <= product.reorder_level + 20 %}
|
||||
<span class="badge badge-warning">
|
||||
<i class="material-icons">info</i> PROSSIMA SOGLIA
|
||||
</span>
|
||||
{% for p in products %}
|
||||
<tr class="{% if p.quantity_in_stock <= p.reorder_level %}low-stock{% endif %}">
|
||||
<td class="text-bold mono">{{ p.code }}</td>
|
||||
<td>{{ p.description }}</td>
|
||||
<td>{{ p.category }}</td>
|
||||
<td class="text-right {% if p.quantity_in_stock <= p.reorder_level %}text-danger text-bold{% endif %}">{{ p.quantity_in_stock }} {{ p.uom }}</td>
|
||||
<td class="text-right">{{ p.reorder_level }} {{ p.uom }}</td>
|
||||
<td class="text-right text-mute">€ {{ "%.2f"|format(p.purchase_price) }}</td>
|
||||
<td class="text-right text-bold">€ {{ "%.2f"|format(p.quantity_in_stock * p.purchase_price) }}</td>
|
||||
<td class="text-center">
|
||||
{% if p.quantity_in_stock <= p.reorder_level %}
|
||||
<span class="badge badge-danger"><i class="material-icons">warning</i> Sottoscorta</span>
|
||||
{% elif p.quantity_in_stock <= p.reorder_level + 20 %}
|
||||
<span class="badge badge-warning"><i class="material-icons">info</i> In esaurimento</span>
|
||||
{% else %}
|
||||
<span class="badge badge-success">
|
||||
<i class="material-icons">check</i> OK
|
||||
</span>
|
||||
<span class="badge badge-success"><i class="material-icons">check</i> OK</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -134,10 +80,4 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function createPOForProduct(productId) {
|
||||
alert('Creazione ordine di acquisto per articolo #' + productId + ' (da implementare)');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user