Manuelle Buchungen um zuweisbare Eingangsbelege erweitern
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "statementallocations" ADD COLUMN "manual_invoice_side" text;
|
||||||
@@ -225,6 +225,13 @@
|
|||||||
"when": 1776298200000,
|
"when": 1776298200000,
|
||||||
"tag": "0031_manual_statementallocations_tax_key",
|
"tag": "0031_manual_statementallocations_tax_key",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 32,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1776298800000,
|
||||||
|
"tag": "0032_manual_statementallocations_invoice_side",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export const statementallocations = pgTable("statementallocations", {
|
|||||||
incominginvoice: bigint("ii_id", { mode: "number" }).references(
|
incominginvoice: bigint("ii_id", { mode: "number" }).references(
|
||||||
() => incominginvoices.id
|
() => incominginvoices.id
|
||||||
),
|
),
|
||||||
|
manualInvoiceSide: text("manual_invoice_side"),
|
||||||
|
|
||||||
tenant: bigint("tenant", { mode: "number" })
|
tenant: bigint("tenant", { mode: "number" })
|
||||||
.notNull()
|
.notNull()
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export default async function bankingRoutes(server: FastifyInstance) {
|
|||||||
const ContraCustomers = aliasedTable(customers, "contra_customers")
|
const ContraCustomers = aliasedTable(customers, "contra_customers")
|
||||||
const ContraVendors = aliasedTable(vendors, "contra_vendors")
|
const ContraVendors = aliasedTable(vendors, "contra_vendors")
|
||||||
const ContraOwnaccounts = aliasedTable(ownaccounts, "contra_ownaccounts")
|
const ContraOwnaccounts = aliasedTable(ownaccounts, "contra_ownaccounts")
|
||||||
|
const ManualInvoices = aliasedTable(incominginvoices, "manual_invoices")
|
||||||
|
const ManualInvoiceVendors = aliasedTable(vendors, "manual_invoice_vendors")
|
||||||
|
|
||||||
const normalizeManualSide = (payload: any, keys: string[]) =>
|
const normalizeManualSide = (payload: any, keys: string[]) =>
|
||||||
keys.filter((key) => payload[key] !== null && payload[key] !== undefined && payload[key] !== "")
|
keys.filter((key) => payload[key] !== null && payload[key] !== undefined && payload[key] !== "")
|
||||||
@@ -49,14 +51,24 @@ export default async function bankingRoutes(server: FastifyInstance) {
|
|||||||
next.contraVendor = null
|
next.contraVendor = null
|
||||||
next.contraOwnaccount = null
|
next.contraOwnaccount = null
|
||||||
next.datevTaxKey = next.datevTaxKey ? String(next.datevTaxKey).trim() : null
|
next.datevTaxKey = next.datevTaxKey ? String(next.datevTaxKey).trim() : null
|
||||||
|
next.manualInvoiceSide = null
|
||||||
return { data: next }
|
return { data: next }
|
||||||
}
|
}
|
||||||
|
|
||||||
const debitKeys = ["account", "customer", "vendor", "ownaccount"]
|
const debitKeys = ["account", "customer", "vendor", "ownaccount"]
|
||||||
const creditKeys = ["contraAccount", "contraCustomer", "contraVendor", "contraOwnaccount"]
|
const creditKeys = ["contraAccount", "contraCustomer", "contraVendor", "contraOwnaccount"]
|
||||||
|
const hasManualInvoice = next.incominginvoice !== null && next.incominginvoice !== undefined && next.incominginvoice !== ""
|
||||||
const debitSide = normalizeManualSide(next, debitKeys)
|
const debitSide = normalizeManualSide(next, debitKeys)
|
||||||
const creditSide = normalizeManualSide(next, creditKeys)
|
const creditSide = normalizeManualSide(next, creditKeys)
|
||||||
|
|
||||||
|
if (hasManualInvoice) {
|
||||||
|
if (next.manualInvoiceSide === "debit") debitSide.push("incominginvoice")
|
||||||
|
else if (next.manualInvoiceSide === "credit") creditSide.push("incominginvoice")
|
||||||
|
else return { error: "Für zugewiesene Eingangsbelege muss Soll oder Haben ausgewählt sein." }
|
||||||
|
} else {
|
||||||
|
next.manualInvoiceSide = null
|
||||||
|
}
|
||||||
|
|
||||||
if (!next.manualBookingDate || !dayjs(next.manualBookingDate).isValid()) {
|
if (!next.manualBookingDate || !dayjs(next.manualBookingDate).isValid()) {
|
||||||
return { error: "Für manuelle Buchungen ist ein gültiges Buchungsdatum erforderlich." }
|
return { error: "Für manuelle Buchungen ist ein gültiges Buchungsdatum erforderlich." }
|
||||||
}
|
}
|
||||||
@@ -745,6 +757,8 @@ export default async function bankingRoutes(server: FastifyInstance) {
|
|||||||
contraCustomer: ContraCustomers,
|
contraCustomer: ContraCustomers,
|
||||||
contraVendor: ContraVendors,
|
contraVendor: ContraVendors,
|
||||||
contraOwnaccount: ContraOwnaccounts,
|
contraOwnaccount: ContraOwnaccounts,
|
||||||
|
incominginvoice: ManualInvoices,
|
||||||
|
incominginvoiceVendor: ManualInvoiceVendors,
|
||||||
})
|
})
|
||||||
.from(statementallocations)
|
.from(statementallocations)
|
||||||
.leftJoin(accounts, eq(statementallocations.account, accounts.id))
|
.leftJoin(accounts, eq(statementallocations.account, accounts.id))
|
||||||
@@ -755,6 +769,8 @@ export default async function bankingRoutes(server: FastifyInstance) {
|
|||||||
.leftJoin(ContraCustomers, eq(statementallocations.contraCustomer, ContraCustomers.id))
|
.leftJoin(ContraCustomers, eq(statementallocations.contraCustomer, ContraCustomers.id))
|
||||||
.leftJoin(ContraVendors, eq(statementallocations.contraVendor, ContraVendors.id))
|
.leftJoin(ContraVendors, eq(statementallocations.contraVendor, ContraVendors.id))
|
||||||
.leftJoin(ContraOwnaccounts, eq(statementallocations.contraOwnaccount, ContraOwnaccounts.id))
|
.leftJoin(ContraOwnaccounts, eq(statementallocations.contraOwnaccount, ContraOwnaccounts.id))
|
||||||
|
.leftJoin(ManualInvoices, eq(statementallocations.incominginvoice, ManualInvoices.id))
|
||||||
|
.leftJoin(ManualInvoiceVendors, eq(ManualInvoices.vendor, ManualInvoiceVendors.id))
|
||||||
.where(and(
|
.where(and(
|
||||||
eq(statementallocations.tenant, req.user.tenant_id),
|
eq(statementallocations.tenant, req.user.tenant_id),
|
||||||
eq(statementallocations.archived, false),
|
eq(statementallocations.archived, false),
|
||||||
@@ -771,6 +787,10 @@ export default async function bankingRoutes(server: FastifyInstance) {
|
|||||||
contraCustomer: row.contraCustomer,
|
contraCustomer: row.contraCustomer,
|
||||||
contraVendor: row.contraVendor,
|
contraVendor: row.contraVendor,
|
||||||
contraOwnaccount: row.contraOwnaccount,
|
contraOwnaccount: row.contraOwnaccount,
|
||||||
|
incominginvoice: row.incominginvoice ? {
|
||||||
|
...row.incominginvoice,
|
||||||
|
vendor: row.incominginvoiceVendor,
|
||||||
|
} : null,
|
||||||
})))
|
})))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|||||||
@@ -341,11 +341,20 @@ export async function buildExportZip(
|
|||||||
const vendor = side === "credit" ? alloc.contraVendor : alloc.vendor;
|
const vendor = side === "credit" ? alloc.contraVendor : alloc.vendor;
|
||||||
const customer = side === "credit" ? alloc.contraCustomer : alloc.customer;
|
const customer = side === "credit" ? alloc.contraCustomer : alloc.customer;
|
||||||
const ownaccount = side === "credit" ? alloc.contraOwnaccount : alloc.ownaccount;
|
const ownaccount = side === "credit" ? alloc.contraOwnaccount : alloc.ownaccount;
|
||||||
|
const incominginvoice = alloc.manualInvoiceSide === side ? alloc.incominginvoice : null;
|
||||||
|
|
||||||
if (account) return { number: account.number, name: account.label, type: "Sachkonto" };
|
if (account) return { number: account.number, name: account.label, type: "Sachkonto" };
|
||||||
if (vendor) return { number: vendor.vendorNumber, name: vendor.name, type: "Kreditor" };
|
if (vendor) return { number: vendor.vendorNumber, name: vendor.name, type: "Kreditor" };
|
||||||
if (customer) return { number: customer.customerNumber, name: customer.name, type: "Debitor" };
|
if (customer) return { number: customer.customerNumber, name: customer.name, type: "Debitor" };
|
||||||
if (ownaccount) return { number: ownaccount.number, name: ownaccount.name, type: "Eigenes Konto" };
|
if (ownaccount) return { number: ownaccount.number, name: ownaccount.name, type: "Eigenes Konto" };
|
||||||
|
if (incominginvoice) {
|
||||||
|
return {
|
||||||
|
number: incominginvoice.vendor?.vendorNumber || "",
|
||||||
|
name: `${incominginvoice.reference || "Eingangsbeleg"} ${incominginvoice.vendor?.name || ""}`.trim(),
|
||||||
|
type: "Eingangsbeleg",
|
||||||
|
reference: incominginvoice.reference || "",
|
||||||
|
};
|
||||||
|
}
|
||||||
return { number: "", name: "", type: prefix };
|
return { number: "", name: "", type: prefix };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -357,7 +366,8 @@ export async function buildExportZip(
|
|||||||
const credit = getManualBookingSide(alloc, "credit");
|
const credit = getManualBookingSide(alloc, "credit");
|
||||||
const dateManual = dayjs(alloc.manualBookingDate).format("DDMM");
|
const dateManual = dayjs(alloc.manualBookingDate).format("DDMM");
|
||||||
const dateManualFull = dayjs(alloc.manualBookingDate).format("DD.MM.YYYY");
|
const dateManualFull = dayjs(alloc.manualBookingDate).format("DD.MM.YYYY");
|
||||||
bookingLines.push(`${displayCurrency(alloc.amount,true)};"S";;;;;${debit.number};${credit.number};"${alloc.datevTaxKey || ""}";${dateManual};"";;;"${`MB ${debit.number} an ${credit.number} ${escapeString(alloc.description)}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${escapeString(debit.name || credit.name)}";"Kundennummer";"${debit.number}";"Belegnummer";"";"Leistungsdatum";"${dateManualFull}";"Belegdatum";"${dateManualFull}";;;;;;;;;;"";;;;;;;;Manuelle-Buchung;${alloc.id};;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;`);
|
const belegnummer = debit.reference || credit.reference || "";
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"S";;;;;${debit.number};${credit.number};"${alloc.datevTaxKey || ""}";${dateManual};"${belegnummer}";;;"${`MB ${debit.number} an ${credit.number} ${escapeString(alloc.description)}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${escapeString(debit.name || credit.name)}";"Kundennummer";"${debit.number}";"Belegnummer";"${belegnummer}";"Leistungsdatum";"${dateManualFull}";"Belegdatum";"${dateManualFull}";;;;;;;;;;"";;;;;;;;Manuelle-Buchung;${alloc.id};;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const accounts = ref([])
|
|||||||
const customers = ref([])
|
const customers = ref([])
|
||||||
const vendors = ref([])
|
const vendors = ref([])
|
||||||
const ownaccounts = ref([])
|
const ownaccounts = ref([])
|
||||||
|
const incomingInvoices = ref([])
|
||||||
const bookings = ref([])
|
const bookings = ref([])
|
||||||
const debitSearch = ref("")
|
const debitSearch = ref("")
|
||||||
const creditSearch = ref("")
|
const creditSearch = ref("")
|
||||||
@@ -51,8 +52,8 @@ const buildEntries = (rows, type, labelBuilder) =>
|
|||||||
key: `${type}:${item.id}`,
|
key: `${type}:${item.id}`,
|
||||||
id: item.id,
|
id: item.id,
|
||||||
type,
|
type,
|
||||||
number: item.number || item.vendorNumber || item.customerNumber || "",
|
number: item.number || item.vendorNumber || item.customerNumber || item.reference || "",
|
||||||
name: item.label || item.name || "",
|
name: item.label || item.name || item.vendor?.name || "",
|
||||||
label: labelBuilder(item),
|
label: labelBuilder(item),
|
||||||
typeLabel:
|
typeLabel:
|
||||||
type === "account"
|
type === "account"
|
||||||
@@ -61,9 +62,23 @@ const buildEntries = (rows, type, labelBuilder) =>
|
|||||||
? "Kreditoren"
|
? "Kreditoren"
|
||||||
: type === "customer"
|
: type === "customer"
|
||||||
? "Debitoren"
|
? "Debitoren"
|
||||||
|
: type === "incominginvoice"
|
||||||
|
? "Eingangsbelege"
|
||||||
: "Zusätzliche Konten"
|
: "Zusätzliche Konten"
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const getIncomingInvoiceGross = (invoice) => {
|
||||||
|
return Number((invoice.accounts || []).reduce((sum, account) => {
|
||||||
|
return sum + Number(account.amountNet || 0) + Number(account.amountTax || 0)
|
||||||
|
}, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIncomingInvoiceOpenAmount = (invoice) => {
|
||||||
|
const gross = getIncomingInvoiceGross(invoice)
|
||||||
|
const allocated = Number((invoice.statementallocations || []).reduce((sum, allocation) => sum + Number(allocation.amount || 0), 0))
|
||||||
|
return Math.abs(gross) - Math.abs(allocated)
|
||||||
|
}
|
||||||
|
|
||||||
const entryGroups = computed(() => ([
|
const entryGroups = computed(() => ([
|
||||||
{
|
{
|
||||||
key: "account",
|
key: "account",
|
||||||
@@ -84,6 +99,11 @@ const entryGroups = computed(() => ([
|
|||||||
key: "ownaccount",
|
key: "ownaccount",
|
||||||
label: "Zusätzliche Konten",
|
label: "Zusätzliche Konten",
|
||||||
entries: buildEntries(ownaccounts.value, "ownaccount", (item) => `${item.number} - ${item.name}`)
|
entries: buildEntries(ownaccounts.value, "ownaccount", (item) => `${item.number} - ${item.name}`)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "incominginvoice",
|
||||||
|
label: "Eingangsbelege",
|
||||||
|
entries: buildEntries(incomingInvoices.value, "incominginvoice", (item) => `${item.reference || "Ohne Referenz"} - ${item.vendor?.name || "Ohne Lieferant"} - Offen ${displayCurrency(getIncomingInvoiceOpenAmount(item))}`)
|
||||||
}
|
}
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -129,6 +149,14 @@ const selectedCredit = computed(() => allEntries.value.find((item) => item.key =
|
|||||||
const selectedTaxKey = computed(() => DATEV_TAX_KEY_ITEMS.find((item) => item.value === form.datevTaxKey))
|
const selectedTaxKey = computed(() => DATEV_TAX_KEY_ITEMS.find((item) => item.value === form.datevTaxKey))
|
||||||
|
|
||||||
const getBookingSide = (booking, side) => {
|
const getBookingSide = (booking, side) => {
|
||||||
|
if (booking.incominginvoice && booking.manualInvoiceSide === side) {
|
||||||
|
return {
|
||||||
|
type: "Eingangsbeleg",
|
||||||
|
number: booking.incominginvoice?.reference || "",
|
||||||
|
name: booking.incominginvoice?.vendor?.name || booking.incominginvoice?.description || ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const map = side === "credit"
|
const map = side === "credit"
|
||||||
? [
|
? [
|
||||||
["contraAccount", "Sachkonto"],
|
["contraAccount", "Sachkonto"],
|
||||||
@@ -161,6 +189,12 @@ const buildSidePayload = (sideKey, target) => {
|
|||||||
if (!type || !id) return
|
if (!type || !id) return
|
||||||
|
|
||||||
const numericId = type === "ownaccount" ? id : Number(id)
|
const numericId = type === "ownaccount" ? id : Number(id)
|
||||||
|
if (type === "incominginvoice") {
|
||||||
|
return {
|
||||||
|
incominginvoice: numericId,
|
||||||
|
manualInvoiceSide: target
|
||||||
|
}
|
||||||
|
}
|
||||||
if (target === "debit") {
|
if (target === "debit") {
|
||||||
if (type === "account") return { account: numericId }
|
if (type === "account") return { account: numericId }
|
||||||
if (type === "customer") return { customer: numericId }
|
if (type === "customer") return { customer: numericId }
|
||||||
@@ -176,11 +210,12 @@ const buildSidePayload = (sideKey, target) => {
|
|||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const [accountRows, customerRows, vendorRows, ownaccountRows, bookingRows] = await Promise.all([
|
const [accountRows, customerRows, vendorRows, ownaccountRows, incomingInvoiceRows, bookingRows] = await Promise.all([
|
||||||
useEntities("accounts").selectSpecial("*", "number", true),
|
useEntities("accounts").selectSpecial("*", "number", true),
|
||||||
useEntities("customers").select(),
|
useEntities("customers").select(),
|
||||||
useEntities("vendors").select(),
|
useEntities("vendors").select(),
|
||||||
useEntities("ownaccounts").select(),
|
useEntities("ownaccounts").select(),
|
||||||
|
useEntities("incominginvoices").select("*, vendor(*), statementallocations(id,amount)"),
|
||||||
useNuxtApp().$api("/api/banking/manual-bookings")
|
useNuxtApp().$api("/api/banking/manual-bookings")
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -188,6 +223,9 @@ const loadData = async () => {
|
|||||||
customers.value = customerRows || []
|
customers.value = customerRows || []
|
||||||
vendors.value = vendorRows || []
|
vendors.value = vendorRows || []
|
||||||
ownaccounts.value = ownaccountRows || []
|
ownaccounts.value = ownaccountRows || []
|
||||||
|
incomingInvoices.value = (incomingInvoiceRows || [])
|
||||||
|
.filter((invoice) => invoice.state === "Gebucht" && !invoice.archived)
|
||||||
|
.filter((invoice) => getIncomingInvoiceOpenAmount(invoice) > 0.004)
|
||||||
bookings.value = (bookingRows || []).sort((a, b) => String(b.manualBookingDate || "").localeCompare(String(a.manualBookingDate || "")))
|
bookings.value = (bookingRows || []).sort((a, b) => String(b.manualBookingDate || "").localeCompare(String(a.manualBookingDate || "")))
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user