Dolce & Gabbana
Dolce & Gabbana Beige Zwart Gestreepte 100% Zijden Verstelbare Heren Nette Stropdas
Dolce & Gabbana Beige Zwart Gestreepte 100% Zijden Verstelbare Heren Nette Stropdas
Kan beschikbaarheid voor afhalen niet laden
Jazeker! Geautomatiseerde factuurverwerkingssystemen gebouwd met Python kunnen variëren van eenvoudige scripts tot uitgebreide, cloudgebaseerde toepassingen. Hier zijn enkele voorbeelden, die verschillende niveaus van complexiteit en functionaliteit vertegenwoordigen:
1. Basis CSV-naar-database-integratie
Doel: Een eenvoudig systeem dat factuurgegevens uit CSV-bestanden leest en deze importeert in een database.
Python-componenten:
- Pandas: Voor het lezen en parseren van CSV-bestanden.
- SQLAlchemy / Psycopg2 (voor PostgreSQL) / sqlite3 (voor SQLite): Voor database-interactie.
-
osmodule: Voor het beheren van bestanden (bijv. verplaatsen van verwerkte bestanden).
Werkwijze:
- Een script controleert periodiek een specifieke map op nieuwe CSV-factuurbestanden.
- Elk nieuw bestand wordt gelezen met Pandas.
- De gegevens worden gevalideerd (bijv. controleren of verplichte velden aanwezig zijn, datums correct zijn).
- De gevalideerde gegevens worden in een SQL-database ingevoegd (bijv. klanten, items, factuurgegevens).
- Het verwerkte CSV-bestand wordt verplaatst naar een archiefmap.
Waarom Python: Pandas maakt het verwerken van tabulaire gegevens zeer efficiënt, en Python heeft uitstekende bibliotheken voor database-connectiviteit.
2. Factuur-PDF-naar-gegevens-extractie met OCR
Doel: Een systeem dat factuurgegevens extraheert uit PDF-bestanden met behulp van Optical Character Recognition (OCR) en deze opslaat.
Python-componenten:
- PyPDF2 / pdfminer.six: Voor het lezen van PDF's.
-
Tesseract (via
pytesseract): Voor OCR om tekst uit afbeeldingen of gescande PDF's te extraheren. -
Reguliere expressies (
remodule): Voor het patroonherkennen van gegevens zoals factuurnummers, bedragen, datums. - Spacy / NLTK: Voor meer geavanceerde Named Entity Recognition (NER) om specifieke entiteiten (bedrijfsnamen, adressen) te identificeren.
- Database-integratie (zoals hierboven): Voor het opslaan van de geëxtraheerde gegevens.
Werkwijze:
- Een PDF-bestand wordt ontvangen (bijv. via e-mail of een upload).
- Als het een afbeeldings-PDF is, wordt OCR toegepast om de tekst te extraheren. Als het een tekst-PDF is, wordt de tekst direct gelezen.
- Reguliere expressies en/of machine learning-modellen (Spacy/NLTK) worden gebruikt om factuurnummer, datum, totale bedrag, leverancier, enz. te identificeren.
- De geëxtraheerde gegevens worden gevalideerd en opgeslagen in een database.
- Een melding kan worden verzonden bij succesvolle verwerking of bij fouten.
Waarom Python: Pythons robuuste bibliotheken voor tekstverwerking, OCR-integratie (pytesseract) en machine learning maken het ideaal voor deze taak.
3. Webgebaseerd factuurbeheersysteem (Web Application)
Doel: Een volwaardige webapplicatie voor het uploaden, verwerken, beheren en visualiseren van facturen.
Python-componenten:
- Django / Flask: Webframeworks voor het bouwen van de back-end en REST API's.
- Celery: Voor het afhandelen van asynchrone taken (bijv. langdurige OCR-processen op de achtergrond).
- PostgreSQL / MySQL: Relationele databases voor het opslaan van alle factuurgegevens.
- Vue.js / React.js (Frontend - optioneel, maar gebruikelijk): Voor een interactieve gebruikersinterface.
- Plotly / Matplotlib (via REST API): Voor het genereren van visualisaties (bijv. uitgaventrends).
- AWS S3 / Google Cloud Storage: Voor het opslaan van de originele factuur-PDF's.
- Stripe / Mollie API: Voor integratie met betalingsverwerkers (bijv. voor het matchen van facturen met betalingen).
Werkwijze:
- Gebruikers uploaden factuur-PDF's via een webinterface.
- De webapplicatie slaat de PDF op in cloudopslag en stuurt een taak naar Celery.
- Celery start een achtergrondtaak die de factuur verwerkt (met OCR/tekstextractie zoals in voorbeeld 2).
- De geëxtraheerde gegevens worden opgeslagen in de database.
- Gebruikers kunnen facturen bekijken, bewerken, goedkeuren, en rapporten genereren via de webinterface.
- Integratie met een ERP-systeem of boekhoudsoftware.
Waarom Python: Django en Flask bieden krachtige tools voor het snel bouwen van schaalbare webapplicaties. Celery maakt robuuste achtergrondtaakverwerking mogelijk.
4. Geautomatiseerd factuurgoedkeuringsworkflow
Doel: Een systeem dat facturen automatisch door een goedkeuringsworkflow stuurt op basis van vooraf gedefinieerde regels.
Python-componenten:
- Flow-engine bibliotheek (custom of lichtgewicht): Voor het definiëren en uitvoeren van workflows.
-
Regelsengine (bijv.
python-business-rulesof eigen implementatie): Om goedkeuringsregels te definiëren (bijv. "bedragen boven €1000 vereisen goedkeuring van afdelingshoofd"). -
E-mailbibliotheken (
smtplib): Voor het versturen van goedkeuringsverzoeken en meldingen. - Database-integratie: Voor het opslaan van factuurstatus en goedkeuringsgeschiedenis.
Werkwijze:
- Zodra een factuur is verwerkt en geëxtraheerde gegevens zijn gevalideerd.
- De regelsengine wordt geraadpleegd om te bepalen wie de factuur moet goedkeuren.
- Automatisch wordt een e-mail met een link naar de factuur en goedkeuringsopties verstuurd naar de relevante personen.
- Na goedkeuring/afwijzing wordt de status van de factuur bijgewerkt in de database en worden eventuele vervolgacties getriggerd (bijv. doorsturen voor betaling).
Waarom Python: Python is uitstekend voor het bouwen van regelsgestuurde systemen en heeft goede e-mailintegratiemogelijkheden.
5. Machine Learning voor betere extractie en matching
Doel: Gebruikmaken van machine learning om de nauwkeurigheid van gegevensxtractie te verbeteren en facturen te matchen met bestelbonnen.
Python-componenten:
- Scikit-learn / TensorFlow / PyTorch: Voor het bouwen en trainen van ML-modellen (bijv. classificatie, Named Entity Recognition).
- Spacy: Voor geavanceerde NLP en NER, vaak gebruikt voor gestructureerde extractie uit ongestructureerde tekst.
- FuzzyWuzzy: Voor "fuzzy matching" van tekst (bijv. het matchen van leveranciersnamen die licht afwijken).
- Database-integratie: Voor het opslaan van trainingsdata en resultaten.
Werkwijze:
- **Verbeterde extractie:** Train een NER-model om specifieke velden (factuurnummer, totale bedrag, BTW-nummer) betrouwbaarder te herkennen, zelfs op wisselende factuurformaten.
- **Bestelbon-matching:** Gebruik ML om inkomende facturen te matchen met openstaande bestelbonnen door overeenkomsten te vinden in artikelen, hoeveelheden en prijzen.
- **Abnormaliteitsdetectie:** Train een model om onregelmatigheden in facturen te signaleren (bijv. ongewoon hoge bedragen, onbekende leveranciers).
Waarom Python: Pythons ecosysteem voor machine learning (Scikit-learn, TensorFlow, PyTorch, Spacy) is ongeëvenaard en maakt het de voorkeurstaal voor dit soort geavanceerde functionaliteit.
Gemeenschappelijke functionaliteiten in veel systemen:
- **Logging:** Bijhouden van alle acties en fouten met de
loggingmodule. - **Foutafhandeling:** Robuuste mechanismen voor het omgaan met mislukte extracties of database-interacties.
- **Rapportering:** Genereren van overzichtsrapporten over verwerkte facturen, foutpercentages, etc.
- **Configuratiebeheer:** Externe configuratiebestanden (JSON, YAML) voor database-credentials, paden, etc.
- **Gebruikersauthenticatie en autorisatie:** Voor webgebaseerde systemen, om te bepalen wie toegang heeft tot welke functionaliteit.
Deze voorbeelden laten zien hoe veelzijdig Python is voor het bouwen van geautomatiseerde factuurverwerkingssystemen, van eenvoudige scripts tot complexe, intelligente platforms. De keuze van de specifieke bibliotheken en architectuur hangt af van de exacte eisen en schaal van het project.
Absoluut! Laten we die voorbeelden uitbreiden met codefragmenten en pseudo-code om te illustreren hoe de Python-implementatie eruit zou kunnen zien. Houd er rekening mee dat dit vereenvoudigde voorbeelden zijn en dat een robuust productiesysteem meer foutafhandeling, logging en beveiliging zou vereisen.
1. Basis CSV-naar-database-integratie
Dit voorbeeld laat zien hoe je CSV-factuurgegevens leest, basic validatie uitvoert en in een SQLite-database invoegt.
import pandas as pd
import sqlite3
import os
from datetime import datetime
# Configuratie
CSV_FOLDER = "invoices_csv"
ARCHIVE_FOLDER = "invoices_archive"
DB_NAME = "invoices.db"
# Zorg ervoor dat mappen bestaan
os.makedirs(CSV_FOLDER, exist_ok=True)
os.makedirs(ARCHIVE_FOLDER, exist_ok=True)
def create_table(conn):
"""Maakt de facturentabel aan als deze nog niet bestaat."""
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS invoices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
invoice_number TEXT NOT NULL UNIQUE,
invoice_date TEXT NOT NULL,
customer_name TEXT,
total_amount REAL NOT NULL,
currency TEXT DEFAULT 'EUR',
processed_at TEXT
);
""")
conn.commit()
def validate_invoice_data(df):
"""Valideert een DataFrame met factuurgegevens."""
required_columns = ['invoice_number', 'invoice_date', 'total_amount']
if not all(col in df.columns for col in required_columns):
raise ValueError(f"Ontbrekende verplichte kolommen. Vereist: {required_columns}")
# Controleer of bedragen numeriek zijn
if not pd.to_numeric(df['total_amount'], errors='coerce').notna().all():
raise ValueError(""total_amount" moet numeriek zijn.")
# Probeer datums te parsen
try:
pd.to_datetime(df['invoice_date'])
except Exception as e:
raise ValueError(f"Ongeldige datumformaat in "invoice_date": {e}")
return True
def process_csv_invoice(filepath, conn):
"""Verwerkt één CSV-factuurbestand."""
try:
df = pd.read_csv(filepath)
print(f"Bestand gelezen: {filepath}")
validate_invoice_data(df)
cursor = conn.cursor()
for index, row in df.iterrows():
# Basic data cleaning/preparation
invoice_number = str(row['invoice_number']).strip()
invoice_date = pd.to_datetime(row['invoice_date']).strftime('%Y-%m-%d')
customer_name = str(row.get('customer_name', '')).strip()
total_amount = float(row['total_amount'])
currency = str(row.get('currency', 'EUR')).strip()
processed_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
try:
cursor.execute("""
INSERT INTO invoices (invoice_number, invoice_date, customer_name, total_amount, currency, processed_at)
VALUES (?, ?, ?, ?, ?, ?)
""", (invoice_number, invoice_date, customer_name, total_amount, currency, processed_at))
print(f"Factuur {invoice_number} succesvol ingevoegd.")
except sqlite3.IntegrityError:
print(f"WAARSCHUWING: Factuurnummer {invoice_number} bestaat al. Overslaan.")
except Exception as e:
print(f"FOUT bij invoegen factuur {invoice_number}: {e}")
conn.commit()
# Verplaats verwerkt bestand naar archief
archive_path = os.path.join(ARCHIVE_FOLDER, os.path.basename(filepath))
os.rename(filepath, archive_path)
print(f"Bestand {os.path.basename(filepath)} verplaatst naar archief.")
except ValueError as ve:
print(f"VALIDATIEFOUT voor {filepath}: {ve}")
except Exception as e:
print(f"ONVERWACHTE FOUT bij verwerken {filepath}: {e}")
def main():
conn = sqlite3.connect(DB_NAME)
create_table(conn)
print(f"Controleren op nieuwe CSV-facturen in {CSV_FOLDER}...")
for filename in os.listdir(CSV_FOLDER):
if filename.endswith('.csv'):
filepath = os.path.join(CSV_FOLDER, filename)
process_csv_invoice(filepath, conn)
conn.close()
print("Verwerking voltooid.")
if __name__ == "__main__":
main()
2. Factuur-PDF-naar-gegevens-extractie met OCR (Pseudo-code en concepten)
Dit voorbeeld focust op de extractie van gegevens uit een PDF. De opslag in een database zou vergelijkbaar zijn met het CSV-voorbeeld.
# Benodigde bibliotheken (installeren met pip: pytesseract, pdfminer.six, pillow)
import pytesseract
from pdfminer.high_level import extract_text
from PIL import Image
import re
import os
# Pad naar tesseract uitvoerbaar bestand (aanpassen indien nodig)
# pytesseract.pytesseract.tesseract_cmd = r'/usr/local/bin/tesseract' # Voor Linux/macOS
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe' # Voor Windows
def extract_text_from_pdf(pdf_path):
"""
Extraheert tekst uit een PDF. Probeert eerst als tekst-PDF,
dan als gescande PDF met OCR.
"""
try:
text = extract_text(pdf_path)
if text.strip(): # Als er tekst is, return deze
print(f"Tekst succesvol geëxtraheerd uit {pdf_path} (tekst-PDF).")
return text
except Exception as e:
print(f"Kon geen tekst extraheren als tekst-PDF, probeer OCR: {e}")
# Als extract_text mislukt of geen tekst oplevert, probeer OCR
try:
# Vereist Poppler voor PDF-naar-afbeelding conversie, of handmatige conversie
# Dit deel is conceptueel, afhankelijk van hoe je de PDF naar afbeelding converteert
# Een veelvoorkomende methode is ImageMagick of pdftoppm (onderdeel van Poppler)
# Voor eenvoud: stel je voor dat we een reeks afbeeldingen hebben gekregen van de PDF
# Voorbeeld: conversie van PDF naar afbeeldingen (vereist 'pdftocairo' of 'convert')
# from pdf2image import convert_from_path
# images = convert_from_path(pdf_path)
# full_ocr_text = ""
# for i, image in enumerate(images):
# page_text = pytesseract.image_to_string(image, lang='nld') # Specificeer Nederlandse taal
# full_ocr_text += page_text + "\n"
# print(f"Tekst succesvol geëxtraheerd uit {pdf_path} met OCR.")
# return full_ocr_text
# Vereenvoudigd voorbeeld voor OCR: stel je voor dat we direct een afbeelding hebben
# Voor dit voorbeeld gebruiken we een dummy tekst als resultaat van OCR
print(f"Voer OCR uit op {pdf_path}... (Dit kan even duren)")
dummy_ocr_text = """
FACTUUR
Factuurnummer: INV-2023-08-001
Datum: 15-08-2023
Bedrijf: Tech Solutions B.V.
Totaal bedrag: EUR 1234.50
BTW: 21%
Vervaldatum: 30-08-2023
"""
return dummy_ocr_text
except Exception as e:
print(f"FOUT bij OCR-extractie uit {pdf_path}: {e}")
return None
def extract_invoice_details(text):
"""Extraheert specifieke factuurdetails met reguliere expressies."""
if not text:
return {}
details = {}
# Factuurnummer (diverse patronen)
invoice_number_match = re.search(r'(Factuurnummer|Invoice N(o|r)?\.?|Ref\.?):?\s*([A-Z0-9\-/]+)', text, re.IGNORECASE)
if invoice_number_match:
details['invoice_number'] = invoice_number_match.group(3).strip()
# Datum (diverse patronen)
date_match = re.search(r'(Datum|Date):?\s*(\d{1,2}[-/]\d{1,2}[-/]\d{2,4})', text, re.IGNORECASE)
if date_match:
details['invoice_date'] = date_match.group(2).strip()
# Totaal bedrag (met of zonder valuta)
total_amount_match = re.search(r'(Totaal bedrag|Total Amount|Amount Due):?\s*(?:EUR|€|\$)?\s*(\d{1,3}(?:[.,]\d{3})*(?:[.,]\d{2}))', text, re.IGNORECASE)
if total_amount_match:
# Vervang komma's door punten voor correcte float conversie en verwijder duizendpunten
amount_str = total_amount_match.group(1).replace('.', '').replace(',', '.')
details['total_amount'] = float(amount_str)
# Bedrijfsnaam (dit is lastiger zonder een vaste positie of machine learning)
# Voor een simpel voorbeeld: probeer een lijn na 'Bedrijf:' te pakken
company_match = re.search(r'(Bedrijf|Company):\s*(.+)', text, re.IGNORECASE)
if company_match:
details['customer_name'] = company_match.group(2).split('\n')[0].strip()
# Pseudo-code voor geavanceerdere NER met Spacy
# import spacy
# nlp = spacy.load("nl_core_news_sm") # Of een custom getraind model
# doc = nlp(text)
# for ent in doc.ents:
# if ent.label_ == "MONEY" and "total" in ent.text.lower():
# details['total_amount_ner'] = ent.text
# if ent.label_ == "ORG": # Organisatie
# # Meer logica nodig om te bepalen welke ORG de leverancier/klant is
# pass
return details
def main_pdf_processing():
PDF_FOLDER = "invoices_pdf"
ARCHIVE_FOLDER = "invoices_pdf_archive"
os.makedirs(PDF_FOLDER, exist_ok=True)
os.makedirs(ARCHIVE_FOLDER, exist_ok=True)
# Dummy PDF-bestanden aanmaken voor test
# In een echt scenario zouden deze bestanden al bestaan.
dummy_pdf_content_text = """
Dit is een tekst-gebaseerde factuur.
FACTUUR #12345
Datum: 2023-09-01
Klant: Jansen B.V.
Totaal: 500.00 EUR
"""
with open(os.path.join(PDF_FOLDER, "invoice_text.pdf"), "w") as f: # Dit is geen echte PDF, maar voor concept
f.write(dummy_pdf_content_text)
# Een 'gescande' PDF zou een afbeelding zijn, die we hier simuleren met een string
dummy_scanned_pdf_content = """
Gescande factuur afbeelding content.
FACTUURNUMMER: ABC-987
INVOICE DATE: 10/09/2023
TOTAL AMOUNT: $750.25
Supplier: Global Supplies Inc.
"""
with open(os.path.join(PDF_FOLDER, "invoice_scanned.pdf"), "w") as f: # Opnieuw, conceptuele
f.write(dummy_scanned_pdf_content)
print(f"Controleren op nieuwe PDF-facturen in {PDF_FOLDER}...")
for filename in os.listdir(PDF_FOLDER):
if filename.endswith('.pdf'):
filepath = os.path.join(PDF_FOLDER, filename)
print(f"\nVerwerken van {filename}...")
extracted_text = extract_text_from_pdf(filepath)
if extracted_text:
invoice_details = extract_invoice_details(extracted_text)
if invoice_details:
print("Succesvol geëxtraheerde details:")
for key, value in invoice_details.items():
print(f" {key}: {value}")
# Hier zou je de gegevens opslaan in een database, vergelijkbaar met het CSV-voorbeeld
# save_to_database(invoice_details, conn)
# Verplaats verwerkt bestand naar archief
archive_path = os.path.join(ARCHIVE_FOLDER, os.path.basename(filepath))
os.rename(filepath, archive_path)
print(f"Bestand {os.path.basename(filepath)} verplaatst naar archief.")
else:
print(f"Kon geen specifieke factuurdetails extraheren uit {filename}.")
else:
print(f"Geen tekst kunnen extraheren uit {filename}.")
if __name__ == "__main__":
# Voor dit voorbeeld, kies welke main-functie je wilt uitvoeren
# main() # Voor CSV-voorbeeld
main_pdf_processing() # Voor PDF-voorbeeld
3. Webgebaseerd factuurbeheersysteem (Pseudo-code met Flask & Celery)
Dit is een vereenvoudigde weergave van een Flask-applicatie met Celery voor achtergrondtaken.
# app.py (Vereenvoudigd Flask-applicatie)
from flask import Flask, request, jsonify, render_template
from celery import Celery
import os
import time # Voor simulatie van lange taak
# from database_utils import save_invoice_data # Een functie om op te slaan
# from pdf_processing_utils import extract_text_from_pdf, extract_invoice_details # Van voorbeeld 2
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0' # Gebruik Redis als broker
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
celery_app = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery_app.conf.update(app.config)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
@celery_app.task
def process_invoice_task(filepath):
"""Achtergrondtaak voor het verwerken van een factuur."""
print(f"Start achtergrondverwerking van {filepath}...")
time.sleep(5) # Simuleer een lange taak (bijv. OCR)
# Pseudo-code voor daadwerkelijke verwerking:
# extracted_text = extract_text_from_pdf(filepath)
# if extracted_text:
# invoice_details = extract_invoice_details(extracted_text)
# if invoice_details:
# save_invoice_data(invoice_details) # Opslaan in database
# return {"status": "success", "details": invoice_details}
# else:
# return {"status": "failed", "message": "Geen details gevonden."}
# else:
# return {"status": "failed", "message": "Tekstextractie mislukt."}
# Dummy resultaat
dummy_details = {
"invoice_number": "WEB-001",
"total_amount": 99.99,
"status": "verwerkt"
}
print(f"Verwerking van {filepath} voltooid.")
return {"status": "success", "details": dummy_details}
@app.route('/')
def index():
return render_template('index.html') # Een eenvoudig HTML-formulier voor upload
@app.route('/upload', methods=['POST'])
def upload_file():
if 'invoice_file' not in request.files:
return jsonify({"error": "Geen bestand geselecteerd"}), 400
file = request.files['invoice_file']
if file.filename == '':
return jsonify({"error": "Geen bestand geselecteerd"}), 400
if file:
filename = secure_filename(file.filename) # secure_filename van werkzeug
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
task = process_invoice_task.delay(filepath) # Start asynchrone taak
return jsonify({"message": "Bestand geüpload en verwerking gestart", "task_id": task.id}), 202
@app.route('/status/<task_id>')
def task_status(task_id):
task = process_invoice_task.AsyncResult(task_id)
if task.state == 'PENDING':
response = {'state': task.state, 'status': 'Wachten op verwerking...'}
elif task.state != 'FAILURE':
response = {'state': task.state, 'status': task.info.get('status', 'Bezig...')}
if 'details' in task.info:
response['details'] = task.info['details']
else:
# Foutafhandeling
response = {'state': task.state, 'status': str(task.info)}
return jsonify(response)
if __name__ == '__main__':
# Om deze te runnen:
# 1. Installeer Flask, Celery, Redis (en andere benodigde bibliotheken)
# 2. Start Redis server
# 3. Start Celery worker: celery -A app.celery_app worker --loglevel=info
# 4. Start Flask app: python app.py
# 5. Maak een `templates/index.html` bestand met een uploadformulier.
app.run(debug=True)
<!-- templates/index.html (eenvoudige front-end) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Factuur Upload</title>
</head>
<body>
<h1>Factuur Uploaden</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="invoice_file" accept=".pdf,.csv">
<input type="submit" value="Upload">
</form>
<div id="status"></div>
<script>
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const statusDiv = document.getElementById('status');
statusDiv.innerHTML = 'Uploaden...';
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
if (response.ok) {
statusDiv.innerHTML = `Upload succesvol. Taak ID: ${data.task_id}. Verwerking gestart...`;
checkTaskStatus(data.task_id);
} else {
statusDiv.innerHTML = `Fout: ${data.error}`;
}
});
async function checkTaskStatus(taskId) {
const statusDiv = document.getElementById('status');
const interval = setInterval(async () => {
const response = await fetch(`/status/${taskId}`);
const data = await response.json();
statusDiv.innerHTML = `Status taak ${taskId}: ${data.status} (State: ${data.state})`;
if (data.state === 'SUCCESS' || data.state === 'FAILURE') {
clearInterval(interval);
if (data.details) {
statusDiv.innerHTML += `<br>Details: ${JSON.stringify(data.details)}`;
}
}
}, 3000); // Controleer elke 3 seconden
}
</script>
</body>
</html>
4. Geautomatiseerd factuurgoedkeuringsworkflow (Pseudo-code)
Dit toont de logica voor een eenvoudige workflow op basis van regels.
# workflow_engine.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Pseudo-database voor factuurstatus en gebruikers
INVOICES_DB = {} # {invoice_id: {data, status, approval_needed_by, approved_by}}
USERS_DB = {
"manager@example.com": {"role": "manager", "threshold": 1000},
"director@example.com": {"role": "director", "threshold":-1}, # Goedkeuring voor alles
"accountant@example.com": {"role": "accountant"}
}
# E-mail configuratie (voor demo, zou in config bestand staan)
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587
SMTP_USER = 'your_email@example.com'
SMTP_PASS = 'your_email_password'
def send_approval_email(to_email, invoice_id, amount):
"""Stuurt een e-mail voor goedkeuring."""
msg = MIMEMultipart("alternative")
msg["Subject"] = f"Factuurgoedkeuring vereist: {invoice_id}"
msg["From"] = SMTP_USER
msg["To"] = to_email
text = f"""
Beste,
Factuur {invoice_id} ter waarde van {amount} EUR vereist uw goedkeuring.
Klik hier om goed te keuren: http://your-app.com/approve/{invoice_id}
Klik hier om af te wijzen: http://your-app.com/reject/{invoice_id}
Met vriendelijke groet,
Uw Factuur Systeem
"""
# HTML versie kan complexer zijn
html = f"""
<html>
<body>
<p>Beste,</p>
<p>Factuur <strong>{invoice_id}</strong> ter waarde van <strong>{amount} EUR</strong> vereist uw goedkeuring.</p>
<p>
<a href="http://your-app.com/approve/{invoice_id}">Goedkeuren</a> |
<a href="http://your-app.com/reject/{invoice_id}">Afwijzen</a>
</p>
<p>Met vriendelijke groet,<br>Uw Factuur Systeem</p>
</body>
</html>
"""
part1 = MIMEText(text, "plain")
part2 = MIMEText(html, "html")
msg.attach(part1)
msg.attach(part2)
try:
# Gebruik context manager voor smtplib
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls() # Secure the connection
server.login(SMTP_USER, SMTP_PASS)
server.send_message(msg)
print(f"Goedkeuringsmail verzonden voor factuur {invoice_id} naar {to_email}")
except Exception as e:
print(f"FOUT bij verzenden e-mail voor {invoice_id} naar {to_email}: {e}")
def determine_approver(amount):
"""Bepaalt wie de factuur moet goedkeuren op basis van het bedrag."""
if amount > USERS_DB["manager@example.com"]["threshold"]:
return "director@example.com"
else:
return "manager@example.com"
def initiate_approval_workflow(invoice_id, invoice_data):
"""Start het goedkeuringsproces voor een factuur."""
print(f"Start goedkeuringsworkflow voor factuur {invoice_id}...")
# Controleer of factuur al bestaat in pseudo-DB
if invoice_id in INVOICES_DB:
print(f"Factuur {invoice_id} is al in de workflow.")
return
approver_email = determine_approver(invoice_data['total_amount'])
INVOICES_DB[invoice_id] = {
"data": invoice_data,
"status": "Wachten op goedkeuring",
"approval_needed_by": approver_email,
"approved_by": None
}
send_approval_email(approver_email, invoice_id, invoice_data['total_amount'])
print(f"Factuur {invoice_id} doorgestuurd naar {approver_email} voor goedkeuring.")
def approve_invoice(invoice_id, approver_email):
"""Markeert een factuur als goedgekeurd."""
if invoice_id not in INVOICES_DB:
print(f"Fout: Factuur {invoice_id} niet gevonden.")
return False
invoice = INVOICES_DB[invoice_id]
if invoice["approval_needed_by"] != approver_email:
print(f"Fout: {approver_email} is niet geautoriseerd om factuur {invoice_id} goed te keuren.")
return False
invoice["status"] = "Goedgekeurd"
invoice["approved_by"] = approver_email
print(f"Factuur {invoice_id} goedgekeurd door {approver_email}. Klaar voor betaling.")
# Trigger verdere acties, bijv. doorsturen naar boekhoudsysteem
return True
def reject_invoice(invoice_id, approver_email, reason):
"""Markeert een factuur als afgewezen."""
if invoice_id not in INVOICES_DB:
print(f"Fout: Factuur {invoice_id} niet gevonden.")
return False
invoice = INVOICES_DB[invoice_id]
if invoice["approval_needed_by"] != approver_email:
print(f"Fout: {approver_email} is niet geautoriseerd om factuur {invoice_id} af te wijzen.")
return False
invoice["status"] = "Afgewezen"
invoice["approved_by"] = approver_email # Degene die afwijst
invoice["rejection_reason"] = reason
print(f"Factuur {invoice_id} afgewezen door {approver_email}. Reden: {reason}")
# Trigger verdere acties, bijv. melding aan de initiator
return True
# Voorbeeldgebruik:
if __name__ == "__main__":
invoice1_data = {"invoice_number": "INV-001", "total_amount": 750.00, "vendor": "ABC Corp"}
invoice2_data = {"invoice_number": "INV-002", "total_amount": 1500.00, "vendor": "XYZ Ltd"}
initiate_approval_workflow("INV-001", invoice1_data)
initiate_approval_workflow("INV-002", invoice2_data)
print("\n" + "-- Factuurstatus na initiatie --")
print(INVOICES_DB)
# Simuleer goedkeuring door manager
print("\n" + "-- Goedkeuring INV-001 door manager --")
approve_invoice("INV-001", "manager@example.com")
# Simuleer afwijzing door directeur (niet geautoriseerd voor INV-001)
print("\n" + "-- Poging tot afwijzing INV-001 door directeur --")
reject_invoice("INV-001", "director@example.com", "Niet mijn factuur.")
# Simuleer goedkeuring door directeur voor INV-002
print("\n" + "-- Goedkeuring INV-002 door directeur --")
approve_invoice("INV-002", "director@example.com")
print("\n" + "-- Factuurstatus na goedkeuringen --")
print(INVOICES_DB)
5. Machine Learning voor betere extractie en matching (Conceptueel)
Dit is meer een conceptuele uitleg, aangezien het trainen van ML-modellen veel data en code vereist. Hier focussen we op de *integratie* van ML.
# ml_invoice_processor.py
import spacy
from fuzzywuzzy import fuzz
from fuzzywuzzy import process
import pandas as pd
# from sklearn.ensemble import RandomForestClassifier # Voor classificatie
# from sklearn.model_selection import train_test_split # Voor training
# Voorbeeld van een getraind Spacy-model voor Factuur NER
# In een echt scenario zou dit model vooraf getraind zijn op factuurdata
try:
nlp_invoice = spacy.load("nl_core_news_sm")
# nlp_invoice.add_pipe("ner_invoice", source="my_custom_invoice_ner_model")
print("Spacy model geladen.")
except:
print("Geen gespecialiseerd Spacy model gevonden, gebruik basismodel.")
nlp_invoice = spacy.load("en_core_web_sm") # Terugval naar Engels als Nederlands niet beschikbaar
# Pseudo-database van bekende leveranciers en bestelbonnen
KNOWN_VENDORS = ["Tech Solutions B.V.", "Global Supplies Inc.", "Innovative Gadgets & Co.", "Office Supplies NL"]
PURCHASE_ORDERS_DB = {
"PO-001": {"vendor": "Tech Solutions B.V.", "items": [{"desc": "Laptop", "qty": 1, "price": 1000}], "total": 1000, "status": "open"},
"PO-002": {"vendor": "Innovative Gadgets & Co.", "items": [{"desc""Mouse", "qty": 10, "price": 20}], "total": 200, "status": "open"},
}
def extract_with_ml(invoice_text):
"""
Gebruikt een ML-model (Spacy NER) om entiteiten te extraheren.
"""
doc = nlp_invoice(invoice_text)
extracted_data = {}
for ent in doc.ents:
# Stel je voor dat je specifieke labels hebt zoals 'INVOICE_NUM', 'AMOUNT', 'VENDOR_NAME'
if ent.label_ == "INVOICE_NUM":
extracted_data['invoice_number_ml'] = ent.text
elif ent.label_ == "AMOUNT":
extracted_data['total_amount_ml'] = float(ent.text.replace(',', '.')) # Conversie
elif ent.label_ == "VENDOR_NAME":
extracted_data['vendor_name_ml'] = ent.text
# Enzovoort voor andere velden
return extracted_data
def fuzzy_match_vendor(extracted_vendor_name):
"""
Gebruikt fuzzy matching om de geëxtraheerde leveranciersnaam te matchen
met bekende leveranciers.
"""
if not extracted_vendor_name:
return None, 0
match, score = process.extractOne(extracted_vendor_name, KNOWN_VENDORS)
return match, score
def match_invoice_to_po(invoice_data, po_db):
"""
Probeert een factuur te matchen met een bestelbon op basis van leverancier en bedrag.
Een meer geavanceerde matching zou ook kijken naar individuele items.
"""
matched_pos = []
invoice_vendor = invoice_data.get('vendor_name_ml')
invoice_total = invoice_data.get('total_amount_ml')
if not invoice_vendor or not invoice_total:
print("Onvoldoende gegevens voor PO matching.")
return []
best_vendor_match, _ = fuzzy_match_vendor(invoice_vendor)
for po_id, po_data in po_db.items():
if po_data['status'] == 'open':
po_vendor_match, _ = fuzzy_match_vendor(po_data['vendor'])
# Controleer of leveranciers redelijk overeenkomen en bedragen dicht bij elkaar liggen
if best_vendor_match == po_vendor_match and abs(invoice_total - po_data['total']) < 50: # Tolerantie van 50 EUR
matched_pos.append({"po_id": po_id, "po_data": po_data, "confidence": 0.9}) # Hoge zekerheid
return matched_pos
def detect_anomaly(invoice_data):
"""
Voorbeeld van anomaliedetectie (pseudo-code).
In een echt systeem zou dit een getraind ML-classificatiemodel zijn.
"""
is_anomaly = False
reasons = []
if invoice_data.get('total_amount_ml', 0) > 5000: # Zeer hoog bedrag
is_anomaly = True
reasons.append("Zeer hoog factuurbedrag.")
# Pseudo-code voor ML-model:
# features = prepare_features(invoice_data) # Converteer data naar numerieke features
# prediction = anomaly_model.predict(features)
# if prediction == 1:
# is_anomaly = True
# reasons.append("ML model detecteerde anomalie.")
return is_anomaly, reasons
# Voorbeeldgebruik:
if __name__ == "__main__":
invoice_text_sample = """
Factuurnummer: FA2023-007
Datum: 2023-10-26
Aan: Klant XYZ
Van: Tech Solutions B.V.
Beschrijving: Consultancy diensten
Bedrag: EUR 1150.00
"""
print("--- ML-extractie ---")
ml_extracted_data = extract_with_ml(invoice_text_sample)
print(f"ML-geëxtraheerde gegevens: {ml_extracted_data}")
print("\n--- Leverancier matching ---")
extracted_vendor = ml_extracted_data.get('vendor_name_ml', 'Onbekende leverancier')
matched_vendor, score = fuzzy_match_vendor(extracted_vendor)
print(f"Geëxtraheerde leverancier: '{extracted_vendor}'")
print(f"Beste match in bekende leveranciers: '{matched_vendor}' (Score: {score})")
print("\n--- Bestelbon matching ---")
invoice_for_po_match = {
"vendor_name_ml": "Tech Solutions B.V.",
"total_amount_ml": 1020.00 # Ligt dicht bij PO-001
}
matched_pos = match_invoice_to_po(invoice_for_po_match, PURCHASE_ORDERS_DB)
if matched_pos:
print("Overeenkomende bestelbonnen gevonden:")
for po in matched_pos:
print(f" PO-ID: {po['po_id']}, Vendor: {po['po_data']['vendor']}, Totaal: {po['po_data']['total']}")
else:
print("Geen overeenkomende bestelbonnen gevonden.")
print("\n--- Anomaliedetectie ---")
# Voorbeeld 1: normale factuur
normal_invoice = {"total_amount_ml": 450.00}
is_anomaly, reasons = detect_anomaly(normal_invoice)
print(f"Normale factuur ({normal_invoice['total_amount_ml']}): Anomalie = {is_anomaly}, Redenen: {reasons}")
# Voorbeeld 2: anomale factuur
anomalous_invoice = {"total_amount_ml": 6000.00}
is_anomaly, reasons = detect_anomaly(anomalous_invoice)
print(f"Anomale factuur ({anomalous_invoice['total_amount_ml']}): Anomalie = {is_anomaly}, Redenen: {reasons}")
Deze codefragmenten en pseudo-code zouden je een goed startpunt moeten geven voor het visualiseren van de implementatie van geautomatiseerde factuurverwerking met Python. Elk van deze componenten kan verder worden uitgebreid en verfijnd, afhankelijk van de specifieke vereisten van je project.
Absoluut! Het bouwen van een geautomatiseerd factuurverwerkingssysteem in Python vereist een combinatie van verschillende tools en platforms, afhankelijk van de complexiteit, schaal en specifieke functionaliteiten die je nodig hebt. Hier is een uitgebreide lijst, gecategoriseerd op hun rol in het systeem:
1. Core Python Bibliotheken & Frameworks
-
Voor Gegevensverwerking & Analyse:
- Pandas: Onmisbaar voor het lezen, manipuleren en analyseren van gestructureerde gegevens (zoals uit CSV's of databases). Zeer efficiënt voor tabulaire gegevens.
- NumPy: Biedt krachtige numerieke mogelijkheden, de basis voor veel andere wetenschappelijke bibliotheken.
-
Voor Webapplicaties & API's (indien nodig):
- Django: Een 'batteries-included' full-stack webframework, ideaal voor complexe applicaties met databases, authenticatie en administratie. Geschikt voor een complete webgebaseerde factuurportal.
- Flask: Een lichtgewicht microframework, flexibeler en beter voor kleinere applicaties of het bouwen van RESTful API's die andere services kunnen gebruiken.
- FastAPI: Een modern, snel (asynchroon) webframework voor het bouwen van API's met automatische documentatie (Swagger UI). Uitstekend voor high-performance services.
-
Voor Achtergrondtaken & Asynchrone Verwerking:
- Celery: Een gedistribueerde taakwachtrij die het mogelijk maakt om langlopende taken (zoals OCR of complexe dataverwerking) asynchroon en buiten de hoofdapplicatiestroom uit te voeren. Vereist een message broker zoals Redis of RabbitMQ.
- Redis / RabbitMQ: Message brokers die Celery en andere gedistribueerde systemen gebruiken om taken in de wachtrij te plaatsen en te beheren.
-
Voor Taakplanning:
- APScheduler: Een flexibele bibliotheek voor het plannen van taken (eenmalig, periodiek, op specifieke datums) binnen een Python-applicatie.
- Cron (Linux) / Windows Task Scheduler: Voor het uitvoeren van Python-scripts op vaste tijden op het besturingssysteem.
2. Gegevens-Extractie & OCR (Optical Character Recognition)
-
Voor PDF Verwerking:
- PyPDF2 / pdfminer.six: Voor het lezen van tekst-gebaseerde PDF's en het extraheren van tekstinhoud.
- pdfplumber: Bouwt voort op pdfminer.six en maakt het eenvoudiger om tekst en tabellen uit PDF's te extraheren, inclusief positionele informatie.
-
Voor OCR:
-
Tesseract OCR: Een open-source OCR-engine. Wordt vaak gebruikt via de Python-wrapper
pytesseract. Goed voor gescande documenten en afbeeldingen. - Google Cloud Vision API / AWS Textract / Azure Cognitive Services: Cloudgebaseerde OCR-services die geavanceerdere en vaak nauwkeurigere resultaten bieden, vooral voor complexe lay-outs en handschrift. Ze hebben Python SDK's voor integratie. Dit zijn vaak de voorkeursopties voor productieomgevingen vanwege hun schaalbaarheid en nauwkeurigheid.
- OpenCV-Python: Voor beeldverwerkingstaken, zoals het voorbewerken van factuurafbeeldingen (bijv. roteren, bijsnijden, ruis verwijderen) om de OCR-nauwkeurigheid te verbeteren.
-
Tesseract OCR: Een open-source OCR-engine. Wordt vaak gebruikt via de Python-wrapper
-
Voor Structuurdetectie & Gegevensuitpakken:
-
Reguliere Expressies (
remodule): Basis voor patroonherkenning in tekst (bijv. factuurnummers, datums, bedragen). - Spacy / NLTK: Voor geavanceerde Natural Language Processing (NLP), waaronder Named Entity Recognition (NER) om specifieke entiteiten (bedrijfsnamen, adressen, bedragen) uit ongestructureerde tekst te identificeren. Spacy is vaak sneller en geschikter voor productietoepassingen.
- Scikit-learn / TensorFlow / PyTorch: Voor het bouwen en trainen van aangepaste Machine Learning-modellen voor complexere gegevensxtractie, classificatie of anomaliedetectie wanneer reguliere expressies tekortschieten.
-
Reguliere Expressies (
3. Databases & Opslag
-
Relationele Databases:
-
PostgreSQL / MySQL: Robuuste, schaalbare databases voor het opslaan van factuurgegevens, gebruikersinformatie, auditlogs, etc. Python heeft uitstekende drivers (bijv.
psycopg2voor PostgreSQL,mysql-connector-pythonvoor MySQL). -
SQLite: Lichtgewicht, bestandsgebaseerd, ideaal voor kleinere projecten, ontwikkeling of embedded systemen. Standaard ingebouwd in Python (
sqlite3module).
-
PostgreSQL / MySQL: Robuuste, schaalbare databases voor het opslaan van factuurgegevens, gebruikersinformatie, auditlogs, etc. Python heeft uitstekende drivers (bijv.
-
Object-Relationele Mappers (ORM's):
- SQLAlchemy: Een krachtige en flexibele ORM die het gemakkelijker maakt om met databases te werken vanuit Python-code, zonder directe SQL te schrijven. Goede integratie met Flask en Django.
- Django ORM: De ingebouwde ORM van Django, naadloos geïntegreerd en zeer productief voor Django-projecten.
-
Cloud Opslag:
-
Amazon S3 / Google Cloud Storage / Azure Blob Storage: Voor het veilig en schaalbaar opslaan van de originele factuur-PDF's, -afbeeldingen en andere binaire bestanden. Python heeft SDK's (
boto3voor AWS,google-cloud-storage,azure-storage-blob) om hiermee te communiceren.
-
Amazon S3 / Google Cloud Storage / Azure Blob Storage: Voor het veilig en schaalbaar opslaan van de originele factuur-PDF's, -afbeeldingen en andere binaire bestanden. Python heeft SDK's (
4. Gebruikersinterface & Visualisatie (indien webgebaseerd)
-
Frontend Frameworks (worden vaak gebruikt met Python backends):
- HTML/CSS/JavaScript: De basis van elke web-UI.
- React / Vue.js / Angular: Moderne JavaScript-frameworks voor het bouwen van dynamische en responsieve gebruikersinterfaces.
-
Visualisatie Bibliotheken (voor rapportages):
- Matplotlib / Seaborn: Voor het creëren van statische plots en grafieken.
- Plotly / Dash: Voor interactieve grafieken en dashboards, Dash is speciaal ontworpen voor analytische webapplicaties in Python.
5. Overige Integraties & Tools
-
E-mail Verwerking:
-
smtplib,emailmodules: Standaard Python-bibliotheken voor het verzenden en ontvangen van e-mails (bijv. voor goedkeuringsverzoeken, foutmeldingen). - Mailgun / SendGrid: E-mail API-services voor bulk e-mails en betrouwbare bezorging. Python SDK's beschikbaar.
-
-
Authenticatie & Autorisatie:
- Flask-Login / Django's ingebouwde auth: Voor het beheren van gebruikerssessies en toegang.
- OAuth2 / JWT: Voor secure API-authenticatie.
-
Versiebeheer:
- Git: Essentieel voor codebeheer en samenwerking (met platforms zoals GitHub, GitLab, Bitbucket).
-
Containerisatie & Orchestratie:
- Docker: Voor het verpakken van je applicatie en al zijn afhankelijkheden in geïsoleerde containers, wat deployment en schaalbaarheid vereenvoudigt.
- Kubernetes: Voor het orkestreren van Docker-containers op schaal in productieomgevingen.
-
Cloud Platforms voor Deployment:
- AWS (EC2, Lambda, S3, RDS, Textract, Sagemaker): Een breed scala aan diensten voor hosting, serverless functies, opslag, databases, en AI/ML.
- Google Cloud Platform (Compute Engine, Cloud Functions, Cloud Storage, Cloud SQL, Vision AI): Vergelijkbaar aanbod van diensten.
- Microsoft Azure (Virtual Machines, Functions, Blob Storage, SQL Database, Cognitive Services): Ook een uitgebreid cloudplatform.
- Heroku / PythonAnywhere: Eenvoudigere PaaS (Platform as a Service) opties voor het snel deployen van Python-webapps.
-
API Integraties (optioneel):
- Boekhoudsoftware API's: Bijv. API's voor Exact Online, QuickBooks, Xero om factuurgegevens direct naar het boekhoudsysteem te exporteren.
- ERP-systemen API's: Voor integratie met SAP, Oracle, etc.
De keuze van deze tools zal afhangen van je specifieke projectvereisten, budget, teamexpertise en de gewenste schaalbaarheid en functionaliteit van het uiteindelijke systeem. Voor een klein project zou je kunnen beginnen met Flask, SQLite en pytesseract. Voor een enterprise-oplossing zou je eerder kiezen voor Django, PostgreSQL, een cloud-OCR-service en gedistribueerde systemen.
Natuurlijk, laten we een typische architectuur schetsen voor een geautomatiseerd factuurverwerkingssysteem in Python, inclusief hoe de verschillende componenten met elkaar samenwerken. Dit is vaak een gedistribueerd systeem om schaalbaarheid, robuustheid en efficiëntie te garanderen.
Typische Architectuur van een Geautomatiseerd Factuurverwerkingssysteem in Python
Hier is een opdeling van de lagen en componenten:
+---------------------+
| |
| Gebruikersinterface |
| (Web / Desktop App) |
| |
+----------+----------+
|
| API Verzoeken (REST / GraphQL)
V
+------------------------------------------------------------------------------------------------------------------+
| Backend Services (Python) |
| |
| +---------------------+ +-----------------------+ +--------------------------+ |
| | | | | | | |
| | API Gateway / | <----> | Webserver (Gunicorn/ | <----> | Web Framework (Django/ | |
| | Load Balancer | | Nginx/Apache) | | Flask/FastAPI) | |
| | (Reverse Proxy) | | | | | |
| +---------------------+ +-----------------------+ +------------+-------------+ |
| | |
| | |
| +--------------------------------------+---------------------------------+
| | |
| V V
| +------------+------------+ +---------------------------------+ |
| | | | | |
| | Asynchrone Taak Wachtrij | | Authenticatie & Autorisatie | |
| | (Celery Worker Pool) | <-----------> | Service (Optioneel) | |
| | | | | |
| +------------+------------+ +---------------------------------+ |
| ^ |
| | Taakberichten (factuur-ID, opslaglocatie) |
| | |
| +--------------------------------------+---------------------------------------------------------------------+ |
| | | |
| | +-----------------------+ +-----------------------+ | |
| | | | | | | |
| | | Message Broker | <--> | Taakqueue Manager | | |
| | | (Redis / RabbitMQ) | | (Celery Beat voor | | |
| | | | | geplande taken) | | |
| | +-----------------------+ +-----------------------+ | |
| | ^ | |
| | | | |
| | +--------------------------------+---------------------------------------------------------------------+ |
| | | | |
| | | +------------------------+ +-----------------------+ +-----------------------+ | |
| | | | | | | | | | |
| | | | Document Ingestie | | OCR & Gegevens- | | Validatie & | | |
| | | | (E-mail Parser, Upload) | | Extractie | | Normalisatie | | |
| | | | | | | | | | |
| | | +-----------+------------+ +-----------+-----------+ +-----------+-----------+ | |
| | | | | | | |
| | | V V V | |
| | | +-----------+------------+ +-----------+-----------+ +-----------+-----------+ | |
| | | | | | | | | | |
| | | | Factuur Opslag |<--->| Data Extractie |<--->| Workflow Engine | | |
| | | | (Cloud Storage: S3) | | (RegEx, NLP, ML) | | (Goedkeuring, Matching) | | |
| | | | | | | | | | |
| | | +-----------+------------+ +-----------+-----------+ +-----------+-----------+ | |
| | | | | | | |
| | | V V V | |
| | | +-----------+------------+<------------->+-----------------------+ | |
| | | | | | | | |
| | | | Primaire Database | | Audit Trails & | | |
| | | | (PostgreSQL / MySQL) | | Logboekregistratie | | |
| | | | | | | | |
| | | +-------------------------+ +-----------------------+ | |
| | | | |
| | | +---------------------------------+ +---------------------------------+ | |
| | | | | | | | |
| | | | Externe Integraties |<----->| Rapportage & Analyse | | |
| | | | (ERP, Boekhoudsoftware, Betalingen) | | (BI Tools, Dashboards) | | |
| | | | | | | | |
| | | +---------------------------------+ +---------------------------------+ | |
| | | | |
| | +---------------------------------------------------------------------------------------------------------+ |
| | |
| +------------------------------------------------------------------------------------------------------------------+
Uitleg van de Componenten en Hun Samenwerking
1. Gebruikersinterface (Frontend)
- Rol: Het gezicht van het systeem waar gebruikers interactie mee hebben. Dit kan een webapplicatie zijn (gebouwd met HTML/CSS/JavaScript en frameworks zoals React of Vue.js), een desktopapplicatie, of zelfs een mobiele app.
- Interactie: Gebruikers uploaden facturen, bekijken de status van verwerkte facturen, corrigeren geëxtraheerde gegevens, keuren facturen goed of af, en bekijken rapporten. Communiceert met de Backend Services via RESTful API-aanroepen of GraphQL.
2. Backend Services (Python)
Dit is het hart van het systeem, meestal gebouwd met Python.
-
API Gateway / Load Balancer:
- Rol: Entree voor alle externe verzoeken. Distributie van verkeer over meerdere webservers (indien schaalbaar), beveiliging (SSL-terminatie, DDoS-bescherming) en eventueel API-ratelimiting.
- Samenwerking: Stuurt verzoeken door naar de Webserver.
-
Webserver (Gunicorn / Nginx / Apache):
- Rol: Draait de Python-webapplicatie. Nginx of Apache worden vaak gebruikt als reverse proxy voor Gunicorn (of uWSGI), wat de Python-applicatie serveert. Dit zorgt voor betere prestaties, statische bestandsserving en beveiliging.
- Samenwerking: Ontvangt verzoeken van de API Gateway en stuurt deze door naar het Web Framework.
-
Web Framework (Django / Flask / FastAPI):
- Rol: Verwerkt binnenkomende HTTP-verzoeken, routeert ze naar de juiste logica, beheert gebruikerssessies, en genereert API-antwoorden. Implementeert de bedrijfslogica die direct door de frontend wordt gebruikt.
- Samenwerking:
- Ontvangt verzoeken van de Webserver.
- Interacteert met de Authenticatiedienst.
- Stuurt (via Message Broker) taken naar de Asynchrone Taak Wachtrij voor langdurige processen.
- Haalt/schrijft gegevens van/naar de Primaire Database.
-
Authenticatie & Autorisatie Service (Optioneel, kan ook in webframework zitten):
- Rol: Beheert gebruikersaanmeldingen, rollen en permissies (bijv. wie facturen mag uploaden, wie mag goedkeuren).
- Samenwerking: Het webframework raadpleegt deze service (of ingebouwde modules) bij elk verzoek dat authenticatie/autorisatie vereist.
3. Asynchrone Taak Verwerking
Cruciaal voor het efficiënt afhandelen van tijdrovende taken zonder de gebruikersinterface te blokkeren.
-
Message Broker (Redis / RabbitMQ):
- Rol: Een tussenpersoon die berichten (taken) opslaat en distribueert tussen de Web Framework (producent) en de Celery Workers (consumenten).
- Samenwerking: Het Web Framework plaatst taken in de wachtrij, en Celery Workers halen taken uit de wachtrij op.
-
Asynchrone Taak Wachtrij (Celery Worker Pool):
- Rol: Een groep Python-processen (Celery Workers) die speciaal zijn ontworpen om langlopende taken op te halen en uit te voeren. Dit omvat de kern van de factuurverwerking.
- Samenwerking: Communiceert met de Message Broker om taken te ontvangen. Voert de logica uit van de Document Ingestie, OCR & Gegevens-Extractie, Validatie & Normalisatie, Workflow Engine.
-
Taakqueue Manager (Celery Beat):
- Rol: Plant periodieke taken (bijv. elke 15 minuten controleren op nieuwe e-mails met facturen, dagelijks rapporten genereren).
- Samenwerking: Stuurt geplande taken naar de Message Broker, die ze vervolgens distribueert naar de Celery Workers.
4. Kern Factuur Verwerkingsmodules (uitgevoerd door Celery Workers)
-
Document Ingestie:
- Rol: Het startpunt van de factuurverwerking. Ontvangt facturen via verschillende kanalen.
- Samenwerking:
- E-mail Parser: Leest inkomende e-mails (bijv. van een specifieke mailbox) en extraheert bijlagen die facturen kunnen zijn (Python e-mailbibliotheken + Celery geplande taak).
- Bestandsupload: Verwerkt uploads via de frontend (Web Framework slaat op en stuurt taak naar Celery).
- SFTP / Map Monitoring: Controleert specifieke mappen of SFTP-servers op nieuwe bestanden (Celery geplande taak).
- Factuur Opslag (Cloud Storage - S3/GCS/Azure Blob): Slaat de originele factuur (PDF, afbeelding) op in een duurzame, schaalbare cloudopslag. Retourneert een verwijzing (URL/pad).
-
OCR & Gegevens-Extractie:
- Rol: Converteert factuurafbeeldingen/PDF's naar leesbare tekst en extraheert relevante gegevens.
- Samenwerking:
- Gebruikt OCR-engines (Tesseract of cloud-API's) om tekst uit het document te halen.
- Past NLP-technieken (Spacy) en reguliere expressies toe om gestructureerde gegevens (factuurnummer, bedrag, datum, leverancier) te identificeren.
- Kan Machine Learning-modellen gebruiken voor complexe extractie of om te leren van nieuwe factuurlay-outs.
-
Validatie & Normalisatie:
- Rol: Controleert de integriteit en correctheid van de geëxtraheerde gegevens. Standaardiseert formaten.
- Samenwerking:
- Valideert datums, bedragen, verplichte velden.
- Normaliseert leveranciersnamen (bijv. met fuzzy matching) tegen een lijst van bekende leveranciers.
- Signaleert inconsistenties of ontbrekende gegevens.
-
Workflow Engine (Goedkeuring, Matching):
- Rol: Stuurt de factuur door de gedefinieerde bedrijfsprocessen.
- Samenwerking:
- Bestelbon Matching: Zoekt naar overeenkomende inkooporders in de Primaire Database op basis van leverancier, bedrag en items.
- Goedkeuringsregels: Evalueert regels (bijv. bedrag > X vereist goedkeuring van manager) en initialiseert een goedkeuringsproces.
- E-mail Notificaties: Stuurt e-mails voor goedkeuring/afwijzing via een e-mailservice.
5. Databases
-
Primaire Database (PostgreSQL / MySQL):
- Rol: De centrale opslag voor alle gestructureerde factuurgegevens (geëxtraheerd, gevalideerd), bestelbonnen, leveranciersinformatie, goedkeuringsstatussen, gebruikersprofielen, etc.
- Samenwerking: Alle backend-componenten lezen en schrijven naar deze database. Python ORM's (SQLAlchemy/Django ORM) abstraheren de database-interactie.
-
Audit Trails & Logboekregistratie:
- Rol: Houdt een gedetailleerd logboek bij van alle acties, wijzigingen en fouten in het systeem. Essentieel voor compliance en debugging.
- Samenwerking: Alle componenten loggen hun activiteiten. Kan een aparte tabel in de primaire database zijn of een gespecialiseerde loggingservice.
6. Externe Integraties
-
ERP / Boekhoudsoftware:
- Rol: Na succesvolle verwerking en goedkeuring exporteert het systeem de factuurgegevens naar externe systemen zoals SAP, Exact Online, QuickBooks.
- Samenwerking: Gebruikt API's (Python SDK's) van de externe systemen om gegevens te verzenden. Dit gebeurt meestal als een laatste stap in de workflow.
-
Betalingsgateways:
- Rol: Voor het initiëren of matchen van betalingen.
- Samenwerking: Kan API's van Stripe, Mollie, etc. gebruiken.
7. Rapportage & Analyse
Rol: Biedt inzicht in de financiële gegevens, verwerkingstijden, foutpercentages, etc.
- Samenwerking: Gebruikt data uit de Primaire Database om dashboards (bijv. met Plotly Dash) of rapporten te genereren die via de gebruikersinterface worden weergegeven. Kan ook Business Intelligence (BI) tools (zoals Tableau, Power BI) voeden.
Samenvatting van de Stroom
- Een factuur (PDF, afbeelding, CSV) komt binnen via de Document Ingestie (e-mail, upload, map).
- De originele factuur wordt opgeslagen in Cloud Opslag.
- Een taak wordt in de Message Broker geplaatst door het Web Framework (of Celery Beat).
- Een Celery Worker pakt de taak op.
- De Worker voert OCR & Gegevens-Extractie uit.
- De geëxtraheerde gegevens ondergaan Validatie & Normalisatie.
- De Workflow Engine bepaalt de volgende stappen (bijv. bestelbon matching, goedkeuring).
- Afhankelijk van de workflow worden e-mails verzonden of de factuur wordt gemarkeerd voor handmatige beoordeling in de Gebruikersinterface.
- Alle factuurgegevens en hun status worden opgeslagen in de Primaire Database.
- Goedgekeurde facturen worden geëxporteerd naar Externe Integraties (ERP/boekhouding).
- Rapportage & Analyse haalt gegevens uit de database voor inzichten.
Deze modulaire aanpak, gecombineerd met Python's rijke ecosysteem, maakt het mogelijk om een robuust, schaalbaar en flexibel geautomatiseerd factuurverwerkingssysteem te bouwen dat kan meegroeien met de behoeften van een organisatie.
Jazeker! Een goed ontworpen datamodel is de ruggengraat van elk robuust factuurverwerkingssysteem. Het zorgt voor gegevensintegriteit, efficiënte opslag en gemakkelijke rapportage. Hieronder schets ik een typisch datamodel met de belangrijkste tabellen, hun attributen en relaties, geschikt voor een relationele database zoals PostgreSQL of MySQL.
Overzicht van het Datamodel (Conceptueel ERD)
+---------------------+
| Users |
+---------------------+
| id (PK) |
| username (UNIQUE) |
| email (UNIQUE) |
| password_hash |
| role (e.g., admin, |
| manager, user)|
| created_at |
| updated_at |
+----------+----------+
| 1
|
V *
+---------------------+ +---------------------+
| Vendors | | PurchaseOrders |
+---------------------+ +---------------------+
| id (PK) | | id (PK) |
| name (UNIQUE) | | po_number (UNIQUE) |
| address | | vendor_id (FK) |
| tax_id / vat_id | | order_date |
| contact_email | | total_amount |
| created_at | | currency |
| updated_at | | status (e.g., open, |
+----------+----------+ | closed) |
| 1 | created_at |
| | updated_at |
V * +----------+----------+
+---------------------+ | 1
| Invoices | |
+---------------------+ V *
| id (PK) | +---------------------+
| invoice_number | | PurchaseOrderItems |
| vendor_id (FK) | +---------------------+
| invoice_date | | id (PK) |
| due_date | | po_id (FK) |
| total_amount | | item_description |
| currency | | quantity |
| tax_amount | | unit_price |
| net_amount | | line_total |
| original_file_url | | created_at |
| extracted_text (TEXT)| +---------------------+
| status (e.g., pending|
| , approved, paid)|
| po_id (FK, nullable) |<------+ (Match: 0..1)
| created_at |
| updated_at |
+----------+----------+
| 1
|
V *
+---------------------+
| InvoiceItems |
+---------------------+
| id (PK) |
| invoice_id (FK) |
| item_description |
| quantity |
| unit_price |
| line_total |
| tax_rate (nullable) |
| created_at |
+---------------------+
^
| *
+----------+----------+
| ApprovalWorkflows |
+---------------------+
| id (PK) |
| invoice_id (FK) |
| approver_id (FK) |<------+ (User.id)
| status (e.g., pending|
| , approved, rejected)|
| comments |
| approved_at |
| created_at |
+---------------------+
+---------------------+
| AuditLogs |
+---------------------+
| id (PK) |
| user_id (FK, nullable)|<------+ (User.id)
| invoice_id (FK, nullable)|<------+ (Invoice.id)
| action (e.g., upload, |
| extract, approve)|
| details (JSON/TEXT) |
| timestamp |
+---------------------+
Belangrijkste Tabellen en Attributen
1. Users (Gebruikers)
Beheert gebruikers van het systeem met hun rollen en toegangsrechten.
-
id(Primary Key, INTEGER) -
username(VARCHAR, UNIQUE, NOT NULL) -
email(VARCHAR, UNIQUE, NOT NULL) -
password_hash(VARCHAR, NOT NULL): Opgeslagen hash van het wachtwoord. -
role(VARCHAR, NOT NULL): Bijv. 'admin', 'manager', 'accountant', 'user'. Bepaalt toegangsrechten. -
first_name(VARCHAR, nullable) -
last_name(VARCHAR, nullable) -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
2. Vendors (Leveranciers)
Informatie over de bedrijven die facturen sturen.
-
id(Primary Key, INTEGER) -
name(VARCHAR, UNIQUE, NOT NULL) -
address(TEXT, nullable) -
tax_id/vat_id(VARCHAR, UNIQUE, nullable): BTW-nummer. -
contact_email(VARCHAR, nullable) -
phone_number(VARCHAR, nullable) -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
3. Invoices (Facturen)
De hoofdtafel voor alle factuurgegevens.
-
id(Primary Key, INTEGER) -
invoice_number(VARCHAR, NOT NULL): Uniek factuurnummer van de leverancier. Vaak geïndexeerd. -
vendor_id(Foreign Key naarVendors.id, NOT NULL): Link naar de leverancier. -
invoice_date(DATE, NOT NULL) -
due_date(DATE, nullable) -
total_amount(DECIMAL(10, 2), NOT NULL) -
currency(VARCHAR(3), NOT NULL, DEFAULT 'EUR') -
tax_amount(DECIMAL(10, 2), nullable) -
net_amount(DECIMAL(10, 2), nullable) -
original_file_url(TEXT, NOT NULL): URL of pad naar de originele factuur (bijv. in S3). -
extracted_text(TEXT, nullable): Ruwe tekst geëxtraheerd door OCR/NLP voor debugging/audit. -
status(VARCHAR, NOT NULL, DEFAULT 'pending'): Bijv. 'pending', 'approved', 'rejected', 'paid', 'archived', 'manual_review_needed'. -
po_id(Foreign Key naarPurchaseOrders.id, nullable): Link naar een gekoppelde inkooporder. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL) -
processed_by_user_id(Foreign Key naarUsers.id, nullable): Wie heeft de factuur handmatig beoordeeld/gecorrigeerd?
4. InvoiceItems (Factuurregels)
Gedetailleerde regels van elke factuur.
-
id(Primary Key, INTEGER) -
invoice_id(Foreign Key naarInvoices.id, NOT NULL) -
item_description(TEXT, NOT NULL) -
quantity(DECIMAL(10, 2), NOT NULL) -
unit_price(DECIMAL(10, 2), NOT NULL) -
line_total(DECIMAL(10, 2), NOT NULL) -
tax_rate(DECIMAL(5, 2), nullable) -
created_at(TIMESTAMP, NOT NULL)
5. PurchaseOrders (Inkooporders)
Informatie over inkooporders, belangrijk voor 2- of 3-wegs matching.
-
id(Primary Key, INTEGER) -
po_number(VARCHAR, UNIQUE, NOT NULL) -
vendor_id(Foreign Key naarVendors.id, NOT NULL) -
order_date(DATE, NOT NULL) -
total_amount(DECIMAL(10, 2), NOT NULL) -
currency(VARCHAR(3), NOT NULL, DEFAULT 'EUR') -
status(VARCHAR, NOT NULL, DEFAULT 'open'): Bijv. 'open', 'partially_received', 'closed'. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
6. PurchaseOrderItems (Inkooporderregels)
Gedetailleerde regels van elke inkooporder.
-
id(Primary Key, INTEGER) -
po_id(Foreign Key naarPurchaseOrders.id, NOT NULL) -
item_description(TEXT, NOT NULL) -
quantity(DECIMAL(10, 2), NOT NULL) -
unit_price(DECIMAL(10, 2), NOT NULL) -
line_total(DECIMAL(10, 2), NOT NULL) -
created_at(TIMESTAMP, NOT NULL)
7. ApprovalWorkflows (Goedkeuringsworkflows)
Houdt de status en geschiedenis van goedkeuringsprocessen bij.
-
id(Primary Key, INTEGER) -
invoice_id(Foreign Key naarInvoices.id, NOT NULL) -
approver_id(Foreign Key naarUsers.id, NOT NULL): Wie moet goedkeuren of heeft goedgekeurd/afgewezen. -
status(VARCHAR, NOT NULL, DEFAULT 'pending'): Bijv. 'pending', 'approved', 'rejected'. -
comments(TEXT, nullable): Opmerkingen van de goedkeurder. -
approved_at(TIMESTAMP, nullable): Tijdstip van goedkeuring/afwijzing. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
8. AuditLogs (Auditlogboeken)
Voor het bijhouden van elke significante actie in het systeem.
-
id(Primary Key, INTEGER) -
user_id(Foreign Key naarUsers.id, nullable): Wie heeft de actie uitgevoerd? Nullable voor geautomatiseerde acties. -
invoice_id(Foreign Key naarInvoices.id, nullable): Op welke factuur had de actie betrekking? -
action(VARCHAR, NOT NULL): Beschrijving van de actie (bijv. 'invoice_uploaded', 'data_extracted', 'invoice_approved', 'data_corrected'). -
details(JSONB / TEXT, nullable): Aanvullende details over de actie (bijv. oude en nieuwe waarden bij een correctie). -
timestamp(TIMESTAMP, NOT NULL)
Relaties en Hun Betekenis
-
One-to-Many (1:N):
- Eén
Vendorkan meerdereInvoiceshebben. - Eén
Invoicekan meerdereInvoiceItemshebben. - Eén
PurchaseOrderkan meerderePurchaseOrderItemshebben. - Eén
Invoicekan meerdereApprovalWorkflowsdoorlopen (denk aan meerdere goedkeuringsstappen). - Eén
Userkan meerdereInvoiceshebben geüpload/verwerkt/goedgekeurd, en meerdereAuditLogshebben gegenereerd.
- Eén
-
One-to-One (1:1) of Zero-to-One (0:1):
- Eén
Invoicekan optioneel worden gekoppeld aan éénPurchaseOrder(viaInvoices.po_id). Dit is een 0:1 relatie, omdat niet elke factuur een inkooporder heeft.
- Eén
Belangrijke Overwegingen bij het Ontwerp
- Gegevensnormalisatie: Zorg ervoor dat gegevens correct zijn genormaliseerd om redundantie te verminderen en gegevensintegriteit te verbeteren.
-
Indizes: Voeg indizes toe aan Foreign Keys (FK's) en veelgebruikte zoekvelden (zoals
invoice_number,vendor_name,status) om de queryprestaties te verbeteren. -
Datum/Tijdstempels: Gebruik
created_atenupdated_atvoor auditdoeleinden en om de levenscyclus van gegevens bij te houden. - Statusvelden: Implementeer een duidelijk gedefinieerd statusworkflow voor facturen om hun voortgang door het systeem te volgen.
-
Foutafhandeling: Overweeg hoe ongeldige of onvolledige gegevens worden opgeslagen en behandeld (bijv. een
error_detailskolom inInvoicesof een aparteErrorLogtabel). -
Tekstuele Gegevens: De
extracted_textkolom kan groot zijn; overweeg of deze echt in de relationele database moet staan of beter extern kan worden opgeslagen (bijv. in object storage) met alleen een link in de database. GebruikTEXTofJSONB(voor PostgreSQL) voor flexibele opslag van ongestructureerde/semi-gestructureerde gegevens.
Dit datamodel vormt een solide basis voor het bouwen van een geautomatiseerd factuurverwerkingssysteem. Met behulp van een Python ORM (zoals SQLAlchemy of Django ORM) kun je deze tabellen eenvoudig als Python-objecten beheren en manipuleren.
Jazeker! Een goed ontworpen datamodel is de ruggengraat van elk robuust factuurverwerkingssysteem. Het zorgt voor gegevensintegriteit, efficiënte opslag en gemakkelijke rapportage. Hieronder schets ik een typisch datamodel met de belangrijkste tabellen, hun attributen en relaties, geschikt voor een relationele database zoals PostgreSQL of MySQL.
Overzicht van het Datamodel (Conceptueel ERD)
+---------------------+
| Users |
+---------------------+
| id (PK) |
| username (UNIQUE) |
| email (UNIQUE) |
| password_hash |
| role (e.g., admin, |
| manager, user)|
| created_at |
| updated_at |
+----------+----------+
| 1
|
V *
+---------------------+ +---------------------+
| Vendors | | PurchaseOrders |
+---------------------+ +---------------------+
| id (PK) | | id (PK) |
| name (UNIQUE) | | po_number (UNIQUE) |
| address | | vendor_id (FK) |
| tax_id / vat_id | | order_date |
| contact_email | | total_amount |
| created_at | | currency |
| updated_at | | status (e.g., open, |
+----------+----------+ | closed) |
| 1 | created_at |
| | updated_at |
V * +----------+----------+
+---------------------+ | 1
| Invoices | |
+---------------------+ V *
| id (PK) | +---------------------+
| invoice_number | | PurchaseOrderItems |
| vendor_id (FK) | +---------------------+
| invoice_date | | id (PK) |
| due_date | | po_id (FK) |
| total_amount | | item_description |
| currency | | quantity |
| tax_amount | | unit_price |
| net_amount | | line_total |
| original_file_url | | created_at |
| extracted_text (TEXT)| +---------------------+
| status (e.g., pending|
| , approved, paid)|
| po_id (FK, nullable) |<------+ (Match: 0..1)
| created_at |
| updated_at |
+----------+----------+
| 1
|
V *
+---------------------+
| InvoiceItems |
+---------------------+
| id (PK) |
| invoice_id (FK) |
| item_description |
| quantity |
| unit_price |
| line_total |
| tax_rate (nullable) |
| created_at |
+---------------------+
^
| *
+----------+----------+
| ApprovalWorkflows |
+---------------------+
| id (PK) |
| invoice_id (FK) |
| approver_id (FK) |<------+ (User.id)
| status (e.g., pending|
| , approved, rejected)|
| comments |
| approved_at |
| created_at |
+---------------------+
+---------------------+
| AuditLogs |
+---------------------+
| id (PK) |
| user_id (FK, nullable)|<------+ (User.id)
| invoice_id (FK, nullable)|<------+ (Invoice.id)
| action (e.g., upload, |
| extract, approve)|
| details (JSON/TEXT) |
| timestamp |
+---------------------+
Belangrijkste Tabellen en Attributen
1. Users (Gebruikers)
Beheert gebruikers van het systeem met hun rollen en toegangsrechten.
-
id(Primary Key, INTEGER) -
username(VARCHAR, UNIQUE, NOT NULL) -
email(VARCHAR, UNIQUE, NOT NULL) -
password_hash(VARCHAR, NOT NULL): Opgeslagen hash van het wachtwoord. -
role(VARCHAR, NOT NULL): Bijv. 'admin', 'manager', 'accountant', 'user'. Bepaalt toegangsrechten. -
first_name(VARCHAR, nullable) -
last_name(VARCHAR, nullable) -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
2. Vendors (Leveranciers)
Informatie over de bedrijven die facturen sturen.
-
id(Primary Key, INTEGER) -
name(VARCHAR, UNIQUE, NOT NULL) -
address(TEXT, nullable) -
tax_id/vat_id(VARCHAR, UNIQUE, nullable): BTW-nummer. -
contact_email(VARCHAR, nullable) -
phone_number(VARCHAR, nullable) -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
3. Invoices (Facturen)
De hoofdtafel voor alle factuurgegevens.
-
id(Primary Key, INTEGER) -
invoice_number(VARCHAR, NOT NULL): Uniek factuurnummer van de leverancier. Vaak geïndexeerd. -
vendor_id(Foreign Key naarVendors.id, NOT NULL): Link naar de leverancier. -
invoice_date(DATE, NOT NULL) -
due_date(DATE, nullable) -
total_amount(DECIMAL(10, 2), NOT NULL) -
currency(VARCHAR(3), NOT NULL, DEFAULT 'EUR') -
tax_amount(DECIMAL(10, 2), nullable) -
net_amount(DECIMAL(10, 2), nullable) -
original_file_url(TEXT, NOT NULL): URL of pad naar de originele factuur (bijv. in S3). -
extracted_text(TEXT, nullable): Ruwe tekst geëxtraheerd door OCR/NLP voor debugging/audit. -
status(VARCHAR, NOT NULL, DEFAULT 'pending'): Bijv. 'pending', 'approved', 'rejected', 'paid', 'archived', 'manual_review_needed'. -
po_id(Foreign Key naarPurchaseOrders.id, nullable): Link naar een gekoppelde inkooporder. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL) -
processed_by_user_id(Foreign Key naarUsers.id, nullable): Wie heeft de factuur handmatig beoordeeld/gecorrigeerd?
4. InvoiceItems (Factuurregels)
Gedetailleerde regels van elke factuur.
-
id(Primary Key, INTEGER) -
invoice_id(Foreign Key naarInvoices.id, NOT NULL) -
item_description(TEXT, NOT NULL) -
quantity(DECIMAL(10, 2), NOT NULL) -
unit_price(DECIMAL(10, 2), NOT NULL) -
line_total(DECIMAL(10, 2), NOT NULL) -
tax_rate(DECIMAL(5, 2), nullable) -
created_at(TIMESTAMP, NOT NULL)
5. PurchaseOrders (Inkooporders)
Informatie over inkooporders, belangrijk voor 2- of 3-wegs matching.
-
id(Primary Key, INTEGER) -
po_number(VARCHAR, UNIQUE, NOT NULL) -
vendor_id(Foreign Key naarVendors.id, NOT NULL) -
order_date(DATE, NOT NULL) -
total_amount(DECIMAL(10, 2), NOT NULL) -
currency(VARCHAR(3), NOT NULL, DEFAULT 'EUR') -
status(VARCHAR, NOT NULL, DEFAULT 'open'): Bijv. 'open', 'partially_received', 'closed'. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
6. PurchaseOrderItems (Inkooporderregels)
Gedetailleerde regels van elke inkooporder.
-
id(Primary Key, INTEGER) -
po_id(Foreign Key naarPurchaseOrders.id, NOT NULL) -
item_description(TEXT, NOT NULL) -
quantity(DECIMAL(10, 2), NOT NULL) -
unit_price(DECIMAL(10, 2), NOT NULL) -
line_total(DECIMAL(10, 2), NOT NULL) -
created_at(TIMESTAMP, NOT NULL)
7. ApprovalWorkflows (Goedkeuringsworkflows)
Houdt de status en geschiedenis van goedkeuringsprocessen bij.
-
id(Primary Key, INTEGER) -
invoice_id(Foreign Key naarInvoices.id, NOT NULL) -
approver_id(Foreign Key naarUsers.id, NOT NULL): Wie moet goedkeuren of heeft goedgekeurd/afgewezen. -
status(VARCHAR, NOT NULL, DEFAULT 'pending'): Bijv. 'pending', 'approved', 'rejected'. -
comments(TEXT, nullable): Opmerkingen van de goedkeurder. -
approved_at(TIMESTAMP, nullable): Tijdstip van goedkeuring/afwijzing. -
created_at(TIMESTAMP, NOT NULL) -
updated_at(TIMESTAMP, NOT NULL)
8. AuditLogs (Auditlogboeken)
Voor het bijhouden van elke significante actie in het systeem.
-
id(Primary Key, INTEGER) -
user_id(Foreign Key naarUsers.id, nullable): Wie heeft de actie uitgevoerd? Nullable voor geautomatiseerde acties. -
invoice_id(Foreign Key naarInvoices.id, nullable): Op welke factuur had de actie betrekking? -
action(VARCHAR, NOT NULL): Beschrijving van de actie (bijv. 'invoice_uploaded', 'data_extracted', 'invoice_approved', 'data_corrected'). -
details(JSONB / TEXT, nullable): Aanvullende details over de actie (bijv. oude en nieuwe waarden bij een correctie). -
timestamp(TIMESTAMP, NOT NULL)
Relaties en Hun Betekenis
-
One-to-Many (1:N):
- Eén
Vendorkan meerdereInvoiceshebben. - Eén
Invoicekan meerdereInvoiceItemshebben. - Eén
PurchaseOrderkan meerderePurchaseOrderItemshebben. - Eén
Invoicekan meerdereApprovalWorkflowsdoorlopen (denk aan meerdere goedkeuringsstappen). - Eén
Userkan meerdereInvoiceshebben geüpload/verwerkt/goedgekeurd, en meerdereAuditLogshebben gegenereerd.
- Eén
-
One-to-One (1:1) of Zero-to-One (0:1):
- Eén
Invoicekan optioneel worden gekoppeld aan éénPurchaseOrder(viaInvoices.po_id). Dit is een 0:1 relatie, omdat niet elke factuur een inkooporder heeft.
- Eén
Belangrijke Overwegingen bij het Ontwerp
- Gegevensnormalisatie: Zorg ervoor dat gegevens correct zijn genormaliseerd om redundantie te verminderen en gegevensintegriteit te verbeteren.
-
Indizes: Voeg indizes toe aan Foreign Keys (FK's) en veelgebruikte zoekvelden (zoals
invoice_number,vendor_name,status) om de queryprestaties te verbeteren. -
Datum/Tijdstempels: Gebruik
created_atenupdated_atvoor auditdoeleinden en om de levenscyclus van gegevens bij te houden. - Statusvelden: Implementeer een duidelijk gedefinieerd statusworkflow voor facturen om hun voortgang door het systeem te volgen.
-
Foutafhandeling: Overweeg hoe ongeldige of onvolledige gegevens worden opgeslagen en behandeld (bijv. een
error_detailskolom inInvoicesof een aparteErrorLogtabel). -
Tekstuele Gegevens: De
extracted_textkolom kan groot zijn; overweeg of deze echt in de relationele database moet staan of beter extern kan worden opgeslagen (bijv. in object storage) met alleen een link in de database. GebruikTEXTofJSONB(voor PostgreSQL) voor flexibele opslag van ongestructureerde/semi-gestructureerde gegevens.
Dit datamodel vormt een solide basis voor het bouwen van een geautomatiseerd factuurverwerkingssysteem. Met behulp van een Python ORM (zoals SQLAlchemy of Django ORM) kun je deze tabellen eenvoudig als Python-objecten beheren en manipuleren.
DOLCE & GABBANA
Prachtige, gloednieuwe, 100% authentieke Dolce & Gabbana das, gemaakt van luxe zijde. Het verstelbare ontwerp zorgt voor een perfecte pasvorm en voegt een geraffineerde Italiaanse flair toe aan zowel formele als eigentijdse looks.
Model: Das
Kleur: Zwart, Beige
Materiaal: 100% Zijde
Breedte: 6 cm
Bevestiging: Geknoopt
Logodetails
Gemaakt in Italië
DOLCE & GABBANA
Prachtige, gloednieuwe, 100% authentieke Dolce & Gabbana das, gemaakt van luxe zijde. Het verstelbare ontwerp zorgt voor een perfecte pasvorm en voegt een geraffineerde Italiaanse flair toe aan zowel formele als eigentijdse looks.
Model: Das
Kleur: Zwart, Beige
Materiaal: 100% Zijde
Breedte: 6 cm
Bevestiging: Geknoopt
Logodetails
Gemaakt in Italië
Delen
