Ausgehende SEPA-Mandate einführen #183
This commit is contained in:
@@ -27,6 +27,7 @@ const columnMap: Record<string, any> = {
|
||||
customerspaces: historyitems.customerspace,
|
||||
customerinventoryitems: historyitems.customerinventoryitem,
|
||||
memberrelations: historyitems.memberrelation,
|
||||
outgoingsepamandates: historyitems.outgoingsepamandate,
|
||||
};
|
||||
|
||||
const insertFieldMap: Record<string, string> = {
|
||||
@@ -53,6 +54,7 @@ const insertFieldMap: Record<string, string> = {
|
||||
customerspaces: "customerspace",
|
||||
customerinventoryitems: "customerinventoryitem",
|
||||
memberrelations: "memberrelation",
|
||||
outgoingsepamandates: "outgoingsepamandate",
|
||||
}
|
||||
|
||||
const parseId = (value: string) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
sql,
|
||||
} from "drizzle-orm"
|
||||
|
||||
import { authProfiles, costcentres } from "../../../db/schema";
|
||||
import { authProfiles, costcentres, customers, entitybankaccounts } from "../../../db/schema";
|
||||
import { resourceConfig } from "../../utils/resource.config";
|
||||
import { useNextNumberRangeNumber } from "../../utils/functions";
|
||||
import { getHistoryEntityLabel, insertHistoryItem } from "../../utils/history";
|
||||
@@ -365,6 +365,50 @@ function prepareEntityBankAccountPayload(payload: Record<string, any>, requireAl
|
||||
return { data: result }
|
||||
}
|
||||
|
||||
async function validateOutgoingSepaMandatePayload(
|
||||
server: FastifyInstance,
|
||||
tenantId: number,
|
||||
payload: Record<string, any>,
|
||||
existing: Record<string, any> | null = null
|
||||
) {
|
||||
const customerId = Number(payload.customer ?? existing?.customer)
|
||||
const bankaccountId = Number(payload.bankaccount ?? existing?.bankaccount)
|
||||
|
||||
if (!customerId || !bankaccountId) {
|
||||
return "Kunde und Bankverbindung sind Pflichtfelder."
|
||||
}
|
||||
|
||||
const [customer] = await server.db
|
||||
.select()
|
||||
.from(customers)
|
||||
.where(and(eq(customers.id, customerId), eq(customers.tenant, tenantId)))
|
||||
.limit(1)
|
||||
|
||||
if (!customer) {
|
||||
return "Kunde nicht gefunden."
|
||||
}
|
||||
|
||||
const [bankaccount] = await server.db
|
||||
.select()
|
||||
.from(entitybankaccounts)
|
||||
.where(and(eq(entitybankaccounts.id, bankaccountId), eq(entitybankaccounts.tenant, tenantId)))
|
||||
.limit(1)
|
||||
|
||||
if (!bankaccount) {
|
||||
return "Bankverbindung nicht gefunden."
|
||||
}
|
||||
|
||||
const assignedBankAccountIds = Array.isArray((customer.infoData as any)?.bankAccountIds)
|
||||
? (customer.infoData as any).bankAccountIds.map((id: any) => Number(id))
|
||||
: []
|
||||
|
||||
if (!assignedBankAccountIds.includes(bankaccountId)) {
|
||||
return "Die Bankverbindung ist dem ausgewählten Kunden nicht zugeordnet."
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default async function resourceRoutes(server: FastifyInstance) {
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -796,6 +840,13 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === "outgoingsepamandates") {
|
||||
const validationError = await validateOutgoingSepaMandatePayload(server, req.user.tenant_id, createData)
|
||||
if (validationError) {
|
||||
return reply.code(400).send({ error: validationError })
|
||||
}
|
||||
}
|
||||
|
||||
if (config.numberRangeHolder && !body[config.numberRangeHolder]) {
|
||||
const numberRangeResource = resource === "members" ? "customers" : resource
|
||||
const result = await useNextNumberRangeNumber(server, req.user.tenant_id, numberRangeResource)
|
||||
@@ -809,6 +860,17 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
||||
|
||||
const [created] = await server.db.insert(table).values(createData).returning()
|
||||
|
||||
if (resource === "outgoingsepamandates" && created?.defaultMandate) {
|
||||
await server.db
|
||||
.update(table)
|
||||
.set({ defaultMandate: false })
|
||||
.where(and(
|
||||
eq(table.tenant, req.user.tenant_id),
|
||||
eq(table.customer, created.customer),
|
||||
sql`${table.id} <> ${created.id}`
|
||||
))
|
||||
}
|
||||
|
||||
if (["products", "services", "hourrates"].includes(resource)) {
|
||||
await recalculateServicePricesForTenant(server, req.user.tenant_id, req.user?.user_id || null);
|
||||
}
|
||||
@@ -917,6 +979,13 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === "outgoingsepamandates") {
|
||||
const validationError = await validateOutgoingSepaMandatePayload(server, tenantId, data, oldRecord)
|
||||
if (validationError) {
|
||||
return reply.code(400).send({ error: validationError })
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(data).forEach((key) => {
|
||||
const value = data[key]
|
||||
const shouldNormalize =
|
||||
@@ -934,6 +1003,17 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
||||
updateWhereCond = applyResourceWhereFilters(resource, table, updateWhereCond)
|
||||
const [updated] = await server.db.update(table).set(data).where(updateWhereCond).returning()
|
||||
|
||||
if (resource === "outgoingsepamandates" && updated?.defaultMandate) {
|
||||
await server.db
|
||||
.update(table)
|
||||
.set({ defaultMandate: false })
|
||||
.where(and(
|
||||
eq(table.tenant, tenantId),
|
||||
eq(table.customer, updated.customer),
|
||||
sql`${table.id} <> ${updated.id}`
|
||||
))
|
||||
}
|
||||
|
||||
if (["products", "services", "hourrates"].includes(resource)) {
|
||||
await recalculateServicePricesForTenant(server, tenantId, userId);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ const HISTORY_ENTITY_LABELS: Record<string, string> = {
|
||||
files: "Dateien",
|
||||
memberrelations: "Mitgliedsverhältnisse",
|
||||
teams: "Teams",
|
||||
outgoingsepamandates: "Ausgehende SEPA-Mandate",
|
||||
}
|
||||
|
||||
export function getHistoryEntityLabel(entity: string) {
|
||||
@@ -94,6 +95,7 @@ export async function insertHistoryItem(
|
||||
incominginvoices: "incomingInvoice",
|
||||
files: "file",
|
||||
memberrelations: "memberrelation",
|
||||
outgoingsepamandates: "outgoingsepamandate",
|
||||
}
|
||||
|
||||
const fkColumn = columnMap[params.entity]
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
letterheads,
|
||||
memberrelations,
|
||||
ownaccounts,
|
||||
outgoingsepamandates,
|
||||
plants,
|
||||
productcategories,
|
||||
products,
|
||||
@@ -53,7 +54,7 @@ export const resourceConfig = {
|
||||
},
|
||||
customers: {
|
||||
searchColumns: ["name", "nameAddition", "customerNumber", "firstname", "lastname", "notes"],
|
||||
mtmLoad: ["contacts","projects","plants","createddocuments","contracts","customerinventoryitems","customerspaces"],
|
||||
mtmLoad: ["contacts","projects","plants","createddocuments","contracts","outgoingsepamandates","customerinventoryitems","customerspaces"],
|
||||
table: customers,
|
||||
numberRangeHolder: "customerNumber",
|
||||
},
|
||||
@@ -77,7 +78,13 @@ export const resourceConfig = {
|
||||
table: contracts,
|
||||
searchColumns: ["name", "notes", "contractNumber", "paymentType", "billingInterval", "sepaRef", "bankingName"],
|
||||
numberRangeHolder: "contractNumber",
|
||||
mtoLoad: ["customer", "contracttype"],
|
||||
mtoLoad: ["customer", "contracttype", "outgoingsepamandate"],
|
||||
},
|
||||
outgoingsepamandates: {
|
||||
table: outgoingsepamandates,
|
||||
searchColumns: ["reference", "status", "mandateType", "sequenceType", "notes"],
|
||||
numberRangeHolder: "reference",
|
||||
mtoLoad: ["customer", "bankaccount"],
|
||||
},
|
||||
contracttypes: {
|
||||
table: contracttypes,
|
||||
@@ -200,7 +207,7 @@ export const resourceConfig = {
|
||||
},
|
||||
createddocuments: {
|
||||
table: createddocuments,
|
||||
mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead","createddocument"],
|
||||
mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead","createddocument", "outgoingsepamandate"],
|
||||
mtmLoad: ["statementallocations","files","createddocuments"],
|
||||
mtmListLoad: ["statementallocations", "files"],
|
||||
},
|
||||
@@ -235,6 +242,10 @@ export const resourceConfig = {
|
||||
table: entitybankaccounts,
|
||||
searchColumns: ["description"],
|
||||
},
|
||||
bankaccount: {
|
||||
table: entitybankaccounts,
|
||||
searchColumns: ["description"],
|
||||
},
|
||||
serialexecutions: {
|
||||
table: serialExecutions
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user