agdar/ZATCA_QUICK_REFERENCE.md
2025-11-02 14:35:35 +03:00

10 KiB

ZATCA E-Invoice Quick Reference Guide

🚀 Getting Started (5 Minutes)

Step 1: Run Migrations

python3 manage.py migrate

Step 2: Configure Your Tenant

# In Django shell or admin
from core.models import Tenant

tenant = Tenant.objects.first()
tenant.vat_number = "300000000000003"  # Your actual VAT number
tenant.name_ar = "مركز أغدار"
tenant.address = "123 Main Street, Riyadh"
tenant.city = "Riyadh"
tenant.postal_code = "12345"
tenant.save()

Step 3: Create Your First Invoice

from finance.models import Invoice, InvoiceLineItem
from core.models import Patient
from datetime import date, timedelta

# Get patient
patient = Patient.objects.first()

# Create invoice
invoice = Invoice.objects.create(
    tenant=tenant,
    patient=patient,
    invoice_type='SIMPLIFIED',  # B2C
    issue_date=date.today(),
    due_date=date.today() + timedelta(days=30),
    subtotal=100.00,
    tax=15.00,  # 15% VAT
    total=115.00,
    status='ISSUED'
)

# Add line item
InvoiceLineItem.objects.create(
    invoice=invoice,
    description="Medical Consultation",
    quantity=1,
    unit_price=100.00,
    total=100.00
)

# Check auto-generated fields
print(f"✅ Invoice Counter: {invoice.invoice_counter}")
print(f"✅ Invoice Hash: {invoice.invoice_hash[:32]}...")
print(f"✅ QR Code: {invoice.qr_code[:50]}...")

📋 Invoice Types Guide

Type Code When to Use ZATCA Process
Standard STANDARD B2B transactions Clearance (real-time)
Simplified SIMPLIFIED B2C transactions Reporting (24 hours)
Standard Credit STANDARD_CREDIT B2B refunds Clearance
Standard Debit STANDARD_DEBIT B2B adjustments Clearance
Simplified Credit SIMPLIFIED_CREDIT B2C refunds Reporting
Simplified Debit SIMPLIFIED_DEBIT B2C adjustments Reporting

🔄 ZATCA Submission Workflows

Workflow 1: Standard Invoice (B2B)

# 1. Create invoice
invoice = Invoice.objects.create(
    invoice_type='STANDARD',
    # ... other fields
)

# 2. Submit for clearance (BEFORE sharing with buyer)
from finance.zatca_tasks import submit_invoice_to_zatca
result = submit_invoice_to_zatca.delay(str(invoice.id), use_sandbox=True)

# 3. Check status
invoice.refresh_from_db()
if invoice.zatca_status == 'CLEARED':
    # 4. Share cleared invoice with buyer
    # Invoice now has ZATCA stamp and updated QR code
    pass

Workflow 2: Simplified Invoice (B2C)

# 1. Create invoice
invoice = Invoice.objects.create(
    invoice_type='SIMPLIFIED',
    # ... other fields
)

# 2. Share with customer immediately (QR code already on invoice)

# 3. Report to ZATCA within 24 hours (automatic via Celery)
# Or manually:
from finance.zatca_tasks import submit_invoice_to_zatca
result = submit_invoice_to_zatca.delay(str(invoice.id), use_sandbox=True)

Workflow 3: Credit Note

# 1. Create credit note referencing original invoice
credit_note = Invoice.objects.create(
    invoice_type='SIMPLIFIED_CREDIT',  # or STANDARD_CREDIT
    billing_reference_id=original_invoice.invoice_number,
    billing_reference_issue_date=original_invoice.issue_date,
    subtotal=-100.00,  # Negative amounts
    tax=-15.00,
    total=-115.00,
    # ... other fields
)

# 2. Submit to ZATCA (same process as regular invoice)

🔍 Checking Invoice Status

Via Django Admin:

  1. Go to Finance → Invoices
  2. Look at "ZATCA Status" column:
    • CLEARED - Standard invoice approved by ZATCA
    • REPORTED - Simplified invoice reported to ZATCA
    • FAILED - Submission failed (check zatca_response)
    • (empty) - Not yet submitted

Via Code:

invoice = Invoice.objects.get(invoice_number='INV-001-2025-12345')

print(f"Status: {invoice.zatca_status}")
print(f"Submitted: {invoice.zatca_submission_date}")
print(f"Response: {invoice.zatca_response}")

🛠️ Common Tasks

Generate PDF Invoice

from finance.pdf_service import PDFService

pdf_bytes = PDFService.generate_invoice_pdf(invoice)

# Save to file
with open(f'invoice_{invoice.invoice_number}.pdf', 'wb') as f:
    f.write(pdf_bytes)

