# ZATCA E-Invoice Quick Reference Guide ## 🚀 Getting Started (5 Minutes) ### Step 1: Run Migrations ```bash python3 manage.py migrate ``` ### Step 2: Configure Your Tenant ```python # 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 ```python 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) ```python # 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) ```python # 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 ```python # 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: ```python 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 ```python 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 ```python 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 ```python # 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 ```python 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 ```python 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 ```python 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python from finance.zatca_tasks import monitor_zatca_compliance # Run compliance monitoring report = monitor_zatca_compliance.delay() # Check results print(report.get()) ``` ### Generate Compliance Report ```python 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 ```python 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: ```bash # 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: - **Portal:** https://fatoora.zatca.gov.sa/ - **Sandbox:** https://sandbox.zatca.gov.sa/ - **Email:** info@zatca.gov.sa - **Phone:** 19993 (Local), +966112048998 (International) ### Developer Resources: - **SDK Download:** https://zatca.gov.sa/en/E-Invoicing/SystemsDevelopers/ - **API Docs:** Available on Developer Portal (requires login) - **Compliance Toolbox:** Web-based validator available --- ## ✅ 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