Multi-currency financial reporting with 16-decimal precision, a digitally signed three-stage approval workflow, real-time dashboards, tax & bank integrations, and a tamper-evident audit trail.
سامانهی مرجع برای توسعه و راهاندازی سامانههای گزارشگیری در سازمانها — با محاسبات دقیق چندارزی، گردشکار تأیید چندمرحلهای همراه با امضای دیجیتال، داشبورد آنی، اتصال به سامانه مودیان و درگاه بانکی، و مسیر حسابرسی تغییرناپذیر.
🇬🇧 English · 🇮🇷 فارسی · 🌐 Project site · 📘 API docs · 🔐 Security
An open-source, production-grade enterprise reporting system. It replaces spreadsheet-and-email approval chains with a single auditable flow: every financial report gets a unique reference, an exactly-computed instalment schedule, a cryptographically signed approval chain, and an immutable change history.
Built as a reference implementation — the kind of codebase you can read to see how financial correctness, multi-stage authorisation and audit integrity are actually enforced, not just described.
| # | Capability | What it guarantees |
|---|---|---|
| 1 | Multi-currency Decimal calculations | Compound interest and amortisation in pure Decimal to 16 places, FX from a central-bank feed with fallback, result in under 50 ms (measured p99: 1.7 ms) |
| 2 | Three-stage approval workflow | Finance manager → inspector → CEO, strictly ordered, each decision signed with RSA-2048/PSS over the report's content hash |
| 3 | Real-time dashboard & alerts | Interactive charts, overdue-instalment detection, MAD-based anomaly detection, delivered over email, SMS and WebSocket |
| 4 | Tax, bank & accounting integrations | Signed invoices to the tax authority, bank settlement confirmations, double-entry vouchers as REST JSON or standard XML — all idempotent with exponential backoff |
| 5 | Layered security & audit trail | Argon2id, JWT, RBAC, field-level encryption, and a hash-chained audit log that pinpoints the exact tampered record |
git clone https://github.com/mmdverse/gozaresh-reporting-system.git
cd gozaresh-reporting-system
docker compose up --build
# Dashboard → http://localhost:3000
# API docs → http://localhost:8000/docsDemo accounts (password DemoPass!2024): alice (requester), bob (finance manager),
carol (inspector), dave (CEO), erin (auditor), root (admin).
Note — the project site is a static page on GitHub Pages and has no backend attached, so the dashboard there only shows setup instructions. Run the stack locally with the command above to use the real application.
Three real defects were found and fixed during development — each is documented with a regression test, because they are the kind of bug that silently corrupts financial data:
- SQLite maps
NUMERICto a C double, so12500000000.1234567890123456came back as…4569549560547. That broke both the precision guarantee and every digital signature. Solved with a customMoneycolumn type. quantize()under the default 28-digit context raisedInvalidOperationfor any rial amount above ~12 digits — entirely normal for IRR.- HTTP exceptions rolled back the session, discarding failed-login audit entries and the account-lockout counter. A genuine security hole.
Full write-up in Technical decisions and docs/SECURITY.md.
FastAPI · Python 3.13 · SQLAlchemy 2.0 · PostgreSQL · Pydantic v2 ·
Next.js 15 · React 19 · TypeScript · WebSocket · Docker · Argon2id · RSA-PSS · JWT
115 tests · 84% coverage · CI runs lint, tests and an end-to-end smoke suite.
📘 Full API reference · 🔐 Security model · 🤝 Contributing
- قابلیتهای اصلی
- راهاندازی سریع
- معماری
- مستندات API
- تصمیمات فنی مهم
- تستها
- استقرار در محیط عملیاتی
- بهینهسازی موتور جستجو
- توسعهدهنده
سیستم نرخ تسعیر را از منابع معتبر دریافت میکند، سود مرکب و اقساط را با دقت Decimal (۱۶ رقم اعشار) محاسبه میکند و مبلغ نهایی را در کمتر از ۵۰ میلیثانیه برمیگرداند.
| موضوع | پیادهسازی |
|---|---|
| دقت | Decimal با ۱۶ رقم اعشار و گرد کردن بانکی (ROUND_HALF_EVEN) — هیچجا از float استفاده نمیشود |
| منابع نرخ | زنجیرهی چندلایه: بانک مرکزی ← API صرافی ← آخرین نرخ معتبر ذخیرهشده |
| کش | کش TTL درونفرایندی (پیشفرض ۳۰۰ ثانیه) برای ماندن زیر آستانه SLA |
| سنجش | زمان هر محاسبه در هدر X-Calc-Duration-Ms و در پاسخ برگردانده میشود |
| نتیجه واقعی | p99 اندازهگیریشده: ۱٫۷ میلیثانیه برای وام ۶۰ ماهه (۲۹ برابر سریعتر از الزام) |
نتیجهی GET /api/v1/calculations/benchmark?iterations=300:
{
"iterations": 300, "sla_ms": 50.0,
"p50_ms": 1.04, "p95_ms": 1.16, "p99_ms": 1.71, "max_ms": 2.18,
"sla_met_ratio": 1.0
}هر درخواست پس از ثبت، بهصورت خودکار برای مدیر مالی ← بازرس ← مدیرعامل ارسال میشود و در هر مرحله لاگ کامل و امضای دیجیتال ثبت میگردد.
- ترتیب مراحل بهصورت سختگیرانه اعمال میشود؛ پرش از یک مرحله خطای
409برمیگرداند. - هر تصمیم با کلید RSA-2048 (RSA-PSS/SHA-256) روی یک payload متعارف (canonical) امضا میشود.
- امضا شامل هش محتوای گزارش است؛ بنابراین هر تغییر بعدی در مبلغ، امضاها را باطل میکند و در
GET /reports/{id}/signaturesگزارش میشود. - رد شدن در هر مرحله، مراحل بعدی را
skippedکرده و گردشکار را متوقف میکند.
- نمودارهای تعاملی (Recharts): وضعیت گزارشها، روند ۱۲ ماهه، توزیع ارزی، گلوگاه مراحل تأیید.
- تشخیص خودکار: اقساط معوق، تراکنش خارج از بازه مجاز، ناهنجاری آماری (z-score اصلاحشده مبتنی بر MAD)، و گردشکارهای متوقفشده.
- ارسال نوتیفیکیشن از سه کانال: ایمیل، پیامک و WebSocket.
- هشدارها
dedupe keyدارند؛ اسکن مکرر، هشدار تکراری تولید نمیکند.
| مقصد | عملیات |
|---|---|
| سامانه مودیان | ساخت و ارسال صورتحساب امضاشده، استعلام وضعیت |
| درگاه بانک | درخواست تسویه، دریافت تأییدیه، اعتبارسنجی IBAN با الگوریتم mod-97 |
| نرمافزار حسابداری | سند حسابداری دوطرفه بهصورت REST/JSON و XML استاندارد (خروجی و ورودی) |
همهی فراخوانیها idempotent هستند (کلید مبتنی بر هش payload)، با backoff نمایی تلاش مجدد میکنند و در integration_logs ثبت میشوند.
| لایه | پیادهسازی |
|---|---|
| احراز هویت | JWT (access + refresh)، کلید API برای سرویسها |
| گذرواژه | Argon2id (۶۴MB حافظه، ۳ دور) + سیاست پیچیدگی |
| قفل حساب | پس از ۵ تلاش ناموفق، ۱۵ دقیقه قفل — لاگ آن حتی هنگام خطای HTTP ثبت میشود |
| مجوزها | RBAC با ۷ نقش و ماتریس دسترسی صریح |
| رمزنگاری داده | رمزگذاری سطح فیلد (Fernet) برای PII + blind index برای جستوجوی دقیق |
| مسیر حسابرسی | زنجیرهی هششده: هر رکورد H(hash قبلی ‖ محتوا) — حذف یا ویرایش تاریخچه بلافاصله شناسایی میشود |
| پاکسازی | گذرواژهها، توکنها و کدهای ملی پیش از ثبت در لاگ حسابرسی حذف میشوند |
| هدرهای امنیتی | CSP، HSTS، X-Frame-Options: DENY، nosniff، محدودیت نرخ درخواست |
GET /api/v1/audit/verify کل زنجیره را بازمحاسبه میکند و شماره دقیق رکورد دستکاریشده را برمیگرداند.
git clone https://github.com/mmdverse/gozaresh-reporting-system.git gozaresh
cd gozaresh
docker compose up --build
# داشبورد: http://localhost:3000
# مستندات: http://localhost:8000/docs# ---------- بکاند ----------
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env # SECRET_KEY را عوض کنید
python scripts/seed.py --reset # ساخت دادههای نمونه
uvicorn app.main:app --reload # http://localhost:8000/docs
# ---------- فرانتاند (ترمینال دوم) ----------
cd frontend
npm install
cp .env.example .env.local # NEXT_PUBLIC_SITE_URL را تنظیم کنید
npm run dev # http://localhost:3000گذرواژهی همه: DemoPass!2024
| کاربر | نقش | دسترسی |
|---|---|---|
alice |
درخواستدهنده | ثبت و ارسال گزارش |
bob |
مدیر مالی | تأیید مرحله اول |
carol |
بازرس | تأیید مرحله دوم + حسابرسی |
dave |
مدیرعامل | تأیید نهایی |
erin |
حسابرس | فقط خواندن + بررسی زنجیره |
root |
مدیر سیستم | دسترسی کامل |
gozaresh/
├── backend/
│ ├── app/
│ │ ├── core/ # پیکربندی، دیتابیس، امنیت، امضای دیجیتال
│ │ │ ├── config.py # تنظیمات مبتنی بر متغیر محیطی
│ │ │ ├── security.py # Argon2، JWT، رمزنگاری فیلد، RBAC
│ │ │ └── signing.py # RSA-PSS + زنجیره هش
│ │ ├── models/ # مدلهای SQLAlchemy 2.0
│ │ │ └── types.py # نوع Money — دقت دقیق در همه بکاندها
│ │ ├── schemas/ # مدلهای Pydantic v2
│ │ ├── services/
│ │ │ ├── calculator.py # موتور مالی (Decimal خالص)
│ │ │ ├── fx.py # نرخ ارز چندمنبعی با fallback
│ │ │ ├── workflow.py # گردشکار سهمرحلهای امضاشده
│ │ │ ├── alerts.py # موتور تشخیص و هشدار
│ │ │ ├── audit.py # مسیر حسابرسی زنجیرهای
│ │ │ ├── dashboard.py # تجمیع دادههای داشبورد
│ │ │ └── notifier.py # ایمیل / پیامک / WebSocket
│ │ ├── integrations/ # مودیان، بانک، حسابداری
│ │ ├── api/v1/ # ۵۱ endpoint
│ │ └── main.py # اپلیکیشن + middleware + زمانبند
│ ├── tests/ # ۱۱۵ تست (پوشش ۸۴٪)
│ └── scripts/seed.py
├── frontend/ # Next.js 15 + React 19 (RTL فارسی)
│ ├── public/og.png # تصویر Open Graph (متن فارسی شکلدهیشده)
│ └── src/
│ ├── app/
│ │ ├── page.tsx # صفحه فرود — استاتیک و بهینه برای SEO
│ │ ├── app/page.tsx # داشبورد (پشت احراز هویت، noindex)
│ │ ├── sitemap.ts # نقشه سایت خودکار
│ │ ├── robots.ts # قوانین خزش
│ │ └── manifest.ts # PWA manifest
│ ├── components/ # داشبورد، محاسبهگر، گردشکار، حسابرسی، پاورقی
│ └── lib/
│ ├── api.ts # کلاینت تایپشده + WebSocket
│ └── seo.ts # متادیتا، کلیدواژهها و JSON-LD
├── scripts/smoke-test.sh # ۲۴ بررسی end-to-end
├── docs/ # مستندات API و مدل امنیتی
└── .github/workflows/ci.yml
جریان یک گزارش:
ثبت ──> محاسبه (Decimal، <50ms) ──> تولید اقساط ──> بررسی بازه مجاز
│
▼
┌───────── ارسال برای تأیید ─────────┐
▼ │
مدیر مالی ──امضا──> بازرس ──امضا──> مدیرعامل ──امضا──> تأییدشده
│ │ │ │
└──── رد ──────┴──── رد ──────┘ ▼
│ مودیان + بانک + حسابداری
▼ │
ردشده ▼
همهچیز در مسیر حسابرسی ثبت میشود
پس از اجرا، مستندات تعاملی در /docs (Swagger) و /redoc در دسترس است. ۵۱ endpoint در ۷ گروه:
| گروه | نمونه endpoint |
|---|---|
auth |
POST /auth/login، POST /auth/refresh، GET /auth/me |
calculations |
POST /calculations/preview، GET /calculations/rates/{base}/{quote}، GET /calculations/benchmark |
reports |
POST /reports، POST /reports/{id}/submit، POST /reports/{id}/decision، GET /reports/{id}/signatures |
dashboard |
GET /dashboard/overview، WS /ws/dashboard |
alerts |
GET /alerts، POST /alerts/scan، POST /alerts/{id}/acknowledge |
audit |
GET /audit/logs، GET /audit/verify، GET /audit/export |
integrations |
POST /integrations/moadian/{id}/submit، POST /integrations/bank/{id}/settle |
جزئیات کامل: docs/API.md · مدل امنیتی: docs/SECURITY.md
در حین توسعه چند مشکل واقعی شناسایی و برطرف شد که ارزش مستندسازی دارند:
SQLite نوع NUMERIC را بهصورت double ذخیره میکند. مقدار
12500000000.1234567890123456 هنگام خواندن به 12500000000.1234569549560547
تبدیل میشد — یعنی تضمین ۱۶ رقم اعشار بیصدا نقض و امضاهای دیجیتال باطل میشدند.
راهحل: نوع سفارشی Money که روی SQLite مقدار را
بهصورت رشتهی صفرپرشده و offsetدار ذخیره میکند (تا ORDER BY همچنان عددی بماند)
و روی PostgreSQL از NUMERIC(38,16) بومی استفاده میکند. تست رگرسیون:
tests/test_calculator.py::TestStoragePrecision.
quantize() با کانتکست پیشفرض ۲۸ رقمی اجرا میشد؛ هر مبلغ ریالی بالای ۱۲ رقم
(کاملاً معمول) خطای InvalidOperation میداد. اکنون همهی عملیات پولی داخل
money_context با دقت ۶۰ رقم اجرا میشوند.
استثنای HTTP باعث rollback سشن میشد و رکورد «ورود ناموفق» و شمارندهی قفل حساب
از بین میرفت — یک نقص امنیتی جدی. اکنون پیش از پرتاب استثنا commit انجام میشود.
SQLite اطلاعات منطقهی زمانی را حذف میکند و مقیاس Decimal را تغییر میدهد؛
Decimal("125") و Decimal("125.0000000000000000") رشتههای متفاوتی تولید میکنند.
هر دو در payload امضا نرمالسازی میشوند تا امضا پس از بارگذاری مجدد از دیتابیس
همچنان معتبر بماند. تست رگرسیون: TestSignaturePersistence.
cd backend
pytest -q # ۱۱۵ تست
pytest -q --cov=app --cov-report=term # با گزارش پوشش
python -m ruff check app tests scripts # لینت
python -m ruff format --check app tests # بررسی فرمت
# تست end-to-end روی سرور در حال اجرا
bash ../scripts/smoke-test.sh http://localhost:8000| مجموعه | تعداد | پوشش |
|---|---|---|
test_calculator.py |
۳۰ | دقت Decimal، SLA، گرد کردن ارزی، ماندگاری در دیتابیس |
test_workflow.py |
۱۵ | ترتیب مراحل، امضای دیجیتال، تشخیص دستکاری |
test_audit.py |
۳۰ | Argon2، رمزنگاری، RBAC، زنجیره هش، قفل حساب |
test_fx_and_alerts.py |
۲۴ | نرخ ارز، تبدیل، هشدارها، داشبورد |
test_integrations.py |
۱۶ | مودیان، بانک، حسابداری، IBAN، idempotency |
اسکریپت smoke-test.sh نیز ۲۴ بررسی end-to-end روی یک سرور واقعی انجام میدهد.
پیش از استقرار حتماً:
SECRET_KEYرا با یک مقدار تصادفی بلند جایگزین کنید (openssl rand -hex 32).DATABASE_URLرا به PostgreSQL تغییر دهید — SQLite فقط برای توسعه است.ENV=prodوDEBUG=falseرا تنظیم کنید (HSTS و پنهانسازی خطاها فعال میشود).FX_OFFLINE_MODE=falseو آدرس واقعی منابع نرخ ارز را وارد کنید.INTEGRATIONS_SANDBOX=falseو اعتبارنامههای واقعی مودیان/بانک را تنظیم کنید.NOTIFICATIONS_DRY_RUN=falseو اطلاعات SMTP و درگاه پیامک را وارد کنید.- کلیدهای امضا (
KEYSTORE_DIR) را به یک HSM یا KMS منتقل کنید؛ کلیدهای فایلی فقط برای نمونهسازی مناسباند. - محدودیت نرخ درخواست را به Redis منتقل کنید تا در حالت چندنمونهای درست کار کند.
- برای مهاجرتهای شِما، Alembic را اضافه کنید (
Base.metadata.create_allفقط برای شروع سریع است).
# نمونهی متغیرهای محیطی برای production
ENV=prod
DEBUG=false
SECRET_KEY=$(openssl rand -hex 32)
DATABASE_URL=postgresql+psycopg://user:pass@db-host:5432/gozaresh
FX_OFFLINE_MODE=false
INTEGRATIONS_SANDBOX=false
NOTIFICATIONS_DRY_RUN=false
ALLOWED_ORIGINS=https://reports.your-org.irصفحهی فرود پروژه بهصورت استاتیک رندر میشود (۳٫۵ کیلوبایت) و کاملاً برای موتورهای جستجو قابل خزش است:
| مورد | پیادهسازی |
|---|---|
| متادیتا | عنوان، توضیحات و کلیدواژههای فارسی و انگلیسی از src/lib/seo.ts |
| داده ساختاریافته | JSON-LD شامل SoftwareApplication، Person، WebSite و FAQPage |
| نقشه سایت | /sitemap.xml تولید خودکار |
| robots | /robots.txt — داشبورد (/app) از ایندکس خارج است تا امتیاز صفحه اصلی رقیق نشود |
| Open Graph | تصویر ۱۲۰۰×۶۳۰ با شکلدهی صحیح متن فارسی |
| ساختار محتوا | یک h1، سرفصلهای منظم h2/h3، و بخش پرسشهای متداول در HTML |
| زبان و جهت | lang="fa" و dir="rtl" |
| PWA | manifest.webmanifest و آیکون SVG |
پیش از انتشار عمومی:
- مقدار
NEXT_PUBLIC_SITE_URLرا در.env.localروی دامنه واقعی تنظیم کنید — تگ canonical، Open Graph، sitemap و JSON-LD همگی از آن مشتق میشوند. - دامنه را در Google Search Console ثبت و
sitemap.xmlرا معرفی کنید. - تصویر Open Graph را با ابزار اعتبارسنجی بررسی کنید.
- تصویر Open Graph در
frontend/public/og.pngقرار دارد. اگر متن صفحه فرود را تغییر دادید، آن را نیز بهروزرسانی کنید — این تصویر با Pillow و کتابخانه raqm ساخته شده، نه باnext/og، چون موتور Satori قابلیت شکلدهی متن فارسی ندارد و حروف را جدا و برعکس رندر میکند.
ساختهشده با ❤️ توسط Mohammad — تلگرام: @llllxyz
اگر این پروژه برایتان مفید بود، یک ⭐ روی گیتهاب دلگرمکننده است.
MIT — جزئیات در LICENSE.
توجه: اتصال به سامانه مودیان و درگاه بانکی بهصورت پیشفرض در حالت sandbox اجرا میشود و پاسخها شبیهسازیشده هستند. برای استفادهی واقعی باید اعتبارنامههای رسمی دریافت و مطابق آخرین مستندات هر سازمان، schema پیامها بهروزرسانی شود.