push
This commit is contained in:
62
app/templates/invoices/customers.html
Normal file
62
app/templates/invoices/customers.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-address-book me-2 text-primary"></i>Kontrahenci{% endblock %}
|
||||
{% block content %}
|
||||
{% set eyebrow='Kartoteka' %}{% set heading='Kontrahenci' %}{% set description='Lista kontrahentów i szybka edycja danych.' %}
|
||||
{% include 'partials/page_header.html' with context %}
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header"><i class="fa-solid fa-user-tie me-2"></i>{{ 'Edytuj kontrahenta' if editing else 'Nowy kontrahent' }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-2"><label class="form-label">Nazwa</label><input class="form-control" name="name" value="{{ editing.name if editing else '' }}" placeholder="Przy pobieraniu z CEIDG uzupełni się automatycznie" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">NIP</label><div class="input-group"><input class="form-control" name="tax_id" value="{{ editing.tax_id if editing else '' }}" placeholder="Wystarczy podać NIP" {% if read_only_mode %}disabled{% endif %}><button class="btn btn-outline-secondary" name="fetch_ceidg" value="1" {% if read_only_mode %}disabled{% endif %}>CEIDG</button></div><div class="form-text">Do pobrania danych z CEIDG wystarczy sam NIP.</div></div>
|
||||
<div class="mb-2"><label class="form-label">REGON</label><input class="form-control" name="regon" value="{{ editing.regon if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">Adres</label><input class="form-control" name="address" value="{{ editing.address if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-3"><label class="form-label">E-mail</label><input class="form-control" name="email" value="{{ editing.email if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="d-grid gap-2"><button class="btn btn-primary" {% if read_only_mode %}disabled{% endif %}>{{ 'Zapisz kontrahenta' if editing else 'Dodaj kontrahenta' }}</button>{% if editing %}<a class="btn btn-outline-secondary" href="{{ url_for('invoices.customers') }}">Anuluj edycję</a>{% endif %}</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div><i class="fa-solid fa-users me-2"></i>Baza kontrahentów</div>
|
||||
<form method="get" class="row g-2 align-items-end w-100 ms-0">
|
||||
<div class="col-md-7"><label class="form-label form-label-sm mb-1">Szukaj</label><input class="form-control form-control-sm" type="search" name="q" value="{{ search or '' }}" placeholder="Szukaj po nazwie, NIP, REGON, mailu..."></div>
|
||||
<div class="col-md-4"><label class="form-label form-label-sm mb-1">Sortowanie</label><select class="form-select form-select-sm" name="sort">
|
||||
<option value="name_asc" {{ 'selected' if sort == 'name_asc' else '' }}>A-Z</option>
|
||||
<option value="name_desc" {{ 'selected' if sort == 'name_desc' else '' }}>Z-A</option>
|
||||
<option value="tax_id_asc" {{ 'selected' if sort == 'tax_id_asc' else '' }}>NIP rosnąco</option>
|
||||
<option value="tax_id_desc" {{ 'selected' if sort == 'tax_id_desc' else '' }}>NIP malejąco</option>
|
||||
</select></div>
|
||||
<div class="col-md-1 d-grid"><button class="btn btn-sm btn-outline-secondary"><i class="fa-solid fa-magnifying-glass"></i></button></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle mb-0">
|
||||
<thead><tr><th>Nazwa</th><th>NIP</th><th>Adres</th><th>E-mail</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr><td><div class="fw-semibold">{{ item.name }}</div><div class="small text-secondary">{{ item.regon }}</div></td><td>{{ item.tax_id }}</td><td>{{ item.address }}</td><td>{{ item.email }}</td><td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ url_for('invoices.customers', customer_id=item.id) }}">Edytuj</a></td></tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="text-secondary text-center py-4">Brak kontrahentów.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-body border-top py-2">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-end mb-0">
|
||||
{% if pagination.has_prev %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.customers', page=pagination.prev_num, q=search, sort=sort) }}">Poprz.</a></li>{% endif %}
|
||||
<li class="page-item disabled"><span class="page-link">{{ pagination.page }} / {{ pagination.pages or 1 }}</span></li>
|
||||
{% if pagination.has_next %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.customers', page=pagination.next_num, q=search, sort=sort) }}">Dalej</a></li>{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
103
app/templates/invoices/detail.html
Normal file
103
app/templates/invoices/detail.html
Normal file
@@ -0,0 +1,103 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-file-lines me-2 text-primary"></i>Faktura{% endblock %}
|
||||
{% block content %}
|
||||
<div class="invoice-detail-layout align-items-start">
|
||||
<div class="invoice-detail-main">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header"><i class="fa-solid fa-circle-info me-2"></i>Szczegóły faktury</div>
|
||||
<div class="card-body">
|
||||
<div class="row small g-2">
|
||||
<div class="col-md-6"><strong>Numer:</strong> {{ invoice.invoice_number }}</div>
|
||||
<div class="col-md-6"><strong>Numer KSeF:</strong> {{ invoice.ksef_number }}</div>
|
||||
<div class="col-md-6"><strong>Kontrahent:</strong> {{ invoice.contractor_name }}</div>
|
||||
<div class="col-md-6"><strong>NIP:</strong> {{ invoice.contractor_nip }}</div>
|
||||
<div class="col-md-6"><strong>Adres:</strong> {{ invoice.contractor_address or '—' }}</div>
|
||||
<div class="col-md-6"><strong>Kartoteka klientów:</strong> {{ linked_customer.name if linked_customer else 'brak powiązania' }}</div>
|
||||
<div class="col-md-4"><strong>Netto:</strong> {{ invoice.net_amount|pln }}</div>
|
||||
<div class="col-md-4"><strong>VAT:</strong> {{ invoice.vat_amount|pln }}</div>
|
||||
<div class="col-md-4"><strong>Brutto:</strong> {{ invoice.gross_amount|pln }}</div>
|
||||
<div class="col-md-6"><strong>Split payment:</strong> <span class="badge text-bg-{{ 'warning' if invoice.split_payment else 'secondary' }}">{{ 'Tak' if invoice.split_payment else 'Nie' }}</span></div>
|
||||
<div class="col-md-6"><strong>Forma płatności:</strong> {{ payment_details.payment_form_label or '—' }}</div><div class="col-md-6"><strong>Rachunek bankowy:</strong> {{ payment_details.bank_account or (invoice.company.bank_account if invoice.company and invoice.source in ['issued', 'nfz'] else '') or '—' }}</div>{% if payment_details.bank_name %}<div class="col-md-6"><strong>Bank:</strong> {{ payment_details.bank_name }}</div>{% endif %}{% if payment_details.payment_due_date %}<div class="col-md-6"><strong>Termin płatności:</strong> {{ payment_details.payment_due_date }}</div>{% endif %}
|
||||
{% if invoice.source in ['issued', 'nfz'] %}
|
||||
<div class="col-md-6"><strong>Status wystawienia:</strong> {{ invoice.issued_status_label }}</div>
|
||||
<div class="col-md-6"><strong>KSeF:</strong> <span class="badge text-bg-{{ 'success' if invoice.issued_to_ksef_at else 'secondary' }}">{{ 'Przesłana do KSeF' if invoice.issued_to_ksef_at else 'Nieprzesłana do KSeF' }}</span></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if invoice.source in ['issued', 'nfz'] and not invoice.issued_to_ksef_at %}<div class="alert alert-warning mt-3 mb-0">Ta faktura nie została jeszcze wysłana do KSeF. Możesz ją edytować i wysłać później.</div>{% elif invoice.source in ['issued', 'nfz'] and invoice.issued_to_ksef_at %}<div class="alert alert-info mt-3 mb-0">Faktura została wysłana do KSeF {{ invoice.issued_to_ksef_at }}. Edycja jest zablokowana.</div>{% endif %}
|
||||
{% if invoice.external_metadata and invoice.external_metadata.get('nfz') %}<div class="alert alert-success mt-3 mb-0"><strong>Moduł NFZ:</strong> {{ invoice.external_metadata.get('nfz', {}).get('recipient_branch_name') }} · okres {{ invoice.external_metadata.get('nfz', {}).get('settlement_from') }} - {{ invoice.external_metadata.get('nfz', {}).get('settlement_to') }} · umowa {{ invoice.external_metadata.get('nfz', {}).get('contract_number') }}</div>{% endif %}
|
||||
<hr>
|
||||
<div class="mb-3">{% for tag in invoice.tags %}<span class="badge text-bg-{{ tag.color }} me-1">{{ tag.name }}</span>{% endfor %}</div>
|
||||
<div class="d-flex gap-2 flex-wrap mb-0">
|
||||
<a class="btn btn-outline-primary" href="{{ url_for('invoices.pdf', invoice_id=invoice.id) }}"><i class="fa-solid fa-file-pdf me-1"></i>Pobierz PDF</a>
|
||||
{% if can_add_seller_customer %}
|
||||
<form method="post" action="{{ url_for('invoices.add_seller_customer', invoice_id=invoice.id) }}">
|
||||
{{ form.csrf_token }}
|
||||
<button class="btn btn-outline-success" {% if edit_locked %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-user-plus me-1"></i>{{ 'Przejdź do kontrahenta' if linked_customer else 'Dodaj sprzedawcę do kontrahentów' }}
|
||||
</button>
|
||||
</form>
|
||||
{% elif linked_customer %}
|
||||
<a class="btn btn-outline-success" href="{{ url_for('invoices.customers', customer_id=linked_customer.id) }}"><i class="fa-solid fa-address-book me-1"></i>Otwórz kontrahenta</a>
|
||||
{% endif %}
|
||||
{% if invoice.source in ['issued', 'nfz'] %}
|
||||
<a class="btn btn-outline-secondary {% if edit_locked %}disabled{% endif %}" href="{{ url_for('invoices.duplicate', invoice_id=invoice.id) }}">
|
||||
<i class="fa-solid fa-copy me-1"></i>Duplikuj do wystawienia
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if invoice.source == 'issued' and not invoice.issued_to_ksef_at %}<a class="btn btn-outline-secondary" href="{{ url_for('invoices.issued_edit', invoice_id=invoice.id) }}"><i class="fa-solid fa-pen-to-square me-1"></i>Edytuj fakturę</a><form method="post" action="{{ url_for('invoices.send_to_ksef', invoice_id=invoice.id) }}">{{ form.csrf_token }}<button class="btn btn-primary"><i class="fa-solid fa-paper-plane me-1"></i>Wyślij do KSeF</button></form>{% elif invoice.source == 'nfz' and not invoice.issued_to_ksef_at %}<a class="btn btn-outline-secondary" href="{{ url_for('nfz.edit', invoice_id=invoice.id) }}"><i class="fa-solid fa-pen-to-square me-1"></i>Edytuj fakturę NFZ</a><form method="post" action="{{ url_for('nfz.send_to_ksef', invoice_id=invoice.id) }}">{{ form.csrf_token }}<button class="btn btn-primary"><i class="fa-solid fa-paper-plane me-1"></i>Wyślij NFZ do KSeF</button></form>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header"><i class="fa-solid fa-eye me-2"></i>Podgląd faktury</div>
|
||||
<div class="card-body">
|
||||
<div class="border rounded p-3 bg-white overflow-auto invoice-preview-surface">{{ invoice.html_preview|safe if invoice.html_preview else 'Brak podglądu HTML.' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="fa-solid fa-code me-2"></i>Surowy XML</span>
|
||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#invoiceRawXml" aria-expanded="false" aria-controls="invoiceRawXml">
|
||||
<i class="fa-solid fa-chevron-down me-1"></i>Pokaż XML
|
||||
</button>
|
||||
</div>
|
||||
<div class="collapse" id="invoiceRawXml">
|
||||
<div class="card-body">
|
||||
<pre class="small bg-body-tertiary p-3 overflow-auto mb-0" style="max-height:26rem; white-space:pre-wrap;">{{ xml_content if xml_content else 'Brak XML.' }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="invoice-detail-sidebar">
|
||||
<div class="invoice-detail-sticky">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header"><i class="fa-solid fa-pen-to-square me-2"></i>Metadane</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="mb-2">{{ form.status.label(class='form-label') }}{{ form.status(class='form-select', disabled=edit_locked) }}</div>
|
||||
<div class="mb-2">{{ form.tags.label(class='form-label') }}{{ form.tags(class='form-control', disabled=edit_locked) }}</div>
|
||||
<div class="mb-2">{{ form.internal_note.label(class='form-label') }}{{ form.internal_note(class='form-control', rows=4, disabled=edit_locked) }}</div>
|
||||
<div class="form-check">{{ form.queue_accounting(class='form-check-input', disabled=edit_locked) }}{{ form.queue_accounting.label(class='form-check-label') }}</div>
|
||||
<div class="form-check mb-2">{{ form.pinned(class='form-check-input', disabled=edit_locked) }}{{ form.pinned.label(class='form-check-label') }}</div>
|
||||
{{ form.submit(class='btn btn-primary w-100', disabled=edit_locked) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header"><i class="fa-solid fa-paper-plane me-2"></i>Wyślij mailem</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ url_for('invoices.send', invoice_id=invoice.id) }}">
|
||||
{{ form.csrf_token }}
|
||||
<input class="form-control mb-2" type="email" name="recipient" placeholder="odbiorca@example.com" {% if edit_locked %}disabled{% endif %}>
|
||||
<button class="btn btn-outline-primary w-100" {% if edit_locked %}disabled{% endif %}><i class="fa-solid fa-envelope me-1"></i>Wyślij PDF</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{% endblock %}
|
||||
149
app/templates/invoices/index.html
Normal file
149
app/templates/invoices/index.html
Normal file
@@ -0,0 +1,149 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-file-import me-2 text-primary"></i>Faktury otrzymane{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set args = request.args.to_dict() %}
|
||||
{% if 'page' in args %}
|
||||
{% set _ = args.pop('page') %}
|
||||
{% endif %}
|
||||
|
||||
<div class="card mb-3 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
|
||||
<div>
|
||||
<h5 class="mb-1">Faktury otrzymane</h5>
|
||||
<div class="text-muted small">Tutaj widzisz tylko dokumenty kosztowe i otrzymane od kontrahentów.</div>
|
||||
</div>
|
||||
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('invoices.issued_list') }}">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square me-1"></i>Przejdź do wystawionych
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="get" class="row g-2">
|
||||
<div class="col-md-1">{{ form.month(class='form-select') }}</div>
|
||||
<div class="col-md-1">{{ form.year(class='form-control', placeholder='Rok') }}</div>
|
||||
<div class="col-md-2">{{ form.contractor(class='form-control', placeholder='Kontrahent') }}</div>
|
||||
<div class="col-md-1">{{ form.nip(class='form-control', placeholder='NIP') }}</div>
|
||||
<div class="col-md-2">{{ form.invoice_type(class='form-select') }}</div>
|
||||
<div class="col-md-2">{{ form.status(class='form-select') }}</div>
|
||||
<div class="col-md-1">{{ form.quick_filter(class='form-select') }}</div>
|
||||
<div class="col-md-2">{{ form.search(class='form-control', placeholder='Szukaj') }}</div>
|
||||
<div class="col-md-2">{{ form.min_amount(class='form-control', placeholder='Min') }}</div>
|
||||
<div class="col-md-2">{{ form.max_amount(class='form-control', placeholder='Max') }}</div>
|
||||
<div class="col-md-2">{{ form.submit(class='btn btn-primary w-100') }}</div>
|
||||
<div class="col-md-2">
|
||||
<a class="btn btn-outline-secondary w-100" href="{{ url_for('invoices.export_csv', **request.args) }}">
|
||||
<i class="fa-solid fa-file-csv me-1"></i>CSV
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<a class="btn btn-outline-secondary w-100" href="{{ url_for('invoices.export_zip', **request.args) }}">
|
||||
<i class="fa-solid fa-file-zipper me-1"></i>ZIP
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('invoices.bulk_action') }}">
|
||||
{{ form.csrf_token }}
|
||||
|
||||
<div class="d-flex gap-2 mb-2 flex-wrap">
|
||||
{% if read_only_mode %}
|
||||
<div class="small text-warning-emphasis align-self-center">Akcje masowe są zablokowane w trybie read only.</div>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-sm btn-outline-primary" name="action" value="mark_accounted" {% if read_only_mode %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-book me-1"></i>Masowo zaksięguj
|
||||
</button>
|
||||
|
||||
<button class="btn btn-sm btn-outline-warning" name="action" value="queue_accounting" {% if read_only_mode %}disabled{% endif %}>
|
||||
<i class="fa-solid fa-inbox me-1"></i>Do księgowości
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;"></th>
|
||||
<th>Numer</th>
|
||||
<th>KSeF</th>
|
||||
<th>Kontrahent</th>
|
||||
<th>NIP</th>
|
||||
<th>Data</th>
|
||||
<th>Netto</th>
|
||||
<th>VAT</th>
|
||||
<th>Brutto</th>
|
||||
<th>Typ</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invoice in pagination.items %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" name="invoice_ids" value="{{ invoice.id }}" {% if read_only_mode %}disabled{% endif %}>
|
||||
</td>
|
||||
<td class="invoice-number-col">{{ invoice.invoice_number }}</td>
|
||||
<td class="invoice-ksef-col ksef-break small">{{ invoice.ksef_number }}</td>
|
||||
<td>{{ invoice.contractor_name }}</td>
|
||||
<td>{{ invoice.contractor_nip }}</td>
|
||||
<td>{{ invoice.issue_date }}</td>
|
||||
<td>{{ invoice.net_amount|pln }}</td>
|
||||
<td>{{ invoice.vat_amount|pln }}</td>
|
||||
<td>{{ invoice.gross_amount|pln }}</td>
|
||||
<td>{{ invoice.invoice_type_label }}</td>
|
||||
<td>
|
||||
<span class="badge text-bg-secondary">{{ invoice.status_label }}</span>
|
||||
</td>
|
||||
<td class="text-end invoice-actions-cell">
|
||||
<div class="invoice-actions-stack">
|
||||
<a class="btn btn-sm btn-outline-primary invoice-action-btn" href="{{ url_for('invoices.detail', invoice_id=invoice.id) }}">
|
||||
<i class="fa-solid fa-folder-open me-1"></i>Otwórz
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-success invoice-action-btn" data-bs-toggle="modal" data-bs-target="#payModalReceived{{ invoice.id }}">
|
||||
<i class="fa-solid fa-wallet me-1"></i>Opłać
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% set payment_details = payment_details_map.get(invoice.id, {}) %}
|
||||
{% set modal_id = 'payModalReceived' ~ invoice.id %}
|
||||
{% include 'partials/payment_modal.html' %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="12" class="text-center text-muted py-4">Brak faktur dla wybranych filtrów.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if pagination.pages and pagination.pages > 1 %}
|
||||
<nav aria-label="Paginacja faktur">
|
||||
<ul class="pagination flex-wrap">
|
||||
{% if pagination.has_prev %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('invoices.index', page=pagination.prev_num, **args) }}">Poprz.</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for pg in range(1, (pagination.pages or 1) + 1) %}
|
||||
<li class="page-item {{ 'active' if pg == pagination.page else '' }}">
|
||||
<a class="page-link" href="{{ url_for('invoices.index', page=pg, **args) }}">{{ pg }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% if pagination.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('invoices.index', page=pagination.next_num, **args) }}">Dalej</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
120
app/templates/invoices/issued_form.html
Normal file
120
app/templates/invoices/issued_form.html
Normal file
@@ -0,0 +1,120 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-square-plus me-2 text-primary"></i>{{ 'Edytuj fakturę' if editing_invoice else 'Wystaw fakturę' }}{% endblock %}
|
||||
{% block content %}
|
||||
{% set eyebrow='Sprzedaż' %}{% set heading=('Edytuj fakturę' if editing_invoice else 'Wystaw fakturę') %}{% set description='Widok uproszczony, dopasowany do stylu panelu administracyjnego.' %}
|
||||
{% include 'partials/page_header.html' with context %}
|
||||
<div class="row g-4">
|
||||
<div class="col-xl-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if read_only_mode %}<div class="alert alert-warning">Tryb tylko do odczytu - wystawianie faktur jest zablokowane.</div>{% endif %}
|
||||
<form method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
{{ form.customer_id.label(class='form-label mb-0') }}
|
||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#customerQuickAddModal"><i class="fa-solid fa-plus me-1"></i>Dodaj</button>
|
||||
</div>
|
||||
{{ form.customer_id(class='form-select', disabled=read_only_mode) }}
|
||||
<div class="form-text"><a href="{{ url_for('invoices.customers') }}">Pełna kartoteka klientów</a></div>
|
||||
</div>
|
||||
<div class="col-md-6">{{ form.numbering_template.label(class='form-label') }}{{ form.numbering_template(class='form-select', disabled=read_only_mode) }}</div>
|
||||
<div class="col-md-6">{{ form.invoice_number.label(class='form-label') }}{{ form.invoice_number(class='form-control', disabled=read_only_mode, placeholder=preview_number) }}<div class="form-text">Puste pole = numer zostanie nadany automatycznie.</div></div>
|
||||
<div class="col-md-6 d-flex align-items-end"><div class="alert alert-secondary w-100 mb-0 small">Proponowany numer: <strong>{{ preview_number or '—' }}</strong></div></div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
{{ form.product_id.label(class='form-label mb-0') }}
|
||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#productQuickAddModal"><i class="fa-solid fa-plus me-1"></i>Dodaj</button>
|
||||
</div>
|
||||
<select class="form-select" id="productField" name="{{ form.product_id.name }}" {% if read_only_mode %}disabled{% endif %}>
|
||||
{% for product in products %}
|
||||
<option value="{{ product.id }}" data-net-price="{{ product.net_price }}" data-vat-rate="{{ product.vat_rate }}" data-split-payment="{{ 'true' if product.split_payment_default else 'false' }}" {{ 'selected' if form.product_id.data == product.id else '' }}>{{ product.name }} - {{ product.net_price }} PLN</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="form-text"><a href="{{ url_for('invoices.products') }}">Pełna kartoteka towarów i usług</a></div>
|
||||
</div>
|
||||
<div class="col-md-3">{{ form.quantity.label(class='form-label') }}{{ form.quantity(class='form-control', disabled=read_only_mode, id='quantityField') }}</div>
|
||||
<div class="col-md-3">{{ form.unit_net.label(class='form-label') }}{{ form.unit_net(class='form-control', disabled=read_only_mode, id='unitNetField') }}</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check form-switch">
|
||||
{{ form.split_payment(class='form-check-input', disabled=read_only_mode, id='splitPaymentField') }}
|
||||
{{ form.split_payment.label(class='form-check-label') }}
|
||||
</div>
|
||||
<div class="form-text" id="splitPaymentHint">Domyślnie włączane dla usług oznaczonych w kartotece. Dla faktur powyżej 15 000 PLN brutto jest wymuszane.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex gap-2 flex-wrap">{% if editing_invoice %}<button class="btn btn-outline-primary" name="save_submit" type="submit" {% if read_only_mode %}disabled{% endif %}>Zapisz zmiany</button><button class="btn btn-primary" name="submit" type="submit" {% if read_only_mode %}disabled{% endif %}>Zapisz i wyślij do KSeF</button>{% else %}{{ form.save_submit(class='btn btn-outline-primary', disabled=read_only_mode) }}{{ form.submit(class='btn btn-primary', disabled=read_only_mode) }}{% endif %}</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header"><i class="fa-solid fa-circle-info me-2"></i>Podpowiedzi</div>
|
||||
<div class="card-body small text-secondary d-flex flex-column gap-3">
|
||||
<div>Dodawanie klientów działa teraz przez wspólne formularze dostępne także w formularzu NFZ. (jeśli moduł włączony)</div>
|
||||
<div>Po zapisaniu nowy klient lub towar zostanie automatycznie podstawiony do formularza.</div>
|
||||
<div>Pełne kartoteki nadal są dostępne z linków pod polami wyboru.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% set quick_return_endpoint = 'invoices.issued_edit' if editing_invoice else 'invoices.issued_new' %}
|
||||
{% set quick_invoice_id = editing_invoice.id if editing_invoice else None %}
|
||||
{% include 'partials/invoice_quick_add_modals.html' %}
|
||||
<script>
|
||||
(() => {
|
||||
const productField = document.getElementById('productField');
|
||||
const quantityField = document.getElementById('quantityField');
|
||||
const unitNetField = document.getElementById('unitNetField');
|
||||
const splitPaymentField = document.getElementById('splitPaymentField');
|
||||
const splitPaymentHint = document.getElementById('splitPaymentHint');
|
||||
if (!productField || !quantityField || !unitNetField || !splitPaymentField) return;
|
||||
|
||||
let userModifiedSplit = false;
|
||||
|
||||
const parseNumber = (value) => {
|
||||
const normalized = String(value || '').replace(/\s+/g, '').replace(',', '.');
|
||||
const parsed = Number.parseFloat(normalized);
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
};
|
||||
|
||||
const syncSplitPayment = (resetToProductDefault = false) => {
|
||||
const selected = productField.options[productField.selectedIndex];
|
||||
const vatRate = parseNumber(selected?.dataset?.vatRate || 0);
|
||||
const defaultSplit = (selected?.dataset?.splitPayment || 'false') === 'true';
|
||||
const gross = parseNumber(quantityField.value) * parseNumber(unitNetField.value) * (1 + vatRate / 100);
|
||||
const forced = gross > 15000;
|
||||
if (forced) {
|
||||
splitPaymentField.checked = true;
|
||||
splitPaymentField.disabled = true;
|
||||
if (splitPaymentHint) splitPaymentHint.textContent = 'Split payment jest wymagany, ponieważ wartość brutto faktury przekracza 15 000 PLN.';
|
||||
return;
|
||||
}
|
||||
splitPaymentField.disabled = {{ 'true' if read_only_mode else 'false' }};
|
||||
if (resetToProductDefault || !userModifiedSplit) {
|
||||
splitPaymentField.checked = defaultSplit;
|
||||
}
|
||||
if (splitPaymentHint) {
|
||||
splitPaymentHint.textContent = defaultSplit
|
||||
? 'Dla wybranej usługi split payment jest domyślnie włączony. Możesz go zmienić dla tej faktury.'
|
||||
: 'Split payment możesz włączyć ręcznie. Dla faktur powyżej 15 000 PLN brutto jest wymuszany.';
|
||||
}
|
||||
};
|
||||
|
||||
productField.addEventListener('change', () => {
|
||||
const selected = productField.options[productField.selectedIndex];
|
||||
if (selected?.dataset?.netPrice && !unitNetField.value) unitNetField.value = selected.dataset.netPrice;
|
||||
userModifiedSplit = false;
|
||||
syncSplitPayment(true);
|
||||
});
|
||||
quantityField.addEventListener('input', () => syncSplitPayment(false));
|
||||
unitNetField.addEventListener('input', () => syncSplitPayment(false));
|
||||
splitPaymentField.addEventListener('change', () => {
|
||||
userModifiedSplit = true;
|
||||
});
|
||||
syncSplitPayment(false);
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
12
app/templates/invoices/issued_list.html
Normal file
12
app/templates/invoices/issued_list.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-paper-plane me-2 text-primary"></i>Faktury wystawione{% endblock %}
|
||||
{% block content %}
|
||||
{% set eyebrow='Sprzedaż' %}{% set heading='Faktury wystawione' %}{% set description='Dokumenty sprzedażowe przygotowane w systemie, z oznaczeniem statusu wysyłki do KSeF.' %}
|
||||
{% set actions %}<a class="btn btn-primary {% if read_only_mode %}disabled{% endif %}" href="{{ url_for('invoices.issued_new') }}"><i class="fa-solid fa-plus me-2"></i>Nowa faktura</a>{% endset %}
|
||||
{% include 'partials/page_header.html' with context %}
|
||||
<div class="card mb-3"><div class="card-body"><form method="get" class="row g-2 align-items-end"><div class="col-md-10"><label class="form-label">Szukaj</label><input class="form-control" type="text" name="q" value="{{ search or '' }}" placeholder="Numer, KSeF, kontrahent, NIP"></div><div class="col-md-2 d-grid"><button class="btn btn-outline-primary"><i class="fa-solid fa-magnifying-glass me-2"></i>Szukaj</button></div></form></div></div>
|
||||
<div class="card">
|
||||
<div class="table-responsive"><table class="table table-hover align-middle mb-0"><thead><tr><th>Numer</th><th>Kontrahent</th><th>Brutto</th><th>KSeF</th><th>Status</th><th></th></tr></thead><tbody>{% for invoice in invoices %}<tr><td><div class="fw-semibold">{{ invoice.invoice_number }}</div><div class="small text-secondary">{{ invoice.issue_date }}</div>{% if invoice.source == 'nfz' %}<div><span class="badge text-bg-success-subtle border nfz-badge mt-1">NFZ</span></div>{% endif %}</td><td>{{ invoice.contractor_name }}</td><td>{{ invoice.gross_amount|pln }}</td><td>{% if invoice.issued_to_ksef_at %}<span class="badge text-bg-success">Przesłana do KSeF</span>{% else %}<span class="badge text-bg-secondary">Nieprzesłana do KSeF</span>{% endif %}<div class="small text-secondary mt-1">{{ invoice.ksef_number }}</div></td><td><span class="badge text-bg-{{ 'success' if invoice.issued_to_ksef_at else 'secondary' }}">{{ invoice.issued_status_label }}</span></td><td class="text-end"><div class="d-inline-flex gap-2 flex-wrap justify-content-end"><a class="btn btn-sm btn-outline-primary invoice-action-btn" href="{{ url_for('invoices.detail', invoice_id=invoice.id) }}"><i class="fa-solid fa-folder-open me-1"></i>Otwórz</a><button type="button" class="btn btn-sm btn-success invoice-action-btn" data-bs-toggle="modal" data-bs-target="#payModalIssued{{ invoice.id }}"><i class="fa-solid fa-wallet me-1"></i>Opłać</button></div>{% set payment_details = payment_details_map.get(invoice.id, {}) %}{% set modal_id = 'payModalIssued' ~ invoice.id %}{% include 'partials/payment_modal.html' %}</td></tr>{% else %}<tr><td colspan="6" class="text-center text-secondary py-4">Brak faktur.</td></tr>{% endfor %}</tbody></table></div>
|
||||
<div class="card-body border-top py-2"><nav><ul class="pagination justify-content-end mb-0">{% if pagination.has_prev %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.issued_list', page=pagination.prev_num, q=search) }}">Poprz.</a></li>{% endif %}{% for pg in range(1, (pagination.pages or 1) + 1) %}<li class="page-item {{ 'active' if pg == pagination.page else '' }}"><a class="page-link" href="{{ url_for('invoices.issued_list', page=pg, q=search) }}">{{ pg }}</a></li>{% endfor %}{% if pagination.has_next %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.issued_list', page=pagination.next_num, q=search) }}">Dalej</a></li>{% endif %}</ul></nav></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
1
app/templates/invoices/month_pdf.html
Normal file
1
app/templates/invoices/month_pdf.html
Normal file
@@ -0,0 +1 @@
|
||||
<!doctype html><html><head><meta charset="utf-8"><style>body{font-family:Helvetica,Arial,sans-serif;font-size:12px}table{width:100%;border-collapse:collapse}td,th{border:1px solid #ccc;padding:6px}</style></head><body><h1>{{ title }}</h1><table><thead><tr><th>Numer</th><th>Kontrahent</th><th>Netto</th><th>VAT</th><th>Brutto</th></tr></thead><tbody>{% for invoice in invoices %}<tr><td>{{ invoice.invoice_number }}</td><td>{{ invoice.contractor_name }}</td><td>{{ invoice.net_amount|pln }}</td><td>{{ invoice.vat_amount|pln }}</td><td>{{ invoice.gross_amount|pln }}</td></tr>{% endfor %}</tbody></table></body></html>
|
||||
9
app/templates/invoices/monthly.html
Normal file
9
app/templates/invoices/monthly.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-calendar-days me-2 text-primary"></i>Zestawienia {{ period_title }}{% endblock %}
|
||||
{% block content %}
|
||||
{% set eyebrow='Analiza' %}{% set heading='Zestawienia ' ~ period_title %}{% set description='Widok roczny, kwartalny i miesięczny z porównaniem do innych lat.' %}
|
||||
{% set actions %}<div class="btn-group" role="group"><a class="btn {{ 'btn-primary' if period == 'year' else 'btn-outline-primary' }}" href="{{ url_for('invoices.monthly', period='year', q=search) }}">Roczne</a><a class="btn {{ 'btn-primary' if period == 'quarter' else 'btn-outline-primary' }}" href="{{ url_for('invoices.monthly', period='quarter', q=search) }}">Kwartalne</a><a class="btn {{ 'btn-primary' if period == 'month' else 'btn-outline-primary' }}" href="{{ url_for('invoices.monthly', period='month', q=search) }}">Miesięczne</a></div>{% endset %}
|
||||
{% include 'partials/page_header.html' with context %}
|
||||
<div class="row g-3 mb-3"><div class="col-lg-8"><div class="card h-100"><div class="card-body"><form method="get" class="row g-2 align-items-end"><input type="hidden" name="period" value="{{ period }}"><div class="col-md-9"><label class="form-label">Szukaj w zestawieniach</label><input class="form-control" type="text" name="q" value="{{ search }}" placeholder="Numer, KSeF, kontrahent, NIP"></div><div class="col-md-3 d-grid"><button class="btn btn-primary" type="submit"><i class="fa-solid fa-magnifying-glass me-2"></i>Szukaj</button></div></form></div></div></div><div class="col-lg-4"><div class="card h-100"><div class="card-header">Statystyka innych lat</div><div class="card-body">{% for item in comparisons %}<div class="d-flex justify-content-between border-bottom py-2 small"><div><div class="fw-semibold">{{ item.year }}</div><div class="text-secondary">{{ item.count }} faktur · netto {{ item.net|pln }}</div></div><div class="text-end"><div>{{ item.gross|pln }}</div>{% if item.delta is not none %}<div class="text-{{ 'success' if item.delta >= 0 else 'danger' }}">{{ '+' if item.delta >= 0 else '' }}{{ item.delta|pln }}</div>{% else %}<div class="text-secondary">bazowy</div>{% endif %}</div></div>{% else %}<div class="text-secondary">Brak danych porównawczych.</div>{% endfor %}</div></div></div></div>
|
||||
{% for group in groups %}<div class="card mb-3"><div class="card-header d-flex justify-content-between flex-wrap gap-2"><span class="fw-semibold">{{ group.label }}</span><span>{{ group.gross|pln }}</span></div><div class="card-body"><div class="row small g-2 mb-3"><div class="col-md-3"><strong>Liczba faktur:</strong> {{ group.count }}</div><div class="col-md-3"><strong>Netto:</strong> {{ group.net|pln }}</div><div class="col-md-3"><strong>VAT:</strong> {{ group.vat|pln }}</div><div class="col-md-3"><strong>Brutto:</strong> {{ group.gross|pln }}</div></div>{% if period == 'month' %}<div class="mb-2"><a class="btn btn-sm btn-outline-primary" href="{{ url_for('invoices.month_pdf', period=group.key) }}">PDF miesiąca</a></div>{% endif %}<div class="table-responsive"><table class="table table-sm align-middle mb-0"><thead><tr><th>Numer</th><th>Kontrahent</th><th>Data</th><th>Brutto</th><th></th></tr></thead><tbody>{% for invoice in group.entries %}<tr><td>{{ invoice.invoice_number }}</td><td>{{ invoice.contractor_name }}</td><td>{{ invoice.issue_date }}</td><td>{{ invoice.gross_amount|pln }}</td><td class="text-end"><a class="btn btn-sm btn-outline-primary invoice-action-btn" href="{{ url_for('invoices.detail', invoice_id=invoice.id) }}"><i class="fa-solid fa-folder-open me-1"></i>Otwórz</a></td></tr>{% else %}<tr><td colspan="5" class="text-center text-secondary py-3">Brak faktur w tym okresie.</td></tr>{% endfor %}</tbody></table></div></div></div>{% else %}<div class="alert alert-info">Brak danych.</div>{% endfor %}
|
||||
{% endblock %}
|
||||
1
app/templates/invoices/pdf.html
Normal file
1
app/templates/invoices/pdf.html
Normal file
@@ -0,0 +1 @@
|
||||
<!doctype html><html><head><meta charset="utf-8"><style>body{font-family:Helvetica,Arial,sans-serif;font-size:12px}table{width:100%;border-collapse:collapse}td,th{border:1px solid #ccc;padding:6px}</style></head><body><h1>Faktura {{ invoice.invoice_number }}</h1><table><tr><th>KSeF</th><td>{{ invoice.ksef_number }}</td></tr><tr><th>Kontrahent</th><td>{{ invoice.contractor_name }}</td></tr><tr><th>NIP</th><td>{{ invoice.contractor_nip }}</td></tr><tr><th>Data</th><td>{{ invoice.issue_date }}</td></tr><tr><th>Netto</th><td>{{ invoice.net_amount|pln }}</td></tr><tr><th>VAT</th><td>{{ invoice.vat_amount|pln }}</td></tr><tr><th>Brutto</th><td>{{ invoice.gross_amount|pln }}</td></tr><tr><th>Rachunek bankowy</th><td>{{ invoice.seller_bank_account or (invoice.company.bank_account if invoice.company else '') or '—' }}</td></tr></table></body></html>
|
||||
65
app/templates/invoices/products.html
Normal file
65
app/templates/invoices/products.html
Normal file
@@ -0,0 +1,65 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}<i class="fa-solid fa-boxes-stacked me-2 text-primary"></i>Towary i usługi{% endblock %}
|
||||
{% block content %}
|
||||
{% set eyebrow='Kartoteka' %}{% set heading='Towary i usługi' %}{% set description='Jednolity widok kartoteki z sortowaniem i paginacją.' %}
|
||||
{% include 'partials/page_header.html' with context %}
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header"><i class="fa-solid fa-box-open me-2"></i>{{ 'Edytuj pozycję' if editing else 'Nowa pozycja' }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-2"><label class="form-label">Nazwa</label><input class="form-control" name="name" value="{{ editing.name if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">SKU</label><input class="form-control" name="sku" value="{{ editing.sku if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">Jednostka</label><input class="form-control" name="unit" value="{{ editing.unit if editing else 'szt.' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">Cena netto</label><input class="form-control" name="net_price" value="{{ editing.net_price if editing else '' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="mb-2"><label class="form-label">VAT %</label><input class="form-control" name="vat_rate" value="{{ editing.vat_rate if editing else '23' }}" {% if read_only_mode %}disabled{% endif %}></div>
|
||||
<div class="form-check mb-3"><input class="form-check-input" type="checkbox" id="split_payment_default" name="split_payment_default" value="1" {{ 'checked' if editing and editing.split_payment_default else '' }} {% if read_only_mode %}disabled{% endif %}><label class="form-check-label" for="split_payment_default">Domyślnie włączaj split payment dla tej usługi</label></div>
|
||||
<div class="d-grid gap-2"><button class="btn btn-primary" {% if read_only_mode %}disabled{% endif %}>{{ 'Zapisz pozycję' if editing else 'Dodaj pozycję' }}</button>{% if editing %}<a class="btn btn-outline-secondary" href="{{ url_for('invoices.products') }}">Anuluj edycję</a>{% endif %}</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div><i class="fa-solid fa-warehouse me-2"></i>Baza towarów i usług</div>
|
||||
<form method="get" class="row g-2 align-items-end w-100 ms-0">
|
||||
<div class="col-md-7"><label class="form-label form-label-sm mb-1">Szukaj</label><input class="form-control form-control-sm" type="search" name="q" value="{{ search or '' }}" placeholder="Szukaj po nazwie, SKU lub jednostce..."></div>
|
||||
<div class="col-md-4"><label class="form-label form-label-sm mb-1">Sortowanie</label><select class="form-select form-select-sm" name="sort">
|
||||
<option value="name_asc" {{ 'selected' if sort == 'name_asc' else '' }}>A-Z</option>
|
||||
<option value="name_desc" {{ 'selected' if sort == 'name_desc' else '' }}>Z-A</option>
|
||||
<option value="price_asc" {{ 'selected' if sort == 'price_asc' else '' }}>Cena rosnąco</option>
|
||||
<option value="price_desc" {{ 'selected' if sort == 'price_desc' else '' }}>Cena malejąco</option>
|
||||
<option value="sku_asc" {{ 'selected' if sort == 'sku_asc' else '' }}>SKU A-Z</option>
|
||||
<option value="sku_desc" {{ 'selected' if sort == 'sku_desc' else '' }}>SKU Z-A</option>
|
||||
</select></div>
|
||||
<div class="col-md-1 d-grid"><button class="btn btn-sm btn-outline-secondary"><i class="fa-solid fa-magnifying-glass"></i></button></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle mb-0">
|
||||
<thead><tr><th>Nazwa</th><th>SKU</th><th>Cena netto</th><th>VAT</th><th>Split payment</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr><td><i class="fa-solid fa-cube text-primary me-2"></i>{{ item.name }}</td><td>{{ item.sku }}</td><td>{{ item.net_price|pln }}</td><td>{{ item.vat_rate }}%</td><td>{% if item.split_payment_default %}<span class="badge text-bg-warning">Domyślny</span>{% else %}<span class="text-secondary">Wyłączony</span>{% endif %}</td><td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ url_for('invoices.products', product_id=item.id) }}">Edytuj</a></td></tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="text-secondary text-center py-4">Brak pozycji.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-body border-top py-2">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-end mb-0">
|
||||
{% if pagination.has_prev %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.products', page=pagination.prev_num, q=search, sort=sort) }}">Poprz.</a></li>{% endif %}
|
||||
<li class="page-item disabled"><span class="page-link">{{ pagination.page }} / {{ pagination.pages or 1 }}</span></li>
|
||||
{% if pagination.has_next %}<li class="page-item"><a class="page-link" href="{{ url_for('invoices.products', page=pagination.next_num, q=search, sort=sort) }}">Dalej</a></li>{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user