Xpensio

Teknik Döküman

Kurumsal Masraf Yönetimi Platformu — Mimari & API Referansı
Sürüm: v2.0  |  Tarih: 23 Mart 2026  |  Platform: NestJS · Next.js · Flutter

Web: app.xpensioapp.com  |  API: api.xpensioapp.com  |  Landing: xpensioapp.com

İçindekiler

  1. Sistem Mimarisi
  2. Proje Klasör Yapısı
  3. Veritabanı Şeması
  4. Authentication & Authorization
  5. API Referansı
  6. OCR Pipeline
  7. ERP Entegrasyonu
  8. Güvenlik Mimarisi
  9. Bildirim Sistemi
  10. Environment Variables
  11. Deploy Rehberi
  12. Hata Kodları & Troubleshooting
  13. Roadmap

1. Sistem Mimarisi

Xpensio, kurumsal masraf yönetimi için geliştirilmiş çok kiracılı (multi-tenant) bir SaaS platformudur. Monorepo yapısında organize edilmiş olup pnpm workspace ile yönetilmektedir. Platform; NestJS tabanlı backend API, Next.js tabanlı web uygulaması, Flutter tabanlı mobil uygulama ve pazarlama odaklı bir landing sayfasından oluşmaktadır.

Backend API

Framework: NestJS 10 (Node.js)
Port: 3001
ORM: Prisma 5.8
DB: PostgreSQL 15
Cache: Redis (opsiyonel)
URL: api.xpensioapp.com

Web Uygulaması

Framework: Next.js 14 App Router
Port: 3000
State: Zustand
UI: Tailwind CSS
Test: Playwright E2E
URL: app.xpensioapp.com

Mobil Uygulama

Framework: Flutter 3
Platform: Android + iOS
State: Provider
Push: Firebase Messaging
Kamera: OCR ile fatura tarama

Landing Page

Framework: Next.js 14
i18n: TR/EN
Fiyatlandırma: €10/€6 per-user
AI Chat: Yerleşik widget
URL: xpensioapp.com

Altyapı Topolojisi

Bileşen Platform URL / Adres Notlar
Backend API Hetzner VPS (Docker Compose) https://api.xpensioapp.com Port 3001, Nginx reverse proxy
PostgreSQL Hetzner VPS (Docker) Internal: postgres:5432 Persistent volume mount
Web App Vercel https://app.xpensioapp.com Auto-deploy from main branch
Landing Vercel https://xpensioapp.com TR/EN, SEO optimized
CDN / SSL Cloudflare DDoS protection, SSL termination
Email SMTP (Resend/SendGrid uyumlu) SMTP_HOST env var Nodemailer ile gönderim
Push Notifications Firebase Cloud Messaging Google Cloud Firebase Admin SDK
OCR (Primary) Google Cloud Gemini 2.5 Flash API Fatura görüntü analizi
OCR (Fallback) OpenAI GPT-4o Vision API Gemini başarısız olursa
OCR (Local Fallback) Sunucu içi Tesseract.js Her iki API de başarısızsa

Masraf Onay Akışı (State Machine)

DRAFT
SUBMITTED
PENDING_MANAGER
PENDING_CFO
(>50K)
PENDING_FINANCE
APPROVED
ERP_SENT
Not: 50.000 TL üzeri masraflar Manager onayından sonra CFO onayına gider. CFO onayı sonrası Finance'a iletilir. ERP_SENT durumu, Finance onaylayıp SAP/ERP sistemine gönderildiğinde atanır. Her aşamada email + push bildirimi tetiklenir.

2. Proje Klasör Yapısı

