Introduced New DB

This commit is contained in:
2025-12-06 10:34:58 +01:00
parent 407592680a
commit 63af22b671
80 changed files with 4509 additions and 154 deletions

24
db/schema/accounts.ts Normal file
View File

@@ -0,0 +1,24 @@
import {
pgTable,
bigint,
timestamp,
text,
} from "drizzle-orm/pg-core"
export const accounts = pgTable("accounts", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
number: text("number").notNull(),
label: text("label").notNull(),
description: text("description"),
})
export type Account = typeof accounts.$inferSelect
export type NewAccount = typeof accounts.$inferInsert

View File

@@ -0,0 +1,83 @@
import {
pgTable,
uuid,
text,
timestamp,
date,
boolean,
bigint,
doublePrecision,
jsonb,
} from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
export const authProfiles = pgTable("auth_profiles", {
id: uuid("id").primaryKey().defaultRandom(),
userId: uuid("user_id").references(() => authUsers.id),
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
firstName: text("first_name").notNull(),
lastName: text("last_name").notNull(),
fullName: text("full_name").generatedAlwaysAs(
`((first_name || ' ') || last_name)`
),
mobileTel: text("mobile_tel"),
fixedTel: text("fixed_tel"),
salutation: text("salutation"),
employeeNumber: text("employee_number"),
weeklyWorkingHours: doublePrecision("weekly_working_hours").default(0),
annualPaidLeaveDays: bigint("annual_paid_leave_days", { mode: "number" }),
weeklyRegularWorkingHours: jsonb("weekly_regular_working_hours").default("{}"),
clothingSizeTop: text("clothing_size_top"),
clothingSizeBottom: text("clothing_size_bottom"),
clothingSizeShoe: text("clothing_size_shoe"),
emailSignature: text("email_signature").default("<p>Mit freundlichen Grüßen</p>"),
birthday: date("birthday"),
entryDate: date("entry_date").defaultNow(),
automaticHourCorrections: jsonb("automatic_hour_corrections").default("[]"),
recreationDaysCompensation: boolean("recreation_days_compensation")
.notNull()
.default(true),
customerForPortal: bigint("customer_for_portal", { mode: "number" }),
pinnedOnNavigation: jsonb("pinned_on_navigation").notNull().default("[]"),
email: text("email"),
tokenId: text("token_id"),
weeklyWorkingDays: doublePrecision("weekly_working_days"),
oldProfileId: uuid("old_profile_id"),
tempConfig: jsonb("temp_config"),
stateCode: text("state_code").default("DE-NI"),
contractType: text("contract_type"),
position: text("position"),
qualification: text("qualification"),
addressStreet: text("address_street"),
addressZip: text("address_zip"),
addressCity: text("address_city"),
active: boolean("active").notNull().default(true),
})
export type AuthProfile = typeof authProfiles.$inferSelect
export type NewAuthProfile = typeof authProfiles.$inferInsert

View File

@@ -0,0 +1,23 @@
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core"
import { authRoles } from "./auth_roles"
export const authRolePermissions = pgTable(
"auth_role_permissions",
{
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
roleId: uuid("role_id")
.notNull()
.references(() => authRoles.id),
permission: text("permission").notNull(),
},
(table) => ({
primaryKey: [table.roleId, table.permission],
})
)
export type AuthRolePermission = typeof authRolePermissions.$inferSelect
export type NewAuthRolePermission = typeof authRolePermissions.$inferInsert

19
db/schema/auth_roles.ts Normal file
View File

@@ -0,0 +1,19 @@
import { pgTable, uuid, text, timestamp, bigint } from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
export const authRoles = pgTable("auth_roles", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
description: text("description"),
createdBy: uuid("created_by").references(() => authUsers.id),
tenantId: bigint("tenant_id", {mode: "number"}),
})
export type AuthRole = typeof authRoles.$inferSelect
export type NewAuthRole = typeof authRoles.$inferInsert

View File

@@ -0,0 +1,22 @@
import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
export const authTenantUsers = pgTable(
"auth_tenant_users",
{
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
userId: uuid("user_id").notNull(),
createdBy: uuid("created_by").references(() => authUsers.id),
},
(table) => ({
primaryKey: [table.tenantId, table.userId],
})
)
export type AuthTenantUser = typeof authTenantUsers.$inferSelect
export type NewAuthTenantUser = typeof authTenantUsers.$inferInsert

View File

@@ -0,0 +1,30 @@
import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
import { authRoles } from "./auth_roles"
export const authUserRoles = pgTable(
"auth_user_roles",
{
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id),
roleId: uuid("role_id")
.notNull()
.references(() => authRoles.id),
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
createdBy: uuid("created_by").references(() => authUsers.id),
},
(table) => ({
primaryKey: [table.userId, table.roleId, table.tenantId],
})
)
export type AuthUserRole = typeof authUserRoles.$inferSelect
export type NewAuthUserRole = typeof authUserRoles.$inferInsert

22
db/schema/auth_users.ts Normal file
View File

@@ -0,0 +1,22 @@
import { pgTable, uuid, text, boolean, timestamp } from "drizzle-orm/pg-core"
export const authUsers = pgTable("auth_users", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
email: text("email").notNull(),
passwordHash: text("password_hash").notNull(),
multiTenant: boolean("multi_tenant").notNull().default(true),
mustChangePassword: boolean("must_change_password").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
ported: boolean("ported").notNull().default(true),
})
export type AuthUser = typeof authUsers.$inferSelect
export type NewAuthUser = typeof authUsers.$inferInsert

52
db/schema/bankaccounts.ts Normal file
View File

