224 lines
5.8 KiB
Vue
224 lines
5.8 KiB
Vue
<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>
|