xpensioapp/                          # Monorepo kök dizini
├── apps/
│   ├── backend/                     # NestJS 10 REST API
│   │   ├── src/
│   │   │   ├── auth/                # JWT auth, login, şifre sıfırlama, change-password
│   │   │   │   ├── auth.controller.ts
│   │   │   │   ├── auth.service.ts
│   │   │   │   ├── jwt.strategy.ts
│   │   │   │   └── guards/          # JwtAuthGuard, RolesGuard
│   │   │   ├── users/               # Kullanıcı CRUD, Excel bulk import
│   │   │   │   ├── users.controller.ts
│   │   │   │   ├── users.service.ts
│   │   │   │   └── dto/
│   │   │   ├── expenses/            # Masraf state machine, export
│   │   │   │   ├── expenses.controller.ts
│   │   │   │   ├── expenses.service.ts
│   │   │   │   └── dto/
│   │   │   ├── receipts/            # OCR pipeline (Gemini → GPT-4o → Tesseract)
│   │   │   │   ├── receipts.controller.ts
│   │   │   │   └── receipts.service.ts
│   │   │   ├── approvals/           # Onay/red işlemleri
│   │   │   ├── notifications/       # Push + email notification
│   │   │   ├── fraud-detection/     # SHA-256 hash + Gemini AI fraud scoring
│   │   │   ├── mail/                # Nodemailer SMTP servis
│   │   │   └── identity/            # ERP adaptörleri
│   │   │       └── adapters/        # SapAdapter, NoneAdapter
│   │   ├── prisma/
│   │   │   ├── schema.prisma        # Veritabanı şeması
│   │   │   ├── migrations/          # Prisma migration dosyaları
│   │   │   └── seed-demo.js         # Demo veri seed scripti
│   │   ├── Dockerfile
│   │   └── .env                     # Backend env vars (gitignore'da)
│   │
│   ├── web/                         # Next.js 14 App Router
│   │   ├── src/
│   │   │   ├── app/                 # Next.js pages (App Router)
│   │   │   │   ├── (auth)/          # Login, register, forgot-password
│   │   │   │   ├── dashboard/       # Ana uygulama sayfaları
│   │   │   │   │   ├── expenses/    # Masraf liste ve detay
│   │   │   │   │   ├── approvals/   # Manager/CFO onay ekranı
│   │   │   │   │   ├── reports/     # Finance raporları + Excel export
│   │   │   │   │   ├── users/       # Admin kullanıcı yönetimi
│   │   │   │   │   └── settings/    # Profil, şifre değiştirme
│   │   │   │   └── api/             # Next.js API routes
│   │   │   ├── components/          # React bileşenleri
│   │   │   └── lib/
│   │   │       ├── api.ts           # Axios API istemcisi
│   │   │       └── store.ts         # Zustand global state
│   │   ├── e2e/                     # Playwright E2E testleri
│   │   │   ├── helpers.ts           # Ortak mock yardımcıları
│   │   │   └── production/          # Production E2E test suite
│   │   └── public/
│   │       └── sw.js                # Service Worker (PWA)
│   │
│   └── mobile/                      # Flutter 3
│       └── expense_mobile/
│           ├── lib/
│           │   ├── main.dart
│           │   ├── screens/         # UI ekranları
│           │   │   ├── login_screen.dart
│           │   │   ├── home_screen.dart
│           │   │   ├── expense_list_screen.dart
│           │   │   ├── expense_form_screen.dart
│           │   │   ├── scan_receipt_screen.dart
│           │   │   ├── approvals_screen.dart
│           │   │   ├── companies_screen.dart
│           │   │   └── profile_screen.dart
│           │   ├── services/
│           │   │   ├── api_service.dart
│           │   │   └── auth_service.dart
│           │   └── models/          # Dart veri modelleri
│           ├── android/
│           ├── ios/
│           └── pubspec.yaml
│
└── website/                         # Landing page (Next.js 14)
    ├── src/
    │   ├── app/                     # Sayfalar
    │   ├── components/
    │   │   ├── Pricing.tsx          # Fiyatlandırma (€10/€6)
    │   │   └── AIChatWidget.tsx     # AI chat widget
    │   └── i18n.ts                  # TR/EN çeviriler
    └── public/

