Working version v32.1.X v32.0.X v29.1.X
Working version v32.1.X v32.0.X v29.1.X Dutch

ORAFIN

Inleiding

ORAFIN is de boekhoudkundige dienst van de Vlaamse overheid (Oracle Finance), gebruikt door het Dienstencentrum Boekhouding (DCB) van het Departement Financiën en Begroting. De Skryv-platformconnector maakt het mogelijk om via ORAFIN uitbetalingen te laten uitvoeren, bijvoorbeeld van een premie.

Belangrijk: deze platformconnector is een aanzet, geen kant-en-klare oplossing. Ze levert de bouwstenen (bestanden opbouwen, versturen, ophalen en verwerken), maar elk project bouwt hier zelf een eigen laag bovenop. Bijvoorbeeld om te bepalen wélk dossier op welk moment een uitbetaling nodig heeft, hoe vaak er gecontroleerd wordt op nieuwe bestanden bij ORAFIN, en hoe de eigen dossierstructuur gekoppeld wordt aan de ORAFIN-bestanden. Zie deze pagina dus als startpunt voor een projectspecifieke ORAFIN-connector, niet als een afgewerkt geheel.

Use cases

Een uitbetaling (bijvoorbeeld van een premie) laten uitvoeren via ORAFIN: begunstigden bundelen, een betalingsopdracht genereren en versturen, de aanvaarding ervan opvolgen, en na effectieve uitbetaling de betrokken dossiers hiervan op de hoogte stellen.

Terugvorderingen zijn geen onderdeel van deze platformconnector. Een project dat dit nodig heeft, implementeert dit zelf, in het verlengde van dezelfde bestandsuitwisselingslogica.

Setup

Onboarding

Je kan de connector enkel in gebruik nemen na een uitvoerige onboardingsprocedure bij ORAFIN. Hierbij verkrijg je alle info voor het opzetten van de connectie, en worden de nodige afspraken gemaakt rond de informatie-uitwisseling (o.a. de betekenis van een aantal vrij invulbare velden, zie ‘formuliertemplates’ verderop).

Dependency toevoegen aan pom.xml bestand

Om de connector te kunnen gebruiken, voeg je deze eerst als maven dependency toe aan het pom.xml bestand van je applicatie. Deze haalt de code voor de connector op bij het maken van de build voor je app.

<dependency>
    <groupId>com.skryv.backend</groupId>
    <artifactId>orafin</artifactId>
    <version>${skryv.version}</version>
</dependency>

Communicatie

De communicatie met ORAFIN verloopt niet via REST-calls, maar via een protocol gebaseerd op tekstbestanden (flat files): informatie zit verpakt in regels met een vaste kolombreedte per veld, niet in JSON of XML.

De flow verloopt via drie bestanden:

  • InFile: een opdracht tot uitbetaling, gecodeerd als tekstbestand en geplaatst op de server van ORAFIN.

  • OutFile: het resultaat van ORAFIN's controle op die opdracht (aanvaard of niet, per begunstigde).

  • BetFile: een bevestiging van ORAFIN dat de effectieve uitbetaling heeft plaatsgevonden.

Deze bestanden bevatten informatie over één of meerdere begunstigden. Je kan dus meerdere uitbetalingen in één bestand bundelen.

De platformconnector maakt hierbij gebruik van twee onafhankelijke, elk apart instelbare mechanismen:

  1. Eigen opslag van de bestanden (los van ORAFIN zelf): lokaal op het bestandssysteem (default), of op AWS S3.

  2. De effectieve verbinding naar de ORAFIN-server: plain FTP (default) of SFTP met authenticatie via private key.

Let op: de platformconnector levert enkel de functies om bestanden op te bouwen, te versturen en te verwerken. Het periodiek controleren op nieuwe bestanden (scheduling) en het koppelen aan je eigen dossierstructuur bouw je zelf, als onderdeel van je projectspecifieke uitbreiding.

Applicatie eigenschappen

Vul onderstaande applicatie eigenschappen in om binnen je project gebruik te maken van de connector.

Eigenschap

Default

Uitleg

Opslag (eigen archief van In-/Out-/BetFiles)

skryv.orafin.storage

local

local of s3.

skryv.orafin.base-folder

orafin

Basismap (lokaal of S3-prefix).

skryv.orafin.in-folder

in

Submap voor InFiles.

skryv.orafin.out-folder

out

Submap voor OutFiles/BetFiles.

skryv.orafin.processed-folder

processed

Submap voor succesvol verwerkte bestanden.

skryv.orafin.error-folder

error

Submap voor bestanden die een verwerkingsfout gaven.

