Added missing files
This commit is contained in:
183
frontend/components/displayTaxSummary.vue
Normal file
183
frontend/components/displayTaxSummary.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script setup lang="ts">
|
||||
import dayjs from "dayjs"
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat"
|
||||
import {
|
||||
formatTaxEvaluationPeriodLabel,
|
||||
formatTaxEvaluationPeriodRange,
|
||||
getCreatedDocumentTaxBreakdown,
|
||||
getIncomingInvoiceTaxBreakdown,
|
||||
getTaxEvaluationPeriodBounds,
|
||||
normalizeTaxEvaluationPeriod
|
||||
} from "~/composables/useTaxEvaluation"
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const loading = ref(true)
|
||||
const summary = ref({
|
||||
label: "",
|
||||
range: "",
|
||||
outputTax: 0,
|
||||
inputTax: 0,
|
||||
balance: 0,
|
||||
outputCount: 0,
|
||||
inputCount: 0,
|
||||
})
|
||||
|
||||
const formatCurrency = (value: number) => {
|
||||
return new Intl.NumberFormat("de-DE", {
|
||||
style: "currency",
|
||||
currency: "EUR"
|
||||
}).format(Number(value || 0))
|
||||
}
|
||||
|
||||
const loadSummary = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const periodType = normalizeTaxEvaluationPeriod(auth.activeTenantData?.taxEvaluationPeriod)
|
||||
const bounds = getTaxEvaluationPeriodBounds(dayjs(), periodType)
|
||||
|
||||
const [docs, incoming] = await Promise.all([
|
||||
useEntities("createddocuments").select(),
|
||||
useEntities("incominginvoices").select()
|
||||
])
|
||||
|
||||
const outputDocs = (docs || []).filter((doc: any) => {
|
||||
if (doc?.state !== "Gebucht") return false
|
||||
if (!["invoices", "advanceInvoices", "cancellationInvoices"].includes(doc?.type)) return false
|
||||
|
||||
const date = dayjs(doc.documentDate)
|
||||
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
||||
})
|
||||
|
||||
const inputDocs = (incoming || []).filter((invoice: any) => {
|
||||
if (invoice?.state !== "Gebucht" || !invoice?.date) return false
|
||||
|
||||
const date = dayjs(invoice.date)
|
||||
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
||||
})
|
||||
|
||||
const outputTax = outputDocs.reduce((sum: number, doc: any) => {
|
||||
const breakdown = getCreatedDocumentTaxBreakdown(doc)
|
||||
return sum + breakdown.tax19 + breakdown.tax7
|
||||
}, 0)
|
||||
|
||||
const inputTax = inputDocs.reduce((sum: number, invoice: any) => {
|
||||
const breakdown = getIncomingInvoiceTaxBreakdown(invoice)
|
||||
return sum + breakdown.tax19 + breakdown.tax7
|
||||
}, 0)
|
||||
|
||||
summary.value = {
|
||||
label: formatTaxEvaluationPeriodLabel(bounds.start, periodType),
|
||||
range: formatTaxEvaluationPeriodRange(bounds.start, periodType),
|
||||
outputTax: Number(outputTax.toFixed(2)),
|
||||
inputTax: Number(inputTax.toFixed(2)),
|
||||
balance: Number((outputTax - inputTax).toFixed(2)),
|
||||
outputCount: outputDocs.length,
|
||||
inputCount: inputDocs.length,
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadSummary)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="tax-summary-top">
|
||||
<div>
|
||||
<p class="tax-summary-period">{{ summary.label }}</p>
|
||||
<p class="tax-summary-range">{{ summary.range }}</p>
|
||||
</div>
|
||||
<UButton
|
||||
size="xs"
|
||||
variant="soft"
|
||||
color="gray"
|
||||
icon="i-heroicons-arrow-top-right-on-square"
|
||||
@click="navigateTo('/accounting/tax')"
|
||||
>
|
||||
Details
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div class="tax-summary-row">
|
||||
<span class="tax-summary-label">USt Rechnungen</span>
|
||||
<span class="tax-summary-value text-amber-600 dark:text-amber-400">
|
||||
{{ loading ? "..." : formatCurrency(summary.outputTax) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="tax-summary-row">
|
||||
<span class="tax-summary-label">Vorsteuer</span>
|
||||
<span class="tax-summary-value text-sky-600 dark:text-sky-400">
|
||||
{{ loading ? "..." : formatCurrency(summary.inputTax) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="tax-summary-row">
|
||||
<span class="tax-summary-label">Ergebnis</span>
|
||||
<span
|
||||
class="tax-summary-value"
|
||||
:class="summary.balance >= 0 ? 'text-rose-600 dark:text-rose-400' : 'text-emerald-600 dark:text-emerald-400'"
|
||||
>
|
||||
{{ loading ? "..." : formatCurrency(summary.balance) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="tax-summary-meta">
|
||||
{{ summary.outputCount }} Ausgangsbelege | {{ summary.inputCount }} Eingangsbelege
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tax-summary-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.tax-summary-period {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
color: rgb(17 24 39);
|
||||
}
|
||||
|
||||
.tax-summary-range,
|
||||
.tax-summary-meta {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
.tax-summary-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tax-summary-label {
|
||||
color: rgb(55 65 81);
|
||||
}
|
||||
|
||||
.tax-summary-value {
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
:deep(.dark) .tax-summary-period {
|
||||
color: rgb(243 244 246);
|
||||
}
|
||||
|
||||
:deep(.dark) .tax-summary-range,
|
||||
:deep(.dark) .tax-summary-meta,
|
||||
:deep(.dark) .tax-summary-label {
|
||||
color: rgb(156 163 175);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user