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
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:
@@ -57,6 +57,16 @@ export const getBankAccountOwnerName = (account: any) => {
|
|||||||
return ownerName || null
|
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) {
|
export function bankStatementService(server: FastifyInstance) {
|
||||||
|
|
||||||
let accessToken: string | null = null
|
let accessToken: string | null = null
|
||||||
@@ -83,7 +93,7 @@ export function bankStatementService(server: FastifyInstance) {
|
|||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
// ✔ Salden laden
|
// ✔ Salden laden
|
||||||
// -----------------------------------------------
|
// -----------------------------------------------
|
||||||
const getBalanceData = async (accountId: string): Promise<any> => {
|
const getBalanceData = async (accountId: string, tenantId: number): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
if (useCentralBanking) return await centralServicesClient.getBankingBalances(accountId)
|
if (useCentralBanking) return await centralServicesClient.getBankingBalances(accountId)
|
||||||
const {data} = await axios.get(
|
const {data} = await axios.get(
|
||||||
@@ -100,15 +110,14 @@ export function bankStatementService(server: FastifyInstance) {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
server.log.error(err.response?.data ?? err)
|
server.log.error(err.response?.data ?? err)
|
||||||
|
|
||||||
const expired =
|
if (isExpiredBankingError(err)) {
|
||||||
err.response?.data?.summary?.includes("expired") ||
|
|
||||||
err.response?.data?.detail?.includes("expired")
|
|
||||||
|
|
||||||
if (expired) {
|
|
||||||
await server.db
|
await server.db
|
||||||
.update(bankaccounts)
|
.update(bankaccounts)
|
||||||
.set({expired: true})
|
.set({expired: true})
|
||||||
.where(eq(bankaccounts.accountId, accountId))
|
.where(and(
|
||||||
|
eq(bankaccounts.accountId, accountId),
|
||||||
|
eq(bankaccounts.tenant, tenantId),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err
|
throw err
|
||||||
@@ -203,7 +212,7 @@ export function bankStatementService(server: FastifyInstance) {
|
|||||||
// ---------------------------
|
// ---------------------------
|
||||||
// 1. BALANCE SYNC
|
// 1. BALANCE SYNC
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
const balData = await getBalanceData(account.accountId)
|
const balData = await getBalanceData(account.accountId, tenantId)
|
||||||
|
|
||||||
if (balData) {
|
if (balData) {
|
||||||
const closing = balData.balances.find(
|
const closing = balData.balances.find(
|
||||||
|
|||||||
@@ -86,7 +86,12 @@ async function requestPushServer<T>(method: "GET" | "POST" | "DELETE", path: str
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const message = data?.message || data?.error || `Push-Server Anfrage fehlgeschlagen (${response.status})`
|
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
|
return data as T
|
||||||
|
|||||||
@@ -1129,9 +1129,17 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
return reply.code(404).send({ error: "Resource not found" })
|
return reply.code(404).send({ error: "Resource not found" })
|
||||||
}
|
}
|
||||||
|
|
||||||
let data: Record<string, any> = { ...body, updated_at: new Date().toISOString(), updated_by: userId }
|
let data: Record<string, any> = { ...body }
|
||||||
//@ts-ignore
|
delete data.updatedAt
|
||||||
delete data.updatedBy; 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") {
|
if (resource === "filetags") {
|
||||||
delete data.isSystemUsed
|
delete data.isSystemUsed
|
||||||
@@ -1144,9 +1152,11 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
if (portalCustomerId) {
|
if (portalCustomerId) {
|
||||||
data = {
|
data = {
|
||||||
...sanitizePortalCustomerUpdate(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") {
|
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 })
|
if (prepared.error) return reply.code(400).send({ error: prepared.error })
|
||||||
data = {
|
data = {
|
||||||
...prepared.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") {
|
if (resource === "costcentres") {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import test from "node:test"
|
import test from "node:test"
|
||||||
import assert from "node:assert/strict"
|
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", () => {
|
test("reads the GoCardless owner_name for a bank account", () => {
|
||||||
assert.equal(
|
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)
|
||||||
assert.equal(getBankAccountOwnerName(null), 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)
|
||||||
|
})
|
||||||
|
|||||||
@@ -74,16 +74,20 @@ const addAccount = async (account) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateAccount = 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 {
|
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
|
// useEntities feuert bereits einen Success-Toast
|
||||||
// reqData.value = null // Das würde das Modal leeren, ggf. gewünscht? Im Original war es drin.
|
await setupPage()
|
||||||
setupPage()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
toast.add({title: "Es gab einen Fehler beim Aktualisieren des Accounts", color:"error"})
|
toast.add({title: "Es gab einen Fehler beim Aktualisieren des Accounts", color:"error"})
|
||||||
|
|||||||
Reference in New Issue
Block a user