Files
FEDEO/backend/db/schema/statementallocations.ts
florianfederspiel af4a283eb5 feat(accounting): Skonto bei Zahlungen auf Ausgangs- und Eingangsbelege buchen
Skonto wird an der Bankzuordnung gespeichert und gleicht den Beleg zusammen
mit der Zahlung aus. Der Betrag wird anteilig nach Steuersätzen auf die
Skontokonten verteilt (Standard SKR03, je Mandant einstellbar), im
DATEV-Export als eigene Buchung je Steuersatz ausgegeben und in der
USt-Auswertung im Zeitraum der Zahlung berücksichtigt. Schlussrechnungen
verteilen den Skonto auf die Restforderung nach Abzug der Abschläge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 11:31:47 +01:00

99 lines
3.2 KiB
TypeScript

import {
pgTable,
uuid,
bigint,
integer,
text,
timestamp,
boolean,
doublePrecision,
jsonb,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { customers } from "./customers"
import { vendors } from "./vendors"
import { ownaccounts } from "./ownaccounts"
import { incominginvoices } from "./incominginvoices"
import { createddocuments } from "./createddocuments"
import { bankstatements } from "./bankstatements"
import { accounts } from "./accounts" // Falls noch nicht erstellt → bitte melden!
import { costcentres } from "./costcentres"
export const statementallocations = pgTable("statementallocations", {
id: uuid("id").primaryKey().defaultRandom(),
// foreign keys
bankstatement: integer("bs_id").references(() => bankstatements.id),
createddocument: integer("cd_id").references(() => createddocuments.id),
amount: doublePrecision("amount").notNull().default(0),
// Skonto mit dem Vorzeichen der Zahlung; amount bleibt der tatsächlich gezahlte Betrag.
cashDiscountAmount: doublePrecision("cash_discount_amount"),
cashDiscountSplit: jsonb("cash_discount_split"),
incominginvoice: bigint("ii_id", { mode: "number" }).references(
() => incominginvoices.id
),
manualInvoiceSide: text("manual_invoice_side"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
account: bigint("account", { mode: "number" }).references(
() => accounts.id
),
contraAccount: bigint("contra_account", { mode: "number" }).references(
() => accounts.id
),
created_at: timestamp("created_at", {
withTimezone: false,
}).defaultNow(),
ownaccount: uuid("ownaccount").references(() => ownaccounts.id),
contraOwnaccount: uuid("contra_ownaccount").references(() => ownaccounts.id),
description: text("description"),
manualBookingDate: text("manual_booking_date"),
datevTaxKey: text("datev_tax_key"),
costcentre: uuid("costcentre").references(() => costcentres.id),
bookingMode: text("booking_mode").notNull().default("expense"),
depreciationMonths: integer("depreciation_months"),
depreciationStartDate: text("depreciation_start_date"),
depreciationMethod: text("depreciation_method"),
depreciationLabel: text("depreciation_label"),
depreciationGroup: text("depreciation_group"),
residualValue: doublePrecision("residual_value"),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
contraCustomer: bigint("contra_customer", { mode: "number" }).references(
() => customers.id
),
contraVendor: bigint("contra_vendor", { mode: "number" }).references(() => vendors.id),
updated_at: timestamp("updated_at", { withTimezone: true }),
updated_by: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type StatementAllocation = typeof statementallocations.$inferSelect
export type NewStatementAllocation =
typeof statementallocations.$inferInsert