425 lines
10 KiB
Markdown
425 lines
10 KiB
Markdown
# 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
|