Bağımlılık Yönetimi

Backend Temel Paketler

  • @nestjs/core — NestJS framework çekirdeği
  • @prisma/client — DB ORM istemcisi
  • @nestjs/jwt — JWT token yönetimi
  • @nestjs/throttler — Rate limiting
  • class-validator — DTO doğrulama
  • bcryptjs — Şifre hashleme
  • nodemailer — SMTP email gönderimi
  • firebase-admin — Push notification
  • xlsx — Excel dosya üretimi/okuma
  • tesseract.js — Yerel OCR fallback
  • @google/generative-ai — Gemini API

Frontend Temel Paketler

  • next 14 — App Router framework
  • zustand — Lightweight state management
  • axios — HTTP istemcisi
  • tailwindcss — Utility-first CSS
  • @playwright/test — E2E test framework
  • Flutter: share_plus — Dosya paylaşımı
  • Flutter: open_filex — Dosya açma
  • Flutter: firebase_messaging — Push notifications
  • Flutter: image_picker — Kamera/galeri
  • Flutter: http — API istekleri

3. Veritabanı Şeması

Xpensio, PostgreSQL 15 üzerinde çalışan ve Prisma 5.8 ile yönetilen ilişkisel bir veritabanı şemasına sahiptir. Tüm veriler orgId alanı üzerinden çok kiracılı (multi-tenant) olarak izole edilmektedir.

User Modeli

Alan Tip Zorunlu Açıklama
idString (cuid)EvetBirincil anahtar, otomatik üretilir
emailStringEvetBenzersiz, login için kullanılır
nameStringEvetKullanıcının tam adı
passwordHashStringEvetbcrypt ile hashlenmiş şifre
roleEnumEvetEMPLOYEE | MANAGER | FINANCE | ADMIN | SUPERUSER
orgIdStringEvetOrganizasyon izolasyonu için FK
subCompanyIdStringHayırAlt şirket ataması (ERP posting için)
gradeStringHayırKullanıcı kademesi (onay limitleri için)
managerIdStringHayırYönetici kullanıcı ID (self FK)
mustChangePasswordBooleanEvetİlk giriş zorunlu şifre değiştirme bayrağı
isApprovedBooleanEvetAdmin tarafından onay durumu
isEmailConfirmedBooleanEvetEmail doğrulama durumu
fcmTokenStringHayırFirebase Cloud Messaging token
passwordResetTokenStringHayırSHA-256 hash'lenmiş reset token
passwordResetExpiryDateTimeHayırReset token geçerlilik süresi (1 saat)
createdAtDateTimeEvetKayıt tarihi
updatedAtDateTimeEvetSon güncelleme tarihi

Expense Modeli

Alan Tip Zorunlu Açıklama
idString (cuid)EvetBirincil anahtar
userIdStringEvetMasrafı oluşturan kullanıcı FK
orgIdStringEvetMulti-tenant izolasyon
amountDecimalEvetBrüt tutar (OCR sonrası kilitlenir)
currencyStringEvetISO 4217: TRY, EUR, USD
taxAmountDecimalHayırKDV tutarı (OCR sonrası kilitlenir)
taxPercentageCodeStringHayırV0,V1,V2,V3,V4 (0,8,10,18,20%)
categoryStringEvetMasraf kategorisi (FOOD, TRAVEL vb.)
documentTypeStringHayırINVOICE, RECEIPT, TICKET
expenseTypeCodeStringHayırERP gönderimi için masraf tipi kodu
vehiclePlateStringHayırAraç plakası (yakıt masrafları)
receiptNumberStringHayırFiş/fatura no (OCR sonrası kilitlenir)
statusEnumEvetDRAFT|SUBMITTED|PENDING_MANAGER|PENDING_CFO|PENDING_FINANCE|APPROVED|REJECTED|ERP_SENT
expenseDateDateTimeEvetMasraf tarihi (OCR sonrası kilitlenir)
projectCodeStringHayırProje kodu (ERP entegrasyonu)
costCenterStringHayırMaliyet merkezi
descriptionStringHayırMasraf açıklaması
fraudScoreIntHayır0-100 arası AI fraud risk skoru
receiptImageUrlStringHayırYüklenen fatura görsel URL
approvedByStringHayırSon onaylayan kullanıcı ID
rejectedByStringHayırReddeden kullanıcı ID
rejectionReasonStringHayırRed gerekçesi

