Files
mareike/app/Domains/Admin/Views/Partials/TenantTaxInfo.vue
T
2026-08-06 11:49:58 +02:00

224 lines
5.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import {computed, ref} from 'vue';
import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
import RichSelectBox from "../../../../Views/Components/RichSelectBox.vue";
import {toast} from "vue3-toastify";
const props = defineProps({
data: {
type: Object,
default: () => ({})
},
})
const {request} = useAjax()
const editing = ref(false)
const reasons = computed(() => props.data.exemption_reasons ?? [])
const liableOptions = [
{value: '1', label: 'Ja Umsatzsteuer wird erhoben'},
{value: '0', label: 'Nein umsatzsteuerbefreit'},
]
const form = ref({
tax_liable: props.data.tax_liable ? '1' : '0',
vat_rate: props.data.vat_rate ?? 0,
tax_exemption_reason: props.data.tax_exemption_reason ?? '',
tax_exemption_note: props.data.tax_exemption_note ?? '',
})
const isLiable = computed(() => form.value.tax_liable === '1')
const isCustomReason = computed(() => form.value.tax_exemption_reason === 'custom')
const selectedReason = computed(
() => reasons.value.find(r => r.value === form.value.tax_exemption_reason) ?? null
)
// Vorschau des Pflichthinweises nach § 14 Abs. 4 Nr. 8 UStG, der später auf der Rechnung erscheint.
const invoiceHint = computed(() => {
if (isLiable.value) {
return `Umsatzsteuerpflichtig USt-Satz: ${form.value.vat_rate || 0} %`
}
if (isCustomReason.value) {
return form.value.tax_exemption_note?.trim() || '—'
}
return selectedReason.value?.text || '—'
})
const reasonLabel = computed(() => selectedReason.value?.label ?? '—')
async function save() {
const saveUrl = props.data.saveEndpoint ?? '/api/v1/admin/tenant/tax'
const response = await request(saveUrl, {
method: 'POST',
body: {
tax_liable: isLiable.value,
vat_rate: Number(form.value.vat_rate) || 0,
tax_exemption_reason: isLiable.value ? null : form.value.tax_exemption_reason,
tax_exemption_note: form.value.tax_exemption_note,
},
})
if (response && response.status === 'success') {
toast.success(response.message)
editing.value = false
} else {
toast.error(response?.message ?? 'Fehler beim Speichern')
}
}
</script>
<template>
<div v-if="!editing">
<table class="data-table">
<tr>
<th>Steuerpflicht</th>
<td>{{ isLiable ? 'Ja Umsatzsteuer wird erhoben' : 'Nein umsatzsteuerbefreit' }}</td>
</tr>
<tr v-if="isLiable">
<th>USt-Satz</th>
<td>{{ form.vat_rate || 0 }} %</td>
</tr>
<tr v-else>
<th>Befreiungsgrund</th>
<td>{{ reasonLabel }}</td>
</tr>
<tr>
<th>Rechnungshinweis</th>
<td>{{ invoiceHint }}</td>
</tr>
</table>
<button class="btn-edit" @click="editing = true">Bearbeiten</button>
</div>
<div v-else>
<table class="data-table">
<tr>
<th>Steuerpflicht</th>
<td>
<RichSelectBox v-model="form.tax_liable" :options="liableOptions"/>
</td>
</tr>
<tr v-if="isLiable">
<th>USt-Satz (%)</th>
<td><input type="number" min="0" max="100" v-model.number="form.vat_rate" class="form-input"/></td>
</tr>
<template v-else>
<tr>
<th>Befreiungsgrund</th>
<td>
<RichSelectBox
v-model="form.tax_exemption_reason"
:options="reasons"
placeholder="Grund auswählen…"
/>
</td>
</tr>
<tr v-if="isCustomReason">
<th>Freitext</th>
<td>
<textarea
v-model="form.tax_exemption_note"
class="form-input"
rows="3"
placeholder="Hinweistext für die Rechnung…"
></textarea>
</td>
</tr>
</template>
<tr>
<th>Rechnungshinweis</th>
<td class="hint-preview">{{ invoiceHint }}</td>
</tr>
</table>
<div class="btn-group">
<button class="btn-save" @click="save">Speichern</button>
<button class="btn-cancel" @click="editing = false">Abbrechen</button>
</div>
</div>
</template>
<style scoped>
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
text-align: left;
padding: 8px 12px;
width: 200px;
color: #374151;
border-bottom: 1px solid #e5e7eb;
vertical-align: top;
}
.data-table td {
padding: 8px 12px;
border-bottom: 1px solid #e5e7eb;
}
.form-input {
width: 100%;
padding: 6px 10px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 0.95rem;
box-sizing: border-box;
font-family: inherit;
}
.hint-preview {
color: #6b7280;
font-style: italic;
}
.btn-edit, .btn-save, .btn-cancel {
margin-top: 15px;
padding: 8px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 0.9rem;
}
.btn-edit {
background-color: #1d4899;
color: #ffffff;
}
.btn-edit:hover {
background-color: #163a7a;
}
.btn-save {
background-color: #16a34a;
color: #ffffff;
}
.btn-save:hover {
background-color: #15803d;
}
.btn-cancel {
background-color: #e5e7eb;
color: #374151;
margin-left: 10px;
}
.btn-cancel:hover {
background-color: #d1d5db;
}
.btn-group {
display: flex;
gap: 10px;
}
</style>