GiroCode in eigene Berichte #
EULANDA® unterstützt in den mitgelieferten Rechnungs-Formularen den GiroCode. Dies ist ein QR-Code der die Zahlungsinformationen enthält. Also Dinge wie Rechnungsnummer, Betrag, Empfängerbank usw. Viele Banken-Apps unterstützen das Einscannen des GiroCodes, so dass der Zahlende nichts mehr manuell eingeben muss. Er kann die gescannten Daten kontrollieren und muss dann nur noch in der App die Zahlung mit seinen üblichen Verfahren freigeben.
Hat man jedoch ein eigenes altes Formular, indem der GiroCode noch nicht enthalten ist, so kann dieser nun mit Hilfe diesen Anleitung nachgerüstet werden.
Nachrüstung des GiroCode #
HINWEIS
Es wegen Kenntnisse im Umgang mit dem Berichts-Designer vorausgesetzt. Wenn Sie sich nicht zutrauen, diese Anleitung umzusetzen, sprechen Sie den Support an. Wir bieten die Integration in Ihr Rechnungsformular zu pauschal 65 EUR an.
- Öffnen Sie zunächst das gewünschte Rechnungsformular im Berichts-Designer.
- Wählen Sie im Formular die Stelle aus, an der Sie den GiroCode positionieren möchten. Es muss ein normales nicht datenbankgebundenes Bild sein. Das Bild muss den Namen
GiroCodeImageNetto
haben, wenn der GiroCode bei Nettorechnungen verwendet werden soll. Bei bruttoorientierten Rechnungen entsprechendGiroCodeImageBrutto
. Ziehen Sie das Bildelement auf die gewünschte Größe und stellen Sie dann die folgenden Eigenschaften ein:- Zentriert
- Seitenverhältnis beibehalten
- Dehnen
- Sie können über die Eigenschaft
Bild...
ein Platzhalter bzw. Dummy-Bild laden, damit Sie später sehen, worum es sich bei dem Element handelt. - Legen Sie im Bereich
Berechnungen
auf dem HauptformularRechnungskopf
unterProgramme
die FunktionGiroCodePayload
an. Zunächst im MenüAnsicht
die OptionVerwendete Module
aktivieren. Hierzu im linken oberen Bereich den EintragProgramme
anwählen, und dann rechts mit der rechten Maus den EintragNeue Prozedur
anwählen. Löschen Sie im unteren Fenster den kompletten Prozedur-Rumpf und fügen Sie an dessen Stelle das folgende Skript ein:
function GiroCodePayload:String;
var
Delimiter : String;
StrValue : String;
Payload : String;
MagicCode: String;
PZ : String;
x : Integer;
loop : Integer;
Ch : Char;
GiroCodeEncoding : String;
UseSecurePurposeMessage : Boolean;
PurposeMessage : String;
SecurePurposeMessage : String;
TempStr : String;
PaymentAmount : String;
i : Integer;
begin
Delimiter := Chr(10);
MagicCode := '271500';
// false = PurposeMessage otherwise SecurePurposeMessage
UseSecurePurposeMessage := false;
// If UseSecurePurposeMessage = false this is used as template otherwise invoice number
PurposeMessage := 'RE: $$INVOICENO$$ ZWECK: $$OBJECT$$';
// Patch allowed variables, so template could be an external string in future
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$INVOICENO$$', RechnungsKopf['KOPFNUMMER']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$OBJECT$$', RechnungsKopf['OBJEKT']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$INVOICENAME$$', RechnungsKopf['VORGANGNAME']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$VAT$$', RechnungsKopf['USTID']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$TAXNO$$', RechnungsKopf['STEUERNUMMER']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$ORDERNO$$', RechnungsKopf['BESTELLNUMMER']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$DEBITNO$$', RechnungsKopf['FIBUKONTO']);
PurposeMessage := ReplaceAllStr(PurposeMessage, '$$CURRENCY$$', RechnungsKopf['WAEHRUNG']);
TempStr := '';
for i := 1 to Length(PurposeMessage) do
begin
// reference: https://www.bundesbank.de/resource/blob/848450/74179e7094d69dca3632eb8605db95c2/mL/technische-spezifikationen-sepa-ueberweisungen-2022-11-data.pdf
// GERMAN Substitiutions
// if Copy(PurposeMessage,i,1) = 'Ä' then TempStr := TempStr + 'Ae' else
// if Copy(PurposeMessage,i,1) = 'Ö' then TempStr := TempStr + 'Oe' else
// if Copy(PurposeMessage,i,1) = 'Ü' then TempStr := TempStr + 'Ue' else
// if Copy(PurposeMessage,i,1) = 'ä' then TempStr := TempStr + 'ae' else
// if Copy(PurposeMessage,i,1) = 'ö' then TempStr := TempStr + 'oe' else
// if Copy(PurposeMessage,i,1) = 'ü' then TempStr := TempStr + 'ue' else
// if Copy(PurposeMessage,i,1) = 'ß' then TempStr := TempStr + 'ss' else
// Germans chars allowed
if Copy(PurposeMessage,i,1) = 'Ä' then TempStr := TempStr + 'Ä' else
if Copy(PurposeMessage,i,1) = 'Ö' then TempStr := TempStr + 'Ö' else
if Copy(PurposeMessage,i,1) = 'Ü' then TempStr := TempStr + 'Ü' else
if Copy(PurposeMessage,i,1) = 'ä' then TempStr := TempStr + 'ä' else
if Copy(PurposeMessage,i,1) = 'ö' then TempStr := TempStr + 'ö' else
if Copy(PurposeMessage,i,1) = 'ü' then TempStr := TempStr + 'ü' else
if Copy(PurposeMessage,i,1) = 'ß' then TempStr := TempStr + 'ß' else
// Symbols allowed
if Copy(PurposeMessage,i,1) = '/' then TempStr := TempStr + '/' else
if Copy(PurposeMessage,i,1) = '?' then TempStr := TempStr + '?' else
if Copy(PurposeMessage,i,1) = ':' then TempStr := TempStr + ':' else
if Copy(PurposeMessage,i,1) = '(' then TempStr := TempStr + '(' else
if Copy(PurposeMessage,i,1) = ')' then TempStr := TempStr + ')' else
if Copy(PurposeMessage,i,1) = '.' then TempStr := TempStr + '.' else
if Copy(PurposeMessage,i,1) = ',' then TempStr := TempStr + ',' else
if Copy(PurposeMessage,i,1) = '+' then TempStr := TempStr + '+' else
if Copy(PurposeMessage,i,1) = '-' then TempStr := TempStr + '-' else
if Copy(PurposeMessage,i,1) = '''' then TempStr := TempStr + '''' else
// Additional symbols allowed
if Copy(PurposeMessage,i,1) = '*' then TempStr := TempStr + '*' else
if Copy(PurposeMessage,i,1) = '&' then TempStr := TempStr + '&' else
if Copy(PurposeMessage,i,1) = '$' then TempStr := TempStr + '$' else
if Copy(PurposeMessage,i,1) = '%' then TempStr := TempStr + '%' else
// Substitiutions
if Copy(PurposeMessage,i,1) = '_' then TempStr := TempStr + '-' else
if Copy(PurposeMessage,i,1) = ';' then TempStr := TempStr + ',' else
if Copy(PurposeMessage,i,1) = '=' then TempStr := TempStr + ':' else
if Copy(PurposeMessage,i,1) = '#' then TempStr := TempStr + '*' else
if Copy(PurposeMessage,i,1) = '{' then TempStr := TempStr + '(' else
if Copy(PurposeMessage,i,1) = '}' then TempStr := TempStr + ')' else
if Copy(PurposeMessage,i,1) = '[' then TempStr := TempStr + '(' else
if Copy(PurposeMessage,i,1) = ']' then TempStr := TempStr + ')' else
// Sets allowed
if (Copy(PurposeMessage,i,1) >= 'A') and (Copy(PurposeMessage,i,1) <= 'Z') then TempStr := TempStr + Copy(PurposeMessage,i,1) else
if (Copy(PurposeMessage,i,1) >= 'a') and (Copy(PurposeMessage,i,1) <= 'z') then TempStr := TempStr + Copy(PurposeMessage,i,1) else
if (Copy(PurposeMessage,i,1) >= '0') and (Copy(PurposeMessage,i,1) <= '9') then TempStr := TempStr + Copy(PurposeMessage,i,1) else
// All other is not allowed and gets blanks
TempStr := TempStr + ' ';
end;
PurposeMessage := ReplaceAllStr(TempStr, ' ', ' ');
SecurePurposeMessage := '';
for i:=1 to length(RechnungsKopf['KopfNummer']) do
begin
Ch := Copy(RechnungsKopf['KopfNummer'], i, 1);
StrValue := GiroCodeValue(Ch);
if StrValue <> '' then SecurePurposeMessage := SecurePurposeMessage + StrValue;
end;
SecurePurposeMessage := SecurePurposeMessage + MagicCode;
x := 0;
while Length(SecurePurposeMessage) > 9 do
begin
x := StrToInt(Copy(SecurePurposeMessage, 1, 9)) mod 97;
SecurePurposeMessage := IntToStr(x) + Copy(SecurePurposeMessage, 10, 255);
end;
x := 98 - (StrToInt(SecurePurposeMessage) mod 97);
PZ := SetStr('R', '0', IntToStr(x), 2);
SecurePurposeMessage := 'RF'+PZ+RechnungsKopf['KopfNummer'];
// 0 = Ansi, 1=UTF8
GirocodeEncoding := '1';
PurposeMessage := Copy(PurposeMessage, 1, 60);
if UseSecurePurposeMessage then
PurposeMessage := ''
else
SecurePurposeMessage := '';
PaymentAmount := RechnungsKopf['VkBrutto'];
// If no commas are found, leave it as it was
// If a decimal separator is found proceed as follows:
// 1. Delete all zeros in the cent range
// 2. Convert comma to decimal point to not depend on regional settings
// 3. If the decimal point is the last character, it will be deleted
if (Pos(',',PaymentAmount) > 0) or (Pos('.',PaymentAmount) > 0) then
PaymentAmount := KillFiller('R','.', ReplaceAllStr(KillFiller('R','0',PaymentAmount),',','.'));
Payload :=
'BCD' + Delimiter +
'002' + Delimiter +
GiroCodeEncoding + Delimiter +
'SCT' + Delimiter +
Grundwerte['Bank.1.BIC'] + Delimiter +
Grundwerte['Firmenstamm.Firma'] + Delimiter +
Grundwerte['Bank.1.IBAN'] + Delimiter +
'EUR' + PaymentAmount + Delimiter +
'EPAY' + Delimiter +
SecurePurposeMessage + Delimiter +
PurposeMessage + Delimiter;
Result := Payload;
end;
- Auf die gleiche Weise legen Sie die Funktion
GiroCodeValue
an:
function GiroCodeValue(Value:String):String;
begin
// Converts a 1-character string to a numeric value valid for the girocode
// as a string, which can then be added to a total string number.
if Length(Value) = 1 then
begin
Value := Uppercase(Value);
if (value >= '0') and (value <= '9') then Result := Value else
if Value = 'A' then Result := '10' else
if Value = 'B' then Result := '11' else
if Value = 'C' then Result := '12' else
if Value = 'D' then Result := '13' else
if Value = 'E' then Result := '14' else
if Value = 'F' then Result := '15' else
if Value = 'G' then Result := '16' else
if Value = 'H' then Result := '17' else
if Value = 'I' then Result := '18' else
if Value = 'J' then Result := '19' else
if Value = 'K' then Result := '20' else
if Value = 'L' then Result := '21' else
if Value = 'M' then Result := '22' else
if Value = 'N' then Result := '23' else
if Value = 'O' then Result := '24' else
if Value = 'P' then Result := '25' else
if Value = 'Q' then Result := '26' else
if Value = 'R' then Result := '27' else
if Value = 'S' then Result := '28' else
if Value = 'T' then Result := '29' else
if Value = 'U' then Result := '30' else
if Value = 'V' then Result := '31' else
if Value = 'W' then Result := '32' else
if Value = 'X' then Result := '33' else
if Value = 'Y' then Result := '34' else
if Value = 'Z' then Result := '35' else
Result := '';
end else Result := '';
end;
- Legen Sie ebenfalls eine Ereignis-Behandlung
GiroCodeOnPrint
an. Hierbei ist der Abschnitt zu berücksichtigen. auf dem Sie ihren GiroCode zuvor als Bild eingefügt haben. Dies kann derHaupt: Rechnungskopf
oderNettosummer: Rechnungskopf
sein. Wählen Sie die entsprechende Lasche unten an. Schalten Sie im MenüAnsicht
auf die OptionEreignisse
um. Sie erhalten dann im linken oberen Teil alle in dem Berichts-Abschnitt verwendeten Elemente angezeigt. Wählen Sie dort das BildGiroCodeImageNetto
aus. Im rechten Teil werden dann alle möglichen Ereignisse zu dem Element angezeigt. Mit der rechten Maus auf dem EintragOnPrint
den PunktNeu
anwählen und dann wie zuvor das folgende Skript einfügen.
procedure GiroCodeImageNettoOnPrint;
begin
ImageBarcode(Report, GirocodeImageNetto, 'QRCODE', GiroCodePayload, '');
end;
Testen des GiroCode #
Vor dem Speichern des Formulars testen Sie dies in der Vorschau. Wenn ein Fehler angezeigt wird, wurde bei der Umsetzung etwas nicht berücksichtigt. Kontrollieren Sie dann die Schritte erneut.
Prüfen Sie auch den GiroCode mit einer Smartphone-App. Es gibt diverse QR-Code Apps, hiermit lassen sich dann die Klartextdaten eines QR-Codes anzeigen, so dass Sie IBAN, Betrag und Rechnungsnummer kontrollieren können. Der Betrag wird im GiroCode ohne Komma dargestellt, also nicht über die großen Beträge wundern.
Wenn diese Prüfung das gewünschte Ergebnis gebracht hat, ist es empfehlenswert eine Überweisung mit der eigenen Bank zu testen. Diese wird natürlich nicht abgesendet, aber dann können Sie die vorausgefüllten Felder genau überprüfen.
Hilfestellung #
HINWEIS
Bei der Umsetzung in eigene Formulare helfen wir gerne, wir berechnen hier jedoch unsere GiroCode-Pauschale, wie oben angegeben.
Vielleicht hilft ihnen aber auch schon das originale Rechnungs-Formular.
Das Bildelement ist im den Original-Formular bei der nettoorientierten Rechnung im Bereich der Endsumme positioniert. Der angezeigte QR-Code ist ein Dummy, hat also keine Auswirkung auf den beim Ausdruck verwendeten GiroCode. Hier könnten Sie ein beliebiges Bild einfügen. Durch einen QR-Code ist aber eher ersichtlich, worum es sich hier handelt.
Wenn Sie dies öffnen und auf die Karteikarte Berechnung wechseln, können Sie sich diese Skripts und vor allem wo diese stehen müssen, genau ansehen. Anbei ein Bild hierzu:
Ähnlich verhält es sich dann beim Ereignis. Hier dazu das passende Bild bei einer nettoorientierten Rechnung.
Funktionsweise #
Die Ereignisbehandlung wird aufgerufen, sobald das eingefügte leere Bild mit dem Elementen-Namen GiroCode
gedruckt werden soll. Die Prozedur ImageBarcode
erzeugt einen Barcode und fügt diesen in den Bild-Container ein. Der Name des Elements wird als zweiter Parameter angegeben. Im dritten Parameter wird der Typ des Barcodes angegeben, hier QRCODE
. Der vierte Parameter enthält die Klartext-Information, welche in einen QR-Code gewandelt werden soll. Dies ist der Funktionsname der oben eingefügten Funktion. Der letzte Parameter wird als leerer String übergeben, er dient zur Übergabe von Optionen.