Bankkonto-Aktualisierung über zentralen Dienst reparieren
All checks were successful
Build and Push Docker Images / build-website (push) Successful in 23s
Build and Push Docker Images / build-central-services-admin (push) Successful in 22s
Build and Push Docker Images / build-docs (push) Successful in 22s
Build and Push Docker Images / build-backend (push) Successful in 44s
Build and Push Docker Images / build-frontend (push) Successful in 1m13s
Build and Push Docker Images / build-central-services-api (push) Successful in 21s

This commit is contained in:
root
2026-09-01 12:24:34 +00:00
parent 98a8d1d061
commit 2428230d94
5 changed files with 62 additions and 23 deletions

View File

@@ -57,6 +57,16 @@ export const getBankAccountOwnerName = (account: any) => {
return ownerName || null
}
export const isExpiredBankingError = (error: any) => {
const values = [
error?.response?.data?.summary,
error?.response?.data?.detail,
error?.response?.data?.message,
error?.message,
]
return values.some((value) => typeof value === "string" && value.toLowerCase().includes("expired"))
}
export function bankStatementService(server: FastifyInstance) {
let accessToken: string | null = null
@@ -83,7 +93,7 @@ export function bankStatementService(server: FastifyInstance) {
// -----------------------------------------------
// ✔ Salden laden
// -----------------------------------------------
const getBalanceData = async (accountId: string): Promise<any> => {
const getBalanceData = async (accountId: string, tenantId: number): Promise<any> => {
try {
if (useCentralBanking) return await centralServicesClient.getBankingBalances(accountId)
const {data} = await axios.get(
@@ -100,15 +110,14 @@ export function bankStatementService(server: FastifyInstance) {
} catch (err: any) {
server.log.error(err.response?.data ?? err)
const expired =
err.response?.data?.summary?.includes("expired") ||
err.response?.data?.detail?.includes("expired")
if (expired) {
if (isExpiredBankingError(err)) {
await server.db
.update(bankaccounts)
.set({expired: true})
.where(eq(bankaccounts.accountId, accountId))
.where(and(
eq(bankaccounts.accountId, accountId),
eq(bankaccounts.tenant, tenantId),
))
}
throw err
@@ -203,7 +212,7 @@ export function bankStatementService(server: FastifyInstance) {
// ---------------------------
// 1. BALANCE SYNC
// ---------------------------
const balData = await getBalanceData(account.accountId)
const balData = await getBalanceData(account.accountId, tenantId)
if (balData) {
const closing = balData.balances.find(

View File

@@ -86,7 +86,12 @@ async function requestPushServer<T>(method: "GET" | "POST" | "DELETE", path: str
if (!response.ok) {
const message = data?.message || data?.error || `Push-Server Anfrage fehlgeschlagen (${response.status})`
throw new Error(message)
const error = Object.assign(new Error(message), {
code: data?.code || data?.error,
status: response.status,
response: { data },
})
throw error
}
return data as T

View File

@@ -1129,9 +1129,17 @@ export default async function resourceRoutes(server: FastifyInstance) {
return reply.code(404).send({ error: "Resource not found" })
}
let data: Record<string, any> = { ...body, updated_at: new Date().toISOString(), updated_by: userId }
//@ts-ignore
delete data.updatedBy; delete data.updatedAt;
let data: Record<string, any> = { ...body }
delete data.updatedAt
delete data.updated_at
delete data.updatedBy
delete data.updated_by
const updatedAt = new Date()
if (Object.prototype.hasOwnProperty.call(table, "updatedAt")) data.updatedAt = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updated_at")) data.updated_at = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updatedBy")) data.updatedBy = userId
if (Object.prototype.hasOwnProperty.call(table, "updated_by")) data.updated_by = userId
if (resource === "filetags") {
delete data.isSystemUsed
@@ -1144,9 +1152,11 @@ export default async function resourceRoutes(server: FastifyInstance) {
if (portalCustomerId) {
data = {
...sanitizePortalCustomerUpdate(data),
updated_at: data.updated_at,
updated_by: data.updated_by,
}
if (Object.prototype.hasOwnProperty.call(table, "updatedAt")) data.updatedAt = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updated_at")) data.updated_at = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updatedBy")) data.updatedBy = userId
if (Object.prototype.hasOwnProperty.call(table, "updated_by")) data.updated_by = userId
}
if (resource === "members") {
@@ -1162,9 +1172,11 @@ export default async function resourceRoutes(server: FastifyInstance) {
if (prepared.error) return reply.code(400).send({ error: prepared.error })
data = {
...prepared.data,
updated_at: data.updated_at,
updated_by: data.updated_by,
}
if (Object.prototype.hasOwnProperty.call(table, "updatedAt")) data.updatedAt = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updated_at")) data.updated_at = updatedAt
if (Object.prototype.hasOwnProperty.call(table, "updatedBy")) data.updatedBy = userId
if (Object.prototype.hasOwnProperty.call(table, "updated_by")) data.updated_by = userId
}
if (resource === "costcentres") {

View File

@@ -1,7 +1,10 @@
import test from "node:test"
import assert from "node:assert/strict"
import { getBankAccountOwnerName } from "../src/modules/cron/bankstatementsync.service"
import {
getBankAccountOwnerName,
isExpiredBankingError,
} from "../src/modules/cron/bankstatementsync.service"
test("reads the GoCardless owner_name for a bank account", () => {
assert.equal(
@@ -15,3 +18,9 @@ test("does not overwrite the stored owner with an empty provider value", () => {
assert.equal(getBankAccountOwnerName({}), null)
assert.equal(getBankAccountOwnerName(null), null)
})
test("recognizes expired EUA errors from direct and central banking responses", () => {
assert.equal(isExpiredBankingError({ response: { data: { detail: "EUA was valid for 90 days and it expired" } } }), true)
assert.equal(isExpiredBankingError(new Error("EUA was valid for 90 days and it expired")), true)
assert.equal(isExpiredBankingError({ response: { data: { message: "temporary provider error" } } }), false)
})

View File

@@ -74,16 +74,20 @@ const addAccount = async (account) => {
}
const updateAccount = async (account) => {
const existingAccount = bankaccounts.value.find(i => i.iban === account.iban)
if (!existingAccount) return
let bankaccountId = bankaccounts.value.find(i => i.iban === account.iban).id
// Fehlerbehandlung analog zu addAccount verbessert
try {
const res = await useEntities("bankaccounts").update(bankaccountId, {accountId: account.id, expired: false}, true)
await useEntities("bankaccounts").update(existingAccount.id, {
accountId: account.id,
ownerName: account.owner_name,
iban: account.iban,
bankId: account.institution_id,
expired: false,
}, true)
// useEntities feuert bereits einen Success-Toast
// reqData.value = null // Das würde das Modal leeren, ggf. gewünscht? Im Original war es drin.
setupPage()
await setupPage()
} catch (error) {
console.log(error)
toast.add({title: "Es gab einen Fehler beim Aktualisieren des Accounts", color:"error"})