FiscGuy
The Modern Python Library for ZIMRA Fiscal Device Integration
FiscGuy gives Django applications a simple, Pythonic interface for every fiscal operation required by the Zimbabwe Revenue Authority — receipt submission, fiscal day management, certificate handling, and more. Built on Django REST Framework, it drops into any Django project in minutes.
Installation • Quick Start • API Reference • REST Endpoints • Docs • Contributing
Features
- Six core API functions —
open_day,close_day,submit_receipt,get_status,get_configuration,get_taxes - Full fiscal day lifecycle — open, manage counters, close with ZIMRA-compliant hash and signature
- Receipt types — Fiscal Invoice, Credit Note, Debit Note with correct counter tracking
- Online FDMS submission — receipts are validated, signed, and submitted directly to FDMS
- Certificate management — CSR generation, device registration, certificate renewal via
init_device - Multi-currency — USD and ZWG support with per-currency counter tracking
- Multiple payment methods — Cash, Card, Mobile Wallet, Bank Transfer, Coupon, Credit, Other
- Buyer management — optional buyer TIN and registration data on receipts
- Cursor pagination — efficient receipt listing for large datasets
- Protected key storage — device private keys are encrypted at rest
- Operational hardening — certificate material is redacted in Django admin and temporary PEM files are cleaned up after use
- Typed exceptions — every error condition has its own exception class
- 90%+ test coverage — mocked ZIMRA and crypto, fast CI
Requirements
- Python 3.11, 3.12, or 3.13
- Django 4.2+
- Django REST Framework 3.14+
Installation
pip install fiscguy
From source
git clone https://github.com/digitaltouchcode/fisc.git
cd fisc
pip install -e ".[dev]"
Quick Start
1. Add to Django settings
# settings.py
INSTALLED_APPS = [
# ...
"fiscguy",
"rest_framework",
]
2. Run migrations
python manage.py migrate
3. Initialise your device
python manage.py init_device
This interactive command collects your device credentials, generates a CSR, registers the device with ZIMRA, and fetches taxes and configuration automatically.
4. Include URLs
# urls.py
from django.urls import path, include
urlpatterns = [
path("fiscguy/", include("fiscguy.urls")),
]
5. Submit your first receipt
from fiscguy import open_day, submit_receipt, close_day
open_day()
receipt = submit_receipt({
"receipt_type": "fiscalinvoice",
"currency": "USD",
"total_amount": "115.00",
"payment_terms": "Cash",
"lines": [
{
"product": "Consulting Service",
"hs_code": "99001000",
"tax_id": 517,
"quantity": "1",
"unit_price": "115.00",
"line_total": "115.00",
"tax_amount": "15.00",
}
],
})
close_day()
REST Endpoints
When URLs are included, FiscGuy exposes the following endpoints:
| Method | Endpoint | Description |
|---|---|---|
POST |
/fiscguy/open-day/ |
Open a new fiscal day |
POST |
/fiscguy/close-day/ |
Close the current fiscal day |
GET |
/fiscguy/get-status/ |
Get device and fiscal day status |
POST |
/fiscguy/get-ping/ |
Ping FDMS to report device is online |
GET |
/fiscguy/receipts/ |
List receipts (cursor paginated) |
POST |
/fiscguy/receipts/ |
Submit a new receipt |
GET |
/fiscguy/receipts/{id}/ |
Retrieve a receipt by ID |
GET |
/fiscguy/configuration/ |
Get taxpayer configuration |
POST |
/fiscguy/sync-config/ |
Manually sync configuration from FDMS |
GET |
/fiscguy/taxes/ |
List all tax types |
POST |
/fiscguy/issue-certificate/ |
Renew device certificate |
* |
/fiscguy/buyer/ |
Buyer CRUD (ModelViewSet) |
Close-Day Status Flow
Closing a fiscal day has two states to track:
- Local database state -
open,close_pending,close_failed,closed - FDMS state - values such as
FiscalDayOpen,FiscalDayCloseInitiated,FiscalDayClosed
FiscGuy does not mark the fiscal day closed locally on the first close request. When FDMS
accepts the request with FiscalDayCloseInitiated, the library keeps the local day in
close_pending, starts background status polling, and only marks the day closed after FDMS
confirms the close.
Typical user-facing messages are:
Close request submitted. FDMS is processing the fiscal day close.Closing fiscal day... waiting for FDMS confirmation.Fiscal day closed successfully.Fiscal day close failed in FDMS.
For frontend integrations, call POST /fiscguy/close-day/ once, then poll
GET /fiscguy/get-status/ until the fiscal day moves from close_pending to closed or
close_failed.
HS Code Mapping
FiscGuy supports hs_code on receipt lines and passes it through to FDMS as
receiptLineHSCode.
Firms should map their product and service catalogues to the correct FDMS-compatible HS codes before going live. In practice, this means:
- Physical goods should use the applicable tariff / HS classification for that item
- Services and intangible supplies should use the appropriate ZIMRA service classifications where applicable
- HS codes should be maintained as product master data, not typed ad hoc at the point of sale
Recommended approach:
- Add
hs_codeto each product or service in your own catalogue - Copy that code into each receipt line when submitting receipts
- Reuse the original receipt line HS code for credit notes and product-linked debit notes where possible
Current behavior:
fiscalinvoicelines accepths_codein the payload and send it to FDMS asreceiptLineHSCodecreditnotelines can inheriths_codefrom the referenced original receipt line when no explicit linehs_codeis provideddebitnotelines can inheriths_codefrom the referenced original receipt line, or fall back to the appropriate ZIMRA service code when the debit line is a service/intangible adjustment
Example receipt line:
{
"product": "Consulting Service",
"hs_code": "99001000",
"tax_id": 517,
"quantity": "1",
"unit_price": "115.00",
"line_total": "115.00",
"tax_amount": "15.00"
}
Use GET /fiscguy/taxes/ to fetch the active device tax table first, then submit the
matching tax_id on each receipt line. Do not rely on tax-name strings in production payloads.
Security
FiscGuy includes baseline protections for certificate and signing material:
- Device private keys are encrypted at rest in the
Certstable - Raw certificate and private-key fields are not exposed through the default Django admin registration
- Temporary PEM files used for mutual TLS are written with restricted permissions and cleaned up after use
- Dependency versions are maintained against known vulnerability advisories
For production deployments, you should still review application-level authentication, admin access, database encryption policy, backup handling, and key-management requirements for your environment.
Pagination
Receipt listing supports cursor-based pagination:
GET /fiscguy/receipts/?page_size=20
Default page size: 10. Maximum: 100.
Error Handling
All operations raise typed exceptions. Import them from fiscguy.exceptions:
from fiscguy.exceptions import (
ReceiptSubmissionError,
CloseDayError,
FiscalDayError,
ConfigurationError,
CertificateError,
DevicePingError,
StatusError,
)
try:
close_day()
except CloseDayError as e:
print(f"Close day failed: {e}")
| Exception | Raised when |
|---|---|
ReceiptSubmissionError |
Receipt processing or FDMS submission fails |
CloseDayError |
FDMS rejects the close day request |
FiscalDayError |
Fiscal day cannot be opened or is already open |
ConfigurationError |
Configuration is missing or sync fails |
CertificateError |
Certificate issuance or renewal fails |
DevicePingError |
Ping to FDMS fails |
StatusError |
Status fetch from FDMS fails |
DeviceRegistrationError |
Device registration with ZIMRA fails |
CryptoError |
RSA signing or hashing fails |
Models
FiscGuy adds the following tables to your database:
| Model | Description |
|---|---|
Device |
Fiscal device registration details |
Configuration |
Taxpayer configuration synced from FDMS |
Certs |
Device certificate and private key |
Taxes |
Tax types synced from FDMS on day open |
FiscalDay |
Daily fiscal period with receipt counter |
FiscalCounter |
Running totals per tax / payment method |
Receipt |
Submitted receipts with hash, signature, QR code |
ReceiptLine |
Individual line items on a receipt |
Buyer |
Optional buyer registration data |
Access them directly:
from fiscguy.models import Device, Receipt, FiscalDay, Taxes
device = Device.objects.first()
open_days = FiscalDay.objects.filter(is_open=True)
receipts = Receipt.objects.select_related("buyer").prefetch_related("lines")
Management Commands
init_device
Interactive device setup for first-time provisioning:
python manage.py init_device
The command will:
- Prompt for
org_name,activation_key,device_id,device_model_name,device_model_version,device_serial_number - Ask whether to use production or testing FDMS
- Generate an RSA key pair and CSR
- Register the device with ZIMRA to obtain a signed certificate
- Fetch and persist configuration and taxes
Testing
# Run all tests
pytest
# With coverage report
pytest --cov=fiscguy --cov-report=html
# Run a specific test file
pytest fiscguy/tests/test_views.py
# Run a specific test
pytest fiscguy/tests/test_closing_day_service.py::TestBuildSaleByTax
All tests mock ZIMRA API calls and crypto operations — no network access required.
Dependency audit:
pip-audit -r requirements.txt
Documentation
Full documentation lives in the docs/ folder:
| Document | Description |
|---|---|
docs/installation.md |
Detailed installation and setup guide |
docs/receipt-types.md |
Fiscal Invoice, Credit Note, Debit Note rules |
docs/fiscal-counters.md |
How counters work and how they are calculated |
docs/closing-day.md |
Closing day hash string and signature spec |
docs/certificate-management.md |
Certificate lifecycle and renewal |
docs/error-reference.md |
All exceptions and what causes them |
docs/changelog.md |
Version history |
docs/contributing.md |
Contributing guidelines |
Contributing
Contributions are welcome. Please read docs/contributing.md first.
# Set up dev environment
git clone https://github.com/digitaltouchcode/fisc.git
cd fisc
pip install -e ".[dev]"
pre-commit install
# Before submitting a PR
black fiscguy
isort fiscguy
flake8 fiscguy
pytest
License
MIT — see LICENSE.
Metadata
Release files for fiscguy 0.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fiscguy-0.1.8.tar.gz | 59.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fiscguy-0.1.8-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 130.8 kB
Release files / fiscguy-0.1.8.tar.gz
| Download URL | fiscguy-0.1.8.tar.gz |
|---|---|
| Size | 59.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6194d4b5285eda78853c696eead42928cae2def4a9eda5015a8f87dcd21cf1f
|
|
BLAKE2b-256 checksum How to use checksums |
8be14e957c502e0bab7a0db6224cce70aae706f55dd66cc93f8990ab24daeafb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 3, 2026.
Transparency logRelease files / fiscguy-0.1.8-py3-none-any.whl
| Download URL | fiscguy-0.1.8-py3-none-any.whl |
|---|---|
| Size | 71.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
56612628039a969a3168eebfd0d061651003203c89d33d4fa65dcb18f1630c7d
|
|
BLAKE2b-256 checksum How to use checksums |
f00e49557391a11805dda2c33c2483b00c01a2497ae35ee2b1200dde2f51b129
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 3, 2026.
Transparency log