PaintCo Invoice Classifier — system architecture and API reference
Single Python server using Flask. No external AI APIs — all classification is done locally with rule-based keyword matching.
classifier.py uses pdfplumber for PDF text extraction and keyword matching for classification. No LLM, no external API calls.
extract_pdf_text() — extracts text from PDF using pdfplumberdetect_format() — identifies supplier from text (wattyl / dulux / haymes / resene / unknown)_parse_wattyl() / _parse_dulux() / _parse_haymes() / _parse_resene() — supplier-specific table parser_parse_unknown() — generic fallback for unrecognised formats_classify_item() — checks description against PAINT_KEYWORDS and MATERIALS_KEYWORDS_confidence_check() — flags items with Review status based on configurable thresholdEach supplier has its own parser due to different table layouts. To add a new supplier: implement a _parse_NEWSUPPLIER() function, register it in the parsers dict inside classify_invoice(), and add detection to detect_format().
email_monitor.py supports two modes:
Mail.Read application permission.Polling runs in a background daemon thread. Configurable interval (default 60s). Found emails are processed then moved to a "Processed" folder. Duplicate detection uses a persistent set of processed message IDs.
sharepoint_client.py uses Microsoft Graph API with MSAL client credentials flow.
Required Graph permissions:
Sites.ReadWrite.All — read/write files in SharePointMail.Read — (for email monitoring) read mailbox messagesThe Excel register is updated locally with openpyxl and uploaded to SharePoint. Uses PUT /drives/{id}/items/{id}/content for updates. Upload retries up to 5 times with a 3s delay if the file is locked (HTTP 423).
pip install flask pdfplumber openpyxl msal export PORT=5000 python3 server.py