Initial commit

This commit is contained in:
Felix Baumann
2026-06-27 17:07:34 +00:00
commit fb47c53d52
256 changed files with 21270 additions and 0 deletions
@@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Kunden
{% endblock %}
{% block header_title %}
Kunde hinzufügen
{% endblock %}
{% block header_description %}
Neuen Kunden anlegen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mb-8">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
<div class="grid grid-cols-1 gap-4">
{{ form.as_p }}
</div>
<button
type="submit"
class="btn mt-4 btn-save">
Speichern
</button>
</form>
</div>
{% endblock %}
@@ -0,0 +1,230 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Kunden
{% endblock %}
{% block header_title %}
{{ customer.customer_number }} - {{ customer.company_name }}
{% endblock %}
{% block header_description %}
Kunde bearbeiten
{% endblock %}
{% block content %}
<form method="post" action="{% url 'invoices:customers_edit' customer.id %}">
{% csrf_token %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-2">Kundendaten</h2>
<div class="grid md:grid-cols-2 gap-4 mb-8">
{% for field in form %}
<div>
<label class="block text-sm font-medium mb-1">
{{ field.label }}
</label>
{{ field }}
{% if field.errors %}
<div class="text-red-600 text-sm mt-1">
{{ field.errors }}
</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="grid lg:grid-cols-2 gap-8">
<!-- Ansprechpartner -->
<div>
<h2 class="font-bold text-lg mb-2">
Ansprechpartner
</h2>
{% for contact in customer.contacts.all %}
<div class="bg-slate-100 dark:bg-slate-700 rounded-xl p-4 mb-3">
<div class="grid md:grid-cols-2 gap-3">
<input
type="text"
name="contact_first_name_{{ contact.id }}"
value="{{ contact.first_name }}"
placeholder="Vorname"
class="border rounded px-3 py-2">
<input
type="text"
name="contact_last_name_{{ contact.id }}"
value="{{ contact.last_name }}"
placeholder="Nachname"
class="border rounded px-3 py-2">
<input
type="email"
name="contact_email_{{ contact.id }}"
value="{{ contact.email }}"
placeholder="E-Mail"
class="border rounded px-3 py-2">
<input
type="text"
name="contact_phone_{{ contact.id }}"
value="{{ contact.phone }}"
placeholder="Telefon"
class="border rounded px-3 py-2">
</div>
<button type="submit"
name="delete_contact_id"
value="{{ contact.id }}"
class="btn btn-delete">
Löschen
</button>
</div>
{% endfor %}
<div id="contacts-container"></div>
<button
type="button"
onclick="addContact()"
class="btn btn-add">
+ Ansprechpartner hinzufügen
</button>
</div>
<!-- Standorte -->
<div>
<h2 class="font-bold text-lg mb-2">
Standorte
</h2>
{% for location in customer.locations.all %}
<div class="bg-slate-100 dark:bg-slate-700 rounded-xl p-4 mb-3">
<div class="grid md:grid-cols-2 gap-3">
<input
type="text"
name="location_name_{{ location.id }}"
value="{{ location.name }}"
placeholder="Name"
class="border rounded px-3 py-2">
<input
type="text"
name="location_street_{{ location.id }}"
value="{{ location.street }}"
placeholder="Straße"
class="border rounded px-3 py-2">
<input
type="text"
name="location_zip_{{ location.id }}"
value="{{ location.zip_code }}"
placeholder="PLZ"
class="border rounded px-3 py-2">
<input
type="text"
name="location_city_{{ location.id }}"
value="{{ location.city }}"
placeholder="Ort"
class="border rounded px-3 py-2">
<input
type="text"
name="location_country_{{ location.id }}"
value="{{ location.country }}"
placeholder="Land"
class="border rounded px-3 py-2 md:col-span-2">
</div>
<button type="submit"
name="delete_location_id"
value="{{ location.id }}"
class="btn btn-delete">
Löschen
</button>
</div>
{% endfor %}
<div id="locations-container"></div>
<button
type="button"
onclick="addLocation()"
class="btn btn-add">
+ Standort hinzufügen
</button>
</div>
</div>
<button
type="submit"
class="btn mt-8 btn-save">
Speichern
</button>
</div>
</form>
<script>
function addContact() {
document.getElementById("contacts-container").insertAdjacentHTML("beforeend", `
<div class="contact-card bg-slate-100 dark:bg-slate-700 rounded-xl p-4 mb-3">
<div class="grid md:grid-cols-2 gap-3">
<input type="text" name="new_contact_first_name" placeholder="Vorname" class="border rounded px-3 py-2">
<input type="text" name="new_contact_last_name" placeholder="Nachname" class="border rounded px-3 py-2">
<input type="email" name="new_contact_email" placeholder="E-Mail" class="border rounded px-3 py-2">
<input type="text" name="new_contact_phone" placeholder="Telefon" class="border rounded px-3 py-2">
</div>
</div>
`);
}
function addLocation() {
document.getElementById("locations-container").insertAdjacentHTML("beforeend", `
<div class="location-card bg-slate-100 dark:bg-slate-700 rounded-xl p-4 mb-3">
<div class="grid md:grid-cols-2 gap-3">
<input type="text" name="new_location_name" placeholder="Name" class="border rounded px-3 py-2">
<input type="text" name="new_location_street" placeholder="Straße" class="border rounded px-3 py-2">
<input type="text" name="new_location_zip" placeholder="PLZ" class="border rounded px-3 py-2">
<input type="text" name="new_location_city" placeholder="Ort" class="border rounded px-3 py-2">
<input type="text" name="new_location_country" placeholder="Land" class="border rounded px-3 py-2 md:col-span-2">
</div>
</div>
`);
}
</script>
{% endblock %}
@@ -0,0 +1,62 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Kunden
{% endblock %}
{% block header_title %}
Kunden
{% endblock %}
{% block header_description %}
Kundenstammdaten verwalten
{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">
Kunden anlegen
</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
<a href="{% url 'invoices:customers_add' %}"
class="btn btn-add">
+ Kunde
</a>
</div>
</div>
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mb-8">
<div class="mb-6">
<h2 class="text-xl font-bold">Kundenliste</h2>
<p class="text-slate-400 text-sm mt-1">Alle Kunden, inkl. Standorte und Ansprechpartner</p>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="mb-6">
<h2 class="text-xl font-bold">Archivierte Kunden</h2>
<p class="text-slate-400 text-sm mt-1">Alle inaktiven Kunden, inkl. Standorte und Ansprechpartner</p>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table_archived %}
</div>
</div>
{% endblock %}
@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Rechnungen{% endblock %}
{% block header_title %}
Kundenrechnung
{% endblock %}
{% block header_description %}
Rechnung auf Basis eines Kunden erstellen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const customer = document.getElementById("id_customer");
const contact = document.getElementById("id_contact");
const location = document.getElementById("id_location");
function fill(select, data) {
select.innerHTML = "<option value=''>---------</option>";
data.forEach(item => {
select.innerHTML += `<option value="${item.id}">${item.text}</option>`;
});
}
if (customer) {
customer.addEventListener("change", function () {
const id = this.value;
if (!id) {
contact.innerHTML = "";
location.innerHTML = "";
return;
}
fetch(`/invoices/ajax/customer/${id}/contacts/`)
.then(r => r.json())
.then(data => fill(contact, data));
fetch(`/invoices/ajax/customer/${id}/locations/`)
.then(r => r.json())
.then(data => fill(location, data));
});
}
});
</script>
{% endblock %}
@@ -0,0 +1,221 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Rechnungen{% endblock %}
{% block header_title %}
Projektrechnung
{% endblock %}
{% block header_description %}
Rechnung auf Basis eines Projekts erstellen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const project = document.getElementById("id_project");
const contact = document.getElementById("id_contact");
const location = document.getElementById("id_location");
const customerFields = [
"id_contact",
"id_location"
];
const manualFields = [
"id_contact_name_manual",
"id_contact_email_manual",
"id_contact_phone_manual",
"id_location_name_manual",
"id_location_street_manual",
"id_location_zip_manual",
"id_location_city_manual",
"id_location_country_manual"
];
function hideField(id){
const field = document.getElementById(id);
if(field){
const row = field.closest("p");
row.style.display = "none";
}
}
function showField(id){
const field = document.getElementById(id);
if(field){
const row = field.closest("p");
row.style.display = "";
}
}
function hideCustomerFields(){
customerFields.forEach(hideField);
}
function showCustomerFields(){
customerFields.forEach(showField);
}
function hideManualFields(){
manualFields.forEach(hideField);
}
function showManualFields(){
manualFields.forEach(showField);
}
function fill(select, data){
if(!select){
return;
}
select.innerHTML = "<option value=''>---------</option>";
data.forEach(item => {
select.innerHTML += `
<option value="${item.id}">
${item.text}
</option>
`;
});
}
function checkProject(projectId){
if(!projectId){
hideCustomerFields();
hideManualFields();
return;
}
fetch(`/invoices/ajax/project/${projectId}/invoice-data/`)
.then(response => response.json())
.then(data => {
if(data.has_customer){
showCustomerFields();
hideManualFields();
fill(
contact,
data.contacts
);
fill(
location,
data.locations
);
} else {
hideCustomerFields();
showManualFields();
}
});
}
// Anfang prüfen
if(project.value){
checkProject(project.value);
} else {
hideCustomerFields();
hideManualFields();
}
project.addEventListener("change", function(){
checkProject(this.value);
});
});
</script>
{% endblock %}
@@ -0,0 +1,239 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Rechnungen{% endblock %}
{% block header_title %}
Rechnung {{ invoice.invoice_number }}
{% endblock %}
{% block header_description %}
Details & Positionen
{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">
Positionen zur Rechnung hinzufügen
</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
{% if invoice.status == "draft" %}
<a href="{% url 'invoices:invoice_item_add' invoice.id %}" class="btn btn-add">+ Position</a>
<a href="{% url 'invoices:invoices_preview' invoice.id %}" class="btn btn-action">Vorschau</a>
<a href="{% url 'invoices:invoices_download' invoice.id %}" onclick="startDownloadAndRedirect()" class="btn btn-action">Download</a>
<a href="{% url 'invoices:invoices_delete' invoice.id %}" class="btn btn-delete">Löschen</a>
{% endif %}
{% if invoice.status == "sent" %}
<a href="{% url 'invoices:invoices_download' invoice.id %}" class="btn btn-action">Download</a>
<a href="{% url 'invoices:invoices_cancel' invoice.id %}" onclick="startDownloadAndRedirect()" class="btn btn-delete">Stornieren</a>
<a href="{% url 'invoices:invoices_paid' invoice.id %}" class="btn btn-action">Zahlung verbuchen</a>
<a href="{% url 'invoices:invoices_overdue' invoice.id %}" class="btn btn-action">Überfällig</a>
{% endif %}
{% if invoice.status == "paid" %}
<a href="{% url 'invoices:invoices_download' invoice.id %}" class="btn btn-action">Download</a>
<a href="{% url 'invoices:invoices_cancel' invoice.id %}" onclick="startDownloadAndRedirect()" class="btn btn-delete">Stornieren</a>
{% endif %}
{% if invoice.status == "canceled" %}
<a href="{% url 'invoices:invoices_download' invoice.id %}" class="btn btn-action">Download</a>
<a href="{% url 'invoices:invoices_cancel' invoice.id %}" class="btn btn-action">Stornorechnung</a>
{% endif %}
{% if invoice.status == "overdue" %}
<a href="{% url 'invoices:invoices_download' invoice.id %}" class="btn btn-action">Download</a>
{% endif %}
</div>
</div>
</div>
</div>
{% if reminders %}
<div class="mb-8 p-6 border border-orange-500 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6 text-orange-500">Mahnungen</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
{% for reminder in reminders %}
{% if reminder.status != "canceled" %}
<div class="bg-slate-100 dark:bg-slate-700 rounded-xl p-6">
<div class="flex justify-between items-start">
<div>
<a href="{% url 'invoices:reminders_detail' reminder.id %}" class="company_link">
{{ reminder.reminder_number }}
</a>
<p class="text-sm text-slate-500 dark:text-slate-300 mt-1">
Mahnstufe {{ reminder.level }}
· {{ reminder.issue_date|date:"d.m.Y" }}
</p>
</div>
<span class="px-3 py-1 rounded-full text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-600 dark:text-slate-200">
{{ reminder.get_status_display }}
</span>
</div>
<div class="mt-4 text-sm text-slate-600 dark:text-slate-300">
{% if reminder.due_date %}
<p>
Neue Zahlungsfrist:
<span class="font-medium">
{{ reminder.due_date|date:"d.m.Y" }}
</span>
</p>
{% endif %}
{% if reminder.fee %}
<p>
Mahngebühr:
<span class="font-medium">
{{ reminder.fee|floatformat:2 }} €
</span>
</p>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
<!-- HEADER INFO -->
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Allgemein</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Kunde -->
<div>
<div class="text-slate-400 text-sm">Kunde</div>
<div class="font-semibold">{{ invoice.customer.company_name }}</div>
</div>
<!-- Projekt -->
<div>
<div class="text-slate-400 text-sm">Projekt</div>
<div class="font-semibold">
{% if invoice.project %}
{{ invoice.project.name }}
{% else %}
-
{% endif %}
</div>
</div>
<!-- Status -->
<div>
<div class="text-slate-400 text-sm">Status</div>
<div class="font-semibold">{{ invoice.get_status_display }}</div>
</div>
<!-- Datum -->
<div>
<div class="text-slate-400 text-sm">Rechnungsdatum</div>
<div class="font-semibold">{{ invoice.issue_date|date:"d.m.Y" }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Fälligkeitsdatum</div>
<div class="font-semibold">{{ invoice.due_date|date:"d.m.Y" }}</div>
</div>
</div>
</div>
<!-- SNAPSHOT: KONTAKT & ADRESSE -->
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Rechnungsadresse</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
<!-- Kontakt -->
<div>
<div class="text-slate-400 text-sm">Ansprechpartner</div>
<div class="font-semibold">{{ invoice.contact_name|default:"-" }}</div>
{% if invoice.contact_email %}
<div class="text-slate-400 text-sm">{{ invoice.contact_email }}</div>
{% endif %}
{% if invoice.contact_phone %}
<div class="text-slate-400 text-sm">{{ invoice.contact_phone }}</div>
{% endif %}
</div>
<!-- Adresse -->
<div>
<div class="text-slate-400 text-sm">Standort</div>
<div class="font-semibold">
{{ invoice.location_name|default:"-" }}
</div>
<div class="text-slate-400 text-sm">
{{ invoice.location_street }}
</div>
<div class="text-slate-400 text-sm">
{{ invoice.location_zip }} {{ invoice.location_city }}
</div>
<div class="text-slate-400 text-sm">
{{ invoice.location_country }}
</div>
</div>
</div>
</div>
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Rechnungshinweis</h2>
<div class="grid grid-cols-1 gap-6">
<div>
<div class="font-semibold">{{ invoice.note|default:"-" }}</div>
</div>
</div>
</div>
<!-- POSITIONEN -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Positionen</h2>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
<div class="mt-4 text-right font-bold">
Gesamt: {{ total_net|floatformat:2 }} €
</div>
</div>
<script>
function startDownloadAndRedirect() {
setTimeout(function() {
window.location.href = "{% url 'invoices:invoices_detail' invoice.id %}";
}, 1000);
}
</script>
{% endblock %}
@@ -0,0 +1,197 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Rechnungen
{% endblock %}
{% block header_title %}
Rechnungsposition hinzufügen
{% endblock %}
{% block header_description %}
Neue Rechnungsposition anlegen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const source = document.getElementById("id_source_type");
const fields = {
item: document.getElementById("id_item"),
time: document.getElementById("id_time_entry"),
expense: document.getElementById("id_expense"),
};
function toggleFields() {
const value = source.value;
Object.keys(fields).forEach(key => {
const field = fields[key];
if (!field) {
return;
}
const row = field.closest("p");
if (key === value) {
row.style.display = "";
} else {
row.style.display = "none";
field.value = "";
}
});
}
source.addEventListener(
"change",
toggleFields
);
// Initial ausführen
toggleFields();
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const source = document.getElementById("id_source_type");
const selects = {
item: document.getElementById("id_item"),
time: document.getElementById("id_time_entry"),
expense: document.getElementById("id_expense"),
};
const description = document.getElementById(
"id_description"
);
const quantity = document.getElementById(
"id_quantity"
);
const unit = document.getElementById(
"id_unit"
);
const price = document.getElementById(
"id_unit_price_net"
);
const tax = document.getElementById(
"id_unit_tax_rate"
);
function loadData(type, id){
if(!id){
return;
}
fetch(
`/invoices/ajax/invoice-item-data/?source=${type}&id=${id}`
)
.then(response => response.json())
.then(data => {
description.value = data.description;
quantity.value = data.quantity;
unit.value = data.unit;
price.value = data.unit_price_net;
tax.value = data.unit_tax_rate;
});
}
Object.keys(selects).forEach(type => {
if(selects[type]){
selects[type].addEventListener(
"change",
function(){
loadData(
type,
this.value
);
}
);
}
});
});
</script>
{% endblock %}
@@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Rechnungen{% endblock %}
{% block header_title %}Rechnungen{% endblock %}
{% block header_description %}Rechnungen erstellen und verwalten{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">Neue Rechnung erstellen</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
<a href="{% url 'invoices:invoice_add_customer' %}" class="btn btn-add">+ Kundenrechnung</a>
<a href="{% url 'invoices:invoice_add_project' %}" class="btn btn-add">+ Projektrechnung</a>
</div>
</div>
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="mb-6">
<h2 class="text-xl font-bold">Rechnungen</h2>
<p class="text-slate-400 text-sm mt-1">Alle Rechnungen mit aktuellem Status</p>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
</div>
{% endblock %}
@@ -0,0 +1,747 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 18mm;
}
body {
font-family: Arial, sans-serif;
font-size: 11px;
color: #334155;
}
h1,h2,h3,p {
margin:0;
}
table {
width:100%;
border-collapse:collapse;
}
.header {
display:flex;
justify-content:space-between;
}
.logo {
height:128px;
margin-bottom:15px;
}
.company-name {
font-size:24px;
font-weight:bold;
}
.invoice-title {
font-size:32px;
font-weight:bold;
}
.info-table td {
padding:4px 0;
}
.label {
color:#64748b;
padding-right:30px;
}
.divider {
border-top:1px solid #e2e8f0;
margin:30px 0;
}
.receiver-title {
font-size:10px;
text-transform:uppercase;
color:#64748b;
letter-spacing:1px;
}
.receiver-name {
font-size:16px;
font-weight:bold;
margin-top:5px;
}
.invoice-heading {
font-size:22px;
font-weight:bold;
margin-bottom:10px;
}
.items-wrapper {
border:1px solid #e2e8f0;
border-radius:10px;
overflow:hidden;
margin-top:20px;
}
.items {
width:100%;
border-collapse:collapse;
}
.items thead th:first-child {
border-top-left-radius:10px;
}
.items thead th:last-child {
border-top-right-radius:10px;
}
.items th {
background:{{ company.color }};
color:white;
padding:10px;
text-align:left;
}
.items td {
padding:10px;
border-bottom:1px solid #e2e8f0;
}
.items tbody tr:last-child td {
border-bottom:none;
}
.right {
text-align:right;
}
.total-box {
margin-top:30px;
margin-left:auto;
width:250px;
}
.total-row {
display:flex;
justify-content:space-between;
padding:6px 0;
}
.total-final {
border-top:1px solid #cbd5e1;
margin-top:10px;
padding-top:10px;
font-size:16px;
font-weight:bold;
}
.note {
margin-top:30px;
padding:12px;
border:1px solid #e2e8f0;
border-radius: 10px;
}
.small-business {
margin-top:25px;
color:#64748b;
}
@page {
size: A4;
margin: 18mm 18mm 35mm 18mm;
}
.footer {
position: fixed;
bottom: -25mm;
left: 0;
right: 0;
padding-top:15px;
border-top:1px solid #e2e8f0;
display:flex;
justify-content:space-between;
font-size:10px;
color:#64748b;
}
.footer-title {
font-weight:bold;
color:#334155;
margin-bottom:5px;
}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="logo">
{% else %}
<h1 class="company-name" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p>
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<p style="margin-top:15px">
{{ company.name }}<br>
{{ company.street }} · {{ company.zip }} {{ company.city }}
</p>
</div>
<div style="text-align:right">
<div class="invoice-title">
RECHNUNG
</div>
<table class="info-table">
<tr>
<td class="label">
Rechnungsnummer
</td>
<td>
{{ invoice.invoice_number }}
</td>
</tr>
<tr>
<td class="label">
Rechnungsdatum
</td>
<td>
{{ invoice.issue_date|date:"d.m.Y" }}
</td>
</tr>
{% if invoice.due_date %}
<tr>
<td class="label">
Fälligkeitsdatum
</td>
<td>
{{ invoice.due_date|date:"d.m.Y" }}
</td>
</tr>
{% endif %}
</table>
</div>
</div>
<div class="divider"></div>
<!-- EMPFÄNGER -->
<div>
<div class="receiver-title">
Rechnungsempfänger
</div>
<div class="receiver-name">
{% if invoice.customer.type == "company" %}
{{ invoice.customer.company_name }}
{% else %}
{{ invoice.contact_name }}
{% endif %}
</div>
{% if invoice.customer.type == "company" %}
<p>
z.Hd. {{ invoice.contact_name }}
</p>
{% endif %}
<p style="margin-top:10px">
{{ invoice.location_street }}<br>
{{ invoice.location_zip }}
{{ invoice.location_city }}
</p>
</div>
<!-- TITEL -->
<div style="margin-top:40px">
<div class="invoice-heading"
style="color:{{ company.color }}">
Rechnung
</div>
{% if company.invoice_header_text %}
<p>
{{ company.invoice_header_text|linebreaksbr }}
</p>
{% endif %}
</div>
<!-- POSITIONEN -->
<div class="items-wrapper">
<table class="items">
<thead>
<tr>
<th>
Nr.
</th>
<th>
Beschreibung
</th>
{% if show_service_date %}
<th class="left">Leistungsdatum</th>
{% endif %}
<th class="left">
Menge
</th>
<th class="left">
Einheit
</th>
<th class="left">
Preis
</th>
{% if not company.is_small_business %}
<th class="left">
Steuer
</th>
{% endif %}
<th class="left">
Gesamt
</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>
{{ forloop.counter }}
</td>
<td>
{{ item.description }}
</td>
{% if show_service_date %}
<td class="left">
{% if item.service_period_from %}
{{ item.service_period_from|date:"d.m.Y" }}
{% endif %}
{% if item.service_period_to %}
- {{ item.service_period_to|date:"d.m.Y" }}
{% endif %}
</td>
{% endif %}
<td class="left">
{{ item.quantity }}
</td>
<td class="left">
{{ item.unit }}
</td>
<td class="left">
{{ item.unit_price_net|floatformat:2 }} €
</td>
{% if not company.is_small_business %}
<td class="left">
{{ item.unit_tax_rate|floatformat:0 }} %
</td>
{% endif %}
<td class="left">
{{ item.calculated_total_net|floatformat:2 }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- SUMMEN -->
<div class="total-box">
<div class="total-row">
<span>
Netto
</span>
<span>
{{ total_net|floatformat:2 }} €
</span>
</div>
{% if not company.is_small_business %}
{% for tax_rate,tax_amount in tax_summary.items %}
<div class="total-row">
<span>
Umsatzsteuer {{ tax_rate|floatformat:0 }} %
</span>
<span>
{{ tax_amount|floatformat:2 }} €
</span>
</div>
{% endfor %}
{% endif %}
<div class="total-row total-final">
<span>
Gesamt
</span>
<span>
{% if company.is_small_business %}
{{ total_net|floatformat:2 }}
{% else %}
{{ total_gross|floatformat:2 }}
{% endif %}
€
</span>
</div>
</div>
{% if invoice.note %}
<div class="note">
<div class="receiver-title">
Rechnungshinweis
</div>
<br>
{{ invoice.note|linebreaksbr }}
</div>
{% endif %}
{% if company.is_small_business %}
<div class="note">
<div class="receiver-title">
Kleinunternehmerhinweis
</div>
<br>
{{ company.small_business_text }}
</div>
{% endif %}
<!-- FOOTER -->
<div class="footer">
<div>
<div class="footer-title">
Kontakt
</div>
{% if company.phone %}
{{ company.phone }}<br>
{% endif %}
{% if company.email %}
{{ company.email }}<br>
{% endif %}
{% if company.website %}
{{ company.website }}
{% endif %}
</div>
<div>
<div class="footer-title">
Steuern
</div>
{% if company.tax_number %}
Steuernummer: {{ company.tax_number }}<br>
{% endif %}
{% if company.vat_number %}
USt-IdNr.: {{ company.vat_number }}
{% endif %}
</div>
<div>
<div class="footer-title">
Bank
</div>
{% if company.bank_name %}
{{ company.bank_name }}<br>
{% endif %}
{% if company.iban %}
IBAN: {{ company.iban }}<br>
{% endif %}
{% if company.bic %}
BIC: {{ company.bic }}
{% endif %}
</div>
</div>
</body>
</html>
@@ -0,0 +1,756 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 18mm;
}
body {
font-family: Arial, sans-serif;
font-size: 11px;
color: #334155;
}
h1,h2,h3,p {
margin:0;
}
table {
width:100%;
border-collapse:collapse;
}
.header {
display:flex;
justify-content:space-between;
}
.logo {
height:128px;
margin-bottom:15px;
}
.company-name {
font-size:24px;
font-weight:bold;
}
.invoice-title {
font-size:32px;
font-weight:bold;
}
.info-table td {
padding:4px 0;
}
.label {
color:#64748b;
padding-right:30px;
}
.divider {
border-top:1px solid #e2e8f0;
margin:30px 0;
}
.receiver-title {
font-size:10px;
text-transform:uppercase;
color:#64748b;
letter-spacing:1px;
}
.receiver-name {
font-size:16px;
font-weight:bold;
margin-top:5px;
}
.invoice-heading {
font-size:22px;
font-weight:bold;
margin-bottom:10px;
}
.items-wrapper {
border:1px solid #e2e8f0;
border-radius:10px;
overflow:hidden;
margin-top:20px;
}
.items {
width:100%;
border-collapse:collapse;
}
.items thead th:first-child {
border-top-left-radius:10px;
}
.items thead th:last-child {
border-top-right-radius:10px;
}
.items th {
background:{{ company.color }};
color:white;
padding:10px;
text-align:left;
}
.items td {
padding:10px;
border-bottom:1px solid #e2e8f0;
}
.items tbody tr:last-child td {
border-bottom:none;
}
.right {
text-align:right;
}
.total-box {
margin-top:30px;
margin-left:auto;
width:250px;
}
.total-row {
display:flex;
justify-content:space-between;
padding:6px 0;
}
.total-final {
border-top:1px solid #cbd5e1;
margin-top:10px;
padding-top:10px;
font-size:16px;
font-weight:bold;
}
.note {
margin-top:30px;
padding:12px;
border:1px solid #e2e8f0;
border-radius: 10px;
}
.small-business {
margin-top:25px;
color:#64748b;
}
@page {
size: A4;
margin: 18mm 18mm 35mm 18mm;
}
.footer {
position: fixed;
bottom: -25mm;
left: 0;
right: 0;
padding-top:15px;
border-top:1px solid #e2e8f0;
display:flex;
justify-content:space-between;
font-size:10px;
color:#64748b;
}
.footer-title {
font-weight:bold;
color:#334155;
margin-bottom:5px;
}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="logo">
{% else %}
<h1 class="company-name" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p>
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<p style="margin-top:15px">
{{ company.name }}<br>
{{ company.street }} · {{ company.zip }} {{ company.city }}
</p>
</div>
<div style="text-align:right">
<div class="invoice-title">
STORNO
</div>
<table class="info-table">
<tr>
<td class="label">
Stornonummer
</td>
<td>
{{ cancel_number }}
</td>
</tr>
<tr>
<td class="label">
Stornodatum
</td>
<td>
{{ cancel_date|date:"d.m.Y" }}
</td>
</tr>
<tr>
<td class="label">
Ursprüngliche Rechnung
</td>
<td>
{{ invoice.invoice_number }}
</td>
</tr>
<tr>
<td class="label">
Rechnungsdatum
</td>
<td>
{{ invoice.issue_date|date:"d.m.Y" }}
</td>
</tr>
</table>
</div>
</div>
<div class="divider"></div>
<!-- EMPFÄNGER -->
<div>
<div class="receiver-title">
Rechnungsempfänger
</div>
<div class="receiver-name">
{% if invoice.customer.type == "company" %}
{{ invoice.customer.company_name }}
{% else %}
{{ invoice.contact_name }}
{% endif %}
</div>
{% if invoice.customer.type == "company" %}
<p>
z.Hd. {{ invoice.contact_name }}
</p>
{% endif %}
<p style="margin-top:10px">
{{ invoice.location_street }}<br>
{{ invoice.location_zip }}
{{ invoice.location_city }}
</p>
</div>
<!-- TITEL -->
<div style="margin-top:40px">
<div class="invoice-heading"
style="color:{{ company.color }}">
Stornorechnung
</div>
<p style="margin-top:10px;">
Diese Stornorechnung storniert die Rechnung
<strong>{{ invoice.invoice_number }}</strong>
vom {{ invoice.issue_date|date:"d.m.Y" }} vollständig.
</p>
<p style="margin-top:5px;">
Stornodatum: {{ cancel_date|date:"d.m.Y" }}
</p>
</div>
<!-- POSITIONEN -->
<div class="items-wrapper">
<table class="items">
<thead>
<tr>
<th>
Nr.
</th>
<th>
Beschreibung
</th>
{% if show_service_date %}
<th class="left">Leistungsdatum</th>
{% endif %}
<th class="left">
Menge
</th>
<th class="left">
Einheit
</th>
<th class="left">
Preis
</th>
{% if not company.is_small_business %}
<th class="left">
Steuer
</th>
{% endif %}
<th class="left">
Gesamt
</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>
{{ forloop.counter }}
</td>
<td>
{{ item.description }}
</td>
{% if show_service_date %}
<td class="left">
{% if item.service_period_from %}
{{ item.service_period_from|date:"d.m.Y" }}
{% endif %}
{% if item.service_period_to %}
- {{ item.service_period_to|date:"d.m.Y" }}
{% endif %}
</td>
{% endif %}
<td class="left">
{{ item.quantity }}
</td>
<td class="left">
{{ item.unit }}
</td>
<td class="left">
-{{ item.unit_price_net|floatformat:2 }} €
</td>
{% if not company.is_small_business %}
<td class="left">
{{ item.unit_tax_rate|floatformat:0 }} %
</td>
{% endif %}
<td class="left">
-{{ item.calculated_total_net|floatformat:2 }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- SUMMEN -->
<div class="total-box">
<div class="total-row">
<span>
Netto
</span>
<span>
-{{ total_net|floatformat:2 }} €
</span>
</div>
{% if not company.is_small_business %}
{% for tax_rate,tax_amount in tax_summary.items %}
<div class="total-row">
<span>
Umsatzsteuer {{ tax_rate|floatformat:0 }} %
</span>
<span>
-{{ tax_amount|floatformat:2 }} €
</span>
</div>
{% endfor %}
{% endif %}
<div class="total-row total-final">
<span>
Gesamt
</span>
<span>
{% if company.is_small_business %}
-{{ total_net|floatformat:2 }}
{% else %}
-{{ total_gross|floatformat:2 }}
{% endif %}
€
</span>
</div>
</div>
<div class="note">
<div class="receiver-title">
Stornohinweis
</div>
<br>
Die Rechnung {{ invoice.invoice_number }}
wird durch diese Stornorechnung vollständig aufgehoben.
{% if original_status == "paid" %}
<br><br>
Die ursprüngliche Rechnung wurde bereits bezahlt.
Es entsteht ein Guthaben in Höhe von
{% if company.is_small_business %}
{{ total_net|floatformat:2 }}
{% else %}
{{ total_gross|floatformat:2 }}
{% endif %}
€.
Die Rückerstattung erfolgt separat.
{% endif %}
</div>
{% if company.is_small_business %}
<div class="note">
<div class="receiver-title">
Kleinunternehmerhinweis
</div>
<br>
{{ company.small_business_text }}
</div>
{% endif %}
<!-- FOOTER -->
<div class="footer">
<div>
<div class="footer-title">
Kontakt
</div>
{% if company.phone %}
{{ company.phone }}<br>
{% endif %}
{% if company.email %}
{{ company.email }}<br>
{% endif %}
{% if company.website %}
{{ company.website }}
{% endif %}
</div>
<div>
<div class="footer-title">
Steuern
</div>
{% if company.tax_number %}
Steuernummer: {{ company.tax_number }}<br>
{% endif %}
{% if company.vat_number %}
USt-IdNr.: {{ company.vat_number }}
{% endif %}
</div>
<div>
<div class="footer-title">
Bank
</div>
{% if company.bank_name %}
{{ company.bank_name }}<br>
{% endif %}
{% if company.iban %}
IBAN: {{ company.iban }}<br>
{% endif %}
{% if company.bic %}
BIC: {{ company.bic }}
{% endif %}
</div>
</div>
</body>
</html>
@@ -0,0 +1,465 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Rechnungen{% endblock %}
{% block header_title %}Rechnung {{ invoice.invoice_number }}{% endblock %}
{% block header_description %}Vorschau{% endblock %}
{% block content %}
<div class="max-w-5xl w-5xl mx-auto bg-white rounded-2xl shadow p-10 text-slate-900">
<!-- HEADER -->
<div class="flex justify-between items-start">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="h-32 mb-4">
{% else %}
<h1 class="text-2xl font-bold" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p class="text-sm text-slate-600 mt-1">
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<div class="mt-4 text-sm">
<p>{{ company.name }}</p>
<p>
{{ company.street }}
·
{{ company.zip }}
{{ company.city }}
</p>
</div>
</div>
<div class="text-right">
<h2 class="text-3xl font-bold ">
RECHNUNG
</h2>
<table class="mt-4 text-sm ml-auto">
<tr>
<td class="text-slate-600 pr-8 py-1">
Rechnungsnummer
</td>
<td class="font-medium">
{{ invoice.invoice_number }}
</td>
</tr>
<tr>
<td class="text-slate-600 pr-8 py-1">
Rechnungsdatum
</td>
<td class="font-medium">
{{ invoice.issue_date|date:"d.m.Y" }}
</td>
</tr>
{% if invoice.due_date %}
<tr>
<td class="text-slate-600 pr-8 py-1">
Fälligkeitsdatum
</td>
<td class="font-medium">
{{ invoice.due_date|date:"d.m.Y" }}
</td>
</tr>
{% endif %}
</table>
</div>
</div>
<div class="my-8 border-t"></div>
<!-- EMPFÄNGER -->
<div class="mb-10">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Rechnungsempfänger
</p>
<p class="font-semibold text-lg ">
{% if invoice.customer.type == "company" %}
{{ invoice.customer.company_name }}
{% else %}
{{ invoice.contact_name }}
{% endif %}
</p>
{% if invoice.customer.type == "company" %}
<p class="text-sm">
z.Hd. {{ invoice.contact_name }}
</p>
{% endif %}
<p class="mt-2 text-sm">
{{ invoice.location_street }}<br>
{{ invoice.location_zip }}
{{ invoice.location_city }}
</p>
</div>
<!-- TITEL -->
<div class="mb-8">
<h2 class="text-2xl font-bold" style="color: {{ company.color }}">
Rechnung
</h2>
{% if company.invoice_header_text %}
<div class="mt-3 text-sm text-slate-600">
{{ company.invoice_header_text|linebreaks }}
</div>
{% endif %}
</div>
<!-- POSITIONEN -->
<div class="border rounded-xl overflow-hidden">
<table class="w-full text-sm">
<thead>
<tr style="background-color: {{ company.color }}" class="text-white">
<th class="p-3 text-left">
Nr.
</th>
<th class="p-3 text-left">
Beschreibung
</th>
{% if show_service_date %}
<th class="p-3 text-left">Leistungsdatum</th>
{% endif %}
<th class="p-3 text-right">
Menge
</th>
<th class="p-3 text-right">
Einheit
</th>
<th class="p-3 text-right">
Preis
</th>
{% if not company.is_small_business %}
<th class="p-3 text-right">
Steuer
</th>
{% endif %}
<th class="p-3 text-right">
Gesamt
</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr class="border-t">
<td class="p-3 text-left">
{{ forloop.counter }}
</td>
<td class="p-3 text-left">
{{ item.description }}
</td>
{% if show_service_date %}
<td class="p-3 text-left">
{% if item.service_period_from %}
{{ item.service_period_from|date:"d.m.Y" }}
{% endif %}
{% if item.service_period_to %}
- {{ item.service_period_to|date:"d.m.Y" }}
{% endif %}
</td>
{% endif %}
<td class="p-3 text-right">
{{ item.quantity }}
</td>
<td class="p-3 text-right">
{{ item.unit }}
</td>
<td class="p-3 text-right">
{{ item.unit_price_net|floatformat:2 }} €
</td>
{% if not company.is_small_business %}
<td class="p-3 text-right">
{{ item.unit_tax_rate|floatformat:0 }} %
</td>
{% endif %}
<td class="p-3 text-right font-medium">
{{ item.calculated_total_net|floatformat:2 }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- SUMMEN -->
<div class="flex justify-end mt-8">
<div class="w-80 text-sm">
<div class="flex justify-between py-2">
<span class="text-slate-600">
Netto
</span>
<span>
{{ total_net|floatformat:2 }} €
</span>
</div>
{% if not company.is_small_business %}
{% for tax_rate, tax_amount in tax_summary.items %}
<div class="flex justify-between py-2">
<span class="text-slate-600">
Umsatzsteuer {{ tax_rate|floatformat:0 }} %
</span>
<span>
{{ tax_amount|floatformat:2 }} €
</span>
</div>
{% endfor %}
{% endif %}
<div class="border-t mt-3 pt-4 flex justify-between text-lg font-bold ">
<span>
Gesamt
</span>
<span>
{% if company.is_small_business %}
{{ total_net|floatformat:2 }} €
{% else %}
{{ total_gross|floatformat:2 }} €
{% endif %}
</span>
</div>
</div>
</div>
<!-- NOTIZ -->
{% if invoice.note %}
<div class="mt-10 border rounded-xl p-4">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Rechnungshinweis
</p>
<div class="text-sm">
{{ invoice.note|linebreaks }}
</div>
</div>
{% endif %}
<!-- KLEINUNTERNEHMER -->
{% if company.is_small_business %}
<div class="mt-10 border rounded-xl p-4">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Kleinunternehmerhinweis
</p>
<div class="text-sm">
{{ company.small_business_text }}
</div>
</div>
{% endif %}
<!-- FOOTER -->
<div class="mt-14 pt-6 border-t grid grid-cols-3 gap-6 text-xs text-slate-600">
<!-- KONTAKT -->
<div>
<p class="font-semibold mb-2">
Kontakt
</p>
{% if company.phone %}
<p>{{ company.phone }}</p>
{% endif %}
{% if company.email %}
<p>{{ company.email }}</p>
{% endif %}
{% if company.website %}
<p>{{ company.website }}</p>
{% endif %}
</div>
<!-- STEUERN -->
<div>
<p class="font-semibold mb-2">
Steuern
</p>
{% if company.tax_number %}
<p>
Steuernummer:
{{ company.tax_number }}
</p>
{% endif %}
{% if company.vat_number %}
<p>
USt-IdNr.:
{{ company.vat_number }}
</p>
{% endif %}
</div>
<!-- BANK -->
<div>
<p class="font-semibold mb-2">
Bankverbindung
</p>
{% if company.bank_name %}
<p>
{{ company.bank_name }}
</p>
{% endif %}
{% if company.iban %}
<p>
IBAN:
{{ company.iban }}
</p>
{% endif %}
{% if company.bic %}
<p>
BIC:
{{ company.bic }}
</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Posten
{% endblock %}
{% block header_title %}
Posten hinzufügen
{% endblock %}
{% block header_description %}
Neue Ware oder Leistung hinzufügen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
<div class="grid grid-cols-1 gap-4">
{{ form.as_p }}
</div>
<button
type="submit"
class="btn mt-4 btn-save">
Speichern
</button>
</form>
</div>
{% endblock %}
@@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Posten
{% endblock %}
{% block header_title %}
Posten
{% endblock %}
{% block header_description %}
Leistungen und Artikel verwalten
{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">
Leistungen und Artikel anlegen
</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
<a href="{% url 'invoices:items_add' %}"
class="btn btn-add">
+ Posten
</a>
</div>
</div>
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mt-8">
<div class="flex flex-wrap justify-between items-center mb-6">
<div>
<h2 class="text-xl font-bold">
Artikel und Leistungen
</h2>
<p class="text-slate-400 text-sm mt-1">
Übersicht über alle Artikel und Leistungen
</p>
</div>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
</div>
{% endblock %}
@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Angebote{% endblock %}
{% block header_title %}
Angebot erstellen
{% endblock %}
{% block header_description %}
Angebot für einen Kunden erstellen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const customer = document.getElementById("id_customer");
const contact = document.getElementById("id_contact");
const location = document.getElementById("id_location");
function fill(select, data) {
select.innerHTML = "<option value=''>---------</option>";
data.forEach(item => {
select.innerHTML += `<option value="${item.id}">${item.text}</option>`;
});
}
if (customer) {
customer.addEventListener("change", function () {
const id = this.value;
if (!id) {
contact.innerHTML = "";
location.innerHTML = "";
return;
}
fetch(`/invoices/ajax/customer/${id}/contacts/`)
.then(r => r.json())
.then(data => fill(contact, data));
fetch(`/invoices/ajax/customer/${id}/locations/`)
.then(r => r.json())
.then(data => fill(location, data));
});
}
});
</script>
{% endblock %}
@@ -0,0 +1,156 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Angebote{% endblock %}
{% block header_title %}
Angebot {{ offer.offer_number }}
{% endblock %}
{% block header_description %}
Details & Positionen
{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">
Positionen zum Angebot hinzufügen
</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
{% if offer.status == "draft" %}
<a href="{% url 'invoices:offers_item_add' offer.id %}" class="btn btn-add">+ Position</a>
<a href="{% url 'invoices:offers_preview' offer.id %}" class="btn btn-action">Vorschau</a>
<a href="{% url 'invoices:offers_download' offer.id %}" onclick="startDownloadAndRedirect()" class="btn btn-action">Download</a>
<a href="{% url 'invoices:offers_delete' offer.id %}" class="btn btn-delete">Löschen</a>
{% endif %}
{% if offer.status == "sent" %}
<a href="{% url 'invoices:offers_download' offer.id %}" class="btn btn-action">Download</a>
<a href="{% url 'invoices:offers_accept' offer.id %}" class="btn btn-action">Akzeptiert</a>
<a href="{% url 'invoices:offers_reject' offer.id %}" class="btn btn-action">Abgelehnt</a>
<a href="{% url 'invoices:offers_expire' offer.id %}" class="btn btn-action">Ausgelaufen</a>
{% endif %}
{% if offer.status == "accepted" or offer.status == "rejected" or offer.status == "expired" %}
<a href="{% url 'invoices:offers_download' offer.id %}" class="btn btn-action">Download</a>
{% endif %}
</div>
</div>
</div>
</div>
<!-- HEADER INFO -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mb-8">
<h2 class="text-xl font-bold mb-6">Allgemein</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Kunde -->
<div>
<div class="text-slate-400 text-sm">Kunde</div>
<div class="font-semibold">{{ offer.customer.company_name }}</div>
</div>
<!-- Status -->
<div>
<div class="text-slate-400 text-sm">Status</div>
<div class="font-semibold">{{ offer.get_status_display }}</div>
</div>
<!-- Datum -->
<div>
<div class="text-slate-400 text-sm">Angebotsdatum</div>
<div class="font-semibold">{{ offer.issue_date|date:"d.m.Y" }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Gültig bis</div>
<div class="font-semibold">{{ offer.due_date|date:"d.m.Y" }}</div>
</div>
</div>
</div>
<!-- SNAPSHOT: KONTAKT & ADRESSE -->
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Angebotsadresse</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
<!-- Kontakt -->
<div>
<div class="text-slate-400 text-sm">Ansprechpartner</div>
<div class="font-semibold">{{ offer.contact_name|default:"-" }}</div>
{% if offer.contact_email %}
<div class="text-slate-400 text-sm">{{ offer.contact_email }}</div>
{% endif %}
{% if offer.contact_phone %}
<div class="text-slate-400 text-sm">{{ offer.contact_phone }}</div>
{% endif %}
</div>
<!-- Adresse -->
<div>
<div class="text-slate-400 text-sm">Standort</div>
<div class="font-semibold">
{{ offer.location_name|default:"-" }}
</div>
<div class="text-slate-400 text-sm">
{{ offer.location_street }}
</div>
<div class="text-slate-400 text-sm">
{{ offer.location_zip }} {{ offer.location_city }}
</div>
<div class="text-slate-400 text-sm">
{{ offer.location_country }}
</div>
</div>
</div>
</div>
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Angebotshinweis</h2>
<div class="grid grid-cols-1 gap-6">
<div>
<div class="font-semibold">{{ offer.note|default:"-" }}</div>
</div>
</div>
</div>
<!-- POSITIONEN -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Positionen</h2>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
<div class="mt-4 text-right font-bold">
Gesamt: {{ total_net|floatformat:2 }} €
</div>
</div>
<script>
function startDownloadAndRedirect() {
setTimeout(function() {
window.location.href = "{% url 'invoices:offers_detail' offer.id %}";
}, 1000);
}
</script>
{% endblock %}
@@ -0,0 +1,197 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen - Rechnungen
{% endblock %}
{% block header_title %}
Rechnungsposition hinzufügen
{% endblock %}
{% block header_description %}
Neue Rechnungsposition anlegen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const source = document.getElementById("id_source_type");
const fields = {
item: document.getElementById("id_item"),
time: document.getElementById("id_time_entry"),
expense: document.getElementById("id_expense"),
};
function toggleFields() {
const value = source.value;
Object.keys(fields).forEach(key => {
const field = fields[key];
if (!field) {
return;
}
const row = field.closest("p");
if (key === value) {
row.style.display = "";
} else {
row.style.display = "none";
field.value = "";
}
});
}
source.addEventListener(
"change",
toggleFields
);
// Initial ausführen
toggleFields();
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const source = document.getElementById("id_source_type");
const selects = {
item: document.getElementById("id_item"),
time: document.getElementById("id_time_entry"),
expense: document.getElementById("id_expense"),
};
const description = document.getElementById(
"id_description"
);
const quantity = document.getElementById(
"id_quantity"
);
const unit = document.getElementById(
"id_unit"
);
const price = document.getElementById(
"id_unit_price_net"
);
const tax = document.getElementById(
"id_unit_tax_rate"
);
function loadData(type, id){
if(!id){
return;
}
fetch(
`/invoices/ajax/invoice-item-data/?source=${type}&id=${id}`
)
.then(response => response.json())
.then(data => {
description.value = data.description;
quantity.value = data.quantity;
unit.value = data.unit;
price.value = data.unit_price_net;
tax.value = data.unit_tax_rate;
});
}
Object.keys(selects).forEach(type => {
if(selects[type]){
selects[type].addEventListener(
"change",
function(){
loadData(
type,
this.value
);
}
);
}
});
});
</script>
{% endblock %}
@@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Angebote{% endblock %}
{% block header_title %}Angebote{% endblock %}
{% block header_description %}Angebote erstellen und verwalten{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">Neues Angebot erstellen</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
<a href="{% url 'invoices:invoices_offers_add' %}" class="btn btn-add">+ Angebot</a>
</div>
</div>
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="mb-6">
<h2 class="text-xl font-bold">Angebote</h2>
<p class="text-slate-400 text-sm mt-1">Alle Angebote mit aktuellem Status</p>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
</div>
{% endblock %}
+732
View File
@@ -0,0 +1,732 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 18mm;
}
body {
font-family: Arial, sans-serif;
font-size: 11px;
color: #334155;
}
h1,h2,h3,p {
margin:0;
}
table {
width:100%;
border-collapse:collapse;
}
.header {
display:flex;
justify-content:space-between;
}
.logo {
height:128px;
margin-bottom:15px;
}
.company-name {
font-size:24px;
font-weight:bold;
}
.invoice-title {
font-size:32px;
font-weight:bold;
}
.info-table td {
padding:4px 0;
}
.label {
color:#64748b;
padding-right:30px;
}
.divider {
border-top:1px solid #e2e8f0;
margin:30px 0;
}
.receiver-title {
font-size:10px;
text-transform:uppercase;
color:#64748b;
letter-spacing:1px;
}
.receiver-name {
font-size:16px;
font-weight:bold;
margin-top:5px;
}
.invoice-heading {
font-size:22px;
font-weight:bold;
margin-bottom:10px;
}
.items-wrapper {
border:1px solid #e2e8f0;
border-radius:10px;
overflow:hidden;
margin-top:20px;
}
.items {
width:100%;
border-collapse:collapse;
}
.items thead th:first-child {
border-top-left-radius:10px;
}
.items thead th:last-child {
border-top-right-radius:10px;
}
.items th {
background:{{ company.color }};
color:white;
padding:10px;
text-align:left;
}
.items td {
padding:10px;
border-bottom:1px solid #e2e8f0;
}
.items tbody tr:last-child td {
border-bottom:none;
}
.right {
text-align:right;
}
.total-box {
margin-top:30px;
margin-left:auto;
width:250px;
}
.total-row {
display:flex;
justify-content:space-between;
padding:6px 0;
}
.total-final {
border-top:1px solid #cbd5e1;
margin-top:10px;
padding-top:10px;
font-size:16px;
font-weight:bold;
}
.note {
margin-top:30px;
padding:12px;
border:1px solid #e2e8f0;
border-radius: 10px;
}
.small-business {
margin-top:25px;
color:#64748b;
}
@page {
size: A4;
margin: 18mm 18mm 35mm 18mm;
}
.footer {
position: fixed;
bottom: -25mm;
left: 0;
right: 0;
padding-top:15px;
border-top:1px solid #e2e8f0;
display:flex;
justify-content:space-between;
font-size:10px;
color:#64748b;
}
.footer-title {
font-weight:bold;
color:#334155;
margin-bottom:5px;
}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="logo">
{% else %}
<h1 class="company-name" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p>
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<p style="margin-top:15px">
{{ company.name }}<br>
{{ company.street }} · {{ company.zip }} {{ company.city }}
</p>
</div>
<div style="text-align:right">
<div class="invoice-title">
ANGEBOT
</div>
<table class="info-table">
<tr>
<td class="label">
Angebotsnummer
</td>
<td>
{{ offer.offer_number }}
</td>
</tr>
<tr>
<td class="label">
Angebotsdatum
</td>
<td>
{{ offer.issue_date|date:"d.m.Y" }}
</td>
</tr>
{% if offer.due_date %}
<tr>
<td class="label">
Gültig bis
</td>
<td>
{{ offer.due_date|date:"d.m.Y" }}
</td>
</tr>
{% endif %}
</table>
</div>
</div>
<div class="divider"></div>
<!-- EMPFÄNGER -->
<div>
<div class="receiver-title">
Angebotsempfänger
</div>
<div class="receiver-name">
{% if offer.customer.type == "company" %}
{{ offer.customer.company_name }}
{% else %}
{{ offer.contact_name }}
{% endif %}
</div>
{% if offer.customer.type == "company" %}
<p>
z.Hd. {{ offer.contact_name }}
</p>
{% endif %}
<p style="margin-top:10px">
{{ offer.location_street }}<br>
{{ offer.location_zip }}
{{ offer.location_city }}
</p>
</div>
<!-- TITEL -->
<div style="margin-top:40px">
<div class="invoice-heading"
style="color:{{ company.color }}">
Angebot
</div>
{% if company.offer_header_text %}
<p>
{{ company.offer_header_text|linebreaksbr }}
</p>
{% endif %}
</div>
<!-- POSITIONEN -->
<div class="items-wrapper">
<table class="items">
<thead>
<tr>
<th>
Nr.
</th>
<th>
Beschreibung
</th>
<th class="left">
Menge
</th>
<th class="left">
Einheit
</th>
<th class="left">
Preis
</th>
{% if not company.is_small_business %}
<th class="left">
Steuer
</th>
{% endif %}
<th class="left">
Gesamt
</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>
{{ forloop.counter }}
</td>
<td>
{{ item.description }}
</td>
<td class="left">
{{ item.quantity }}
</td>
<td class="left">
{{ item.unit }}
</td>
<td class="left">
{{ item.unit_price_net|floatformat:2 }} €
</td>
{% if not company.is_small_business %}
<td class="left">
{{ item.unit_tax_rate|floatformat:0 }} %
</td>
{% endif %}
<td class="left">
{{ item.calculated_total_net|floatformat:2 }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- SUMMEN -->
<div class="total-box">
<div class="total-row">
<span>
Netto
</span>
<span>
{{ total_net|floatformat:2 }} €
</span>
</div>
{% if not company.is_small_business %}
{% for tax_rate,tax_amount in tax_summary.items %}
<div class="total-row">
<span>
Umsatzsteuer {{ tax_rate|floatformat:0 }} %
</span>
<span>
{{ tax_amount|floatformat:2 }} €
</span>
</div>
{% endfor %}
{% endif %}
<div class="total-row total-final">
<span>
Gesamt
</span>
<span>
{% if company.is_small_business %}
{{ total_net|floatformat:2 }}
{% else %}
{{ total_gross|floatformat:2 }}
{% endif %}
€
</span>
</div>
</div>
{% if offer.note %}
<div class="note">
<div class="receiver-title">
Angebotshinweis
</div>
<br>
{{ offer.note|linebreaksbr }}
</div>
{% endif %}
{% if company.is_small_business %}
<div class="note">
<div class="receiver-title">
Kleinunternehmerhinweis
</div>
<br>
{{ company.small_business_text }}
</div>
{% endif %}
<!-- FOOTER -->
<div class="footer">
<div>
<div class="footer-title">
Kontakt
</div>
{% if company.phone %}
{{ company.phone }}<br>
{% endif %}
{% if company.email %}
{{ company.email }}<br>
{% endif %}
{% if company.website %}
{{ company.website }}
{% endif %}
</div>
<div>
<div class="footer-title">
Steuern
</div>
{% if company.tax_number %}
Steuernummer: {{ company.tax_number }}<br>
{% endif %}
{% if company.vat_number %}
USt-IdNr.: {{ company.vat_number }}
{% endif %}
</div>
<div>
<div class="footer-title">
Bank
</div>
{% if company.bank_name %}
{{ company.bank_name }}<br>
{% endif %}
{% if company.iban %}
IBAN: {{ company.iban }}<br>
{% endif %}
{% if company.bic %}
BIC: {{ company.bic }}
{% endif %}
</div>
</div>
</body>
</html>
@@ -0,0 +1,450 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Angebote{% endblock %}
{% block header_title %}Angebot {{ offer.offer_number }}{% endblock %}
{% block header_description %}Vorschau{% endblock %}
{% block content %}
<div class="w-5xl max-w-5xl mx-auto bg-white rounded-2xl shadow p-10 text-slate-900">
<!-- HEADER -->
<div class="flex justify-between items-start">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="h-32 mb-4">
{% else %}
<h1 class="text-2xl font-bold" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p class="text-sm text-slate-600 mt-1">
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<div class="mt-4 text-sm">
<p>{{ company.name }}</p>
<p>
{{ company.street }}
·
{{ company.zip }}
{{ company.city }}
</p>
</div>
</div>
<div class="text-right">
<h2 class="text-3xl font-bold ">
ANGEBOT
</h2>
<table class="mt-4 text-sm ml-auto">
<tr>
<td class="text-slate-600 pr-8 py-1">
Angebotsnummer
</td>
<td class="font-medium">
{{ offer.offer_number }}
</td>
</tr>
<tr>
<td class="text-slate-600 pr-8 py-1">
Angebotsdatum
</td>
<td class="font-medium">
{{ offer.issue_date|date:"d.m.Y" }}
</td>
</tr>
{% if offer.due_date %}
<tr>
<td class="text-slate-600 pr-8 py-1">
Gültig bis
</td>
<td class="font-medium">
{{ offer.due_date|date:"d.m.Y" }}
</td>
</tr>
{% endif %}
</table>
</div>
</div>
<div class="my-8 border-t"></div>
<!-- EMPFÄNGER -->
<div class="mb-10">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Angebotsempfänger
</p>
<p class="font-semibold text-lg ">
{% if offer.customer.type == "company" %}
{{ offer.customer.company_name }}
{% else %}
{{ offer.contact_name }}
{% endif %}
</p>
{% if offer.customer.type == "company" %}
<p class="text-sm">
z.Hd. {{ offer.contact_name }}
</p>
{% endif %}
<p class="mt-2 text-sm">
{{ offer.location_street }}<br>
{{ offer.location_zip }}
{{ offer.location_city }}
</p>
</div>
<!-- TITEL -->
<div class="mb-8">
<h2 class="text-2xl font-bold" style="color: {{ company.color }}">
Angebot
</h2>
{% if company.offer_header_text %}
<div class="mt-3 text-sm text-slate-600">
{{ company.offer_header_text|linebreaks }}
</div>
{% endif %}
</div>
<!-- POSITIONEN -->
<div class="border rounded-xl overflow-hidden">
<table class="w-full text-sm">
<thead>
<tr style="background-color: {{ company.color }}" class="text-white">
<th class="p-3 text-left">
Nr.
</th>
<th class="p-3 text-left">
Beschreibung
</th>
<th class="p-3 text-right">
Menge
</th>
<th class="p-3 text-right">
Einheit
</th>
<th class="p-3 text-right">
Preis
</th>
{% if not company.is_small_business %}
<th class="p-3 text-right">
Steuer
</th>
{% endif %}
<th class="p-3 text-right">
Gesamt
</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr class="border-t">
<td class="p-3 text-left">
{{ forloop.counter }}
</td>
<td class="p-3 text-left">
{{ item.description }}
</td>
<td class="p-3 text-right">
{{ item.quantity }}
</td>
<td class="p-3 text-right">
{{ item.unit }}
</td>
<td class="p-3 text-right">
{{ item.unit_price_net|floatformat:2 }} €
</td>
{% if not company.is_small_business %}
<td class="p-3 text-right">
{{ item.unit_tax_rate|floatformat:0 }} %
</td>
{% endif %}
<td class="p-3 text-right font-medium">
{{ item.calculated_total_net|floatformat:2 }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- SUMMEN -->
<div class="flex justify-end mt-8">
<div class="w-80 text-sm">
<div class="flex justify-between py-2">
<span class="text-slate-600">
Netto
</span>
<span>
{{ total_net|floatformat:2 }} €
</span>
</div>
{% if not company.is_small_business %}
{% for tax_rate, tax_amount in tax_summary.items %}
<div class="flex justify-between py-2">
<span class="text-slate-600">
Umsatzsteuer {{ tax_rate|floatformat:0 }} %
</span>
<span>
{{ tax_amount|floatformat:2 }} €
</span>
</div>
{% endfor %}
{% endif %}
<div class="border-t mt-3 pt-4 flex justify-between text-lg font-bold ">
<span>
Gesamt
</span>
<span>
{% if company.is_small_business %}
{{ total_net|floatformat:2 }} €
{% else %}
{{ total_gross|floatformat:2 }} €
{% endif %}
</span>
</div>
</div>
</div>
<!-- NOTIZ -->
{% if offer.note %}
<div class="mt-10 border rounded-xl p-4">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Angebotshinweis
</p>
<div class="text-sm">
{{ offer.note|linebreaks }}
</div>
</div>
{% endif %}
<!-- KLEINUNTERNEHMER -->
{% if company.is_small_business %}
<div class="mt-10 border rounded-xl p-4">
<p class="text-xs uppercase tracking-wide text-slate-600 mb-2">
Kleinunternehmerhinweis
</p>
<div class="text-sm">
{{ company.small_business_text }}
</div>
</div>
{% endif %}
<!-- FOOTER -->
<div class="mt-14 pt-6 border-t grid grid-cols-3 gap-6 text-xs text-slate-600">
<!-- KONTAKT -->
<div>
<p class="font-semibold mb-2">
Kontakt
</p>
{% if company.phone %}
<p>{{ company.phone }}</p>
{% endif %}
{% if company.email %}
<p>{{ company.email }}</p>
{% endif %}
{% if company.website %}
<p>{{ company.website }}</p>
{% endif %}
</div>
<!-- STEUERN -->
<div>
<p class="font-semibold mb-2">
Steuern
</p>
{% if company.tax_number %}
<p>
Steuernummer:
{{ company.tax_number }}
</p>
{% endif %}
{% if company.vat_number %}
<p>
USt-IdNr.:
{{ company.vat_number }}
</p>
{% endif %}
</div>
<!-- BANK -->
<div>
<p class="font-semibold mb-2">
Bankverbindung
</p>
{% if company.bank_name %}
<p>
{{ company.bank_name }}
</p>
{% endif %}
{% if company.iban %}
<p>
IBAN:
{{ company.iban }}
</p>
{% endif %}
{% if company.bic %}
<p>
BIC:
{{ company.bic }}
</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
+61
View File
@@ -0,0 +1,61 @@
{% extends "base.html" %}
{% block title %}
Rechnungswesen
{% endblock %}
{% block header_title %}
Rechnungswesen
{% endblock %}
{% block header_description %}
Angebote, Rechnungen, Mahnungen verwalten
{% endblock %}
{% block content %}
<!-- MODULE GRID -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<a href="{% url 'invoices:invoices_offers_overview' %}" class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="text-2xl mb-2">📄</div>
<h2 class="text-xl font-bold">Angebote</h2>
<p class="text-slate-400 text-sm mt-1">Angebote erstellen und verwalten</p>
</a>
<a href="{% url 'invoices:invoices_invoices_overview' %}" class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="text-2xl mb-2">🧾</div>
<h2 class="text-xl font-bold">Rechnungen</h2>
<p class="text-slate-400 text-sm mt-1">Rechnungen erstellen und verwalten</p>
</a>
<a href="{% url 'invoices:invoices_reminders_overview' %}" class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="text-2xl mb-2">⚠️</div>
<h2 class="text-xl font-bold">Mahnungen</h2>
<p class="text-slate-400 text-sm mt-1">Offene Forderungen verfolgen</p>
</a>
<a href="{% url 'invoices:customers_overview' %}" class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="text-2xl mb-2">👥</div>
<h2 class="text-xl font-bold">Kunden</h2>
<p class="text-slate-400 text-sm mt-1">Kundenstammdaten verwalten</p>
</a>
<a href="{% url 'invoices:items_overview' %}" class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="text-2xl mb-2">📦</div>
<h2 class="text-xl font-bold">Posten</h2>
<p class="text-slate-400 text-sm mt-1">Leistungen und Artikel verwalten</p>
</a>
</div>
<!-- OPTIONAL INFO BOX -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mt-8">
<h2 class="text-xl font-bold">
Zusammenfassung
</h2>
<p class="text-slate-400 text-sm mt-1">
folgt
</p>
</div>
{% endblock %}
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Mahnungen{% endblock %}
{% block header_title %}
Mahnung erstellen
{% endblock %}
{% block header_description %}
Mahnung für einen Kunden erstellen
{% endblock %}
{% block content %}
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<form method="post" enctype="multipart/form-data" class="space-y-4">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-save">
Speichern
</button>
</form>
</div>
{% endblock %}
@@ -0,0 +1,184 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Mahnungen{% endblock %}
{% block header_title %}
Mahnung {{ reminder.reminder_number }}
{% endblock %}
{% block header_description %}
Details
{% endblock %}
{% block content %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">
Mahnung verwalten
</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
{% if reminder.status == "draft" %}
<a href="{% url 'invoices:reminders_download' reminder.id %}" onclick="startDownloadAndRedirect()" class="btn btn-action">Download</a>
<a href="{% url 'invoices:reminders_delete' reminder.id %}" class="btn btn-delete">Löschen</a>
{% endif %}
{% if reminder.status == "sent" %}
<a href="{% url 'invoices:reminders_download' reminder.id %}" class="btn btn-action">Download</a>
<a href="{% url 'invoices:reminders_cancel' reminder.id %}" class="btn btn-delete">Stornieren</a>
<a href="{% url 'invoices:reminders_paid' reminder.id %}" class="btn btn-action">Zahlung verbuchen</a>
<a href="{% url 'invoices:reminders_expire' reminder.id %}" class="btn btn-action">Überfällig</a>
{% endif %}
{% if reminder.status == "paid" or reminder.status == "canceled" or reminder.status == "expired" %}
<a href="{% url 'invoices:reminders_download' reminder.id %}" class="btn btn-action">Download</a>
{% endif %}
</div>
</div>
</div>
</div>
<!-- HEADER INFO -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mb-8">
<h2 class="text-xl font-bold mb-6">Allgemein</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Status -->
<div>
<div class="text-slate-400 text-sm">Status</div>
<div class="font-semibold">{{ reminder.get_status_display }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Level</div>
<div class="font-semibold">{{ reminder.level }}/3</div>
</div>
<!-- Datum -->
<div>
<div class="text-slate-400 text-sm">Mahnungsdatum</div>
<div class="font-semibold">{{ reminder.issue_date|date:"d.m.Y" }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Neues Zahlungsziel</div>
<div class="font-semibold">{{ reminder.due_date|date:"d.m.Y" }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Mahngebühr</div>
<div class="font-semibold">{{ reminder.fee }} €</div>
</div>
</div>
</div>
<!-- HEADER INFO -->
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors mb-8">
<h2 class="text-xl font-bold mb-6">Rechnungsinformationen</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div>
<div class="text-slate-400 text-sm">Rechnungsnummer</div>
<div class="font-semibold"><a class="company_link" href="{% url 'invoices:invoices_detail' reminder.invoice.id %}">{{ reminder.invoice.invoice_number }}</a></div>
</div>
<!-- Kunde -->
<div>
<div class="text-slate-400 text-sm">Kunde</div>
<div class="font-semibold">{{ reminder.invoice.customer.company_name }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Projekt</div>
<div class="font-semibold">{{ reminder.invoice.project.name|default:"-" }}</div>
</div>
<!-- Status -->
<div>
<div class="text-slate-400 text-sm">Rechnungsstatus</div>
<div class="font-semibold">{{ reminder.invoice.get_status_display }}</div>
</div>
<!-- Datum -->
<div>
<div class="text-slate-400 text-sm">Rechnungsdatum</div>
<div class="font-semibold">{{ reminder.invoice.issue_date|date:"d.m.Y" }}</div>
</div>
<div>
<div class="text-slate-400 text-sm">Ursprüngliches Zahlungsziel</div>
<div class="font-semibold">{{ reminder.invoice.due_date|date:"d.m.Y" }}</div>
</div>
</div>
</div>
<!-- SNAPSHOT: KONTAKT & ADRESSE -->
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Mahnungsadresse</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
<!-- Kontakt -->
<div>
<div class="text-slate-400 text-sm">Ansprechpartner</div>
<div class="font-semibold">{{ reminder.invoice.contact_name|default:"-" }}</div>
{% if reminder.invoice.contact_email %}
<div class="text-slate-400 text-sm">{{ reminder.invoice.contact_email }}</div>
{% endif %}
{% if reminder.invoice.contact_phone %}
<div class="text-slate-400 text-sm">{{ reminder.invoice.contact_phone }}</div>
{% endif %}
</div>
<!-- Adresse -->
<div>
<div class="text-slate-400 text-sm">Standort</div>
<div class="font-semibold">
{{ reminder.invoice.location_name|default:"-" }}
</div>
<div class="text-slate-400 text-sm">
{{ reminder.invoice.location_street }}
</div>
<div class="text-slate-400 text-sm">
{{ reminder.invoice.location_zip }} {{ reminder.invoice.location_city }}
</div>
<div class="text-slate-400 text-sm">
{{ reminder.invoice.location_country }}
</div>
</div>
</div>
</div>
<div class="mb-8 p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<h2 class="text-xl font-bold mb-6">Mahnungshinweis</h2>
<div class="grid grid-cols-1 gap-6">
<div>
<div class="font-semibold">{{ reminder.note|default:"-" }}</div>
</div>
</div>
</div>
<script>
function startDownloadAndRedirect() {
setTimeout(function() {
window.location.href = "{% url 'invoices:reminders_detail' reminder.id %}";
}, 1000);
}
</script>
{% endblock %}
@@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Rechnungswesen - Mahnungen{% endblock %}
{% block header_title %}Mahnungen{% endblock %}
{% block header_description %}Offene Forderungen verfolgen{% endblock %}
{% block content %}
{% load django_tables2 %}
<div class="mb-8">
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h2 class="text-xl font-bold">Aktionen</h2>
<p class="text-slate-400 text-sm mt-1">Neue Mahnung erstellen</p>
</div>
<div class="flex flex-col sm:flex-row gap-2">
<a href="{% url 'invoices:invoices_reminders_add' %}" class="btn btn-add">+ Mahnung</a>
</div>
</div>
</div>
</div>
<div class="p-6 rounded-xl shadow bg-white dark:bg-slate-800 text-slate-900 dark:text-white transition-colors">
<div class="mb-6">
<h2 class="text-xl font-bold">Mahnungen</h2>
<p class="text-slate-400 text-sm mt-1">Alle Mahnungen mit aktuellem Status</p>
</div>
<div class="overflow-x-auto rounded-xl">
{% render_table table %}
</div>
</div>
{% endblock %}
@@ -0,0 +1,663 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 18mm;
}
body {
font-family: Arial, sans-serif;
font-size: 11px;
color: #334155;
}
h1,h2,h3,p {
margin:0;
}
table {
width:100%;
border-collapse:collapse;
}
.header {
display:flex;
justify-content:space-between;
}
.logo {
height:128px;
margin-bottom:15px;
}
.company-name {
font-size:24px;
font-weight:bold;
}
.invoice-title {
font-size:32px;
font-weight:bold;
}
.info-table td {
padding:4px 0;
}
.label {
color:#64748b;
padding-right:30px;
}
.divider {
border-top:1px solid #e2e8f0;
margin:30px 0;
}
.receiver-title {
font-size:10px;
text-transform:uppercase;
color:#64748b;
letter-spacing:1px;
}
.receiver-name {
font-size:16px;
font-weight:bold;
margin-top:5px;
}
.invoice-heading {
font-size:22px;
font-weight:bold;
margin-bottom:10px;
}
.items-wrapper {
border:1px solid #e2e8f0;
border-radius:10px;
overflow:hidden;
margin-top:20px;
}
.items {
width:100%;
border-collapse:collapse;
}
.items thead th:first-child {
border-top-left-radius:10px;
}
.items thead th:last-child {
border-top-right-radius:10px;
}
.items th {
background:{{ company.color }};
color:white;
padding:10px;
text-align:left;
}
.items td {
padding:10px;
border-bottom:1px solid #e2e8f0;
}
.items tbody tr:last-child td {
border-bottom:none;
}
.right {
text-align:right;
}
.total-box {
margin-top:30px;
margin-left:auto;
width:250px;
}
.total-row {
display:flex;
justify-content:space-between;
padding:6px 0;
}
.total-final {
border-top:1px solid #cbd5e1;
margin-top:10px;
padding-top:10px;
font-size:16px;
font-weight:bold;
}
.note {
margin-top:30px;
padding:12px;
border:1px solid #e2e8f0;
border-radius: 10px;
}
.small-business {
margin-top:25px;
color:#64748b;
}
@page {
size: A4;
margin: 18mm 18mm 35mm 18mm;
}
.footer {
position: fixed;
bottom: -25mm;
left: 0;
right: 0;
padding-top:15px;
border-top:1px solid #e2e8f0;
display:flex;
justify-content:space-between;
font-size:10px;
color:#64748b;
}
.footer-title {
font-weight:bold;
color:#334155;
margin-bottom:5px;
}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div>
{% if company.logo %}
<img src="{{ company.logo.url }}" class="logo">
{% else %}
<h1 class="company-name" style="color: {{ company.color }}">
{{ company.companyname }}
</h1>
{% if company.slogan %}
<p>
{{ company.slogan }}
</p>
{% endif %}
{% endif %}
<p style="margin-top:15px">
{{ company.name }}<br>
{{ company.street }} · {{ company.zip }} {{ company.city }}
</p>
</div>
<div style="text-align:right">
<div class="invoice-title">
MAHNUNG
</div>
<table class="info-table">
<tr>
<td class="label">Mahnungsnummer</td>
<td>{{ reminder.reminder_number }}</td>
</tr>
<tr>
<td class="label">Mahnungsdatum</td>
<td>{{ reminder.issue_date|date:"d.m.Y" }}</td>
</tr>
<tr>
<td class="label">Mahnungsstufe</td>
<td>{{ reminder.level }}. Mahnung</td>
</tr>
<tr>
<td class="label">Ursprüngliches Zahlungsziel</td>
<td>{{ reminder.invoice.due_date|date:"d.m.Y" }}</td>
</tr>
<tr>
<td class="label">Neues Zahlungsziel</td>
<td>{{ reminder.due_date|date:"d.m.Y" }}</td>
</tr>
<tr>
<td class="label">Rechnungsnummer</td>
<td>{{ reminder.invoice.invoice_number }}</td>
</tr>
</table>
</div>
</div>
<div class="divider"></div>
<!-- EMPFÄNGER -->
<div>
<div class="receiver-title">
Empfänger
</div>
<div class="receiver-name">
{% if reminder.invoice.customer.type == "company" %}
{{ reminder.invoice.customer.company_name }}
{% else %}
{{ reminder.invoice.contact_name }}
{% endif %}
</div>
{% if reminder.invoice.customer.type == "company" %}
<p>
z.Hd. {{ reminder.invoice.contact_name }}
</p>
{% endif %}
<p style="margin-top:10px">
{{ reminder.invoice.location_street }}<br>
{{ reminder.invoice.location_zip }}
{{ reminder.invoice.location_city }}
</p>
</div>
<!-- TITEL -->
<div style="margin-top:40px">
<div class="invoice-heading"
style="color:{{ company.color }}">
Mahnung
</div>
{% if company.reminder_header_text %}
<p>
{{ company.reminder_header_text|linebreaksbr }}
</p>
{% endif %}
</div>
<!-- FORDERUNGSÜBERSICHT -->
<div class="items-wrapper">
<table class="items">
<thead>
<tr>
<th>
Beschreibung
</th>
<th class="right">
Betrag
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Rechnung {{ reminder.invoice.invoice_number }}
</td>
<td>
{{ invoice_amount|floatformat:2 }} €
</td>
</tr>
{% if previous_reminders or reminder.fee > 0 %}
{% for old_reminder in previous_reminders %}
<tr>
<td>
{{ old_reminder.level }}. Mahnung
({{ old_reminder.issue_date|date:"d.m.Y" }})
</td>
<td>
{{ old_reminder.fee|floatformat:2 }} €
</td>
</tr>
{% endfor %}
<tr>
<td>
Aktuelle Mahngebühr
</td>
<td>
{{ reminder.fee|floatformat:2 }} €
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
<!-- GESAMTFORDERUNG -->
<div class="total-box">
<div class="total-row">
<span>
Offener Rechnungsbetrag
</span>
<span>
{{ invoice_amount|floatformat:2 }} €
</span>
</div>
{% if previous_reminders or reminder.fee > 0 %}
<div class="total-row">
<span>
Mahngebühren
</span>
<span>
{{ total_fees|floatformat:2 }} €
</span>
</div>
{% endif %}
<div class="total-row total-final">
<span>
Gesamtforderung
</span>
<span>
{{ total_due|floatformat:2 }} €
</span>
</div>
</div>
{% if reminder.note %}
<div class="note">
<div class="receiver-title">
Mahnungshinweis
</div>
<br>
{{ reminder.note|linebreaksbr }}
</div>
{% endif %}
<!-- FOOTER -->
<div class="footer">
<div>
<div class="footer-title">
Kontakt
</div>
{% if company.phone %}
{{ company.phone }}<br>
{% endif %}
{% if company.email %}
{{ company.email }}<br>
{% endif %}
{% if company.website %}
{{ company.website }}
{% endif %}
</div>
<div>
<div class="footer-title">
Steuern
</div>
{% if company.tax_number %}
Steuernummer: {{ company.tax_number }}<br>
{% endif %}
{% if company.vat_number %}
USt-IdNr.: {{ company.vat_number }}
{% endif %}
</div>
<div>
<div class="footer-title">
Bank
</div>
{% if company.bank_name %}
{{ company.bank_name }}<br>
{% endif %}
{% if company.iban %}
IBAN: {{ company.iban }}<br>
{% endif %}
{% if company.bic %}
BIC: {{ company.bic }}
{% endif %}
</div>
</div>
</body>
</html>
@@ -0,0 +1,218 @@
<div>
<div class="grid md:grid-cols-2 xl:grid-cols-3 gap-6">
<!-- Ansprechpartner -->
<div>
<h3 class="font-bold mb-3">
Ansprechpartner
</h3>
{% for contact in item.contacts.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
<div class="font-medium">
{{ contact.first_name }}
{{ contact.last_name }}
</div>
{% if contact.phone %}
<div class="text-sm text-slate-400">
{{ contact.phone }}
</div>
{% endif %}
{% if contact.email %}
<div class="text-sm text-slate-400">
{{ contact.email }}
</div>
{% endif %}
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Ansprechpartner vorhanden
</div>
{% endfor %}
</div>
<!-- Standorte -->
<div>
<h3 class="font-bold mb-3">
Standorte
</h3>
{% for location in item.locations.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
<div class="font-medium">
{{ location.name }}
</div>
<div class="text-sm text-slate-400">
{{ location.street }}
</div>
<div class="text-sm text-slate-400">
{{ location.zip_code }}
{{ location.city }}
</div>
<div class="text-sm text-slate-400">
{{ location.country }}
</div>
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Standorte vorhanden
</div>
{% endfor %}
</div>
<!-- Projekte -->
<div>
<h3 class="font-bold mb-3">
Projekte
</h3>
{% for project in item.projects.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
{{ project.name }}
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Projekte zugeordnet
</div>
{% endfor %}
</div>
</div>
<div class="mt-5 flex gap-2">
<a
href="{% url 'invoices:customers_detail' item.id %}"
class="btn btn-edit">
Bearbeiten
</a>
<form
method="post"
action="{% url 'invoices:customers_archive' item.id %}">
{% csrf_token %}
<button
type="submit"
name="archiv"
value="1"
class="btn btn-action">
Archivieren
</button>
</form>
</div>
</div>
@@ -0,0 +1,218 @@
<div>
<div class="grid md:grid-cols-2 xl:grid-cols-3 gap-6">
<!-- Ansprechpartner -->
<div>
<h3 class="font-bold mb-3">
Ansprechpartner
</h3>
{% for contact in item.contacts.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
<div class="font-medium">
{{ contact.first_name }}
{{ contact.last_name }}
</div>
{% if contact.phone %}
<div class="text-sm text-slate-400">
{{ contact.phone }}
</div>
{% endif %}
{% if contact.email %}
<div class="text-sm text-slate-400">
{{ contact.email }}
</div>
{% endif %}
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Ansprechpartner vorhanden
</div>
{% endfor %}
</div>
<!-- Standorte -->
<div>
<h3 class="font-bold mb-3">
Standorte
</h3>
{% for location in item.locations.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
<div class="font-medium">
{{ location.name }}
</div>
<div class="text-sm text-slate-400">
{{ location.street }}
</div>
<div class="text-sm text-slate-400">
{{ location.zip_code }}
{{ location.city }}
</div>
<div class="text-sm text-slate-400">
{{ location.country }}
</div>
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Standorte vorhanden
</div>
{% endfor %}
</div>
<!-- Projekte -->
<div>
<h3 class="font-bold mb-3">
Projekte
</h3>
{% for project in item.projects.all %}
<div class="
bg-slate-100
dark:bg-slate-700
rounded-xl
p-3
mb-2
">
{{ project.name }}
</div>
{% empty %}
<div class="
bg-white
dark:bg-slate-800
border
border-orange-500
rounded-xl
p-6
text-orange-500
">
Keine Projekte zugeordnet
</div>
{% endfor %}
</div>
</div>
<div class="mt-5 flex gap-2">
<a
href="{% url 'invoices:customers_detail' item.id %}"
class="btn btn-edit">
Bearbeiten
</a>
<form
method="post"
action="{% url 'invoices:customers_reactivate' item.id %}">
{% csrf_token %}
<button
type="submit"
name="archiv"
value="1"
class="btn btn-action">
Aktivieren
</button>
</form>
</div>
</div>
@@ -0,0 +1,20 @@
<form
method="post"
action="{% url 'invoices:invoice_item_delete' invoice.id record.id %}">
{% csrf_token %}
<input
type="hidden"
name="delete_item"
value="{{ record.id }}">
<button
type="submit"
class="btn btn-delete">
Löschen
</button>
</form>
@@ -0,0 +1,7 @@
{% if record.service_period_from %}
{{ record.service_period_from|date:"d.m.Y" }}
{% endif %}
{% if record.service_period_to %}
- {{ record.service_period_to|date:"d.m.Y" }}
{% endif %}
@@ -0,0 +1 @@
USt. {{ record.unit_tax_rate|floatformat:0 }}%
@@ -0,0 +1 @@
{{ record.calculated_total_net|floatformat:2 }} €
@@ -0,0 +1 @@
{{ record.unit_price_net }} €
@@ -0,0 +1,7 @@
<a
href="{% url 'invoices:invoices_detail' record.id %}"
class="btn btn-edit">
Bearbeiten
</a>
@@ -0,0 +1,35 @@
<form
id="save-form-{{ record.id }}"
method="post"
action="{% url 'invoices:items_edit' record.id %}">
{% csrf_token %}
</form>
<div class="flex gap-2">
<button
type="submit"
form="save-form-{{ record.id }}"
class="btn btn-save">
Speichern
</button>
<form
method="post"
action="{% url 'invoices:items_delete' record.id %}">
{% csrf_token %}
<button
type="submit"
class="btn btn-delete">
Löschen
</button>
</form>
</div>
@@ -0,0 +1,6 @@
<input
type="text"
name="description"
form="save-form-{{ record.id }}"
value="{{ record.description }}"
class="border rounded px-2 py-1 w-full">
@@ -0,0 +1,16 @@
<select
name="item_type"
form="save-form-{{ record.id }}"
class="border rounded px-2 py-1 w-full">
{% for v,l in table.types %}
<option
value="{{ v }}"
{% if record.item_type == v %}selected{% endif %}>
{{ l }}
</option>
{% endfor %}
</select>
@@ -0,0 +1,7 @@
<input
type="number"
step="0.01"
name="price_net"
form="save-form-{{ record.id }}"
value="{{ record.price_net }}"
class="border rounded px-2 py-1 w-full">
@@ -0,0 +1,7 @@
<input
type="number"
step="0.01"
name="tax_rate"
form="save-form-{{ record.id }}"
value="{{ record.tax_rate }}"
class="border rounded px-2 py-1 w-full">
@@ -0,0 +1,20 @@
<select
name="unit"
form="save-form-{{ record.id }}"
class="border rounded px-2 py-1 w-full">
{% for unit in table.units %}
<option
value="{{ unit.id }}"
{% if record.unit_id == unit.id %}selected{% endif %}>
{{ unit.name }}
</option>
{% endfor %}
</select>
@@ -0,0 +1,20 @@
<form
method="post"
action="{% url 'invoices:offers_item_delete' table.offer.id record.id %}">
{% csrf_token %}
<input
type="hidden"
name="delete_item"
value="{{ record.id }}">
<button
type="submit"
class="btn btn-delete">
Löschen
</button>
</form>
@@ -0,0 +1 @@
USt. {{ record.unit_tax_rate|floatformat:0 }}%
@@ -0,0 +1 @@
{{ record.calculated_total_net|floatformat:2 }} €
@@ -0,0 +1 @@
{{ record.unit_price_net }} €
@@ -0,0 +1,7 @@
<a
href="{% url 'invoices:offers_detail' record.id %}"
class="btn btn-edit">
Bearbeiten
</a>
@@ -0,0 +1,7 @@
<a
href="{% url 'invoices:reminders_detail' record.id %}"
class="btn btn-edit">
Bearbeiten
</a>
@@ -0,0 +1,7 @@
<a
class="company_link"
href="{% url 'invoices:invoices_detail' record.invoice.id %}">
{{ record.invoice.invoice_number }}
</a>