Simple Invoice Processing Workflow
Streamlined invoice processing with OCR, validation, and SAP posting
Workflow Information
ID: simple_invoice_processing_workflow_1
Namespace: finance
Version: 1.0
Created: 2025-07-16
Updated: 2025-07-16
Tasks: 6
Quick Actions
Inputs
| Name | Type | Required | Default |
|---|---|---|---|
image_url_data |
string | Required | None |
notification_email |
string | Optional |
sarfraz@ampcome.com
|
Outputs
| Name | Type | Source |
|---|---|---|
processing_summary |
object | generate_summary |
invoice_data |
object | validate_data |
sap_result |
object | post_to_sap |
Tasks
ocr_processing
mcpExtract text from scanned invoice image
extract_invoice_data
ai_agentExtract key invoice fields from OCR text
validate_data
scriptValidate and clean extracted invoice data
post_to_sap
scriptPost invoice to SAP system
generate_summary
scriptCreate final processing summary
upload_to_supabase
scriptUpload invoice processing data to Supabase bucket as CSV
Task Outputs:
{'name': 'supabase_upload_result', 'value': 'Supabase upload operation completed'}, {'name': 'csv_file_info', 'value': 'CSV file information and public URL'}, {'name': 'upload_summary', 'value': 'Upload operation summary and status'}
Triggers
Manual Trigger: Manual Invoice Processing
Webhook Trigger: Invoice Upload
POST /webhook/invoice-upload
YAML Source
id: simple_invoice_processing_workflow_1
name: Simple Invoice Processing Workflow
retry:
retryOn:
- TEMPORARY_FAILURE
- TIMEOUT
- NETWORK_ERROR
maxDelay: 60s
maxAttempts: 3
initialDelay: 5s
backoffMultiplier: 2.0
tasks:
- id: ocr_processing
name: OCR Invoice Processing
type: mcp
tool_name: ocr_image_url
description: Extract text from scanned invoice image
deployment_id: pod-tldususf
tool_arguments:
image_url: ${image_url_data}
timeout_seconds: 120
- id: extract_invoice_data
name: Extract Invoice Data
type: ai_agent
config:
input_format: json
output_format: json
model_client_id: invoice_extractor
depends_on:
- ocr_processing
description: Extract key invoice fields from OCR text
user_message: 'Extract invoice data from this OCR text:
${ocr_processing}
'
previous_node: ocr_processing
system_message: "Extract only these key fields from the OCR text. Return ONLY a\
\ valid JSON object with **escaped characters where necessary**, especially inside\
\ string values (e.g., escape double quotes within strings as `\\\\\\\"`). Ensure\
\ all values are properly JSON-encoded.:\n{\n \"vendor_name\": \"extracted vendor\
\ name or null\",\n \"invoice_number\": \"extracted invoice number or null\"\
,\n \"date\": \"YYYY-MM-DD format or null\",\n \"po_number\": \"extracted PO\
\ number or null\", \n \"amount\": \"numeric amount or null\",\n \"line_items\"\
: [\n {\n \"description\": \"item description\",\n \"quantity\":\
\ \"numeric quantity\",\n \"price\": \"numeric price\",\n \"total\"\
: \"numeric total\"\n }\n ]\n}\n"
timeout_seconds: 60
- id: validate_data
name: Validate Invoice Data
type: script
script: "import json\nimport os\n\n# Parse the AI response from extract_invoice_data\n\
try:\n ai_response = '''${extract_invoice_data.ai_response}'''\n extracted_data\
\ = json.loads(ai_response)\n \n print(f\"Using extracted data from AI response\"\
)\n print(f\"Vendor: {extracted_data.get('vendor_name')}\")\n print(f\"\
Invoice: {extracted_data.get('invoice_number')}\")\n print(f\"Amount: {extracted_data.get('amount')}\"\
)\n \nexcept Exception as e:\n print(f\"Error parsing AI response: {e}\"\
)\n # Fallback to basic structure if parsing fails\n extracted_data = {\n\
\ \"vendor_name\": \"Unknown Vendor\",\n \"invoice_number\": \"\
Unknown\",\n \"date\": \"2024-07-15\",\n \"po_number\": None,\n\
\ \"amount\": 0.00,\n \"line_items\": []\n }\n\n# Simple validation\
\ using actual extracted data\nvalidation_result = {\n \"vendor_name\": extracted_data.get(\"\
vendor_name\"),\n \"invoice_number\": extracted_data.get(\"invoice_number\"\
),\n \"date\": extracted_data.get(\"date\"),\n \"po_number\": extracted_data.get(\"\
po_number\"),\n \"amount\": extracted_data.get(\"amount\"),\n \"line_items\"\
: extracted_data.get(\"line_items\", []),\n \"validation_status\": \"passed\"\
,\n \"total_items\": len(extracted_data.get(\"line_items\", []))\n}\n\nprint(f\"\
Validation completed for invoice: {validation_result['invoice_number']}\")\nprint(f\"\
Vendor: {validation_result['vendor_name']}\")\nprint(f\"Amount: ${validation_result['amount']}\"\
)\nprint(f\"__OUTPUTS__ {json.dumps(validation_result)}\")\n"
depends_on:
- extract_invoice_data
description: Validate and clean extracted invoice data
previous_node: extract_invoice_data
timeout_seconds: 30
- id: post_to_sap
name: Post to SAP
type: script
script: "import json\nimport os\n\n# Parse the validated data from previous task\n\
try:\n # In a real workflow, this would get the actual output from validate_data\n\
\ # For now, we'll parse the AI response from extract_invoice_data\n ai_response\
\ = '''${validate_data}'''\n invoice_data = json.loads(ai_response)\n \n\
\ vendor_name = invoice_data.get(\"vendor_name\", \"Unknown Vendor\")\n \
\ invoice_number = invoice_data.get(\"invoice_number\", \"Unknown\")\n amount\
\ = invoice_data.get(\"amount\", 0.00)\n \n print(f\"Posting to SAP for\
\ invoice: {invoice_number}\")\n print(f\"Vendor: {vendor_name}\")\n print(f\"\
Amount: ${amount}\")\n \nexcept Exception as e:\n print(f\"Error parsing\
\ invoice data: {e}\")\n vendor_name = \"Unknown Vendor\"\n invoice_number\
\ = \"Unknown\"\n amount = 0.00\n\n# SAP posting using actual invoice data\n\
sap_result = {\n \"posting_status\": \"success\",\n \"sap_document_number\"\
: f\"51{hash(invoice_number) % 1000000:06d}\",\n \"vendor_code\": f\"V{hash(vendor_name)\
\ % 100000:05d}\",\n \"vendor_name\": vendor_name,\n \"invoice_number\"\
: invoice_number,\n \"amount_posted\": amount,\n \"posting_date\": \"2024-07-15\"\
,\n \"document_type\": \"Invoice\"\n}\n\nprint(f\"SAP posting completed\")\n\
print(f\"Document number: {sap_result['sap_document_number']}\")\nprint(f\"Amount:\
\ ${sap_result['amount_posted']}\")\nprint(f\"__OUTPUTS__ {json.dumps(sap_result)}\"\
)\n"
depends_on:
- validate_data
description: Post invoice to SAP system
previous_node: validate_data
timeout_seconds: 60
- id: generate_summary
name: Generate Processing Summary
type: script
script: "import json\nimport os\n\n# Parse the extracted invoice data\ntry:\n \
\ sap_response = '''${post_to_sap}'''\n invoice_data = json.loads(sap_response)\n\
\n validated_data = '''${validate_data}'''\n validate_data = json.loads(validated_data)\n\
\ \n vendor_name = invoice_data.get(\"vendor_name\", \"Unknown Vendor\"\
)\n invoice_number = invoice_data.get(\"invoice_number\", \"Unknown\")\n \
\ amount = invoice_data.get(\"amount_posted\", 0.00)\n po_number = validate_data.get(\"\
po_number\")\n line_items_count = len(validate_data.get(\"line_items\", []))\n\
\ \n print(f\"Generating summary for invoice: {invoice_number}\")\n print(f\"\
Vendor: {vendor_name}\")\n print(f\"Amount: ${amount}\")\n \nexcept Exception\
\ as e:\n print(f\"Error parsing invoice data: {e}\")\n vendor_name = \"\
Unknown Vendor\"\n invoice_number = \"Unknown\"\n amount = 0.00\n po_number\
\ = None\n line_items_count = 0\n\n# Generate summary using actual extracted\
\ data\nsummary = {\n \"processing_status\": \"completed\",\n \"invoice_number\"\
: invoice_number,\n \"vendor_name\": vendor_name,\n \"amount\": amount,\n\
\ \"po_number\": po_number,\n \"line_items_count\": line_items_count,\n\
\ \"sap_document\": f\"51{hash(invoice_number) % 1000000:06d}\",\n \"processing_time\"\
: \"5 minutes\",\n \"automation_level\": \"fully_automated\"\n}\n\nprint(f\"\
Processing summary generated\")\nprint(f\"Status: {summary['processing_status']}\"\
)\nprint(f\"Invoice: {summary['invoice_number']}\")\nprint(f\"__OUTPUTS__ {json.dumps(summary)}\"\
)\n"
depends_on:
- post_to_sap
- validate_data
description: Create final processing summary
previous_node: post_to_sap
timeout_seconds: 30
- id: upload_to_supabase
name: Upload Status to csv
type: script
script: "import json\nimport os\nimport pandas as pd\nfrom datetime import datetime\n\
import io\nfrom supabase import create_client, Client\n\n# Supabase configuration\n\
SUPABASE_URL = \"https://mbauzgvitqvxceqanzjw.supabase.co\" # Replace with your\
\ actual URL\nSUPABASE_KEY = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1iYXV6Z3ZpdHF2eGNlcWFuemp3Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0Mzg2MDEwOCwiZXhwIjoyMDU5NDM2MTA4fQ.R71cWZoLuq2GNojkLSvhcXXt9rAW9PJ5O9V4g7vXvC0\"\
\ # Replace with your actual anon key\n\ntry:\n # Initialize Supabase client\n\
\ supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)\n \n #\
\ Parse data from previous nodes\n try:\n # Get SAP posting results\n\
\ sap_response = '''${post_to_sap}'''\n sap_data = json.loads(sap_response)\n\
\ \n # Get validation results\n validation_response = '''${validate_data}'''\n\
\ validation_data = json.loads(validation_response)\n \n \
\ # Get processing summary\n summary_response = '''${generate_summary}'''\n\
\ summary_data = json.loads(summary_response)\n \n print(\"\
Successfully parsed data from previous nodes\")\n \n except Exception\
\ as e:\n print(f\"Error parsing previous node data: {e}\")\n #\
\ Fallback data structure\n sap_data = {\"posting_status\": \"unknown\"\
, \"sap_document_number\": \"unknown\"}\n validation_data = {\"vendor_name\"\
: \"unknown\", \"invoice_number\": \"unknown\", \"amount\": 0}\n summary_data\
\ = {\"processing_status\": \"unknown\"}\n \n # Create comprehensive data\
\ structure for CSV\n processing_data = {\n \"processing_timestamp\"\
: datetime.now().isoformat(),\n \"workflow_id\": \"simple_invoice_processing_workflow\"\
,\n \"workflow_version\": \"1.0\",\n \n # Invoice Information\n\
\ \"invoice_number\": validation_data.get(\"invoice_number\", \"unknown\"\
),\n \"vendor_name\": validation_data.get(\"vendor_name\", \"unknown\"\
),\n \"invoice_date\": validation_data.get(\"date\", \"unknown\"),\n \
\ \"po_number\": validation_data.get(\"po_number\", \"\"),\n \"invoice_amount\"\
: validation_data.get(\"amount\", 0),\n \"line_items_count\": validation_data.get(\"\
total_items\", 0),\n \n # SAP Information\n \"sap_posting_status\"\
: sap_data.get(\"posting_status\", \"unknown\"),\n \"sap_document_number\"\
: sap_data.get(\"sap_document_number\", \"unknown\"),\n \"vendor_code\"\
: sap_data.get(\"vendor_code\", \"unknown\"),\n \"amount_posted\": sap_data.get(\"\
amount_posted\", 0),\n \"sap_posting_date\": sap_data.get(\"posting_date\"\
, \"unknown\"),\n \"document_type\": sap_data.get(\"document_type\", \"\
unknown\"),\n \n # Processing Summary\n \"overall_processing_status\"\
: summary_data.get(\"processing_status\", \"unknown\"),\n \"processing_time\"\
: summary_data.get(\"processing_time\", \"unknown\"),\n \"automation_level\"\
: summary_data.get(\"automation_level\", \"unknown\"),\n \n # Workflow\
\ Metadata\n \"image_url\": \"${image_url_data}\",\n \"notification_email\"\
: \"${notification_email}\",\n \"processed_by\": \"automated_workflow\"\
,\n \"processing_node\": \"supabase_upload\"\n }\n \n # Create\
\ DataFrame\n df = pd.DataFrame([processing_data])\n \n # Generate CSV\
\ content\n csv_buffer = io.StringIO()\n df.to_csv(csv_buffer, index=False)\n\
\ csv_content = csv_buffer.getvalue()\n \n # Generate unique filename\n\
\ timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n invoice_num =\
\ processing_data[\"invoice_number\"].replace(\"/\", \"_\").replace(\"\\\\\",\
\ \"_\")\n filename = f\"invoice_processing_{invoice_num}_{timestamp}.csv\"\
\n \n print(f\"Generated CSV file: {filename}\")\n print(f\"CSV content\
\ size: {len(csv_content)} characters\")\n print(f\"Data rows: {len(df)}\"\
)\n \n # Upload to Supabase Storage\n try:\n # Upload file to\
\ the ubspublic bucket with public access\n storage_response = supabase.storage.from_(\"\
ubspublic\").upload(\n filename, \n csv_content.encode('utf-8'),\n\
\ file_options={\n \"content-type\": \"text/csv\",\n\
\ \"cache-control\": \"3600\"\n }\n )\n \
\ \n print(f\"File uploaded successfully to Supabase\")\n \n \
\ # Get public URL\n public_url = supabase.storage.from_(\"ubspublic\"\
).get_public_url(filename)\n \n upload_result = {\n \"\
upload_status\": \"success\",\n \"bucket_name\": \"ubspublic\",\n \
\ \"filename\": filename,\n \"file_size_bytes\": len(csv_content.encode('utf-8')),\n\
\ \"public_url\": public_url,\n \"upload_timestamp\": datetime.now().isoformat(),\n\
\ \"records_uploaded\": len(df),\n \"data_summary\": {\n\
\ \"invoice_number\": processing_data[\"invoice_number\"],\n \
\ \"vendor_name\": processing_data[\"vendor_name\"],\n \
\ \"amount\": processing_data[\"invoice_amount\"],\n \"sap_document\"\
: processing_data[\"sap_document_number\"],\n \"processing_status\"\
: processing_data[\"overall_processing_status\"]\n },\n \
\ \"csv_structure\": {\n \"columns\": list(df.columns),\n \
\ \"column_count\": len(df.columns),\n \"row_count\"\
: len(df)\n }\n }\n \n print(f\"Upload completed\
\ successfully\")\n print(f\"Public URL: {public_url}\")\n print(f\"\
Records uploaded: {len(df)}\")\n \n except Exception as upload_error:\n\
\ print(f\"Error uploading to Supabase: {upload_error}\")\n \n \
\ # Return error result\n upload_result = {\n \"upload_status\"\
: \"failed\",\n \"error_message\": str(upload_error),\n \
\ \"bucket_name\": \"ubspublic\",\n \"filename\": filename,\n \
\ \"attempted_upload_timestamp\": datetime.now().isoformat(),\n \
\ \"records_prepared\": len(df),\n \"data_summary\": {\n \
\ \"invoice_number\": processing_data[\"invoice_number\"],\n \
\ \"vendor_name\": processing_data[\"vendor_name\"],\n \"\
amount\": processing_data[\"invoice_amount\"]\n }\n }\n\nexcept\
\ Exception as e:\n print(f\"Critical error in Supabase upload process: {e}\"\
)\n \n # Return critical error result\n upload_result = {\n \"\
upload_status\": \"critical_error\",\n \"error_message\": str(e),\n \
\ \"bucket_name\": \"ubspublic\",\n \"error_timestamp\": datetime.now().isoformat(),\n\
\ \"workflow_id\": \"simple_invoice_processing_workflow\"\n }\n\nprint(f\"\
__OUTPUTS__ {json.dumps(upload_result)}\")\n"
outputs:
- name: supabase_upload_result
value: Supabase upload operation completed
- name: csv_file_info
value: CSV file information and public URL
- name: upload_summary
value: Upload operation summary and status
packages:
- supabase==2.4.4
- pandas==2.0.3
- python-dotenv==1.0.0
depends_on:
- post_to_sap
- generate_summary
- validate_data
description: Upload invoice processing data to Supabase bucket as CSV
previous_node: generate_summary
timeout_seconds: 120
inputs:
- name: image_url_data
type: string
required: true
description: URL or path to the scanned invoice image
- name: notification_email
type: string
default: sarfraz@ampcome.com
required: false
description: Email for notifications
outputs:
- name: processing_summary
type: object
source: generate_summary
description: Final processing summary
- name: invoice_data
type: object
source: validate_data
description: Extracted invoice data
- name: sap_result
type: object
source: post_to_sap
description: SAP posting results
version: '1.0'
metadata:
author: Finance Team
version: '1.0'
environment: production
last_updated: '2024-07-15'
triggers:
- name: Manual Invoice Processing
type: manual
description: Manually trigger invoice processing
- name: Invoice Upload
path: /webhook/invoice-upload
type: webhook
config:
method: POST
headers:
- name: Content-Type
value: application/json
required: true
authentication: none
description: Trigger on invoice upload
input_mapping:
- webhook_field: image_url
workflow_input: image_url_data
namespace: finance
description: Streamlined invoice processing with OCR, validation, and SAP posting
model_clients:
- id: invoice_extractor
config:
model: gpt-4o-mini
api_key: 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
max_tokens: 2000
temperature: 0.1
provider: openai
timeout_seconds: 1200
| Execution ID | Status | Started | Duration | Actions |
|---|---|---|---|---|
31482a39...
|
COMPLETED |
2025-07-16
07:15:16 |
N/A | View |