Diğer Modeller

Model Alanlar Açıklama
Organization id, name, erpType, sapBukrs, createdAt Kiracı organizasyon. erpType: NONE | SAP_ECC | SAP_S4HANA. sapBukrs: ERP Şirket Kodu
SubCompany id, orgId, name, sapBukrs, createdAt Alt şirket. SAP posting için orgId+subCompanyId kombinasyonu zorunlu
RefreshToken id, userId, token (SHA-256 hash), expiresAt, createdAt Tüm session'ları yönetmek için DB'de saklanan refresh token'lar
Notification id, userId, type, title, body, isRead, createdAt Kullanıcı bildirim geçmişi. type: EXPENSE_SUBMITTED | EXPENSE_APPROVED | EXPENSE_REJECTED
ApprovalHistory id, expenseId, userId, action, comment, createdAt Onay/red geçmişi audit logu
Prisma Versiyon Uyarısı: Prisma v7, url = env("DATABASE_URL") sözdiziminde kırıcı değişiklik içermektedir. Üretim ortamında kesinlikle prisma@5.8.0 kullanılmalıdır. Docker entrypoint'te ./node_modules/.bin/prisma önce denenir, bulunamazsa npx prisma@5.8.0 fallback çalışır.

14. Sürüm Notları (v1.4 — Nisan 2026)

ModülYenilik
SAP HRKullanıcı masraf yeri (cost center) günlük SAP userlist sync ile otomatik güncelleniyor — fallback zinciri: OPHCODE / KOSTL / COSTCENTER / RELID
SAP OAuth2Dinamik token akışı: POST /sap-auth/token, SapJwtGuard, ABAP ZCL_XPENSIO_AUTH + ZXPENSIO_TOKEN; client_id/secret ZEXP_CFG_01 üzerinden yönetilir
SAP TLSSetup wizard'da özel CA sertifikası (PEM) yükleme; Axios https.Agent({ ca }) ile tek yönlü TLS desteği
Setup WizardHR / Finance endpoint path'leri ayrı sekmede override edilebilir (ZEXP_CFG_01 muadili)
Expense SnapshotMasraf girildiği anda snapshotCostCenter*, snapshotDepartment*, snapshotPosition*, snapshotUpperManager*, snapshotCapturedAt alanları kayıt altına alınır — sonradan kullanıcı verisi değişse de rapor doğru görünür
TESLIM_ALINDIYeni ExpenseStatus ara durumu. PDF rapor üzerine QR (xpensio://receive/<id>?t=<token>) basılır, finans mobil app'ten okur. Endpoint: POST /reports/:id/receive
Mobilev1.4.0+40: mobile_scanner ile QR teslim alma ekranı, FINANCE/ADMIN rolüne özel
UserChangeHistoryField-level diff izleme (department, manager, cost center, position, role, grade vb.). Kaynak: SAP_SYNC | ADMIN. GET /users/:id/history + admin UI "Değişiklik Geçmişi" sekmesi
Menü GörünürlüğüOrg Chart, Pozisyonlar, Şirketler, Masraf Yerleri, Setup, Fraud, Audit Logs → yalnızca super admin. Backend SuperAdminGuard + Sidebar superAdminOnly filtresi
Grade LimitleriŞirket bazlı grade harcama limitleri (grade_policies.company_id)
Report NumberingOrg-wide tek artan rapor numarası (report_number_sequences org bazlı)