Initial for #123

This commit is contained in:
2026-02-21 22:21:10 +01:00
parent d27e437ba6
commit 27be8241bf
9 changed files with 98 additions and 7 deletions

View File

@@ -127,6 +127,13 @@
"when": 1771704862789,
"tag": "0017_slow_the_hood",
"breakpoints": true
},
{
"idx": 18,
"version": "7",
"when": 1773000900000,
"tag": "0018_account_chart",
"breakpoints": true
}
]
}

View File

@@ -16,6 +16,7 @@ export const accounts = pgTable("accounts", {
number: text("number").notNull(),
label: text("label").notNull(),
accountChart: text("accountChart").notNull().default("skr03"),
description: text("description"),
})

View File

@@ -94,6 +94,7 @@ export const tenants = pgTable(
projects: { prefix: "PRJ-", suffix: "", nextNumber: 1000 },
costcentres: { prefix: "KST-", suffix: "", nextNumber: 1000 },
}),
accountChart: text("accountChart").notNull().default("skr03"),
standardEmailForInvoices: text("standardEmailForInvoices"),

View File

@@ -94,6 +94,7 @@ export default async function adminRoutes(server: FastifyInstance) {
short: tenants.short,
locked: tenants.locked,
numberRanges: tenants.numberRanges,
accountChart: tenants.accountChart,
extraModules: tenants.extraModules,
})
.from(authTenantUsers)

View File

@@ -54,6 +54,7 @@ export default async function meRoutes(server: FastifyInstance) {
extraModules: tenants.extraModules,
businessInfo: tenants.businessInfo,
numberRanges: tenants.numberRanges,
accountChart: tenants.accountChart,
dokuboxkey: tenants.dokuboxkey,
standardEmailForInvoices: tenants.standardEmailForInvoices,
standardPaymentDays: tenants.standardPaymentDays,

View File

@@ -586,6 +586,9 @@ export default async function resourceRoutes(server: FastifyInstance) {
try {
if (!req.user?.tenant_id) return reply.code(400).send({ error: "No tenant selected" });
const { resource } = req.params as { resource: string };
if (resource === "accounts") {
return reply.code(403).send({ error: "Accounts are read-only" })
}
const body = req.body as Record<string, any>;
const config = resourceConfig[resource];
const table = config.table;
@@ -656,6 +659,9 @@ export default async function resourceRoutes(server: FastifyInstance) {
server.put("/resource/:resource/:id", async (req, reply) => {
try {
const { resource, id } = req.params as { resource: string; id: string }
if (resource === "accounts") {
return reply.code(403).send({ error: "Accounts are read-only" })
}
const body = req.body as Record<string, any>
const tenantId = req.user?.tenant_id
const userId = req.user?.user_id

View File

@@ -1,9 +1,9 @@
import { FastifyInstance } from "fastify"
import { asc, desc } from "drizzle-orm"
import { asc, desc, eq } from "drizzle-orm"
import { sortData } from "../utils/sort"
// Schema imports
import { accounts, units,countrys } from "../../db/schema"
import { accounts, units, countrys, tenants } from "../../db/schema"
const TABLE_MAP: Record<string, any> = {
accounts,
@@ -40,6 +40,44 @@ export default async function resourceRoutesSpecial(server: FastifyInstance) {
// Wir geben IMMER alle Spalten zurück → kompatibel zum Frontend
// ---------------------------------------
if (resource === "accounts") {
const [tenant] = await server.db
.select({
accountChart: tenants.accountChart,
})
.from(tenants)
.where(eq(tenants.id, Number(req.user.tenant_id)))
.limit(1)
const activeAccountChart = tenant?.accountChart || "skr03"
let data
if (sort && (accounts as any)[sort]) {
const col = (accounts as any)[sort]
data = ascQuery === "true"
? await server.db
.select()
.from(accounts)
.where(eq(accounts.accountChart, activeAccountChart))
.orderBy(asc(col))
: await server.db
.select()
.from(accounts)
.where(eq(accounts.accountChart, activeAccountChart))
.orderBy(desc(col))
} else {
data = await server.db
.select()
.from(accounts)
.where(eq(accounts.accountChart, activeAccountChart))
}
return sortData(
data,
sort as any,
ascQuery === "true"
)
}
let query = server.db.select().from(table)
// ---------------------------------------

View File

@@ -11,7 +11,7 @@ import { s3 } from "./s3";
import { secrets } from "./secrets";
// Drizzle schema
import { vendors, accounts } from "../../db/schema";
import { vendors, accounts, tenants } from "../../db/schema";
import {eq} from "drizzle-orm";
let openai: OpenAI | null = null;
@@ -163,13 +163,22 @@ export const getInvoiceDataFromGPT = async function (
.from(vendors)
.where(eq(vendors.tenant,tenantId));
const [tenant] = await server.db
.select({ accountChart: tenants.accountChart })
.from(tenants)
.where(eq(tenants.id, tenantId))
.limit(1)
const activeAccountChart = tenant?.accountChart || "skr03"
const accountList = await server.db
.select({
id: accounts.id,
label: accounts.label,
number: accounts.number,
})
.from(accounts);
.from(accounts)
.where(eq(accounts.accountChart, activeAccountChart));
// ---------------------------------------------------------
// 4) GPT ANALYSIS

View File

@@ -15,6 +15,11 @@ const setupPage = async () => {
const features = ref(auth.activeTenantData.features)
const businessInfo = ref(auth.activeTenantData.businessInfo)
const accountChart = ref(auth.activeTenantData.accountChart || "skr03")
const accountChartOptions = [
{ label: "SKR 03", value: "skr03" },
{ label: "Verein", value: "verein" }
]
const updateTenant = async (newData) => {
@@ -24,6 +29,11 @@ const updateTenant = async (newData) => {
data: newData,
}
})
if (res) {
itemInfo.value = res
auth.activeTenantData = res
}
}
setupPage()
@@ -63,8 +73,8 @@ setupPage()
</div>
<div v-if="item.label === 'Rechnung & Kontakt'">
<UCard class="mt-5">
<UForm class="w-1/2">
<UCard class="mt-5">
<UForm class="w-1/2">
<UFormGroup
label="Firmenname:"
>
@@ -90,6 +100,23 @@ setupPage()
>
Speichern
</UButton>
<UFormGroup
label="Kontenrahmen:"
class="mt-6"
>
<USelectMenu
v-model="accountChart"
:options="accountChartOptions"
option-attribute="label"
value-attribute="value"
/>
</UFormGroup>
<UButton
class="mt-3"
@click="updateTenant({accountChart: accountChart})"
>
Kontenrahmen speichern
</UButton>
</UForm>
</UCard>