@@ -0,0 +1,52 @@
import {
pgTable,
bigint,
timestamp,
text,
doublePrecision,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const bankaccounts = pgTable("bankaccounts", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name"),
iban: text("iban").notNull(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
bankId: text("bankId").notNull(),
ownerName: text("ownerName"),
accountId: text("accountId").notNull(),
balance: doublePrecision("balance"),
expired: boolean("expired").notNull().default(false),
datevNumber: text("datevNumber"),
syncedAt: timestamp("synced_at", { withTimezone: true })
.notNull()
.defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type BankAccount = typeof bankaccounts.$inferSelect
export type NewBankAccount = typeof bankaccounts.$inferInsert

View File

@@ -0,0 +1,30 @@
import {
pgTable,
uuid,
timestamp,
text,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const bankrequisitions = pgTable("bankrequisitions", {
id: uuid("id").primaryKey(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
institutionId: text("institutionId"),
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
status: text("status"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type BankRequisition = typeof bankrequisitions.$inferSelect
export type NewBankRequisition = typeof bankrequisitions.$inferInsert

View File

@@ -0,0 +1,70 @@
import {
pgTable,
bigint,
timestamp,
text,
doublePrecision,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { bankaccounts } from "./bankaccounts"
import { createddocuments } from "./createddocuments"
import { tenants } from "./tenants"
import { incominginvoices } from "./incominginvoices"
import { contracts } from "./contracts"
import { authUsers } from "./auth_users"
export const bankstatements = pgTable("bankstatements", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
account: bigint("account", { mode: "number" })
.notNull()
.references(() => bankaccounts.id),
date: text("date").notNull(),
credIban: text("credIban"),
credName: text("credName"),
text: text("text"),
amount: doublePrecision("amount").notNull(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
debIban: text("debIban"),
debName: text("debName"),
gocardlessId: text("gocardlessId"),
currency: text("currency"),
valueDate: text("valueDate"),
incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references(
() => incominginvoices.id
),
mandateId: text("mandateId"),
contract: bigint("contract", { mode: "number" }).references(
() => contracts.id
),
createdDocument: bigint("createdDocument", { mode: "number" }).references(
() => createddocuments.id
),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type BankStatement = typeof bankstatements.$inferSelect
export type NewBankStatement = typeof bankstatements.$inferInsert

View File

@@ -0,0 +1,27 @@
import {
pgTable,
uuid,
timestamp,
text,
} from "drizzle-orm/pg-core"
import { checks } from "./checks"
export const checkexecutions = pgTable("checkexecutions", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
check: uuid("check").references(() => checks.id),
executedAt: timestamp("executed_at"),
// ❌ executed_by removed (was 0_profiles)
description: text("description"),
})
export type CheckExecution = typeof checkexecutions.$inferSelect
export type NewCheckExecution = typeof checkexecutions.$inferInsert

52
db/schema/checks.ts Normal file
View File

@@ -0,0 +1,52 @@
import {
pgTable,
uuid,
timestamp,
text,
bigint,
boolean,
jsonb,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { vehicles } from "./vehicles"
import { inventoryItems } from "./inventoryitems"
import { authUsers } from "./auth_users"
export const checks = pgTable("checks", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
vehicle: bigint("vehicle", { mode: "number" })
.references(() => vehicles.id),
// ❌ profile removed (old 0_profiles reference)
inventoryItem: bigint("inventoryitem", { mode: "number" })
.references(() => inventoryItems.id),
tenant: bigint("tenant", { mode: "number" })
.references(() => tenants.id),
name: text("name"),
type: text("type"),
distance: bigint("distance", { mode: "number" }).default(1),
distanceUnit: text("distanceUnit").default("days"),
description: text("description"),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Check = typeof checks.$inferSelect
export type NewCheck = typeof checks.$inferInsert

32
db/schema/citys.ts Normal file
View File

@@ -0,0 +1,32 @@
import {
pgTable,
bigint,
text,
jsonb,
} from "drizzle-orm/pg-core"
export const citys = pgTable("citys", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
name: text("name"),
short: text("short"),
long: text("long"),
geometry: jsonb("geometry"),
zip: bigint("zip", { mode: "number" }),
districtCode: bigint("districtCode", { mode: "number" }),
countryName: text("countryName"),
countryCode: bigint("countryCode", { mode: "number" }),
districtName: text("districtName"),
geopoint: text("geopoint"),
})
export type City = typeof citys.$inferSelect
export type NewCity = typeof citys.$inferInsert

66
db/schema/contacts.ts Normal file
View File

@@ -0,0 +1,66 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
date,
uuid,
} from "drizzle-orm/pg-core"
import { customers } from "./customers"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const contacts = pgTable(
"contacts",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
firstName: text("firstName"),
lastName: text("lastName"),
email: text("email"),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
tenant: bigint("tenant", { mode: "number" }).notNull(),
phoneMobile: text("phoneMobile"),
phoneHome: text("phoneHome"),
heroId: text("heroId"),
role: text("role"),
fullName: text("fullName"),
salutation: text("salutation"),
vendor: bigint("vendor", { mode: "number" }), // vendors folgt separat
active: boolean("active").notNull().default(true),
birthday: date("birthday"),
notes: text("notes"),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
title: text("title"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
}
)
export type Contact = typeof contacts.$inferSelect
export type NewContact = typeof contacts.$inferInsert

76
db/schema/contracts.ts Normal file
View File

@@ -0,0 +1,76 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { contacts } from "./contacts"
import { authUsers } from "./auth_users"
export const contracts = pgTable(
"contracts",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" }).notNull(),
name: text("name").notNull(),
customer: bigint("customer", { mode: "number" })
.notNull()
.references(() => customers.id),
notes: text("notes"),
active: boolean("active").notNull().default(true),
recurring: boolean("recurring").notNull().default(false),
rhythm: jsonb("rhythm"),
startDate: timestamp("startDate", { withTimezone: true }),
endDate: timestamp("endDate", { withTimezone: true }),
signDate: timestamp("signDate", { withTimezone: true }),
duration: text("duration"),
contact: bigint("contact", { mode: "number" }).references(
() => contacts.id
),
bankingIban: text("bankingIban"),
bankingBIC: text("bankingBIC"),
bankingName: text("bankingName"),
bankingOwner: text("bankingOwner"),
sepaRef: text("sepaRef"),
sepaDate: timestamp("sepaDate", { withTimezone: true }),
paymentType: text("paymentType"),
invoiceDispatch: text("invoiceDispatch"),
ownFields: jsonb("ownFields").notNull().default({}),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
contractNumber: text("contractNumber"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
}
)
export type Contract = typeof contracts.$inferSelect
export type NewContract = typeof contracts.$inferInsert

50
db/schema/costcentres.ts Normal file
View File

@@ -0,0 +1,50 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { inventoryItems } from "./inventoryitems"
import { projects } from "./projects"
import { vehicles } from "./vehicles"
import { authUsers } from "./auth_users"
export const costcentres = pgTable("costcentres", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
number: text("number").notNull(),
name: text("name").notNull(),
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
project: bigint("project", { mode: "number" }).references(() => projects.id),
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(
() => inventoryItems.id
),
description: text("description"),
archived: boolean("archived").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type CostCentre = typeof costcentres.$inferSelect
export type NewCostCentre = typeof costcentres.$inferInsert

21
db/schema/countrys.ts Normal file
View File

@@ -0,0 +1,21 @@
import {
pgTable,
bigint,
timestamp,
text,
} from "drizzle-orm/pg-core"
export const countrys = pgTable("countrys", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
})
export type Country = typeof countrys.$inferSelect
export type NewCountry = typeof countrys.$inferInsert

View File

@@ -0,0 +1,121 @@
import {
pgTable,
bigint,
timestamp,
text,
jsonb,
boolean,
smallint,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { contacts } from "./contacts"
import { contracts } from "./contracts"
import { letterheads } from "./letterheads"
import { projects } from "./projects"
import { plants } from "./plants"
import { authUsers } from "./auth_users"
export const createddocuments = pgTable("createddocuments", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
type: text("type").notNull().default("INVOICE"),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
contact: bigint("contact", { mode: "number" }).references(
() => contacts.id
),
address: jsonb("address"),
project: bigint("project", { mode: "number" }).references(
() => projects.id
),
documentNumber: text("documentNumber"),
documentDate: text("documentDate"),
state: text("state").notNull().default("Entwurf"),
info: jsonb("info"),
createdBy: uuid("createdBy").references(() => authUsers.id),
title: text("title"),
description: text("description"),
startText: text("startText"),
endText: text("endText"),
rows: jsonb("rows").default([]),
deliveryDateType: text("deliveryDateType"),
paymentDays: smallint("paymentDays"),
deliveryDate: text("deliveryDate"),
contactPerson: uuid("contactPerson"),
serialConfig: jsonb("serialConfig").default({}),
linkedDocument: bigint("linkedDocument", { mode: "number" }).references(
() => createddocuments.id
),
agriculture: jsonb("agriculture"),
letterhead: bigint("letterhead", { mode: "number" }).references(
() => letterheads.id
),
advanceInvoiceResolved: boolean("advanceInvoiceResolved")
.notNull()
.default(false),
usedAdvanceInvoices: jsonb("usedAdvanceInvoices").notNull().default([]),
archived: boolean("archived").notNull().default(false),
deliveryDateEnd: text("deliveryDateEnd"),
plant: bigint("plant", { mode: "number" }).references(() => plants.id),
taxType: text("taxType"),
customSurchargePercentage: smallint("customSurchargePercentage")
.notNull()
.default(0),
report: jsonb("report").notNull().default({}),
availableInPortal: boolean("availableInPortal")
.notNull()
.default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
created_by: uuid("created_by").references(() => authUsers.id),
payment_type: text("payment_type").default("transfer"),
contract: bigint("contract", { mode: "number" }).references(
() => contracts.id
),
})
export type CreatedDocument = typeof createddocuments.$inferSelect
export type NewCreatedDocument = typeof createddocuments.$inferInsert

View File

@@ -0,0 +1,43 @@
import {
pgTable,
uuid,
timestamp,
bigint,
text,
jsonb,
boolean,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { vendors } from "./vendors"
import { authUsers } from "./auth_users"
export const createdletters = pgTable("createdletters", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
contentJson: jsonb("content_json").default([]),
contentText: text("content_text"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type CreatedLetter = typeof createdletters.$inferSelect
export type NewCreatedLetter = typeof createdletters.$inferInsert

69
db/schema/customers.ts Normal file
View File

@@ -0,0 +1,69 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
smallint,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const customers = pgTable(
"customers",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
customerNumber: text("customerNumber").notNull(),
name: text("name").notNull(),
tenant: bigint("tenant", { mode: "number" }).notNull(),
infoData: jsonb("infoData").default({}),
active: boolean("active").notNull().default(true),
notes: text("notes"),
type: text("type").default("Privat"),
heroId: text("heroId"),
isCompany: boolean("isCompany").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
customPaymentDays: smallint("customPaymentDays"),
firstname: text("firstname"),
lastname: text("lastname"),
archived: boolean("archived").notNull().default(false),
customSurchargePercentage: smallint("customSurchargePercentage")
.notNull()
.default(0),
salutation: text("salutation"),
title: text("title"),
nameAddition: text("nameAddition"),
availableInPortal: boolean("availableInPortal")
.notNull()
.default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
customPaymentType: text("custom_payment_type"), // ENUM payment_types separat?
}
)
export type Customer = typeof customers.$inferSelect
export type NewCustomer = typeof customers.$inferInsert

29
db/schema/devices.ts Normal file
View File

@@ -0,0 +1,29 @@
import {
pgTable,
uuid,
timestamp,
text,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
export const devices = pgTable("devices", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
type: text("type").notNull(),
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
password: text("password"),
externalId: text("externalId"),
})
export type Device = typeof devices.$inferSelect
export type NewDevice = typeof devices.$inferInsert

View File

@@ -0,0 +1,28 @@
import { pgTable, uuid, timestamp, text, boolean, bigint } from "drizzle-orm/pg-core"
import { spaces } from "./spaces"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const documentboxes = pgTable("documentboxes", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
space: bigint("space", { mode: "number" }).references(() => spaces.id),
key: text("key").notNull(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type DocumentBox = typeof documentboxes.$inferSelect
export type NewDocumentBox = typeof documentboxes.$inferInsert

97
db/schema/enums.ts Normal file
View File

@@ -0,0 +1,97 @@
import { pgEnum } from "drizzle-orm/pg-core"
// public.textTemplatePositions
export const textTemplatePositionsEnum = pgEnum("texttemplatepositions", [
"startText",
"endText",
])
// public.folderFunctions
export const folderFunctionsEnum = pgEnum("folderfunctions", [
"none",
"yearSubCategory",
"incomingInvoices",
"invoices",
"quotes",
"confirmationOrders",
"deliveryNotes",
"vehicleData",
"reminders",
"taxData",
"deposit",
"timeEvaluations",
])
// public.locked_tenant
export const lockedTenantEnum = pgEnum("locked_tenant", [
"maintenance_tenant",
"maintenance",
"general",
"no_subscription",
])
// public.credential_types
export const credentialTypesEnum = pgEnum("credential_types", [
"mail",
"m365",
])
// public.payment_types
export const paymentTypesEnum = pgEnum("payment_types", [
"transfer",
"direct_debit",
])
// public.notification_status
export const notificationStatusEnum = pgEnum("notification_status", [
"queued",
"sent",
"failed",
"read",
])
// public.notification_channel
export const notificationChannelEnum = pgEnum("notification_channel", [
"email",
"inapp",
"sms",
"push",
"webhook",
])
// public.notification_severity
export const notificationSeverityEnum = pgEnum("notification_severity", [
"info",
"success",
"warning",
"error",
])
// public.times_state
export const timesStateEnum = pgEnum("times_state", [
"submitted",
"approved",
"draft",
])
export const helpdeskStatusEnum = [
"open",
"in_progress",
"waiting_for_customer",
"answered",
"closed",
] as const
export const helpdeskPriorityEnum = [
"low",
"normal",
"high",
] as const
export const helpdeskDirectionEnum = [
"incoming",
"outgoing",
"internal",
"system",
] as const

60
db/schema/events.ts Normal file
View File

@@ -0,0 +1,60 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { authUsers } from "./auth_users"
export const events = pgTable(
"events",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" }).notNull(),
name: text("name").notNull(),
startDate: timestamp("startDate", { withTimezone: true }).notNull(),
endDate: timestamp("endDate", { withTimezone: true }),
eventtype: text("eventtype").default("Umsetzung"),
project: bigint("project", { mode: "number" }), // FK follows when projects.ts exists
resources: jsonb("resources").default([]),
notes: text("notes"),
link: text("link"),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
vehicles: jsonb("vehicles").notNull().default([]),
inventoryitems: jsonb("inventoryitems").notNull().default([]),
inventoryitemgroups: jsonb("inventoryitemgroups").notNull().default([]),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
vendor: bigint("vendor", { mode: "number" }), // will link once vendors.ts is created
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
}
)
export type Event = typeof events.$inferSelect
export type NewEvent = typeof events.$inferInsert

79
db/schema/files.ts Normal file
View File

@@ -0,0 +1,79 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { projects } from "./projects"
import { customers } from "./customers"
import { contracts } from "./contracts"
import { vendors } from "./vendors"
import { incominginvoices } from "./incominginvoices"
import { plants } from "./plants"
import { createddocuments } from "./createddocuments"
import { vehicles } from "./vehicles"
import { products } from "./products"
import { inventoryItems } from "./inventoryitems"
import { folders } from "./folders"
import { filetags } from "./filetags"
import { authUsers } from "./auth_users"
import { authProfiles } from "./auth_profiles"
import { spaces } from "./spaces"
import { documentboxes } from "./documentboxes"
import { checks } from "./checks"
export const files = pgTable("files", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
path: text("path"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
project: bigint("project", { mode: "number" }).references(() => projects.id),
customer: bigint("customer", { mode: "number" }).references(() => customers.id),
contract: bigint("contract", { mode: "number" }).references(() => contracts.id),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
incominginvoice: bigint("incominginvoice", { mode: "number" }).references(() => incominginvoices.id),
plant: bigint("plant", { mode: "number" }).references(() => plants.id),
createddocument: bigint("createddocument", { mode: "number" }).references(() => createddocuments.id),
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
product: bigint("product", { mode: "number" }).references(() => products.id),
check: uuid("check").references(() => checks.id),
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(() => inventoryItems.id),
folder: uuid("folder").references(() => folders.id),
mimeType: text("mimeType"),
archived: boolean("archived").notNull().default(false),
space: bigint("space", { mode: "number" }).references(() => spaces.id),
type: uuid("type").references(() => filetags.id),
documentbox: uuid("documentbox").references(() => documentboxes.id),
name: text("name"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
createdBy: uuid("created_by").references(() => authUsers.id),
authProfile: uuid("auth_profile").references(() => authProfiles.id),
})
export type File = typeof files.$inferSelect
export type NewFile = typeof files.$inferInsert

33
db/schema/filetags.ts Normal file
View File

@@ -0,0 +1,33 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
export const filetags = pgTable("filetags", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
color: text("color"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
createdDocumentType: text("createddocumenttype").default(""),
incomingDocumentType: text("incomingDocumentType"),
archived: boolean("archived").notNull().default(false),
})
export type FileTag = typeof filetags.$inferSelect
export type NewFileTag = typeof filetags.$inferInsert

51
db/schema/folders.ts Normal file
View File

@@ -0,0 +1,51 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
integer,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { filetags } from "./filetags"
import { folderFunctionsEnum } from "./enums"
export const folders = pgTable("folders", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
icon: text("icon"),
parent: uuid("parent").references(() => folders.id),
isSystemUsed: boolean("isSystemUsed").notNull().default(false),
function: folderFunctionsEnum("function"),
year: integer("year"),
standardFiletype: uuid("standardFiletype").references(() => filetags.id),
standardFiletypeIsOptional: boolean("standardFiletypeIsOptional")
.notNull()
.default(true),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type Folder = typeof folders.$inferSelect
export type NewFolder = typeof folders.$inferInsert

View File

@@ -0,0 +1,35 @@
import {
pgTable,
bigint,
timestamp,
text,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
export const generatedexports = pgTable("exports", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id),
startDate: timestamp("start_date", { withTimezone: true }).notNull(),
endDate: timestamp("end_date", { withTimezone: true }).notNull(),
validUntil: timestamp("valid_until", { withTimezone: true }),
type: text("type").notNull().default("datev"),
url: text("url").notNull(),
filePath: text("file_path"),
})
export type Export = typeof generatedexports.$inferSelect
export type NewExport = typeof generatedexports.$inferInsert

View File

@@ -0,0 +1,22 @@
import {
pgTable,
bigint,
timestamp,
text,
} from "drizzle-orm/pg-core"
export const globalmessages = pgTable("globalmessages", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
title: text("title"),
description: text("description"),
})
export type GlobalMessage = typeof globalmessages.$inferSelect
export type NewGlobalMessage = typeof globalmessages.$inferInsert

View File

@@ -0,0 +1,17 @@
import {
pgTable,
timestamp,
bigint,
} from "drizzle-orm/pg-core"
import { globalmessages } from "./globalmessages"
export const globalmessagesseen = pgTable("globalmessagesseen", {
message: bigint("message", { mode: "number" })
.notNull()
.references(() => globalmessages.id),
seenAt: timestamp("seen_at", { withTimezone: true })
.notNull()
.defaultNow(),
})

View File

@@ -0,0 +1,44 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { helpdesk_channel_types } from "./helpdesk_channel_types"
export const helpdesk_channel_instances = pgTable("helpdesk_channel_instances", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade" }),
typeId: text("type_id")
.notNull()
.references(() => helpdesk_channel_types.id),
name: text("name").notNull(),
isActive: boolean("is_active").notNull().default(true),
config: jsonb("config").notNull(),
publicConfig: jsonb("public_config").notNull().default({}),
publicToken: text("public_token").unique(),
secretToken: text("secret_token"),
createdBy: uuid("created_by").references(() => authUsers.id),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
})
export type HelpdeskChannelInstance =
typeof helpdesk_channel_instances.$inferSelect
export type NewHelpdeskChannelInstance =
typeof helpdesk_channel_instances.$inferInsert

View File

@@ -0,0 +1,9 @@
import { pgTable, text } from "drizzle-orm/pg-core"
export const helpdesk_channel_types = pgTable("helpdesk_channel_types", {
id: text("id").primaryKey(),
description: text("description").notNull(),
})
export type HelpdeskChannelType = typeof helpdesk_channel_types.$inferSelect
export type NewHelpdeskChannelType = typeof helpdesk_channel_types.$inferInsert

View File

@@ -0,0 +1,45 @@
import {
pgTable,
uuid,
timestamp,
text,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { contacts } from "./contacts"
import { helpdesk_channel_instances } from "./helpdesk_channel_instances" // placeholder
export const helpdesk_contacts = pgTable("helpdesk_contacts", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade" }),
customerId: bigint("customer_id", { mode: "number" })
.references(() => customers.id, { onDelete: "set null" }),
email: text("email"),
phone: text("phone"),
externalRef: jsonb("external_ref"),
displayName: text("display_name"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
sourceChannelId: uuid("source_channel_id").references(
() => helpdesk_channel_instances.id,
{ onDelete: "set null" }
),
contactId: bigint("contact_id", { mode: "number" }).references(
() => contacts.id,
{ onDelete: "set null" }
),
})
export type HelpdeskContact = typeof helpdesk_contacts.$inferSelect
export type NewHelpdeskContact = typeof helpdesk_contacts.$inferInsert

View File

@@ -0,0 +1,34 @@
import {
pgTable,
uuid,
text,
} from "drizzle-orm/pg-core"
import { helpdesk_conversations } from "./helpdesk_conversations"
import { authUsers } from "./auth_users"
export const helpdesk_conversation_participants = pgTable(
"helpdesk_conversation_participants",
{
conversationId: uuid("conversation_id")
.notNull()
.references(() => helpdesk_conversations.id, { onDelete: "cascade" }),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id, { onDelete: "cascade" }),
role: text("role"),
},
(table) => ({
pk: {
name: "helpdesk_conversation_participants_pkey",
columns: [table.conversationId, table.userId],
},
})
)
export type HelpdeskConversationParticipant =
typeof helpdesk_conversation_participants.$inferSelect
export type NewHelpdeskConversationParticipant =
typeof helpdesk_conversation_participants.$inferInsert

View File

@@ -0,0 +1,59 @@
import {
pgTable,
uuid,
timestamp,
text,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { helpdesk_contacts } from "./helpdesk_contacts"
import { contacts } from "./contacts"
import { customers } from "./customers"
import { authUsers } from "./auth_users"
import { helpdesk_channel_instances } from "./helpdesk_channel_instances"
export const helpdesk_conversations = pgTable("helpdesk_conversations", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade" }),
channelInstanceId: uuid("channel_instance_id")
.notNull()
.references(() => helpdesk_channel_instances.id, { onDelete: "cascade" }),
contactId: uuid("contact_id").references(() => helpdesk_contacts.id, {
onDelete: "set null",
}),
subject: text("subject"),
status: text("status").notNull().default("open"),
priority: text("priority").default("normal"),
assigneeUserId: uuid("assignee_user_id").references(() => authUsers.id),
lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
customerId: bigint("customer_id", { mode: "number" }).references(
() => customers.id,
{ onDelete: "set null" }
),
contactPersonId: bigint("contact_person_id", { mode: "number" }).references(
() => contacts.id,
{ onDelete: "set null" }
),
ticketNumber: text("ticket_number"),
})
export type HelpdeskConversation =
typeof helpdesk_conversations.$inferSelect
export type NewHelpdeskConversation =
typeof helpdesk_conversations.$inferInsert

View File

@@ -0,0 +1,46 @@
import {
pgTable,
uuid,
timestamp,
text,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { helpdesk_contacts } from "./helpdesk_contacts"
import { helpdesk_conversations } from "./helpdesk_conversations"
import { authUsers } from "./auth_users"
export const helpdesk_messages = pgTable("helpdesk_messages", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade" }),
conversationId: uuid("conversation_id")
.notNull()
.references(() => helpdesk_conversations.id, { onDelete: "cascade" }),
direction: text("direction").notNull(),
authorUserId: uuid("author_user_id").references(() => authUsers.id),
payload: jsonb("payload").notNull(),
rawMeta: jsonb("raw_meta"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
contactId: uuid("contact_id").references(() => helpdesk_contacts.id, {
onDelete: "set null",
}),
externalMessageId: text("external_message_id").unique(),
receivedAt: timestamp("received_at", { withTimezone: true }).defaultNow(),
})
export type HelpdeskMessage = typeof helpdesk_messages.$inferSelect
export type NewHelpdeskMessage = typeof helpdesk_messages.$inferInsert

View File

@@ -0,0 +1,33 @@
import {
pgTable,
uuid,
timestamp,
text,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const helpdesk_routing_rules = pgTable("helpdesk_routing_rules", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade" }),
name: text("name").notNull(),
condition: jsonb("condition").notNull(),
action: jsonb("action").notNull(),
createdBy: uuid("created_by").references(() => authUsers.id),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
})
export type HelpdeskRoutingRule =
typeof helpdesk_routing_rules.$inferSelect
export type NewHelpdeskRoutingRule =
typeof helpdesk_routing_rules.$inferInsert

140
db/schema/historyitems.ts Normal file
View File

@@ -0,0 +1,140 @@
import {
pgTable,
bigint,
uuid,
timestamp,
text,
jsonb,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { vendors } from "./vendors"
import { projects } from "./projects"
import { plants } from "./plants"
import { incominginvoices } from "./incominginvoices"
import { contacts } from "./contacts"
import { inventoryItems } from "./inventoryitems"
import { products } from "./products"
import { tasks } from "./tasks"
import { vehicles } from "./vehicles"
import { bankstatements } from "./bankstatements"
import { spaces } from "./spaces"
import { costcentres } from "./costcentres"
import { ownaccounts } from "./ownaccounts"
import { createddocuments } from "./createddocuments"
import { documentboxes } from "./documentboxes"
import { hourrates } from "./hourrates"
import { projecttypes } from "./projecttypes"
import { checks } from "./checks"
import { services } from "./services"
import { events } from "./events"
import { inventoryitemgroups } from "./inventoryitemgroups"
import { authUsers } from "./auth_users"
import {files} from "./files";
export const historyitems = pgTable("historyitems", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
text: text("text").notNull(),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id,
{ onDelete: "cascade" }
),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
project: bigint("project", { mode: "number" }).references(
() => projects.id,
{ onDelete: "cascade" }
),
plant: bigint("plant", { mode: "number" }).references(
() => plants.id,
{ onDelete: "cascade" }
),
incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references(
() => incominginvoices.id,
{ onDelete: "cascade" }
),
contact: bigint("contact", { mode: "number" }).references(() => contacts.id, {
onDelete: "cascade",
}),
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(
() => inventoryItems.id,
{ onDelete: "cascade" }
),
product: bigint("product", { mode: "number" }).references(
() => products.id,
{ onDelete: "cascade" }
),
event: bigint("event", { mode: "number" }).references(() => events.id),
newVal: text("newVal"),
oldVal: text("oldVal"),
task: bigint("task", { mode: "number" }).references(() => tasks.id),
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
bankstatement: bigint("bankstatement", { mode: "number" }).references(
() => bankstatements.id
),
space: bigint("space", { mode: "number" }).references(() => spaces.id),
config: jsonb("config"),
projecttype: bigint("projecttype", { mode: "number" }).references(
() => projecttypes.id
),
check: uuid("check").references(() => checks.id),
service: bigint("service", { mode: "number" }).references(
() => services.id
),
createddocument: bigint("createddocument", { mode: "number" }).references(
() => createddocuments.id
),
file: uuid("file").references(() => files.id),
inventoryitemgroup: uuid("inventoryitemgroup").references(
() => inventoryitemgroups.id
),
source: text("source").default("Software"),
costcentre: uuid("costcentre").references(() => costcentres.id),
ownaccount: uuid("ownaccount").references(() => ownaccounts.id),
documentbox: uuid("documentbox").references(() => documentboxes.id),
hourrate: uuid("hourrate").references(() => hourrates.id),
createdBy: uuid("created_by").references(() => authUsers.id),
action: text("action"),
})
export type HistoryItem = typeof historyitems.$inferSelect
export type NewHistoryItem = typeof historyitems.$inferInsert

18
db/schema/holidays.ts Normal file
View File

@@ -0,0 +1,18 @@
import { pgTable, bigint, date, text, timestamp } from "drizzle-orm/pg-core"
export const holidays = pgTable("holidays", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedAlwaysAsIdentity(),
date: date("date").notNull(),
name: text("name").notNull(),
stateCode: text("state_code").notNull(),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
})
export type Holiday = typeof holidays.$inferSelect
export type NewHoliday = typeof holidays.$inferInsert

27
db/schema/hourrates.ts Normal file
View File

@@ -0,0 +1,27 @@
import { pgTable, uuid, timestamp, text, boolean, bigint, doublePrecision } from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const hourrates = pgTable("hourrates", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
purchasePrice: doublePrecision("purchasePrice").notNull(),
sellingPrice: doublePrecision("sellingPrice").notNull(),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type HourRate = typeof hourrates.$inferSelect
export type NewHourRate = typeof hourrates.$inferInsert

View File

@@ -0,0 +1,63 @@
import {
pgTable,
bigint,
timestamp,
text,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { vendors } from "./vendors"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const incominginvoices = pgTable("incominginvoices", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
state: text("state").notNull().default("Entwurf"),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
reference: text("reference"),
date: text("date"),
document: bigint("document", { mode: "number" }),
dueDate: text("dueDate"),
description: text("description"),
paymentType: text("paymentType"),
accounts: jsonb("accounts").notNull().default([
{
account: null,
taxType: null,
amountNet: null,
amountTax: 19,
costCentre: null,
},
]),
paid: boolean("paid").notNull().default(false),
expense: boolean("expense").notNull().default(true),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type IncomingInvoice = typeof incominginvoices.$inferSelect
export type NewIncomingInvoice = typeof incominginvoices.$inferInsert

70
db/schema/index.ts Normal file
View File

@@ -0,0 +1,70 @@
export * from "./accounts"
export * from "./auth_profiles"
export * from "./auth_role_permisssions"
export * from "./auth_roles"
export * from "./auth_tenant_users"
export * from "./auth_user_roles"
export * from "./auth_users"
export * from "./bankaccounts"
export * from "./bankrequisitions"
export * from "./bankstatements"
export * from "./checkexecutions"
export * from "./checks"
export * from "./citys"
export * from "./contacts"
export * from "./contracts"
export * from "./costcentres"
export * from "./countrys"
export * from "./createddocuments"
export * from "./createdletters"
export * from "./customers"
export * from "./devices"
export * from "./documentboxes"
export * from "./enums"
export * from "./events"
export * from "./files"
export * from "./filetags"
export * from "./folders"
export * from "./generatedexports"
export * from "./globalmessages"
export * from "./globalmessagesseen"
export * from "./helpdesk_channel_instances"
export * from "./helpdesk_channel_types"
export * from "./helpdesk_contacts"
export * from "./helpdesk_conversation_participants"
export * from "./helpdesk_conversations"
export * from "./helpdesk_messages"
export * from "./helpdesk_routing_rules"
export * from "./historyitems"
export * from "./holidays"
export * from "./hourrates"
export * from "./incominginvoices"
export * from "./inventoryitemgroups"
export * from "./inventoryitems"
export * from "./letterheads"
export * from "./movements"
export * from "./notifications_event_types"
export * from "./notifications_items"
export * from "./notifications_preferences"
export * from "./notifications_preferences_defaults"
export * from "./ownaccounts"
export * from "./plants"
export * from "./productcategories"
export * from "./products"
export * from "./projects"
export * from "./projecttypes"
export * from "./servicecategories"
export * from "./services"
export * from "./spaces"
export * from "./staff_time_entries"
export * from "./staff_time_entry_connects"
export * from "./staff_zeitstromtimestamps"
export * from "./statementallocations"
export * from "./tasks"
export * from "./taxtypes"
export * from "./tenants"
export * from "./texttemplates"
export * from "./units"
export * from "./user_credentials"
export * from "./vehicles"
export * from "./vendors"

View File

@@ -0,0 +1,39 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
jsonb, bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const inventoryitemgroups = pgTable("inventoryitemgroups", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" }).notNull().references(() => tenants.id),
name: text("name").notNull(),
inventoryitems: jsonb("inventoryitems").notNull().default([]),
description: text("description"),
archived: boolean("archived").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
usePlanning: boolean("usePlanning").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type InventoryItemGroup = typeof inventoryitemgroups.$inferSelect
export type NewInventoryItemGroup = typeof inventoryitemgroups.$inferInsert

View File

@@ -0,0 +1,68 @@
import {
pgTable,
bigint,
timestamp,
text,
boolean,
doublePrecision,
uuid,
jsonb,
date,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { vendors } from "./vendors"
import { spaces } from "./spaces"
import { authUsers } from "./auth_users"
export const inventoryItems = pgTable("inventoryitems", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
usePlanning: boolean("usePlanning").notNull().default(false),
description: text("description"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
currentSpace: bigint("currentSpace", { mode: "number" }).references(
() => spaces.id
),
articleNumber: text("articleNumber"),
serialNumber: text("serialNumber"),
purchaseDate: date("purchaseDate"),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
quantity: bigint("quantity", { mode: "number" }).notNull().default(0),
purchasePrice: doublePrecision("purchasePrice").default(0),
manufacturer: text("manufacturer"),
manufacturerNumber: text("manufacturerNumber"),
currentValue: doublePrecision("currentValue"),
archived: boolean("archived").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() =>
authUsers.id
),
})
export type InventoryItem = typeof inventoryItems.$inferSelect
export type NewInventoryItem = typeof inventoryItems.$inferInsert

39
db/schema/letterheads.ts Normal file
View File

@@ -0,0 +1,39 @@
import {
pgTable,
bigint,
timestamp,
text,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const letterheads = pgTable("letterheads", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").default("Standard"),
path: text("path").notNull(),
documentTypes: text("documentTypes").array().notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type Letterhead = typeof letterheads.$inferSelect
export type NewLetterhead = typeof letterheads.$inferInsert

49
db/schema/movements.ts Normal file
View File

@@ -0,0 +1,49 @@
import {
pgTable,
bigint,
timestamp,
text,
uuid,
} from "drizzle-orm/pg-core"
import { products } from "./products"
import { spaces } from "./spaces"
import { tenants } from "./tenants"
import { projects } from "./projects"
import { authUsers } from "./auth_users"
export const movements = pgTable("movements", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
quantity: bigint("quantity", { mode: "number" }).notNull(),
productId: bigint("productId", { mode: "number" })
.notNull()
.references(() => products.id),
spaceId: bigint("spaceId", { mode: "number" }).references(() => spaces.id),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
projectId: bigint("projectId", { mode: "number" }).references(
() => projects.id
),
notes: text("notes"),
serials: text("serials").array(),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Movement = typeof movements.$inferSelect
export type NewMovement = typeof movements.$inferInsert

View File

@@ -0,0 +1,34 @@
import {
pgTable,
text,
jsonb,
boolean,
timestamp,
} from "drizzle-orm/pg-core"
import {notificationSeverityEnum} from "./enums";
export const notificationsEventTypes = pgTable("notifications_event_types", {
eventKey: text("event_key").primaryKey(),
displayName: text("display_name").notNull(),
description: text("description"),
category: text("category"),
severity: notificationSeverityEnum("severity").notNull().default("info"),
allowedChannels: jsonb("allowed_channels").notNull().default(["inapp", "email"]),
payloadSchema: jsonb("payload_schema"),
isActive: boolean("is_active").notNull().default(true),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
})
export type NotificationsEventType =
typeof notificationsEventTypes.$inferSelect
export type NewNotificationsEventType =
typeof notificationsEventTypes.$inferInsert

View File

@@ -0,0 +1,54 @@
import {
pgTable,
uuid,
bigint,
text,
jsonb,
timestamp,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { notificationsEventTypes } from "./notifications_event_types"
import {notificationChannelEnum, notificationStatusEnum} from "./enums";
export const notificationsItems = pgTable("notifications_items", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, { onDelete: "cascade", onUpdate: "cascade" }),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id, { onDelete: "cascade", onUpdate: "cascade" }),
eventType: text("event_type")
.notNull()
.references(() => notificationsEventTypes.eventKey, {
onUpdate: "cascade",
onDelete: "restrict",
}),
title: text("title").notNull(),
message: text("message").notNull(),
payload: jsonb("payload"),
channel: notificationChannelEnum("channel").notNull(),
status: notificationStatusEnum("status").notNull().default("queued"),
error: text("error"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
sentAt: timestamp("sent_at", { withTimezone: true }),
readAt: timestamp("read_at", { withTimezone: true }),
})
export type NotificationItem = typeof notificationsItems.$inferSelect
export type NewNotificationItem = typeof notificationsItems.$inferInsert

View File

@@ -0,0 +1,60 @@
import {
pgTable,
uuid,
bigint,
text,
boolean,
timestamp,
uniqueIndex,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { notificationsEventTypes } from "./notifications_event_types"
import {notificationChannelEnum} from "./enums";
export const notificationsPreferences = pgTable(
"notifications_preferences",
{
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
eventType: text("event_type")
.notNull()
.references(() => notificationsEventTypes.eventKey, {
onDelete: "restrict",
onUpdate: "cascade",
}),
channel: notificationChannelEnum("channel").notNull(),
enabled: boolean("enabled").notNull().default(true),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(table) => ({
uniquePrefs: uniqueIndex(
"notifications_preferences_tenant_id_user_id_event_type_chan_key",
).on(table.tenantId, table.userId, table.eventType, table.channel),
}),
)
export type NotificationPreference =
typeof notificationsPreferences.$inferSelect
export type NewNotificationPreference =
typeof notificationsPreferences.$inferInsert

View File

@@ -0,0 +1,52 @@
import {
pgTable,
uuid,
bigint,
text,
boolean,
timestamp,
uniqueIndex,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { notificationsEventTypes } from "./notifications_event_types"
import {notificationChannelEnum} from "./enums";
export const notificationsPreferencesDefaults = pgTable(
"notifications_preferences_defaults",
{
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
eventKey: text("event_key")
.notNull()
.references(() => notificationsEventTypes.eventKey, {
onDelete: "restrict",
onUpdate: "cascade",
}),
channel: notificationChannelEnum("channel").notNull(),
enabled: boolean("enabled").notNull().default(true),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(table) => ({
uniqueDefaults: uniqueIndex(
"notifications_preferences_defau_tenant_id_event_key_channel_key",
).on(table.tenantId, table.eventKey, table.channel),
}),
)
export type NotificationPreferenceDefault =
typeof notificationsPreferencesDefaults.$inferSelect
export type NewNotificationPreferenceDefault =
typeof notificationsPreferencesDefaults.$inferInsert

39
db/schema/ownaccounts.ts Normal file
View File

@@ -0,0 +1,39 @@
import {
pgTable,
uuid,
timestamp,
text,
boolean,
jsonb,
bigint,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const ownaccounts = pgTable("ownaccounts", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
number: text("number").notNull(),
name: text("name").notNull(),
description: text("description"),
archived: boolean("archived").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type OwnAccount = typeof ownaccounts.$inferSelect
export type NewOwnAccount = typeof ownaccounts.$inferInsert

56
db/schema/plants.ts Normal file
View File

@@ -0,0 +1,56 @@
import {
pgTable,
bigint,
timestamp,
text,
jsonb,
boolean,
uuid,
date,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { contracts } from "./contracts"
import { authUsers } from "./auth_users"
export const plants = pgTable("plants", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
infoData: jsonb("infoData"),
contract: bigint("contract", { mode: "number" }).references(
() => contracts.id
),
description: jsonb("description").default({
html: "",
json: [],
text: "",
}),
archived: boolean("archived").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Plant = typeof plants.$inferSelect
export type NewPlant = typeof plants.$inferInsert

View File

@@ -0,0 +1,37 @@
import {
pgTable,
bigint,
timestamp,
text,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const productCategories = pgTable("productcategories", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
description: text("description"),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type ProductCategory = typeof productCategories.$inferSelect
export type NewProductCategory = typeof productCategories.$inferInsert

69
db/schema/products.ts Normal file
View File

@@ -0,0 +1,69 @@
import {
pgTable,
bigint,
timestamp,
text,
doublePrecision,
boolean,
smallint,
uuid,
jsonb,
json,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { units } from "./units"
import { authUsers } from "./auth_users"
export const products = pgTable("products", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
manufacturer: text("manufacturer"),
unit: bigint("unit", { mode: "number" })
.notNull()
.references(() => units.id),
tags: json("tags").notNull().default([]),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
ean: text("ean"),
barcode: text("barcode"),
purchasePrice: doublePrecision("purchasePrice"),
sellingPrice: doublePrecision("sellingPrice"),
description: text("description"),
manufacturerNumber: text("manufacturerNumber"),
vendorAllocation: jsonb("vendorAllocation").default([]),
articleNumber: text("articleNumber"),
barcodes: text("barcodes").array().notNull().default([]),
productcategories: jsonb("productcategories").default([]),
archived: boolean("archived").notNull().default(false),
taxPercentage: smallint("taxPercentage").notNull().default(19),
markupPercentage: doublePrecision("markupPercentage"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Product = typeof products.$inferSelect
export type NewProduct = typeof products.$inferInsert

78
db/schema/projects.ts Normal file
View File

@@ -0,0 +1,78 @@
import {
pgTable,
bigint,
timestamp,
text,
jsonb,
json,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { customers } from "./customers"
import { contracts } from "./contracts"
import { projecttypes } from "./projecttypes"
import { authUsers } from "./auth_users"
export const projects = pgTable("projects", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
notes: text("notes"),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
phases: jsonb("phases").default([]),
description: json("description"),
forms: jsonb("forms").default([]),
heroId: text("heroId"),
measure: text("measure"),
material: jsonb("material"),
plant: bigint("plant", { mode: "number" }),
profiles: uuid("profiles").array().notNull().default([]),
projectNumber: text("projectNumber"),
contract: bigint("contract", { mode: "number" }).references(
() => contracts.id
),
projectType: text("projectType").default("Projekt"),
projecttype: bigint("projecttype", { mode: "number" }).references(
() => projecttypes.id
),
archived: boolean("archived").notNull().default(false),
customerRef: text("customerRef"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
activePhase: text("active_phase"),
})
export type Project = typeof projects.$inferSelect
export type NewProject = typeof projects.$inferInsert

41
db/schema/projecttypes.ts Normal file
View File

@@ -0,0 +1,41 @@
import {
pgTable,
bigint,
timestamp,
text,
jsonb,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const projecttypes = pgTable("projecttypes", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
initialPhases: jsonb("initialPhases"),
addablePhases: jsonb("addablePhases"),
icon: text("icon"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type ProjectType = typeof projecttypes.$inferSelect
export type NewProjectType = typeof projecttypes.$inferInsert

View File

@@ -0,0 +1,39 @@
import {
pgTable,
bigint,
timestamp,
text,
doublePrecision,
boolean,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const serviceCategories = pgTable("servicecategories", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
description: text("description"),
discount: doublePrecision("discount").default(0),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type ServiceCategory = typeof serviceCategories.$inferSelect
export type NewServiceCategory = typeof serviceCategories.$inferInsert

63
db/schema/services.ts Normal file
View File

@@ -0,0 +1,63 @@
import {
pgTable,
bigint,
timestamp,
text,
doublePrecision,
jsonb,
boolean,
smallint,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { units } from "./units"
import { authUsers } from "./auth_users"
export const services = pgTable("services", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
sellingPrice: doublePrecision("sellingPrice"),
description: text("description"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
unit: bigint("unit", { mode: "number" }).references(() => units.id),
serviceNumber: bigint("serviceNumber", { mode: "number" }),
tags: jsonb("tags").default([]),
servicecategories: jsonb("servicecategories").notNull().default([]),
archived: boolean("archived").notNull().default(false),
purchasePriceComposed: jsonb("purchasePriceComposed")
.notNull()
.default({ total: 0 }),
sellingPriceComposed: jsonb("sellingPriceComposed")
.notNull()
.default({ total: 0 }),
taxPercentage: smallint("taxPercentage").notNull().default(19),
materialComposition: jsonb("materialComposition").notNull().default([]),
personalComposition: jsonb("personalComposition").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Service = typeof services.$inferSelect
export type NewService = typeof services.$inferInsert

49
db/schema/spaces.ts Normal file
View File

@@ -0,0 +1,49 @@
import {
pgTable,
bigint,
timestamp,
text,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const spaces = pgTable("spaces", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name"),
type: text("type").notNull(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
spaceNumber: text("spaceNumber").notNull(),
parentSpace: bigint("parentSpace", { mode: "number" }).references(
() => spaces.id
),
infoData: jsonb("infoData")
.notNull()
.default({ zip: "", city: "", streetNumber: "" }),
description: text("description"),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Space = typeof spaces.$inferSelect
export type NewSpace = typeof spaces.$inferInsert

View File

@@ -0,0 +1,68 @@
import {
pgTable,
uuid,
bigint,
timestamp,
integer,
text,
boolean,
numeric,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { timesStateEnum } from "./enums"
import {sql} from "drizzle-orm";
export const staffTimeEntries = pgTable("staff_time_entries", {
id: uuid("id").primaryKey().defaultRandom(),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id, { onDelete: "cascade" }),
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
stoppedAt: timestamp("stopped_at", { withTimezone: true }),
durationMinutes: integer("duration_minutes").generatedAlwaysAs(
sql`CASE
WHEN stopped_at IS NOT NULL
THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60)
ELSE NULL
END`
),
type: text("type").default("work"),
description: text("description"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
archived: boolean("archived").notNull().default(false),
updatedBy: uuid("updated_by").references(() => authUsers.id),
source: text("source"),
state: timesStateEnum("state").notNull().default("draft"),
device: uuid("device"),
internalNote: text("internal_note"),
vacationReason: text("vacation_reason"),
vacationDays: numeric("vacation_days", { precision: 5, scale: 2 }),
approvedBy: uuid("approved_by").references(() => authUsers.id),
approvedAt: timestamp("approved_at", { withTimezone: true }),
sickReason: text("sick_reason"),
})
export type StaffTimeEntry = typeof staffTimeEntries.$inferSelect
export type NewStaffTimeEntry = typeof staffTimeEntries.$inferInsert

View File

@@ -0,0 +1,38 @@
import {
pgTable,
uuid,
bigint,
timestamp,
integer,
text,
} from "drizzle-orm/pg-core"
import { staffTimeEntries } from "./staff_time_entries"
import {sql} from "drizzle-orm";
export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", {
id: uuid("id").primaryKey().defaultRandom(),
timeEntryId: uuid("time_entry_id")
.notNull()
.references(() => staffTimeEntries.id, { onDelete: "cascade" }),
projectId: bigint("project_id", { mode: "number" }), // referenziert später projects.id
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
stoppedAt: timestamp("stopped_at", { withTimezone: true }).notNull(),
durationMinutes: integer("duration_minutes").generatedAlwaysAs(
sql`(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)`
),
notes: text("notes"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
})
export type StaffTimeEntryConnect =
typeof staffTimeEntryConnects.$inferSelect
export type NewStaffTimeEntryConnect =
typeof staffTimeEntryConnects.$inferInsert

View File

@@ -0,0 +1,44 @@
import {
pgTable,
uuid,
timestamp,
bigint,
text,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authProfiles } from "./auth_profiles"
import { staffTimeEntries } from "./staff_time_entries"
export const staffZeitstromTimestamps = pgTable("staff_zeitstromtimestamps", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
profile: uuid("profile")
.notNull()
.references(() => authProfiles.id),
key: text("key").notNull(),
intent: text("intent").notNull(),
time: timestamp("time", { withTimezone: true }).notNull(),
staffTimeEntry: uuid("staff_time_entry").references(
() => staffTimeEntries.id
),
internalNote: text("internal_note"),
})
export type StaffZeitstromTimestamp =
typeof staffZeitstromTimestamps.$inferSelect
export type NewStaffZeitstromTimestamp =
typeof staffZeitstromTimestamps.$inferInsert

View File

@@ -0,0 +1,69 @@
import {
pgTable,
uuid,
bigint,
integer,
text,
timestamp,
boolean,
doublePrecision,
} 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!
export const statementAllocations = pgTable("statementallocations", {
id: uuid("id").primaryKey().defaultRandom(),
// foreign keys
bsId: integer("bs_id")
.notNull()
.references(() => bankstatements.id),
cdId: integer("cd_id").references(() => createddocuments.id),
amount: doublePrecision("amount").notNull().default(0),
iiId: bigint("ii_id", { mode: "number" }).references(
() => incominginvoices.id
),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
account: bigint("account", { mode: "number" }).references(
() => accounts.id
),
createdAt: timestamp("created_at", {
withTimezone: false,
}).defaultNow(),
ownaccount: uuid("ownaccount").references(() => ownaccounts.id),
description: text("description"),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
archived: boolean("archived").notNull().default(false),
})
export type StatementAllocation = typeof statementAllocations.$inferSelect
export type NewStatementAllocation =
typeof statementAllocations.$inferInsert

51
db/schema/tasks.ts Normal file
View File

@@ -0,0 +1,51 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { customers } from "./customers"
export const tasks = pgTable("tasks", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
description: text("description"),
categorie: text("categorie"),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
// FIXED: user_id statt profile, verweist auf auth_users.id
userId: uuid("user_id").references(() => authUsers.id),
project: bigint("project", { mode: "number" }),
plant: bigint("plant", { mode: "number" }),
customer: bigint("customer", { mode: "number" }).references(
() => customers.id
),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Task = typeof tasks.$inferSelect
export type NewTask = typeof tasks.$inferInsert

28
db/schema/taxtypes.ts Normal file
View File

@@ -0,0 +1,28 @@
import {
pgTable,
bigint,
timestamp,
text,
uuid,
} from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
export const taxTypes = pgTable("taxtypes", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
label: text("label").notNull(),
percentage: bigint("percentage", { mode: "number" }).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type TaxType = typeof taxTypes.$inferSelect
export type NewTaxType = typeof taxTypes.$inferInsert

140
db/schema/tenants.ts Normal file
View File

@@ -0,0 +1,140 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
integer,
smallint,
date,
uuid,
pgEnum,
} from "drizzle-orm/pg-core"
import { authUsers } from "./auth_users"
import {lockedTenantEnum} from "./enums";
export const tenants = pgTable(
"tenants",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
short: text("short").notNull(),
calendarConfig: jsonb("calendarConfig").default({
eventTypes: [
{ color: "blue", label: "Büro" },
{ color: "yellow", label: "Besprechung" },
{ color: "green", label: "Umsetzung" },
{ color: "red", label: "Vor Ort Termin" },
],
}),
timeConfig: jsonb("timeConfig").notNull().default({}),
tags: jsonb("tags").notNull().default({
products: [],
documents: [],
}),
measures: jsonb("measures")
.notNull()
.default([
{ name: "Netzwerktechnik", short: "NWT" },
{ name: "Elektrotechnik", short: "ELT" },
{ name: "Photovoltaik", short: "PV" },
{ name: "Videüberwachung", short: "VÜA" },
{ name: "Projekt", short: "PRJ" },
{ name: "Smart Home", short: "SHO" },
]),
businessInfo: jsonb("businessInfo").default({
zip: "",
city: "",
name: "",
street: "",
}),
features: jsonb("features").default({
objects: true,
calendar: true,
contacts: true,
projects: true,
vehicles: true,
contracts: true,
inventory: true,
accounting: true,
timeTracking: true,
planningBoard: true,
workingTimeTracking: true,
}),
ownFields: jsonb("ownFields"),
numberRanges: jsonb("numberRanges")
.notNull()
.default({
vendors: { prefix: "", suffix: "", nextNumber: 10000 },
customers: { prefix: "", suffix: "", nextNumber: 10000 },
products: { prefix: "AT-", suffix: "", nextNumber: 1000 },
quotes: { prefix: "AN-", suffix: "", nextNumber: 1000 },
confirmationOrders: { prefix: "AB-", suffix: "", nextNumber: 1000 },
invoices: { prefix: "RE-", suffix: "", nextNumber: 1000 },
spaces: { prefix: "LP-", suffix: "", nextNumber: 1000 },
inventoryitems: { prefix: "IA-", suffix: "", nextNumber: 1000 },
projects: { prefix: "PRJ-", suffix: "", nextNumber: 1000 },
costcentres: { prefix: "KST-", suffix: "", nextNumber: 1000 },
}),
standardEmailForInvoices: text("standardEmailForInvoices"),
extraModules: jsonb("extraModules").notNull().default([]),
isInTrial: boolean("isInTrial").default(false),
trialEndDate: date("trialEndDate"),
stripeCustomerId: text("stripeCustomerId"),
hasActiveLicense: boolean("hasActiveLicense").notNull().default(false),
userLicenseCount: integer("userLicenseCount")
.notNull()
.default(0),
workstationLicenseCount: integer("workstationLicenseCount")
.notNull()
.default(0),
standardPaymentDays: smallint("standardPaymentDays")
.notNull()
.default(14),
dokuboxEmailAddresses: jsonb("dokuboxEmailAddresses").default([]),
dokuboxkey: uuid("dokuboxkey").notNull().defaultRandom(),
autoPrepareIncomingInvoices: boolean("autoPrepareIncomingInvoices")
.default(true),
portalDomain: text("portalDomain"),
portalConfig: jsonb("portalConfig")
.notNull()
.default({ primayColor: "#69c350" }),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
locked: lockedTenantEnum("locked"),
}
)
export type Tenant = typeof tenants.$inferSelect
export type NewTenant = typeof tenants.$inferInsert

View File

@@ -0,0 +1,44 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import { textTemplatePositionsEnum } from "./enums"
export const textTemplates = pgTable("texttemplates", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
name: text("name").notNull(),
text: text("text").notNull(),
documentType: text("documentType").default(""),
default: boolean("default").notNull().default(false),
pos: textTemplatePositionsEnum("pos").notNull(),
archived: boolean("archived").notNull().default(false),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type TextTemplate = typeof textTemplates.$inferSelect
export type NewTextTemplate = typeof textTemplates.$inferInsert

27
db/schema/units.ts Normal file
View File

@@ -0,0 +1,27 @@
import {
pgTable,
bigint,
timestamp,
text,
} from "drizzle-orm/pg-core"
export const units = pgTable("units", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
single: text("single").notNull(),
multiple: text("multiple"),
short: text("short"),
step: text("step").notNull().default("1"),
})
export type Unit = typeof units.$inferSelect
export type NewUnit = typeof units.$inferInsert

View File

@@ -0,0 +1,53 @@
import {
pgTable,
uuid,
timestamp,
bigint,
boolean,
jsonb,
numeric, pgEnum,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
import {credentialTypesEnum} from "./enums";
export const userCredentials = pgTable("user_credentials", {
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
userId: uuid("user_id")
.notNull()
.references(() => authUsers.id),
updatedAt: timestamp("updated_at", { withTimezone: true }),
tenantId: bigint("tenant_id", { mode: "number" })
.notNull()
.references(() => tenants.id),
smtpPort: numeric("smtp_port"),
smtpSsl: boolean("smtp_ssl"),
type: credentialTypesEnum("type").notNull(),
imapPort: numeric("imap_port"),
imapSsl: boolean("imap_ssl"),
emailEncrypted: jsonb("email_encrypted"),
passwordEncrypted: jsonb("password_encrypted"),
smtpHostEncrypted: jsonb("smtp_host_encrypted"),
imapHostEncrypted: jsonb("imap_host_encrypted"),
accessTokenEncrypted: jsonb("access_token_encrypted"),
refreshTokenEncrypted: jsonb("refresh_token_encrypted"),
})
export type UserCredential = typeof userCredentials.$inferSelect
export type NewUserCredential = typeof userCredentials.$inferInsert

57
db/schema/vehicles.ts Normal file
View File

@@ -0,0 +1,57 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
doublePrecision,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const vehicles = pgTable("vehicles", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
licensePlate: text("licensePlate"),
name: text("name"),
type: text("type"),
active: boolean("active").default(true),
// FIXED: driver references auth_users.id
driver: uuid("driver").references(() => authUsers.id),
vin: text("vin"),
tankSize: doublePrecision("tankSize").notNull().default(0),
archived: boolean("archived").notNull().default(false),
buildYear: text("buildYear"),
towingCapacity: bigint("towingCapacity", { mode: "number" }),
powerInKW: bigint("powerInKW", { mode: "number" }),
color: text("color"),
profiles: jsonb("profiles").notNull().default([]),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Vehicle = typeof vehicles.$inferSelect
export type NewVehicle = typeof vehicles.$inferInsert

45
db/schema/vendors.ts Normal file
View File

@@ -0,0 +1,45 @@
import {
pgTable,
bigint,
text,
timestamp,
boolean,
jsonb,
uuid,
} from "drizzle-orm/pg-core"
import { tenants } from "./tenants"
import { authUsers } from "./auth_users"
export const vendors = pgTable("vendors", {
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedByDefaultAsIdentity(),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
name: text("name").notNull(),
vendorNumber: text("vendorNumber").notNull(),
tenant: bigint("tenant", { mode: "number" })
.notNull()
.references(() => tenants.id),
infoData: jsonb("infoData").notNull().default({}),
notes: text("notes"),
hasSEPA: boolean("hasSEPA").notNull().default(false),
profiles: jsonb("profiles").notNull().default([]),
archived: boolean("archived").notNull().default(false),
defaultPaymentMethod: text("defaultPaymentMethod"),
updatedAt: timestamp("updated_at", { withTimezone: true }),
updatedBy: uuid("updated_by").references(() => authUsers.id),
})
export type Vendor = typeof vendors.$inferSelect
export type NewVendor = typeof vendors.$inferInsert