skryv.s3.bucket

wvl-app.dev.dossier-import

Enkel relevant bij storage=s3.

skryv.orafin.encryption.enabled

false

Enkel relevant bij storage=s3: server-side encryptie voor opgeslagen bestanden.

skryv.orafin.encryption.algorithm

aws:kms

Encryptie-algoritme, enkel bij encryption.enabled=true.

skryv.orafin.encryption.key

—

KMS-sleutel-id, enkel bij encryption.enabled=true.

Connectie met ORAFIN-service

conn.orafin.connection

ftp

ftp of sftp.

skryv.orafin.host

localhost

Hostnaam van de ORAFIN-server.

skryv.orafin.port

21

Poort.

skryv.orafin.username

skryv

Gebruikersnaam.

skryv.orafin.password

-

Wachtwoord. Enkel relevant bij connection=ftp.

skryv.orafin.proxy.host

-

Optionele proxy (enkel bij connection=sftp).

skryv.orafin.proxy.port

22

Proxypoort.

conn.orafin.key-manager

file

file of aws — waar de private key vandaan komt. Enkel relevant bij connection=sftp.

conn.orafin.private-key-path

-

Pad naar de private key (lokaal bestand, of secret-naam bij key-manager=aws).

conn.orafin.private-key-password

-

Wachtwoord van de private key.

conn.orafin.root-folder

.

Basismap op de ORAFIN-server zelf.

conn.orafin.in-folder

IN

Submap op de ORAFIN-server waar InFiles geplaatst worden.

conn.orafin.out-folder

OUT

Submap op de ORAFIN-server waar OutFiles/BetFiles opgehaald worden.

Formuliertemplates voor uitwisseling met ORAFIN

Om de connector te kunnen gebruiken, voorzie je in je project twee formuliertemplates (documenten binnen de scope van het dossier). De platformconnector verwacht deze structuur letterlijk. De veldnamen hieronder komen rechtstreeks uit de broncode, dus wijzig ze niet.

Je project kan bijkomende, eigen velden toevoegen aan beide documenten (bv. extra Spare-velden die specifiek met ORAFIN afgesproken zijn voor dat project). De platformconnector negeert onbekende velden gewoon. De opgelijste velden zijn echter degene die de platformcode zelf effectief leest of schrijft, en dus niet vrijblijvend zijn.

Input-document: betalingsregels

Gelezen door prepareOrafinBulkDossiers. Bevat de ruwe betalingsaanvragen vóór ze gebundeld worden.

Veld

Datatype

Verplicht

Uitleg

begunstigden

Lijst

Ja

Lijst van begunstigden voor dit dossier. Minstens één vereist — een leeg begunstigden-lijst resulteert in een fout.

begunstigden[].type

SelectedOption

Ja

KSZ of VOP (particulier) of KBO (onderneming).

begunstigden[].natuurlijke_persoon.name

String

CV

Naam van de particulier. Verplicht bij type = KSZ/VOP.

begunstigden[].natuurlijke_persoon.ssin

String

CV

Rijksregisternummer. Verplicht bij type = KSZ/VOP.

begunstigden[].naam_rechtspersoon

String

CV

Naam van de onderneming. Verplicht bij type = KBO.

begunstigden[].btw

String

CV

KBO-nummer. Verplicht bij type = KBO.

begunstigden[].premie

Number

Ja

Uit te betalen bedrag. Dossiers waarvan de som van alle premies niet groter is dan 0, worden genegeerd.

begunstigden[].rekeningnummer

String

Ja

Rekeningnummer. Numerieke waarden worden automatisch voorafgegaan door BE.

begunstigden[].circulaire_cheque

Boolean

Nee

Indien true: rekeningnummer en BIC worden genegeerd, en vervangen door een vaste circulaire-cheque-rekening.

begunstigden[].bic

String

Nee

Enkel nodig bij een buitenlandse rekening. Spaties worden automatisch verwijderd.

begunstigden[].DistributionSetName

String

Nee

Boekhoudkundige sleutel, af te spreken met ORAFIN.

begunstigden[].Spare6TermDate

String

Nee

Aantal dagen uitstel van betaling.

Trackingdocument: orafin

Dit document wordt door je project zelf aangemaakt/bijgewerkt (via writeOrafinDocument) en vervolgens door de overige functies (createOrafinInFile, processOrafinOutFile, processPayments, enzovoort) gelezen en bijgewerkt. Het bevat twee delen: de dossiers/begunstigden zelf, en een settings-sectie met omgevingsspecifieke standaardwaarden.

Dossiers en begunstigden

