Compare commits
2 Commits
94008e8072
...
7ad55aaa0b
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ad55aaa0b | |||
| 8301d8777e |
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "incominginvoices" ADD COLUMN "tax_amount_override" numeric(12, 2);
|
||||||
20
backend/db/migrations/0063_skr03_output_tax_account.sql
Normal file
20
backend/db/migrations/0063_skr03_output_tax_account.sql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
UPDATE "accounts"
|
||||||
|
SET "label" = 'Umsatzsteuer 19 %',
|
||||||
|
"description" = NULL
|
||||||
|
WHERE "accountChart" = 'skr03'
|
||||||
|
AND "number" = '1776';
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO "accounts" ("number", "label", "description", "accountChart")
|
||||||
|
SELECT '1776', 'Umsatzsteuer 19 %', NULL, 'skr03'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "accounts"
|
||||||
|
WHERE "accountChart" = 'skr03'
|
||||||
|
AND "number" = '1776'
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
SELECT setval(
|
||||||
|
pg_get_serial_sequence('accounts', 'id'),
|
||||||
|
GREATEST(COALESCE((SELECT MAX("id") FROM "accounts"), 1), 1),
|
||||||
|
true
|
||||||
|
);
|
||||||
@@ -414,6 +414,20 @@
|
|||||||
"when": 1786280400000,
|
"when": 1786280400000,
|
||||||
"tag": "0061_additional_skr03_accounts",
|
"tag": "0061_additional_skr03_accounts",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 59,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1786284000000,
|
||||||
|
"tag": "0062_incoming_invoice_tax_override",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 60,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1786287600000,
|
||||||
|
"tag": "0063_skr03_output_tax_account",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
text,
|
text,
|
||||||
boolean,
|
boolean,
|
||||||
jsonb,
|
jsonb,
|
||||||
|
numeric,
|
||||||
uuid,
|
uuid,
|
||||||
} from "drizzle-orm/pg-core"
|
} from "drizzle-orm/pg-core"
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ export const incominginvoices = pgTable("incominginvoices", {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
taxAmountOverride: numeric("tax_amount_override", { precision: 12, scale: 2 }),
|
||||||
|
|
||||||
paid: boolean("paid").notNull().default(false),
|
paid: boolean("paid").notNull().default(false),
|
||||||
expense: boolean("expense").notNull().default(true),
|
expense: boolean("expense").notNull().default(true),
|
||||||
|
|
||||||
|
|||||||
@@ -80,18 +80,17 @@ const formatDatevDate = (date: dayjs.ConfigType, format: string) => {
|
|||||||
return parsed.isValid() ? parsed.tz(DATEV_TIMEZONE).format(format) : "";
|
return parsed.isValid() ? parsed.tz(DATEV_TIMEZONE).format(format) : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIncomingInvoiceTaxReview = (account: any) => {
|
const getIncomingInvoiceTaxOverride = (invoice: any) => {
|
||||||
const taxRate = account.taxType === "19" ? 19 : account.taxType === "7" ? 7 : null;
|
if (invoice.taxAmountOverride === null || invoice.taxAmountOverride === undefined || invoice.taxAmountOverride === "") return null;
|
||||||
if (taxRate === null) return null;
|
|
||||||
|
|
||||||
const amountNet = Number(account.amountNet);
|
const calculatedTax = (invoice.accounts as any[] || []).reduce((sum, account) => sum + Number(account.amountTax || 0), 0);
|
||||||
const amountTax = Number(account.amountTax);
|
const overriddenTax = Number(invoice.taxAmountOverride);
|
||||||
if (!Number.isFinite(amountNet) || !Number.isFinite(amountTax)) return null;
|
if (!Number.isFinite(overriddenTax) || Math.abs(overriddenTax - calculatedTax) < 0.005) return null;
|
||||||
|
|
||||||
const calculatedTax = Number((amountNet * (taxRate / 100)).toFixed(2));
|
return {
|
||||||
if (Math.abs(amountTax - calculatedTax) < 0.005) return null;
|
amount: overriddenTax,
|
||||||
|
correction: Number((overriddenTax - calculatedTax).toFixed(2)),
|
||||||
return `USt pruefen ${displayCurrency(amountTax, true)}`;
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCreatedDocumentRevenueLines = (document: any) => {
|
const getCreatedDocumentRevenueLines = (document: any) => {
|
||||||
@@ -368,7 +367,12 @@ export async function buildExportZip(
|
|||||||
// ER
|
// ER
|
||||||
incominginvoicesList.forEach(ii => {
|
incominginvoicesList.forEach(ii => {
|
||||||
const accs = ii.accounts as any[] || [];
|
const accs = ii.accounts as any[] || [];
|
||||||
accs.forEach(account => {
|
const taxOverride = getIncomingInvoiceTaxOverride(ii);
|
||||||
|
const correctionAccountIndex = taxOverride
|
||||||
|
? Math.max(0, accs.findIndex(account => account.taxType === "19" || account.taxType === "7"))
|
||||||
|
: -1;
|
||||||
|
|
||||||
|
accs.forEach((account, accountIndex) => {
|
||||||
let file = filesIncomingInvoices.find(i => i.incominginvoice === ii.id);
|
let file = filesIncomingInvoices.find(i => i.incominginvoice === ii.id);
|
||||||
let accountData = accountsList.find(i => i.id === account.account);
|
let accountData = accountsList.find(i => i.id === account.account);
|
||||||
if (!accountData) return;
|
if (!accountData) return;
|
||||||
@@ -381,10 +385,13 @@ export async function buildExportZip(
|
|||||||
else if(account.taxType === '7I') buschluessel = "18";
|
else if(account.taxType === '7I') buschluessel = "18";
|
||||||
else buschluessel = "-";
|
else buschluessel = "-";
|
||||||
|
|
||||||
let amountGross =/* account.amountGross ? account.amountGross : */(account.amountNet || 0) + (account.amountTax || 0);
|
let amountGross = Number(account.amountNet || 0) + Number(account.amountTax || 0);
|
||||||
|
if (taxOverride && accountIndex === correctionAccountIndex) amountGross += taxOverride.correction;
|
||||||
let shSelector = Math.sign(amountGross) === -1 ? "H" : "S";
|
let shSelector = Math.sign(amountGross) === -1 ? "H" : "S";
|
||||||
const taxReview = getIncomingInvoiceTaxReview(account);
|
const taxReview = taxOverride && accountIndex === correctionAccountIndex
|
||||||
let text = `${taxReview ? `${taxReview} - ` : ""}ER ${ii.reference}: ${escapeString(ii.description)}`.substring(0,59);
|
? `USt pruefen ${displayCurrency(taxOverride.amount, true)} - `
|
||||||
|
: "";
|
||||||
|
let text = `${taxReview}ER ${ii.reference}: ${escapeString(ii.description)}`.substring(0,59);
|
||||||
const vend = ii.vendor; // durch Mapping verfügbar
|
const vend = ii.vendor; // durch Mapping verfügbar
|
||||||
|
|
||||||
bookingLines.push(`${Math.abs(amountGross).toFixed(2).replace(".",",")};"${shSelector}";;;;;${accountData.number};${vend?.vendorNumber || ""};"${buschluessel}";${formatDatevDate(ii.date, "DDMM")};"${ii.reference}";;;"${text}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${vend?.name || ""}";"Kundennummer";"${vend?.vendorNumber || ""}";"Belegnummer";"${ii.reference}";"Leistungsdatum";"${formatDatevDate(ii.date, "DD.MM.YYYY")}";"Belegdatum";"${formatDatevDate(ii.date, "DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
bookingLines.push(`${Math.abs(amountGross).toFixed(2).replace(".",",")};"${shSelector}";;;;;${accountData.number};${vend?.vendorNumber || ""};"${buschluessel}";${formatDatevDate(ii.date, "DDMM")};"${ii.reference}";;;"${text}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${vend?.name || ""}";"Kundennummer";"${vend?.vendorNumber || ""}";"Belegnummer";"${ii.reference}";"Leistungsdatum";"${formatDatevDate(ii.date, "DD.MM.YYYY")}";"Belegdatum";"${formatDatevDate(ii.date, "DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
|||||||
@@ -287,6 +287,12 @@ const getIncomingInvoiceTaxBreakdown = (invoice: any): TaxBreakdown => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (invoice?.taxAmountOverride !== null && invoice?.taxAmountOverride !== undefined && invoice?.taxAmountOverride !== "") {
|
||||||
|
const correction = Number(invoice.taxAmountOverride) - breakdown.tax19 - breakdown.tax7;
|
||||||
|
if (breakdown.tax19 !== 0 || breakdown.net19 !== 0) breakdown.tax19 += correction;
|
||||||
|
else if (breakdown.tax7 !== 0 || breakdown.net7 !== 0) breakdown.tax7 += correction;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
net19: roundMoney(breakdown.net19),
|
net19: roundMoney(breakdown.net19),
|
||||||
tax19: roundMoney(breakdown.tax19),
|
tax19: roundMoney(breakdown.tax19),
|
||||||
@@ -297,9 +303,14 @@ const getIncomingInvoiceTaxBreakdown = (invoice: any): TaxBreakdown => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getIncomingInvoiceSignedAmount = (invoice: any) => {
|
const getIncomingInvoiceSignedAmount = (invoice: any) => {
|
||||||
const amount = (invoice.accounts || []).reduce((sum: number, account: any) => {
|
const totals = (invoice.accounts || []).reduce((result: { net: number, tax: number }, account: any) => ({
|
||||||
return sum + Number(account.amountNet || 0) + Number(account.amountTax || 0);
|
net: result.net + Number(account.amountNet || 0),
|
||||||
}, 0);
|
tax: result.tax + Number(account.amountTax || 0),
|
||||||
|
}), { net: 0, tax: 0 });
|
||||||
|
const tax = invoice.taxAmountOverride !== null && invoice.taxAmountOverride !== undefined && invoice.taxAmountOverride !== ""
|
||||||
|
? Number(invoice.taxAmountOverride)
|
||||||
|
: totals.tax;
|
||||||
|
const amount = totals.net + tax;
|
||||||
|
|
||||||
return roundMoney(invoice.expense === false ? amount : amount * -1);
|
return roundMoney(invoice.expense === false ? amount : amount * -1);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
formatTaxEvaluationPeriodRange,
|
formatTaxEvaluationPeriodRange,
|
||||||
getCreatedDocumentTaxBreakdown,
|
getCreatedDocumentTaxBreakdown,
|
||||||
getIncomingInvoiceTaxBreakdown,
|
getIncomingInvoiceTaxBreakdown,
|
||||||
|
getManualBookingTaxBreakdown,
|
||||||
getTaxEvaluationPeriodBounds,
|
getTaxEvaluationPeriodBounds,
|
||||||
normalizeTaxEvaluationPeriod
|
normalizeTaxEvaluationPeriod
|
||||||
} from "~/composables/useTaxEvaluation"
|
} from "~/composables/useTaxEvaluation"
|
||||||
@@ -40,7 +41,10 @@ const loadSummary = async () => {
|
|||||||
const periodType = normalizeTaxEvaluationPeriod(auth.activeTenantData?.taxEvaluationPeriod)
|
const periodType = normalizeTaxEvaluationPeriod(auth.activeTenantData?.taxEvaluationPeriod)
|
||||||
const bounds = getTaxEvaluationPeriodBounds(dayjs(), periodType)
|
const bounds = getTaxEvaluationPeriodBounds(dayjs(), periodType)
|
||||||
|
|
||||||
const { createdDocuments: docs, incomingInvoices: incoming } = await loadCoreData()
|
const [{ createdDocuments: docs, incomingInvoices: incoming }, manualBookings] = await Promise.all([
|
||||||
|
loadCoreData(),
|
||||||
|
useNuxtApp().$api("/api/banking/manual-bookings") as Promise<any[]>
|
||||||
|
])
|
||||||
|
|
||||||
const outputDocs = (docs || []).filter((doc: any) => {
|
const outputDocs = (docs || []).filter((doc: any) => {
|
||||||
if (doc?.state !== "Gebucht") return false
|
if (doc?.state !== "Gebucht") return false
|
||||||
@@ -67,12 +71,28 @@ const loadSummary = async () => {
|
|||||||
return sum + breakdown.tax19 + breakdown.tax7
|
return sum + breakdown.tax19 + breakdown.tax7
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
|
const manualTax = (manualBookings || [])
|
||||||
|
.filter((booking: any) => {
|
||||||
|
const date = dayjs(booking.manualBookingDate)
|
||||||
|
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
||||||
|
})
|
||||||
|
.reduce((sum: { outputTax19: number; inputTax19: number }, booking: any) => {
|
||||||
|
const breakdown = getManualBookingTaxBreakdown(booking)
|
||||||
|
return {
|
||||||
|
outputTax19: sum.outputTax19 + breakdown.outputTax19,
|
||||||
|
inputTax19: sum.inputTax19 + breakdown.inputTax19,
|
||||||
|
}
|
||||||
|
}, { outputTax19: 0, inputTax19: 0 })
|
||||||
|
|
||||||
|
const totalOutputTax = outputTax + manualTax.outputTax19
|
||||||
|
const totalInputTax = inputTax + manualTax.inputTax19
|
||||||
|
|
||||||
summary.value = {
|
summary.value = {
|
||||||
label: formatTaxEvaluationPeriodLabel(bounds.start, periodType),
|
label: formatTaxEvaluationPeriodLabel(bounds.start, periodType),
|
||||||
range: formatTaxEvaluationPeriodRange(bounds.start, periodType),
|
range: formatTaxEvaluationPeriodRange(bounds.start, periodType),
|
||||||
outputTax: Number(outputTax.toFixed(2)),
|
outputTax: Number(totalOutputTax.toFixed(2)),
|
||||||
inputTax: Number(inputTax.toFixed(2)),
|
inputTax: Number(totalInputTax.toFixed(2)),
|
||||||
balance: Number((outputTax - inputTax).toFixed(2)),
|
balance: Number((totalOutputTax - totalInputTax).toFixed(2)),
|
||||||
outputCount: outputDocs.length,
|
outputCount: outputDocs.length,
|
||||||
inputCount: inputDocs.length,
|
inputCount: inputDocs.length,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,13 +222,24 @@ export const getIncomingInvoiceImmediateExpenseNet = (invoice: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getIncomingInvoiceImmediateExpenseGross = (invoice: any) => {
|
export const getIncomingInvoiceImmediateExpenseGross = (invoice: any) => {
|
||||||
return Number(((invoice?.accounts || []).reduce((sum: number, account: any) => {
|
const immediateAccounts = (invoice?.accounts || []).filter((account: any) => {
|
||||||
|
const normalized = normalizeIncomingInvoiceAccount(account, invoice?.date)
|
||||||
|
return !isDepreciationBookingMode(normalized.bookingMode)
|
||||||
|
})
|
||||||
|
const gross = immediateAccounts.reduce((sum: number, account: any) => {
|
||||||
const normalized = normalizeIncomingInvoiceAccount(account, invoice?.date)
|
const normalized = normalizeIncomingInvoiceAccount(account, invoice?.date)
|
||||||
if (isDepreciationBookingMode(normalized.bookingMode)) return sum
|
|
||||||
|
|
||||||
const amountGross = Number(normalized.amountGross)
|
const amountGross = Number(normalized.amountGross)
|
||||||
return sum + (Number.isFinite(amountGross) ? amountGross : Number(normalized.amountNet || 0) + Number(normalized.amountTax || 0))
|
return sum + (Number.isFinite(amountGross) ? amountGross : Number(normalized.amountNet || 0) + Number(normalized.amountTax || 0))
|
||||||
}, 0)).toFixed(2))
|
}, 0)
|
||||||
|
const calculatedTax = (invoice?.accounts || []).reduce((sum: number, account: any) => sum + Number(account.amountTax || 0), 0)
|
||||||
|
const correction = immediateAccounts.length > 0
|
||||||
|
&& invoice?.taxAmountOverride !== null
|
||||||
|
&& invoice?.taxAmountOverride !== undefined
|
||||||
|
&& invoice?.taxAmountOverride !== ""
|
||||||
|
? Number(invoice.taxAmountOverride) - calculatedTax
|
||||||
|
: 0
|
||||||
|
|
||||||
|
return Number((gross + correction).toFixed(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getIncomingInvoiceDepreciationRows = (invoice: any, rangeStart: any, rangeEnd: any) => {
|
export const getIncomingInvoiceDepreciationRows = (invoice: any, rangeStart: any, rangeEnd: any) => {
|
||||||
|
|||||||
@@ -12,17 +12,15 @@ export const useSum = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getIncomingInvoiceSum = (invoice) => {
|
const getIncomingInvoiceSum = (invoice) => {
|
||||||
let sum = 0
|
const totals = (invoice.accounts || []).reduce((result, account) => ({
|
||||||
invoice.accounts.forEach(account => {
|
net: result.net + Number(account.amountNet || 0),
|
||||||
|
tax: result.tax + Number(account.amountTax || 0)
|
||||||
|
}), { net: 0, tax: 0 })
|
||||||
|
const tax = invoice.taxAmountOverride !== null && invoice.taxAmountOverride !== undefined && invoice.taxAmountOverride !== ""
|
||||||
|
? Number(invoice.taxAmountOverride)
|
||||||
|
: totals.tax
|
||||||
|
|
||||||
|
return (totals.net + tax).toFixed(2)
|
||||||
sum += account.amountTax
|
|
||||||
sum += account.amountNet
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
return sum.toFixed(2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCreatedDocumentSum = (createddocument,createddocuments = []) => {
|
const getCreatedDocumentSum = (createddocument,createddocuments = []) => {
|
||||||
|
|||||||
@@ -152,6 +152,12 @@ export const getIncomingInvoiceTaxBreakdown = (invoice: any) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (invoice?.taxAmountOverride !== null && invoice?.taxAmountOverride !== undefined && invoice?.taxAmountOverride !== "") {
|
||||||
|
const correction = Number(invoice.taxAmountOverride) - breakdown.tax19 - breakdown.tax7
|
||||||
|
if (breakdown.tax19 !== 0 || breakdown.net19 !== 0) breakdown.tax19 += correction
|
||||||
|
else if (breakdown.tax7 !== 0 || breakdown.net7 !== 0) breakdown.tax7 += correction
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
net19: Number(breakdown.net19.toFixed(2)),
|
net19: Number(breakdown.net19.toFixed(2)),
|
||||||
tax19: Number(breakdown.tax19.toFixed(2)),
|
tax19: Number(breakdown.tax19.toFixed(2)),
|
||||||
@@ -160,3 +166,33 @@ export const getIncomingInvoiceTaxBreakdown = (invoice: any) => {
|
|||||||
net0: Number(breakdown.net0.toFixed(2)),
|
net0: Number(breakdown.net0.toFixed(2)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSkr03AccountNumber = (account: any) => {
|
||||||
|
if (!account || String(account.accountChart || "").toLowerCase() !== "skr03") return null
|
||||||
|
return String(account.number || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getManualBookingTaxBreakdown = (booking: any) => {
|
||||||
|
const amount = Number(booking?.amount || 0)
|
||||||
|
const breakdown = { outputTax19: 0, inputTax19: 0 }
|
||||||
|
|
||||||
|
if (!Number.isFinite(amount) || amount === 0) return breakdown
|
||||||
|
|
||||||
|
const applySide = (account: any, side: "debit" | "credit") => {
|
||||||
|
const accountNumber = getSkr03AccountNumber(account)
|
||||||
|
|
||||||
|
if (accountNumber === "1576") {
|
||||||
|
breakdown.inputTax19 += side === "debit" ? amount : -amount
|
||||||
|
} else if (accountNumber === "1776") {
|
||||||
|
breakdown.outputTax19 += side === "credit" ? amount : -amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applySide(booking.account, "debit")
|
||||||
|
applySide(booking.contraAccount, "credit")
|
||||||
|
|
||||||
|
return {
|
||||||
|
outputTax19: Number(breakdown.outputTax19.toFixed(2)),
|
||||||
|
inputTax19: Number(breakdown.inputTax19.toFixed(2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,9 +46,7 @@ const currentBalance = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getIncomingInvoiceGross = (invoice) => {
|
const getIncomingInvoiceGross = (invoice) => {
|
||||||
return Number((invoice.accounts || []).reduce((sum, account) => {
|
return Number(useSum().getIncomingInvoiceSum(invoice))
|
||||||
return sum + Number(account.amountNet || 0) + Number(account.amountTax || 0)
|
|
||||||
}, 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIncomingInvoiceOpenAmount = (invoice) => {
|
const getIncomingInvoiceOpenAmount = (invoice) => {
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ const buildEntries = (rows, type, labelBuilder) =>
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const getIncomingInvoiceGross = (invoice) => {
|
const getIncomingInvoiceGross = (invoice) => {
|
||||||
return Number((invoice.accounts || []).reduce((sum, account) => {
|
return Number(useSum().getIncomingInvoiceSum(invoice))
|
||||||
return sum + Number(account.amountNet || 0) + Number(account.amountTax || 0)
|
|
||||||
}, 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIncomingInvoiceOpenAmount = (invoice) => {
|
const getIncomingInvoiceOpenAmount = (invoice) => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
formatTaxEvaluationPeriodRange,
|
formatTaxEvaluationPeriodRange,
|
||||||
getCreatedDocumentTaxBreakdown,
|
getCreatedDocumentTaxBreakdown,
|
||||||
getIncomingInvoiceTaxBreakdown,
|
getIncomingInvoiceTaxBreakdown,
|
||||||
|
getManualBookingTaxBreakdown,
|
||||||
getTaxEvaluationPeriodBounds,
|
getTaxEvaluationPeriodBounds,
|
||||||
normalizeTaxEvaluationPeriod,
|
normalizeTaxEvaluationPeriod,
|
||||||
shiftTaxEvaluationPeriodStart
|
shiftTaxEvaluationPeriodStart
|
||||||
@@ -18,6 +19,7 @@ const auth = useAuthStore()
|
|||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const createdDocuments = ref<any[]>([])
|
const createdDocuments = ref<any[]>([])
|
||||||
const incomingInvoices = ref<any[]>([])
|
const incomingInvoices = ref<any[]>([])
|
||||||
|
const manualBookings = ref<any[]>([])
|
||||||
|
|
||||||
const periodType = computed(() => normalizeTaxEvaluationPeriod(auth.activeTenantData?.taxEvaluationPeriod))
|
const periodType = computed(() => normalizeTaxEvaluationPeriod(auth.activeTenantData?.taxEvaluationPeriod))
|
||||||
|
|
||||||
@@ -40,13 +42,15 @@ const loadData = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [docs, incoming] = await Promise.all([
|
const [docs, incoming, manual] = await Promise.all([
|
||||||
useEntities("createddocuments").select(),
|
useEntities("createddocuments").select(),
|
||||||
useEntities("incominginvoices").select()
|
useEntities("incominginvoices").select(),
|
||||||
|
useNuxtApp().$api("/api/banking/manual-bookings")
|
||||||
])
|
])
|
||||||
|
|
||||||
createdDocuments.value = (docs || []).filter(isRelevantOutputDocument)
|
createdDocuments.value = (docs || []).filter(isRelevantOutputDocument)
|
||||||
incomingInvoices.value = (incoming || []).filter(isRelevantInputInvoice)
|
incomingInvoices.value = (incoming || []).filter(isRelevantInputInvoice)
|
||||||
|
manualBookings.value = (manual as any[]) || []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -69,6 +73,19 @@ const periods = computed(() => {
|
|||||||
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const manualTax = manualBookings.value
|
||||||
|
.filter((booking) => {
|
||||||
|
const date = dayjs(booking.manualBookingDate)
|
||||||
|
return date.isValid() && !date.isBefore(bounds.start, "day") && !date.isAfter(bounds.end, "day")
|
||||||
|
})
|
||||||
|
.reduce((sum, booking) => {
|
||||||
|
const breakdown = getManualBookingTaxBreakdown(booking)
|
||||||
|
return {
|
||||||
|
outputTax19: sum.outputTax19 + breakdown.outputTax19,
|
||||||
|
inputTax19: sum.inputTax19 + breakdown.inputTax19,
|
||||||
|
}
|
||||||
|
}, { outputTax19: 0, inputTax19: 0 })
|
||||||
|
|
||||||
const output = outputDocs.reduce((sum, doc) => {
|
const output = outputDocs.reduce((sum, doc) => {
|
||||||
const breakdown = getCreatedDocumentTaxBreakdown(doc)
|
const breakdown = getCreatedDocumentTaxBreakdown(doc)
|
||||||
return {
|
return {
|
||||||
@@ -91,6 +108,9 @@ const periods = computed(() => {
|
|||||||
}
|
}
|
||||||
}, { net19: 0, tax19: 0, net7: 0, tax7: 0, net0: 0 })
|
}, { net19: 0, tax19: 0, net7: 0, tax7: 0, net0: 0 })
|
||||||
|
|
||||||
|
output.tax19 = Number((output.tax19 + manualTax.outputTax19).toFixed(2))
|
||||||
|
input.tax19 = Number((input.tax19 + manualTax.inputTax19).toFixed(2))
|
||||||
|
|
||||||
const outputTax = Number((output.tax19 + output.tax7).toFixed(2))
|
const outputTax = Number((output.tax19 + output.tax7).toFixed(2))
|
||||||
const inputTax = Number((input.tax19 + input.tax7).toFixed(2))
|
const inputTax = Number((input.tax19 + input.tax7).toFixed(2))
|
||||||
const balance = Number((outputTax - inputTax).toFixed(2))
|
const balance = Number((outputTax - inputTax).toFixed(2))
|
||||||
@@ -146,7 +166,7 @@ onMounted(loadData)
|
|||||||
</h2>
|
</h2>
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
Intervall: {{ periodType === "monthly" ? "monatlich" : periodType === "quarterly" ? "quartalsweise" : "jährlich" }}.
|
Intervall: {{ periodType === "monthly" ? "monatlich" : periodType === "quarterly" ? "quartalsweise" : "jährlich" }}.
|
||||||
Berücksichtigt werden gebuchte Ausgangsrechnungen, Abschlags- und Stornorechnungen sowie gebuchte Eingangsbelege mit Datum.
|
Berücksichtigt werden gebuchte Ausgangsrechnungen, Abschlags- und Stornorechnungen, gebuchte Eingangsbelege sowie manuelle Buchungen auf SKR03 1576 und 1776.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="currentPeriod" class="text-sm text-gray-500 dark:text-gray-400">
|
<p v-if="currentPeriod" class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
{{ currentPeriod.range }}
|
{{ currentPeriod.range }}
|
||||||
|
|||||||
@@ -230,13 +230,7 @@ const calculateOpenSum = (statement) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getInvoiceSum = (invoice, onlyOpenSum) => {
|
const getInvoiceSum = (invoice, onlyOpenSum) => {
|
||||||
let sum = 0
|
let sum = Number(useSum().getIncomingInvoiceSum(invoice))
|
||||||
if (invoice.accounts) {
|
|
||||||
invoice.accounts.forEach(account => {
|
|
||||||
sum += (account.amountTax || 0)
|
|
||||||
sum += (account.amountNet || 0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onlyOpenSum) sum = sum + Number(invoice.statementallocations.reduce((n, {amount}) => n + amount, 0))
|
if (onlyOpenSum) sum = sum + Number(invoice.statementallocations.reduce((n, {amount}) => n + amount, 0))
|
||||||
|
|
||||||
|
|||||||
@@ -130,13 +130,7 @@ const separateIBAN = (input) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getInvoiceSum = (invoice, onlyOpenSum) => {
|
const getInvoiceSum = (invoice, onlyOpenSum) => {
|
||||||
let sum = 0
|
let sum = Number(useSum().getIncomingInvoiceSum(invoice))
|
||||||
if (invoice.accounts) {
|
|
||||||
invoice.accounts.forEach(account => {
|
|
||||||
sum += (account.amountTax || 0)
|
|
||||||
sum += (account.amountNet || 0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onlyOpenSum) sum = sum + Number(invoice.statementallocations.reduce((n, {amount}) => n + amount, 0))
|
if (onlyOpenSum) sum = sum + Number(invoice.statementallocations.reduce((n, {amount}) => n + amount, 0))
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ watch(() => itemInfo.value.date, (value) => {
|
|||||||
|
|
||||||
// --- Berechnungslogik ---
|
// --- Berechnungslogik ---
|
||||||
const useNetMode = ref(false)
|
const useNetMode = ref(false)
|
||||||
|
const editingTotalTax = ref(false)
|
||||||
|
const totalTaxDraft = ref(null)
|
||||||
|
|
||||||
const taxOptions = ref([
|
const taxOptions = ref([
|
||||||
{ label: "19% USt", percentage: 19, key: "19" },
|
{ label: "19% USt", percentage: 19, key: "19" },
|
||||||
@@ -186,24 +188,17 @@ const totalCalculated = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
totalGross = Number(totalNet + totalAmount19Tax + totalAmount7Tax)
|
const calculatedTax = Number(totalAmount19Tax + totalAmount7Tax)
|
||||||
|
const hasTaxOverride = itemInfo.value.taxAmountOverride !== null && itemInfo.value.taxAmountOverride !== undefined && itemInfo.value.taxAmountOverride !== ""
|
||||||
|
const totalTax = hasTaxOverride ? Number(itemInfo.value.taxAmountOverride) : calculatedTax
|
||||||
|
totalGross = Number(totalNet + totalTax)
|
||||||
|
|
||||||
return { totalNet, totalAmount19Tax, totalAmount7Tax, totalGross }
|
return { totalNet, totalAmount19Tax, totalAmount7Tax, calculatedTax, totalTax, totalGross, hasTaxOverride }
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasAmount = (value) => value !== null && value !== undefined && value !== ""
|
const hasAmount = (value) => value !== null && value !== undefined && value !== ""
|
||||||
const hasValidNumber = (value) => hasAmount(value) && Number.isFinite(Number(value))
|
const hasValidNumber = (value) => hasAmount(value) && Number.isFinite(Number(value))
|
||||||
const isDepreciationItem = (item) => isDepreciationBookingMode(item?.bookingMode)
|
const isDepreciationItem = (item) => isDepreciationBookingMode(item?.bookingMode)
|
||||||
const getCalculatedTax = (item) => {
|
|
||||||
const taxRate = Number(taxOptions.value.find((tax) => tax.key === item.taxType)?.percentage || 0)
|
|
||||||
|
|
||||||
return Number((Number(item.amountNet || 0) * (taxRate / 100)).toFixed(2))
|
|
||||||
}
|
|
||||||
const hasManualTaxDifference = (item) => {
|
|
||||||
if (!hasValidNumber(item.amountTax) || !hasValidNumber(item.amountNet)) return false
|
|
||||||
|
|
||||||
return Math.abs(Number(item.amountTax) - getCalculatedTax(item)) >= 0.005
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateBookingMode = (item) => {
|
const updateBookingMode = (item) => {
|
||||||
ensureDepreciationDefaults(item, itemInfo.value.date)
|
ensureDepreciationDefaults(item, itemInfo.value.date)
|
||||||
@@ -226,14 +221,6 @@ const recalculateItem = (item, source) => {
|
|||||||
calculateFromNet()
|
calculateFromNet()
|
||||||
} else if (source === 'gross') {
|
} else if (source === 'gross') {
|
||||||
calculateFromGross()
|
calculateFromGross()
|
||||||
} else if (source === 'tax') {
|
|
||||||
if(!hasValidNumber(item.amountTax)) return
|
|
||||||
|
|
||||||
if((useNetMode.value || !hasAmount(item.amountGross)) && hasAmount(item.amountNet)) {
|
|
||||||
item.amountGross = Number((Number(item.amountNet) + Number(item.amountTax)).toFixed(2))
|
|
||||||
} else if(hasAmount(item.amountGross)) {
|
|
||||||
item.amountNet = Number((Number(item.amountGross) - Number(item.amountTax)).toFixed(2))
|
|
||||||
}
|
|
||||||
} else if (source === 'taxType' || source === 'manual') {
|
} else if (source === 'taxType' || source === 'manual') {
|
||||||
if(hasAmount(item.amountNet)) calculateFromNet()
|
if(hasAmount(item.amountNet)) calculateFromNet()
|
||||||
else if(hasAmount(item.amountGross)) calculateFromGross()
|
else if(hasAmount(item.amountGross)) calculateFromGross()
|
||||||
@@ -246,6 +233,19 @@ const moveGrossToNet = (item) => {
|
|||||||
recalculateItem(item, 'net')
|
recalculateItem(item, 'net')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startEditingTotalTax = () => {
|
||||||
|
totalTaxDraft.value = totalCalculated.value.totalTax.toFixed(2)
|
||||||
|
editingTotalTax.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyTotalTaxOverride = () => {
|
||||||
|
if (!hasValidNumber(totalTaxDraft.value)) return
|
||||||
|
|
||||||
|
const value = Number(Number(totalTaxDraft.value).toFixed(2))
|
||||||
|
itemInfo.value.taxAmountOverride = Math.abs(value - totalCalculated.value.calculatedTax) >= 0.005 ? value : null
|
||||||
|
editingTotalTax.value = false
|
||||||
|
}
|
||||||
|
|
||||||
// --- Saving ---
|
// --- Saving ---
|
||||||
const updateIncomingInvoice = async (setBooked = false) => {
|
const updateIncomingInvoice = async (setBooked = false) => {
|
||||||
if (setBooked && hasBlockingIncomingInvoiceErrors.value) {
|
if (setBooked && hasBlockingIncomingInvoiceErrors.value) {
|
||||||
@@ -889,30 +889,13 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-6 md:col-span-3">
|
<div class="col-span-6 md:col-span-3">
|
||||||
<UFormField label="Steuerbetrag" help="Kann bei Rundungsabweichungen angepasst werden">
|
<UFormField label="Steuerbetrag" help="Automatisch berechnet">
|
||||||
<UInput
|
<UInput class="w-full" :model-value="item.amountTax" disabled color="gray">
|
||||||
class="w-full"
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
:model-value="item.amountTax"
|
|
||||||
:disabled="mode === 'show'"
|
|
||||||
@update:model-value="(val) => { item.amountTax = Number(val); recalculateItem(item, 'tax') }"
|
|
||||||
>
|
|
||||||
<template #trailing>€</template>
|
<template #trailing>€</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="hasManualTaxDifference(item)" class="col-span-12">
|
|
||||||
<UAlert
|
|
||||||
color="warning"
|
|
||||||
variant="soft"
|
|
||||||
icon="i-heroicons-exclamation-triangle"
|
|
||||||
title="Steuerbetrag manuell angepasst"
|
|
||||||
description="FEDEO verwendet diesen Betrag für Auswertungen und Summen. Im DATEV-Export wird bei der Automatikbuchung nur der Bruttobetrag mit Steuerschlüssel übertragen; DATEV berechnet die Steuer dort erneut."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-12 flex justify-end gap-2">
|
<div class="col-span-12 flex justify-end gap-2">
|
||||||
<UButton
|
<UButton
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -958,14 +941,34 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
<span>Netto Gesamt</span>
|
<span>Netto Gesamt</span>
|
||||||
<span>{{ totalCalculated.totalNet.toFixed(2) }} €</span>
|
<span>{{ totalCalculated.totalNet.toFixed(2) }} €</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-gray-500" v-if="totalCalculated.totalAmount7Tax > 0">
|
<div class="flex items-center justify-between gap-3 text-gray-500">
|
||||||
<span>+ 7% USt</span>
|
<span class="flex items-center gap-2">
|
||||||
<span>{{ totalCalculated.totalAmount7Tax.toFixed(2) }} €</span>
|
USt gesamt
|
||||||
</div>
|
<UBadge v-if="totalCalculated.hasTaxOverride" size="xs" color="warning" variant="soft">Manuell</UBadge>
|
||||||
<div class="flex justify-between text-gray-500" v-if="totalCalculated.totalAmount19Tax > 0">
|
</span>
|
||||||
<span>+ 19% USt</span>
|
<div v-if="editingTotalTax" class="flex items-center gap-1">
|
||||||
<span>{{ totalCalculated.totalAmount19Tax.toFixed(2) }} €</span>
|
<UInput v-model="totalTaxDraft" type="number" step="0.01" size="xs" class="w-28">
|
||||||
|
<template #trailing>€</template>
|
||||||
|
</UInput>
|
||||||
|
<UButton icon="i-heroicons-check" size="xs" variant="ghost" @click="applyTotalTaxOverride" />
|
||||||
|
<UButton icon="i-heroicons-x-mark" size="xs" color="neutral" variant="ghost" @click="editingTotalTax = false" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex items-center gap-1">
|
||||||
|
<span>{{ totalCalculated.totalTax.toFixed(2) }} €</span>
|
||||||
|
<UButton
|
||||||
|
v-if="mode !== 'show'"
|
||||||
|
icon="i-heroicons-pencil"
|
||||||
|
size="xs"
|
||||||
|
color="neutral"
|
||||||
|
variant="ghost"
|
||||||
|
aria-label="Gesamten USt-Betrag bearbeiten"
|
||||||
|
@click="startEditingTotalTax"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="totalCalculated.hasTaxOverride" class="text-xs text-amber-600 dark:text-amber-400">
|
||||||
|
DATEV berechnet die USt aus dem Bruttobetrag erneut. Der Export wird mit „USt prüfen“ markiert.
|
||||||
|
</p>
|
||||||
<div class="flex justify-between font-bold text-xl text-gray-900 dark:text-white pt-2 border-t dark:border-gray-700">
|
<div class="flex justify-between font-bold text-xl text-gray-900 dark:text-white pt-2 border-t dark:border-gray-700">
|
||||||
<span>Rechnungsbetrag</span>
|
<span>Rechnungsbetrag</span>
|
||||||
<span>{{ totalCalculated.totalGross.toFixed(2) }} €</span>
|
<span>{{ totalCalculated.totalGross.toFixed(2) }} €</span>
|
||||||
|
|||||||
@@ -141,12 +141,7 @@ const displayCurrency = (value, currency = "€") => {
|
|||||||
return `${Number(value).toFixed(2).replace(".",",")} ${currency}`
|
return `${Number(value).toFixed(2).replace(".",",")} ${currency}`
|
||||||
}
|
}
|
||||||
const getInvoiceSum = (invoice) => {
|
const getInvoiceSum = (invoice) => {
|
||||||
let sum = 0
|
return useSum().getIncomingInvoiceSum(invoice)
|
||||||
invoice.accounts.forEach(account => {
|
|
||||||
sum += account.amountTax
|
|
||||||
sum += account.amountNet
|
|
||||||
})
|
|
||||||
return sum.toFixed(2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPaidAmount = (item) => {
|
const getPaidAmount = (item) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user