Add CBankverbindungen entity with multilingual support, tooltips, and layout definitions
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Custom\Classes\FormulaFunctions\IbanGroup;
|
||||
|
||||
use Espo\Core\Formula\Functions\BaseFunction;
|
||||
use Espo\Core\Formula\ArgumentList;
|
||||
|
||||
/**
|
||||
* IBAN-Validierung mit Modulo-97-Algorithmus (ISO 13616)
|
||||
* Mathematische Prüfung ohne externe Datenbank
|
||||
*/
|
||||
class ValidateType extends BaseFunction
|
||||
{
|
||||
public function process(ArgumentList $args)
|
||||
{
|
||||
if (count($args) < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$iban = $this->evaluate($args[0]);
|
||||
|
||||
if (!$iban || !is_string($iban)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Leerzeichen entfernen und in Großbuchstaben umwandeln
|
||||
$iban = strtoupper(str_replace(' ', '', trim($iban)));
|
||||
|
||||
// Basis-Format prüfen: Mindestens 15, maximal 34 Zeichen
|
||||
$length = strlen($iban);
|
||||
if ($length < 15 || $length > 34) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format prüfen: 2 Buchstaben (Ländercode) + 2 Ziffern (Prüfziffer) + Rest alphanumerisch
|
||||
if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/', $iban)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ländercode extrahieren und Länge prüfen (optionale erweiterte Prüfung)
|
||||
$countryCode = substr($iban, 0, 2);
|
||||
$expectedLength = $this->getIbanLength($countryCode);
|
||||
if ($expectedLength && $length !== $expectedLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modulo-97-Algorithmus (ISO 13616)
|
||||
// 1. IBAN umstellen: ersten 4 Zeichen ans Ende
|
||||
$rearranged = substr($iban, 4) . substr($iban, 0, 4);
|
||||
|
||||
// 2. Buchstaben in Zahlen umwandeln (A=10, B=11, ..., Z=35)
|
||||
$numericString = '';
|
||||
for ($i = 0; $i < strlen($rearranged); $i++) {
|
||||
$char = $rearranged[$i];
|
||||
if (ctype_alpha($char)) {
|
||||
// A=65 -> 10, B=66 -> 11, ..., Z=90 -> 35
|
||||
$numericString .= (string)(ord($char) - 55);
|
||||
} else {
|
||||
$numericString .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Modulo 97 berechnen (schrittweise wegen großer Zahlen)
|
||||
$remainder = 0;
|
||||
for ($i = 0; $i < strlen($numericString); $i++) {
|
||||
$remainder = ($remainder * 10 + (int)$numericString[$i]) % 97;
|
||||
}
|
||||
|
||||
// 4. Rest muss genau 1 sein für gültige IBAN
|
||||
return $remainder === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die erwartete Länge einer IBAN für einen Ländercode zurück
|
||||
* Quelle: ISO 13616 / SWIFT IBAN Registry
|
||||
*/
|
||||
private function getIbanLength(string $countryCode): ?int
|
||||
{
|
||||
$lengths = [
|
||||
'AD' => 24, 'AE' => 23, 'AL' => 28, 'AT' => 20, 'AZ' => 28,
|
||||
'BA' => 20, 'BE' => 16, 'BG' => 22, 'BH' => 22, 'BR' => 29,
|
||||
'BY' => 28, 'CH' => 21, 'CR' => 22, 'CY' => 28, 'CZ' => 24,
|
||||
'DE' => 22, 'DK' => 18, 'DO' => 28, 'EE' => 20, 'EG' => 29,
|
||||
'ES' => 24, 'FI' => 18, 'FO' => 18, 'FR' => 27, 'GB' => 22,
|
||||
'GE' => 22, 'GI' => 23, 'GL' => 18, 'GR' => 27, 'GT' => 28,
|
||||
'HR' => 21, 'HU' => 28, 'IE' => 22, 'IL' => 23, 'IS' => 26,
|
||||
'IT' => 27, 'JO' => 30, 'KW' => 30, 'KZ' => 20, 'LB' => 28,
|
||||
'LC' => 32, 'LI' => 21, 'LT' => 20, 'LU' => 20, 'LV' => 21,
|
||||
'MC' => 27, 'MD' => 24, 'ME' => 22, 'MK' => 19, 'MR' => 27,
|
||||
'MT' => 31, 'MU' => 30, 'NL' => 18, 'NO' => 15, 'PK' => 24,
|
||||
'PL' => 28, 'PS' => 29, 'PT' => 25, 'QA' => 29, 'RO' => 24,
|
||||
'RS' => 22, 'SA' => 24, 'SE' => 24, 'SI' => 19, 'SK' => 24,
|
||||
'SM' => 27, 'TN' => 24, 'TR' => 26, 'UA' => 29, 'VA' => 22,
|
||||
'VG' => 24, 'XK' => 20,
|
||||
];
|
||||
|
||||
return $lengths[$countryCode] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,52 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Bankverbindung erstellen"
|
||||
"Create CBankverbindungen": "Bankverbindung erstellen",
|
||||
"CBankverbindungen": "Bankverbindungen"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"iban": "IBAN",
|
||||
"bic": "BIC/SWIFT",
|
||||
"kontoinhaber": "Kontoinhaber",
|
||||
"bankname": "Bankname",
|
||||
"istAktiv": "Ist aktiv",
|
||||
"istStandard": "Standard-Konto",
|
||||
"advowareKontoId": "Advoware Konto-ID",
|
||||
"advowareLastSync": "Advoware letzte Synchronisation",
|
||||
"syncStatus": "Sync-Status",
|
||||
"beteiligte": "Beteiligter",
|
||||
"createdAt": "Erstellt am",
|
||||
"modifiedAt": "Geändert am",
|
||||
"createdBy": "Erstellt von",
|
||||
"modifiedBy": "Geändert von",
|
||||
"assignedUser": "Zugewiesen an",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"links": {
|
||||
"beteiligte": "Beteiligter",
|
||||
"createdBy": "Erstellt von",
|
||||
"modifiedBy": "Geändert von",
|
||||
"assignedUser": "Zugewiesen an",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"tooltips": {
|
||||
"name": "Bezeichnung der Bankverbindung (z.B. 'Hauptkonto', 'Geschäftskonto', 'Mietkautionskonto')",
|
||||
"iban": "Internationale Bankkontonummer im Format DE89370400440532013000. Nur Großbuchstaben und Ziffern, 2-stelliger Ländercode gefolgt von Prüfziffer und Kontonummer (max. 34 Zeichen)",
|
||||
"bic": "Bank Identifier Code (SWIFT-Code) im Format COBADEFFXXX. 8 oder 11 Zeichen: 4 Zeichen Bankcode, 2 Zeichen Ländercode, 2 Zeichen Ortscode, optional 3 Zeichen Filialcode",
|
||||
"kontoinhaber": "Vollständiger Name des Kontoinhabers, wie auf dem Bankkonto hinterlegt",
|
||||
"bankname": "Name der kontoführenden Bank (z.B. 'Sparkasse Köln Bonn', 'Deutsche Bank')",
|
||||
"istAktiv": "Deaktivierte Bankverbindungen werden nicht mehr für neue Transaktionen verwendet, bleiben aber im System erhalten",
|
||||
"istStandard": "Das Standard-Konto wird automatisch vorgeschlagen, wenn für diesen Beteiligten eine Zahlung erfasst wird. Nur ein Konto kann als Standard markiert sein",
|
||||
"beteiligte": "Der Beteiligte, dem diese Bankverbindung zugeordnet ist",
|
||||
"advowareKontoId": "Eindeutige Konto-ID aus Advoware für die Synchronisation. Wird automatisch bei der Synchronisation gesetzt",
|
||||
"advowareLastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation mit Advoware. Wird automatisch aktualisiert",
|
||||
"syncStatus": "Status der Advoware-Synchronisation: 'Synchronisiert' = Daten sind aktuell, 'Abweichungen' = Daten weichen ab, 'Fehlgeschlagen' = Sync-Fehler aufgetreten"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"clean": "Synchronisiert",
|
||||
"unclean": "Abweichungen",
|
||||
"failed": "Fehlgeschlagen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
"calls1": "Anrufe",
|
||||
"contactsBeteiligte": "Freigegebene Nutzer",
|
||||
"dokumentesBeteiligte": "Dokumente",
|
||||
"bankverbindungens": "Bankverbindungen",
|
||||
"betnr": "Advoware Identifikator",
|
||||
"advowareLastSync": "Advoware letzte Synchronisation",
|
||||
"syncStatus": "Sync-Status"
|
||||
@@ -28,7 +29,8 @@
|
||||
"adressens": "Adressen",
|
||||
"calls1": "Anrufe",
|
||||
"contactsBeteiligte": "Freigegebene Nutzer",
|
||||
"dokumentesBeteiligte": "Dokumente"
|
||||
"dokumentesBeteiligte": "Dokumente",
|
||||
"bankverbindungens": "Bankverbindungen"
|
||||
},
|
||||
"labels": {
|
||||
"Create CBeteiligte": "Beteiligte erstellen"
|
||||
|
||||
@@ -1,9 +1,41 @@
|
||||
{
|
||||
"fields": {
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Create Bankverbindung"
|
||||
}
|
||||
}
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"iban": "IBAN",
|
||||
"bic": "BIC/SWIFT",
|
||||
"kontoinhaber": "Account Holder",
|
||||
"bankname": "Bank Name",
|
||||
"istAktiv": "Is Active",
|
||||
"istStandard": "Default Account",
|
||||
"advowareKontoId": "Advoware Account ID",
|
||||
"advowareLastSync": "Advoware Last Sync",
|
||||
"syncStatus": "Sync Status",
|
||||
"beteiligte": "Participant"
|
||||
},
|
||||
"links": {
|
||||
"beteiligte": "Participant"
|
||||
},
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Create Bank Account"
|
||||
},
|
||||
"tooltips": {
|
||||
"name": "Name or description of the bank account (e.g. 'Main Account', 'Business Account', 'Deposit Account')",
|
||||
"iban": "International Bank Account Number in format DE89370400440532013000. Only uppercase letters and digits, 2-letter country code followed by check digit and account number (max. 34 characters)",
|
||||
"bic": "Bank Identifier Code (SWIFT code) in format COBADEFFXXX. 8 or 11 characters: 4 characters bank code, 2 characters country code, 2 characters location code, optionally 3 characters branch code",
|
||||
"kontoinhaber": "Full name of the account holder as registered with the bank",
|
||||
"bankname": "Name of the bank (e.g. 'Sparkasse Cologne Bonn', 'Deutsche Bank')",
|
||||
"istAktiv": "Inactive bank accounts are no longer used for new transactions but remain in the system",
|
||||
"istStandard": "The default account is automatically suggested when a payment is recorded for this participant. Only one account can be marked as default",
|
||||
"beteiligte": "The participant to whom this bank account belongs",
|
||||
"advowareKontoId": "Unique account ID from Advoware for synchronization. Automatically set during synchronization",
|
||||
"advowareLastSync": "Time of last successful synchronization with Advoware. Automatically updated",
|
||||
"syncStatus": "Advoware synchronization status: 'Synchronized' = data is current, 'Discrepancies' = data differs, 'Failed' = sync error occurred"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"clean": "Synchronized",
|
||||
"unclean": "Discrepancies",
|
||||
"failed": "Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"label": "Übersicht",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "name"},
|
||||
{"name": "beteiligte"}
|
||||
],
|
||||
[
|
||||
{"name": "istAktiv"},
|
||||
{"name": "istStandard"}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Bankdaten",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "iban"},
|
||||
{"name": "bic"}
|
||||
],
|
||||
[
|
||||
{"name": "kontoinhaber"},
|
||||
{"name": "bankname"}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Advoware Sync",
|
||||
"tabBreak": true,
|
||||
"tabLabel": "Erweitert",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "advowareKontoId"},
|
||||
{"name": "syncStatus"},
|
||||
{"name": "advowareLastSync"}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "istAktiv"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "iban"
|
||||
},
|
||||
{
|
||||
"name": "bic"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "bankname"
|
||||
},
|
||||
{
|
||||
"name": "istStandard"
|
||||
}
|
||||
]
|
||||
],
|
||||
"style": "default",
|
||||
"label": ""
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true,
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "beteiligte",
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "iban",
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "bankname",
|
||||
"width": 15
|
||||
},
|
||||
{
|
||||
"name": "istAktiv",
|
||||
"width": 10
|
||||
},
|
||||
{
|
||||
"name": "istStandard",
|
||||
"width": 10
|
||||
},
|
||||
{
|
||||
"name": "syncStatus",
|
||||
"width": 10
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{"name": "name"},
|
||||
{"name": "iban"},
|
||||
{"name": "istAktiv"}
|
||||
]
|
||||
12
custom/Espo/Custom/Resources/metadata/app/formula.json
Normal file
12
custom/Espo/Custom/Resources/metadata/app/formula.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"functionList": [
|
||||
"__APPEND__",
|
||||
{
|
||||
"name": "iban\\validate",
|
||||
"insertText": "iban\\validate(IBAN)"
|
||||
}
|
||||
],
|
||||
"functionClassNameMap": {
|
||||
"iban\\validate": "Espo\\Custom\\Classes\\FormulaFunctions\\IbanGroup\\ValidateType"
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,89 @@
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"pattern": "$noBadCharacters"
|
||||
"pattern": "$noBadCharacters",
|
||||
"tooltip": true
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
"iban": {
|
||||
"type": "varchar",
|
||||
"required": false,
|
||||
"maxLength": 42,
|
||||
"pattern": "/^[A-Z]{2}[0-9]{2}[\\s]?[A-Z0-9]{4}[\\s]?[A-Z0-9]{4}[\\s]?[A-Z0-9]{4}[\\s]?[A-Z0-9]{4}[\\s]?[A-Z0-9]{0,4}[\\s]?[A-Z0-9]{0,2}$/i",
|
||||
"tooltip": true,
|
||||
"isCustom": true,
|
||||
"copyToClipboard": true,
|
||||
"audited": true,
|
||||
"options": []
|
||||
},
|
||||
"bic": {
|
||||
"type": "varchar",
|
||||
"required": false,
|
||||
"maxLength": 11,
|
||||
"pattern": "/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/i",
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"kontoinhaber": {
|
||||
"type": "varchar",
|
||||
"required": false,
|
||||
"maxLength": 255,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"bankname": {
|
||||
"type": "varchar",
|
||||
"required": false,
|
||||
"maxLength": 255,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"istAktiv": {
|
||||
"type": "bool",
|
||||
"default": true,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"istStandard": {
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"advowareKontoId": {
|
||||
"type": "int",
|
||||
"required": false,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"advowareLastSync": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"readOnly": true,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"syncStatus": {
|
||||
"type": "enum",
|
||||
"required": false,
|
||||
"options": [
|
||||
"clean",
|
||||
"unclean",
|
||||
"failed"
|
||||
],
|
||||
"style": {
|
||||
"clean": "success",
|
||||
"unclean": "warning",
|
||||
"failed": "danger"
|
||||
},
|
||||
"default": "clean",
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"beteiligte": {
|
||||
"type": "link",
|
||||
"required": false,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
@@ -37,6 +116,12 @@
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"beteiligte": {
|
||||
"type": "belongsTo",
|
||||
"entity": "CBeteiligte",
|
||||
"foreign": "bankverbindungens",
|
||||
"isCustom": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
@@ -60,6 +145,9 @@
|
||||
"orderBy": "createdAt",
|
||||
"order": "desc"
|
||||
},
|
||||
"formula": {
|
||||
"beforeSaveApiScript": "// IBAN-Validierung mit Modulo-97-Algorithmus\nif (!string\\isEmpty(iban)) {\n // Leerzeichen entfernen für Validierung\n $ibanClean = string\\replace(iban, ' ', '');\n \n // Mathematische IBAN-Prüfung\n if (!iban\\validate($ibanClean)) {\n recordService\\throwBadRequest('Ungültige IBAN: Die Prüfziffer ist mathematisch falsch. Bitte überprüfen Sie die eingegebene IBAN.');\n }\n}"
|
||||
},
|
||||
"indexes": {
|
||||
"name": {
|
||||
"columns": [
|
||||
|
||||
@@ -152,6 +152,9 @@
|
||||
],
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"bankverbindungens": {
|
||||
"type": "linkMultiple"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
@@ -258,6 +261,13 @@
|
||||
"entity": "CDokumente",
|
||||
"audited": false,
|
||||
"isCustom": true
|
||||
},
|
||||
"bankverbindungens": {
|
||||
"type": "hasMany",
|
||||
"foreign": "beteiligte",
|
||||
"entity": "CBankverbindungen",
|
||||
"audited": false,
|
||||
"isCustom": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
Reference in New Issue
Block a user