Veld

Datatype

Uitleg

dossiers

Lijst

Eén item per dossier.

dossiers[].dossiernumber

String

Dossiernummer/-label.

dossiers[].beneficiaries

Lijst

Begunstigden van dit dossier.

dossiers[].beneficiaries[].InvoiceNum

String

Unieke id van de betalingsopdracht (gegenereerd door de connector).

dossiers[].beneficiaries[].id

String

Rijksregisternummer of KBO-nummer.

dossiers[].beneficiaries[].name

String

Naam begunstigde.

dossiers[].beneficiaries[].type

ChoiceOption

Overgenomen uit het input-document.

dossiers[].beneficiaries[].premium

Number

Uit te betalen bedrag.

dossiers[].beneficiaries[].accountnumber

String

Rekeningnummer.

dossiers[].beneficiaries[].bic

String

BIC, indien van toepassing.

dossiers[].beneficiaries[].circulaire_cheque

Boolean

Zie hierboven.

dossiers[].beneficiaries[].DistributionSetName

String

Zie hierboven.

dossiers[].beneficiaries[].Spare6TermDate

String

Zie hierboven.

dossiers[].beneficiaries[].status

String

Wordt ingevuld door processOrafinOutFile op basis van het ORAFIN-antwoord.

dossiers[].beneficiaries[].error_code

String

Wordt ingevuld door processOrafinOutFile bij een foutieve status.

dossiers[].beneficiaries[].action

ChoiceOption

Vervolgactie, bepaald via een DMN-beslissingstabel (orafin_OUT) op basis van status/error_code. Enkel begunstigden met actie include worden effectief in een InFile opgenomen.

Settings-sectie

Vaste standaardwaarden, gebruikt bij het opbouwen van de InFile.

Veld

Uitleg

ISOCur

Valuta, doorgaans EUR.

TermsName

Betalingstermijn, af te spreken met ORAFIN.

Source

Bron-identificatie, gebruikt in de InFile-bestandsnaam.

Spare1Dienst

Af te spreken met ORAFIN.

spare9ogm_prefix

Vast voorvoegsel voor de gestructureerde mededeling.

LineNumber

Af te spreken met ORAFIN.

LineType

Af te spreken met ORAFIN.

ShipToLocationCode

Af te spreken met ORAFIN.

DistributionSetName

Standaard boekhoudkundige sleutel, tenzij per begunstigde overschreven.

Spare1ProdCat

Af te spreken met ORAFIN.

Scheduler

De platformconnector bevat zelf geen scheduler. Er is geen ingebouwd periodiek mechanisme dat automatisch nieuwe OutFiles/BetFiles bij ORAFIN ophaalt. Dit moet elk project zelf voorzien, want zonder een periodieke check komt er nooit een antwoord van ORAFIN binnen om te verwerken.