# Or return as HTTP response
from django.http import HttpResponse
response = HttpResponse(pdf_bytes, content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="invoice_{invoice.invoice_number}.pdf"'
return response

Generate XML

from finance.zatca_service import ZATCAService

zatca = ZATCAService(use_sandbox=True)
xml = zatca.generate_xml_invoice(invoice)

# Save to invoice
invoice.xml_content = xml
invoice.save()

Validate QR Code

# QR code is automatically generated
qr_code = invoice.qr_code

# Decode to verify
import base64
decoded = base64.b64decode(qr_code)

# First byte is tag 1 (Seller's Name)
# Second byte is length
# Following bytes are the value

Check Invoice Sequence

from finance.csid_manager import InvoiceCounterManager

is_valid, gaps = InvoiceCounterManager.validate_counter_sequence(tenant)

if not is_valid:
    print(f"⚠️ Gaps found in sequence: {gaps}")
else:
    print("✅ Invoice sequence is valid")

🔐 CSID Management

Check Active CSID

from finance.csid_manager import CSIDManager

csid = CSIDManager.get_active_csid(tenant)

if csid:
    print(f"✅ Active CSID: {csid.common_name}")
    print(f"📅 Expires in: {csid.days_until_expiry} days")
    print(f"📊 Invoices signed: {csid.invoices_signed}")
else:
    print("⚠️ No active CSID - please onboard via FATOORA portal")

Check if Renewal Needed

needs_renewal, message = CSIDManager.check_expiry_and_renew(tenant)

if needs_renewal:
    print(f"⚠️ {message}")
else:
    print("✅ CSID is valid")

🔧 Troubleshooting

Issue: Invoice counter not incrementing

Solution: Check that transaction locking is working

# Counter should auto-increment with SELECT FOR UPDATE lock
# Check last invoice counter
last = Invoice.objects.filter(tenant=tenant).order_by('-invoice_counter').first()
print(f"Last counter: {last.invoice_counter if last else 'None'}")

Issue: QR code not generating

Solution: Check that invoice has all required fields

# QR code requires:
# - Tenant name
# - Tenant VAT number
# - Issue date and time
# - Total and tax amounts

# Verify tenant has VAT number
print(f"VAT Number: {invoice.tenant.vat_number}")

Issue: ZATCA submission failing

Solution: Check CSID and error response

# Check CSID
csid = CSIDManager.get_active_csid(tenant)
print(f"CSID valid: {csid.is_valid if csid else 'No CSID'}")

# Check error response
print(f"Error: {invoice.zatca_response}")

Issue: Hash chain broken

Solution: Validate sequence

# Check for gaps
is_valid, gaps = InvoiceCounterManager.validate_counter_sequence(tenant)
print(f"Sequence valid: {is_valid}")
print(f"Gaps: {gaps}")

📊 Monitoring & Reports

Daily Compliance Check

from finance.zatca_tasks import monitor_zatca_compliance

# Run compliance monitoring
report = monitor_zatca_compliance.delay()

# Check results
print(report.get())

Generate Compliance Report

from finance.zatca_tasks import generate_compliance_report

report = generate_compliance_report.delay(
    tenant_id=str(tenant.id),
    start_date='2025-01-01',
    end_date='2025-01-31'
)

print(report.get())

Retry Failed Submissions

from finance.zatca_tasks import retry_failed_submissions

# Retry all failed invoices for tenant
results = retry_failed_submissions.delay(tenant_id=str(tenant.id))

print(f"Retried: {results.get()}")

🎯 Best Practices

1. Invoice Creation

Always set correct invoice_type (STANDARD vs SIMPLIFIED) Let counter auto-increment (don't set manually) Verify patient nationality for VAT calculation Add line items before finalizing

2. ZATCA Submission

Standard invoices: Clear BEFORE sharing with buyer Simplified invoices: Share immediately, report within 24h Monitor zatca_status field Handle failures gracefully

3. Credit/Debit Notes

Always reference original invoice Use negative amounts for credits Follow same submission process as original

4. CSID Management

Monitor expiry dates (renew 30 days before) Keep one active CSID per tenant Revoke compromised CSIDs immediately Track usage statistics


📱 API Endpoints Reference

Sandbox (Testing):

  • Base: https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation
  • Clearance: /invoices/clearance/single
  • Reporting: /invoices/reporting/single
  • Compliance CSID: /compliance
  • Production CSID: /production/csids

Production:

  • Base: https://gw-fatoora.zatca.gov.sa/e-invoicing/core
  • Same endpoints as sandbox

🔑 Environment Variables

Add to your .env file:

# ZATCA Configuration
ZATCA_USE_SANDBOX=True  # Set to False for production
ZATCA_VAT_NUMBER=300000000000003
ENCRYPTION_KEY=your-32-byte-encryption-key-here

# Optional
ZATCA_CSID=your-production-csid
ZATCA_SECRET=your-production-secret

📞 Support Contacts

ZATCA:

Developer Resources:


Pre-Production Checklist

  • Migrations run successfully
  • Tenant VAT number configured
  • Test invoice created with QR code
  • QR code validated with ZATCA mobile app
  • Tested in ZATCA sandbox
  • Production CSID obtained
  • Celery workers running
  • Monitoring configured
  • Staff trained
  • Backup system in place

🎓 Quick Tips

💡 Tip 1: Use sandbox for all testing - never test with production CSID 💡 Tip 2: QR codes can be scanned with ZATCA mobile app to verify 💡 Tip 3: Invoice counter gaps will trigger compliance alerts 💡 Tip 4: Simplified invoices can be shared immediately, no need to wait for ZATCA 💡 Tip 5: Standard invoices MUST be cleared before sharing with buyer


Last Updated: October 27, 2025
Version: 1.0
Status: Production Ready