Een typische aanpak, als vertrekpunt (geen voorgeschreven implementatie):

  1. Een periodieke taak (bv. via Spring's @Scheduled, met een cron-expressie als applicatie-eigenschap), die op een vast tijdstip:

    • OrafinManager.readOrafinOutFiles() aanroept om nieuwe bestanden bij ORAFIN op te halen, lokaal op te slaan, en van de ORAFIN-server te verwijderen.

    • de opgehaalde bestandsnamen onderverdeelt op prefix (bv. bestanden die starten met OUT_AP_INVOICES zijn OutFiles, bestanden die starten met OUT_AP_BET zijn BetFiles).

  2. Verdere verwerking triggeren per bestand. Bijvoorbeeld door een eigen BPMN-proces te starten met de bestandsnamen als procesvariabele, dat vervolgens OrafinService.processOrafinOutFile(...)/analyzeBetFile(...) aanroept (rechtstreeks, of via je eigen projectlaag).

  3. Voorkom overlappende uitvoeringen: als een vorige verwerkingscyclus nog niet afgerond is (bv. een vorig BPMN-proces nog actief), start dan geen nieuwe. Anders loop je het risico dat hetzelfde bestand dubbel verwerkt wordt.

Onderstaand schema illustreert precies dit patroon in de praktijk, gebaseerd op hoe een bestaand project dit heeft aangepakt. Het is geen exacte specificatie van wat de platformconnector zelf doet, en geen verplichte structuur. De concrete keuzes (cron-frequentie, hoe je bestanden aan dossiers koppelt, welk BPMN-proces je opzet) zijn projectspecifiek.

De werking van de scheduler is hier via BPMN-notatie beschreven, maar programmatie en uitvoering gebeuren in de praktijk via een ‘full-code’ Java-extensie.

OrafinConnectieProcess.drawio.png

Het schema onderscheidt twee niveaus:

  • Scope: individueel dossier (bovenaan). Dit toont het uitbetalingsproces van één dossier. Dit proces vult het ORAFIN-formulier in en wacht daarna passief op berichten, die het ontvangt zodra de scheduler een antwoord van ORAFIN verwerkt heeft.

  • Scope: over alle dossiers heen (onderaan, ‘Skryv applicatie’). Dit toont de scheduler, die je zelf opzet (zie hierboven ‘Scheduler (project-verantwoordelijkheid)’). Deze verzamelt 's morgens alle klaarstaande ORAFIN-formulieren, bundelt ze in één InFile, en verstuurt die naar ORAFIN. 's Avonds haalt de scheduler de OutFile en de BetFile op, en stuurt telkens een bericht naar alle betrokken dossiers. Cruciaal daarbij zijn de messages die vanuit de scheduler naar de individuele dossiers gestuurd worden. Daarbij heb je nood aan een combinatie van twee unieke ID’s: enerzijds de process business key (unieke ID van het dossier) en anderzijds de payment ID (unieke ID van de betaling). Het is immers mogelijk om binnen één dossier meerdere uitbetalingsverzoeken te voorzien. Deze moeten afzonderlijk aanstuurbaar zijn.

Services en functies

Herinnering: dit zijn de generieke bouwstenen van de platformconnector. Ze verwachten dat elk project zelf instaat voor het ophalen en wegschrijven van het onderliggende Skryv-document (de rawDocumentValue/Map-parameters). De connector doet dit niet zelf. Een project bouwt hier typisch een eigen facade-laag overheen die deze functies aanroept en de documentopslag regelt.

Overzicht

Functie

Retourtype

Info

prepareOrafinBulkDossiers

List<OrafinBulkDossier>

Bundelt begunstigden per dossier tot een uitbetalingsstructuur, op basis van een lijst dossiers met hun betalingsregels.

writeOrafinDocument

void

Schrijft een lijst gebundelde dossiers (orafinDossiers-procesvariabele) naar het ORAFIN-Skryv-document.

createOrafinInFile

OrafinFile

Bouwt de InFile-inhoud op basis van het ORAFIN-document van een dossier.

processOrafinOutFile

void

Verwerkt een opgehaalde OutFile en werkt de status van elke begunstigde bij.

sendImportedMessages

void

Stuurt een bericht naar elk dossier waarvan de uitbetaling geïmporteerd is door ORAFIN.

getImportedMessagesReceivers

List<String>

Zelfde als hierboven, maar geeft de lijst dossiernummers terug in plaats van zelf te versturen.

analyzeOutFile

String

Leest de omschrijving uit een OutFile, zonder deze te verwerken.

sendPaidMessages

void

Stuurt een bericht naar elk dossier waarvan de uitbetaling effectief betaald is.

getPaidMessagesReceivers

List<MessagePaidDossier>

Zelfde als hierboven, maar geeft de lijst betaalde dossiers terug in plaats van zelf te versturen.

analyzeBetFile

List<PaymentsWithDescription>

Leest een BetFile en groepeert de uitgevoerde betalingen per omschrijving.

moveBetToProcessed

void

Verplaatst een verwerkte BetFile naar de "processed"-map.

processPayments

void

Werkt de begunstigden in het ORAFIN-document bij met de effectief uitgevoerde betalingen.

getListOfCurrentDossiers

List<String>

Geeft de lijst dossiernummers terug die momenteel in het ORAFIN-document zitten.

Deze functies zijn rechtstreeks aanroepbaar vanuit Java-code door OrafinService (bean orafinService) te injecteren in je klasse:

Java
@RequiredArgsConstructor
public class MyOwnService {
    private final OrafinService orafinService;
}

prepareOrafinBulkDossiers

Java-code

List<OrafinBulkDossier> bulkDossiers = orafinService.prepareOrafinBulkDossiers(prepareRequests);

Input

Inputparameters

Data type

Uitleg

prepareRequests

List<PrepareOrafinRequest>

Per dossier: dossierId, dossierLabel, en rawDocumentValue (de ruwe inhoud van het betalingsregels-document van dat dossier).

Output

Een lijst OrafinBulkDossier, elk met dossierId, dossierLabel, beneficiaries (lijst begunstigden). Dossiers waarvan de som van alle premies niet groter dan 0 is, worden er automatisch uitgefilterd.

Error-handling

Gooit een niet-opvangbare IllegalStateException als een dossier geen enkele begunstigde bevat in zijn betalingsregels-document.

writeOrafinDocument

Java-code

orafinService.writeOrafinDocument(execution);

Input

Inputparameters

Data type

Uitleg

scope

VariableScope (bv. execution)

Verwacht een procesvariabele orafinDossiers (het resultaat van prepareOrafinBulkDossiers).

Output

Geen output (void). Zet zelf niets weg in het Skryv-document. Geeft enkel een OrafinDocument-structuur terug die je project vervolgens zelf naar het ORAFIN-document moet schrijven.

createOrafinInFile

Java-code

OrafinFile inFile = orafinService.createOrafinInFile(rawDocumentValue, dossierId);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document van het dossier.

dossierId

String

Dossier-id.

Output

Een OrafinFile-object met de opgebouwde I-/L-records voor elke begunstigde met actie ‘include’. Je project stuurt dit zelf door naar OrafinManager.saveOrafinInFile(...) om het effectief te versturen. Dat gebeurt hier niet automatisch.

processOrafinOutFile

Java-code

orafinService.processOrafinOutFile(rawDocumentValue, outFileName);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document van het dossier.

outFileName

String

Bestandsnaam van de opgehaalde OutFile.

Output

Geen output (void). Werkt rawDocumentValue zelf bij (status/foutcode/vervolgactie per begunstigde). Je project moet dit bijgewerkte document zelf terugschrijven.

Error-handling

Bij eender welke fout tijdens verwerking wordt de OutFile automatisch verplaatst naar de foutmap. Er wordt geen exceptie doorgegeven, enkel gelogd.

sendImportedMessages / getImportedMessagesReceivers

Java-code

orafinService.sendImportedMessages(rawDocumentValue);
// of, om zelf te versturen in plaats van de connector dit te laten doen:
List<String> ontvangers = orafinService.getImportedMessagesReceivers(rawDocumentValue);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document.

Output

sendImportedMessages: geen output. Verstuurt zelf een bericht met vaste naam (niet configureerbaar) naar elk dossier waarvan de import volledig is en waarvoor nog geen bericht verstuurd werd.
getImportedMessagesReceivers: geeft in plaats daarvan de lijst dossiernummers terug, zodat je project zelf bepaalt hoe/wanneer het bericht verstuurd wordt.

analyzeOutFile

Java-code

String omschrijving = orafinService.analyzeOutFile(filename);

Input

Inputparameters

Data type

Uitleg

filename

String

Bestandsnaam van de op te halen OutFile.

Output

De omschrijving uit de eerste regel van de OutFile. Puur om snel te kunnen inspecteren zonder het bestand effectief te verwerken.

sendPaidMessages / getPaidMessagesReceivers

Java-code

orafinService.sendPaidMessages(rawDocumentValue, dossierId);
// of:
List<MessagePaidDossier> betaaldeDossiers = orafinService.getPaidMessagesReceivers(rawDocumentValue, dossierId);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document.

dossierId

String

Dossier-id, wordt meegegeven in het bericht.

Output

Analoog aan de Imported-versie hierboven, maar voor de effectieve uitbetaling: vast berichtnaam-patroon, of een lijst MessagePaidDossier (met dossierLabel, paymentDate, mbId) om zelf te versturen.

analyzeBetFile

Java-code

List<PaymentsWithDescription> payments = orafinService.analyzeBetFile(filename);

Input

Inputparameters

Data type

Uitleg

filename

String

Bestandsnaam van de te analyseren BetFile.

Output

Een lijst, gegroepeerd per omschrijving, van uitgevoerde betalingen (invoiceNumber, paymentDate, paymentAmount).

moveBetToProcessed

Java-code

orafinService.moveBetToProcessed(filename);

Input

Inputparameters

Data type

Uitleg

filename

String

Bestandsnaam van de BetFile die naar de "processed"-map verplaatst moet worden.

Output

Geen output (void). Verplaatst het bestand naar de ‘processed’-map.

processPayments

Java-code

orafinService.processPayments(rawDocumentValue, payments);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document.

payments

List<ExecutedPayment>

Resultaat van analyzeBetFile.

Output

Geen output (void). Werkt rawDocumentValue zelf bij met de betaaldatum per begunstigde. Je project schrijft dit bijgewerkte document zelf terug.

getListOfCurrentDossiers

Java-code

List<String> dossiernummers = orafinService.getListOfCurrentDossiers(rawDocumentValue);

Input

Inputparameters

Data type

Uitleg

rawDocumentValue

Map

Ruwe inhoud van het ORAFIN-document.

Output

Lijst van dossiernummers die momenteel in het ORAFIN-document zitten.