Auto commit: 2026-02-06T00:08:49.034Z

This commit is contained in:
Flatlogic Bot 2026-02-06 00:08:49 +00:00
parent e6fa05f94e
commit cddb69516f
21 changed files with 871 additions and 234 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -43,14 +43,9 @@ CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SAMESITE = "None"
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
@ -63,10 +58,7 @@ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Disable X-Frame-Options middleware to allow Flatlogic preview iframes.
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
X_FRAME_OPTIONS = 'ALLOWALL'
@ -81,7 +73,6 @@ TEMPLATES = [
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# IMPORTANT: do not remove injects PROJECT_DESCRIPTION/PROJECT_IMAGE_URL and cache-busting timestamp
'core.context_processors.project_context',
@ -110,26 +101,6 @@ DATABASES = {
},
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/

View File

@ -14,13 +14,11 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("core.urls")),
]

Binary file not shown.

27
core/forms.py Normal file
View File

@ -0,0 +1,27 @@
from django import forms
from .models import Firma, Fatura, FaturaKalemi
class FirmaForm(forms.ModelForm):
class Meta:
model = Firma
fields = ['ad', 'vergi_no', 'mersis_no', 'adres']
widgets = {
'ad': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Firma Tam Adı'}),
'vergi_no': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Vergi No / TC Kimlik No'}),
'mersis_no': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'MERSİS No (Opsiyonel)'}),
'adres': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Firma Adresi'}),
}
class FaturaForm(forms.ModelForm):
class Meta:
model = Fatura
fields = ['firma', 'fatura_no', 'tarih', 'ara_toplam', 'kdv_toplam', 'genel_toplam', 'pdf_dosyasi']
widgets = {
'firma': forms.Select(attrs={'class': 'form-select'}),
'fatura_no': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Fatura No'}),
'tarih': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'ara_toplam': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'kdv_toplam': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'genel_toplam': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'pdf_dosyasi': forms.FileInput(attrs={'class': 'form-control'}),
}

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Fatura Yönetim Sistemi{% endblock %}</title>
<title>{% block title %}FaturaYol - Akıllı Fatura Yönetimi{% endblock %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
@ -13,9 +13,10 @@
<style>
:root {
--primary-color: #2563eb;
--sidebar-bg: #1e293b;
--sidebar-hover: #334155;
--bg-body: #f8fafc;
--sidebar-bg: #0f172a;
--sidebar-hover: #1e293b;
--sidebar-active: #334155;
--bg-body: #f1f5f9;
--text-main: #1e293b;
--text-muted: #64748b;
}
@ -24,94 +25,105 @@
background-color: var(--bg-body);
color: var(--text-main);
overflow-x: hidden;
margin: 0;
padding: 0;
}
#wrapper {
display: flex;
width: 100%;
align-items: stretch;
min-height: 100vh;
}
#sidebar {
min-width: 260px;
max-width: 260px;
width: 280px;
min-width: 280px;
background: var(--sidebar-bg);
color: #fff;
transition: all 0.3s;
min-height: 100vh;
z-index: 1000;
box-shadow: 4px 0 10px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
#sidebar .sidebar-header {
padding: 20px;
background: rgba(0,0,0,0.1);
padding: 30px 25px;
background: rgba(0,0,0,0.2);
border-bottom: 1px solid rgba(255,255,255,0.05);
}
#sidebar ul.components {
padding: 20px 0;
padding: 20px 15px;
}
#sidebar ul li {
padding: 0 10px;
margin-bottom: 5px;
}
#sidebar ul li a {
padding: 12px 15px;
display: block;
color: #cbd5e1;
padding: 12px 20px;
display: flex;
align-items: center;
color: #94a3b8;
text-decoration: none;
border-radius: 8px;
margin-bottom: 5px;
border-radius: 10px;
font-weight: 500;
transition: 0.2s;
transition: all 0.2s;
}
#sidebar ul li a:hover, #sidebar ul li.active > a {
#sidebar ul li a:hover {
color: #fff;
background: var(--sidebar-hover);
}
#sidebar ul li.active > a {
color: #fff;
background: var(--sidebar-active);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
}
#sidebar ul li a i {
margin-right: 10px;
font-size: 1.1rem;
margin-right: 15px;
font-size: 1.2rem;
}
#content {
width: 100%;
padding: 30px;
flex: 1;
min-width: 0;
padding: 40px;
transition: all 0.3s;
}
.navbar {
.top-navbar {
background: #fff;
border-bottom: 1px solid #e2e8f0;
margin-bottom: 30px;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
padding: 15px 25px;
margin-bottom: 35px;
border-radius: 15px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
display: flex;
align-items: center;
justify-content: space-between;
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
margin-bottom: 24px;
border-radius: 16px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
transition: transform 0.2s;
}
.stat-card {
padding: 24px;
display: flex;
align-items: center;
.card:hover {
transform: translateY(-2px);
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-right: 16px;
.btn-primary {
background-color: var(--primary-color);
border: none;
padding: 10px 20px;
border-radius: 10px;
font-weight: 600;
}
.bg-primary-soft { background-color: #dbeafe; color: #2563eb; }
.bg-success-soft { background-color: #d1fae5; color: #10b981; }
.bg-warning-soft { background-color: #fef3c7; color: #f59e0b; }
.table thead th {
background-color: #f8fafc;
border-bottom: 2px solid #e2e8f0;
text-transform: uppercase;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.05em;
color: var(--text-muted);
.badge-soft-success { background-color: #d1fae5; color: #065f46; }
.badge-soft-primary { background-color: #dbeafe; color: #1e40af; }
@media (max-width: 992px) {
#sidebar {
margin-left: -280px;
}
#sidebar.active {
margin-left: 0;
}
#content {
padding: 20px;
}
}
</style>
{% block extra_css %}{% endblock %}
@ -121,49 +133,68 @@
<!-- Sidebar -->
<nav id="sidebar">
<div class="sidebar-header">
<h4 class="mb-0 fw-bold"><i class="bi bi-receipt-cutoff me-2"></i>FaturaYol</h4>
<a href="{% url 'home' %}" class="text-white text-decoration-none">
<h3 class="mb-0 fw-bold d-flex align-items-center">
<i class="bi bi-lightning-charge-fill text-primary me-2"></i>
FaturaYol
</h3>
</a>
<small class="text-muted">Akıllı Arşivleme Sistemi</small>
</div>
<ul class="list-unstyled components">
<li class="{% if active_menu == 'dashboard' %}active{% endif %}">
<a href="{% url 'home' %}"><i class="bi bi-speedometer2"></i> Gösterge Paneli</a>
<a href="{% url 'home' %}">
<i class="bi bi-grid-1x2-fill"></i>
<span>Gösterge Paneli</span>
</a>
</li>
<li class="{% if active_menu == 'archive' %}active{% endif %}">
<a href="#"><i class="bi bi-archive"></i> Fatura Arşivi</a>
<a href="{% url 'fatura_arsivi' %}">
<i class="bi bi-collection-fill"></i>
<span>Fatura Arşivi</span>
</a>
</li>
<li class="{% if active_menu == 'reports' %}active{% endif %}">
<a href="#"><i class="bi bi-graph-up"></i> İstatistik & Raporlar</a>
</li>
<li class="mt-4 px-3">
<span class="text-muted small text-uppercase fw-bold">Yönetim</span>
</li>
<li>
<a href="/admin/"><i class="bi bi-gear"></i> Sistem Ayarları</a>
<a href="{% url 'raporlar' %}">
<i class="bi bi-bar-chart-line-fill"></i>
<span>İstatistik & Raporlar</span>
</a>
</li>
</ul>
<div class="mt-auto p-4">
<div class="card bg-primary text-white border-0 p-3" style="background: linear-gradient(45deg, #2563eb, #4f46e5);">
<small class="opacity-75 mb-1">Kullanım Durumu</small>
<div class="progress mb-2" style="height: 6px; background: rgba(255,255,255,0.2);">
<div class="progress-bar bg-white" style="width: 45%;"></div>
</div>
<small class="small">Sürüm 1.0.4</small>
</div>
</div>
</nav>
<!-- Page Content -->
<div id="content">
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<span class="navbar-text fw-semibold">Hoş geldiniz!</span>
<div class="ms-auto d-flex align-items-center">
<div class="dropdown">
<button class="btn btn-link text-dark text-decoration-none dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle me-1"></i> Yönetici
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#">Profil</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Çıkış Yap</a></li>
</ul>
<div class="container-fluid p-0">
<div class="top-navbar">
<div class="d-flex align-items-center">
<h5 class="mb-0 fw-bold">{% block page_title %}Genel Bakış{% endblock %}</h5>
</div>
<div class="d-flex align-items-center">
<div class="me-4 d-none d-md-block">
<form action="{% url 'search' %}" method="get">
<div class="input-group">
<span class="input-group-text bg-light border-0"><i class="bi bi-search"></i></span>
<input type="text" name="q" class="form-control bg-light border-0" placeholder="Fatura veya firma ara..." style="width: 300px;" value="{{ query|default:'' }}">
</div>
</form>
</div>
</div>
</div>
</nav>
{% block content %}{% endblock %}
{% block content %}{% endblock %}
</div>
</div>
</div>

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block title %}Silmeyi Onayla - FaturaYol{% endblock %}
{% block page_title %}{{ type }} Silme İşlemi{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card border-0 shadow-sm">
<div class="card-body p-5 text-center">
<div class="bg-danger bg-opacity-10 text-danger mx-auto mb-4 rounded-circle d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<i class="bi bi-exclamation-triangle-fill fs-1"></i>
</div>
<h4 class="fw-bold mb-3">Emin misiniz?</h4>
<p class="text-muted mb-4">
<strong>{{ object }}</strong> isimli {{ type|lower }} kaydını silmek üzeresiniz.
Bu işlem geri alınamaz ve bu kayda bağlı tüm veriler silinecektir.
</p>
<form method="post">
{% csrf_token %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-danger rounded-pill py-2 fw-bold">Evet, Sil</button>
<a href="{{ request.META.HTTP_REFERER|default:'/' }}" class="btn btn-light rounded-pill py-2">İptal</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block title %}Fatura Arşivi - FaturaYol{% endblock %}
{% block page_title %}Fatura Arşivi{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col-md-8">
<p class="text-muted">Firmalara göre gruplanmış fatura listesi. Detaylı görüntülemek için bir firmaya tıklayın.</p>
</div>
<div class="col-md-4 text-end">
<a href="{% url 'firma_ekle' %}" class="btn btn-primary rounded-pill px-4">
<i class="bi bi-folder-plus me-2"></i> Yeni Firma Ekle
</a>
</div>
</div>
<div class="row">
{% for firma in firmalar %}
<div class="col-md-4 col-xl-3 mb-4">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4 text-center">
<div class="position-absolute top-0 end-0 p-2">
<a href="{% url 'firma_sil' firma.pk %}" class="btn btn-link text-danger p-0" title="Firmayı Sil"><i class="bi bi-trash"></i></a>
</div>
<div class="bg-primary-soft stat-icon mx-auto mb-3" style="width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: #e0e7ff; color: #4338ca;">
<i class="bi bi-building" style="font-size: 1.5rem;"></i>
</div>
<h5 class="fw-bold mb-1 text-truncate">{{ firma.ad }}</h5>
<p class="text-muted small mb-3">VN: {{ firma.vergi_no }}</p>
<div class="d-flex justify-content-between align-items-center bg-light p-2 rounded-3 mb-3">
<div class="text-start ps-2">
<small class="text-muted d-block" style="font-size: 0.65rem; text-transform: uppercase;">Fatura</small>
<span class="fw-bold">{{ firma.fatura_sayisi }} Adet</span>
</div>
<div class="text-end pe-2">
<small class="text-muted d-block" style="font-size: 0.65rem; text-transform: uppercase;">Toplam</small>
<span class="fw-bold text-primary">{{ firma.toplam_tutar|default:0|floatformat:2 }} ₺</span>
</div>
</div>
<a href="{% url 'firma_detay' firma.pk %}" class="btn btn-outline-primary btn-sm w-100 rounded-pill">
Dosyaları Görüntüle <i class="bi bi-chevron-right ms-1"></i>
</a>
</div>
</div>
</div>
{% empty %}
<div class="col-12 text-center py-5">
<i class="bi bi-folder2-open text-muted" style="font-size: 4rem;"></i>
<p class="mt-3 text-muted">Henüz kayıtlı bir firma veya fatura bulunamadı.</p>
<a href="{% url 'firma_ekle' %}" class="btn btn-primary mt-2">İlk Firmayı Ekle</a>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,105 @@
{% extends "base.html" %}
{% block title %}Fatura Detayı - {{ fatura.fatura_no }}{% endblock %}
{% block page_title %}Fatura Detayı{% endblock %}
{% block content %}
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'fatura_arsivi' %}" class="text-decoration-none">Arşiv</a></li>
<li class="breadcrumb-item"><a href="{% url 'firma_detay' fatura.firma.pk %}" class="text-decoration-none">{{ fatura.firma.ad }}</a></li>
<li class="breadcrumb-item active">{{ fatura.fatura_no }}</li>
</ol>
</nav>
<div class="row">
<div class="col-lg-8">
<!-- PDF Önizleme Kartı (Simüle edilmiş, PDF.js veya iframe kullanılabilir) -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold">PDF Önizleme</h5>
<a href="{{ fatura.pdf_dosyasi.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
<i class="bi bi-box-arrow-up-right me-1"></i> Tam Ekran
</a>
</div>
<div class="card-body p-0" style="height: 600px; background: #525659;">
{% if fatura.pdf_dosyasi %}
<iframe src="{{ fatura.pdf_dosyasi.url }}" width="100%" height="100%" frameborder="0"></iframe>
{% else %}
<div class="h-100 d-flex align-items-center justify-content-center text-white flex-column">
<i class="bi bi-file-earmark-pdf mb-3" style="font-size: 3rem;"></i>
<p>PDF dosyası bulunamadı.</p>
</div>
{% endif %}
</div>
</div>
</div>
<div class="col-lg-4">
<!-- Fatura Bilgileri Yan Panel -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="fw-bold mb-4">Fatura Bilgileri</h5>
<div class="mb-3">
<label class="text-muted small text-uppercase fw-bold">Firma</label>
<p class="fw-bold mb-0 text-primary">{{ fatura.firma.ad }}</p>
</div>
<div class="row mb-3">
<div class="col-6">
<label class="text-muted small text-uppercase fw-bold">Tarih</label>
<p class="fw-bold mb-0">{{ fatura.tarih|date:"d.m.Y" }}</p>
</div>
<div class="col-6">
<label class="text-muted small text-uppercase fw-bold">Fatura No</label>
<p class="fw-bold mb-0">{{ fatura.fatura_no }}</p>
</div>
</div>
<hr class="my-4">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Ara Toplam</span>
<span class="fw-bold">{{ fatura.ara_toplam }} ₺</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">KDV Toplam</span>
<span class="fw-bold">{{ fatura.kdv_toplam }} ₺</span>
</div>
<div class="d-flex justify-content-between mt-3 pt-3 border-top">
<span class="h5 fw-bold mb-0">Genel Toplam</span>
<span class="h5 fw-bold mb-0 text-primary">{{ fatura.genel_toplam }} ₺</span>
</div>
</div>
</div>
<!-- Kalem Detayları -->
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<div class="p-3 border-bottom">
<h5 class="fw-bold mb-0">Ürün / Hizmet Kalemleri</h5>
</div>
<div class="list-group list-group-flush">
{% for kalem in kalemler %}
<div class="list-group-item p-3">
<div class="d-flex justify-content-between align-items-start mb-1">
<h6 class="fw-bold mb-0">{{ kalem.urun_adi }}</h6>
<span class="badge bg-light text-dark">{{ kalem.adet }} Adet</span>
</div>
<div class="d-flex justify-content-between small text-muted">
<span>Birim: {{ kalem.birim_fiyat }} ₺</span>
<span class="fw-bold text-dark">Toplam: {{ kalem.toplam_tutar }} ₺</span>
</div>
</div>
{% empty %}
<div class="p-4 text-center text-muted">
Kalem verisi bulunamadı.
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block title %}{{ title }} - FaturaYol{% endblock %}
{% block page_title %}{{ title }}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-4">
<h5 class="fw-bold mb-3">Fatura Detayları</h5>
{% for field in form %}
<div class="mb-3">
<label class="form-label fw-medium">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="d-flex justify-content-between align-items-center mt-4">
<a href="{% url 'home' %}" class="btn btn-light rounded-pill px-4">İptal</a>
<button type="submit" class="btn btn-primary rounded-pill px-5">Faturayı Yükle</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,76 @@
{% extends "base.html" %}
{% block title %}Fatura Arşivi - FaturaYol{% endblock %}
{% block page_title %}{{ firma.ad }}{% endblock %}
{% block content %}
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'fatura_arsivi' %}" class="text-decoration-none">Arşiv</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ firma.ad }}</li>
</ol>
</nav>
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold"><i class="bi bi-files me-2 text-primary"></i>Tüm Faturalar</h5>
<div class="btn-group">
<a href="{% url 'fatura_ekle' %}?firma={{ firma.pk }}" class="btn btn-sm btn-primary rounded-pill px-3"><i class="bi bi-plus-lg me-1"></i> Yeni PDF Yükle</a>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="ps-4">Fatura No</th>
<th>Tarih</th>
<th>Ara Toplam</th>
<th>KDV</th>
<th>Genel Toplam</th>
<th>Durum</th>
<th class="text-end pe-4">İşlemler</th>
</tr>
</thead>
<tbody>
{% for fatura in faturalar %}
<tr>
<td class="ps-4 fw-medium">{{ fatura.fatura_no }}</td>
<td>{{ fatura.tarih|date:"d.m.Y" }}</td>
<td>{{ fatura.ara_toplam }} ₺</td>
<td>{{ fatura.kdv_toplam }} ₺</td>
<td class="fw-bold text-primary">{{ fatura.genel_toplam }} ₺</td>
<td>
{% if fatura.islenmis %}
<span class="badge badge-soft-success px-2 py-1 rounded-pill">İşlendi</span>
{% else %}
<span class="badge bg-light text-dark px-2 py-1 rounded-pill border">Bekliyor</span>
{% endif %}
</td>
<td class="text-end pe-4">
<a href="{% url 'fatura_detay' fatura.pk %}" class="btn btn-sm btn-light rounded-circle me-1" title="Görüntüle">
<i class="bi bi-eye"></i>
</a>
{% if fatura.pdf_dosyasi %}
<a href="{{ fatura.pdf_dosyasi.url }}" target="_blank" class="btn btn-sm btn-light rounded-circle me-1" title="PDF İndir">
<i class="bi bi-download"></i>
</a>
{% endif %}
<a href="{% url 'fatura_sil' fatura.pk %}" class="btn btn-sm btn-light text-danger rounded-circle" title="Faturayı Sil">
<i class="bi bi-trash"></i>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center py-5 text-muted">
<p class="mb-0">Bu firmaya ait henüz fatura bulunamadı.</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block title %}{{ title }} - FaturaYol{% endblock %}
{% block page_title %}{{ title }}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form method="post">
{% csrf_token %}
<div class="mb-4">
<h5 class="fw-bold mb-3">Firma Bilgileri</h5>
{% for field in form %}
<div class="mb-3">
<label class="form-label fw-medium">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="d-flex justify-content-between align-items-center mt-4">
<a href="{% url 'fatura_arsivi' %}" class="btn btn-light rounded-pill px-4">İptal</a>
<button type="submit" class="btn btn-primary rounded-pill px-5">Kaydet</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -3,132 +3,140 @@
{% block title %}Gösterge Paneli | FaturaYol{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-12 mb-4">
<h2 class="fw-bold">Gösterge Paneli</h2>
<p class="text-muted">Fatura yönetim sistemine genel bakış.</p>
<div class="row">
<div class="col-12 mb-4">
<h2 class="fw-bold">Gösterge Paneli</h2>
<p class="text-muted">Fatura yönetim sistemine genel bakış ve hızlı istatistikler.</p>
</div>
</div>
<!-- Stats Cards -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card stat-card border-0 shadow-sm">
<div class="card-body d-flex align-items-center p-4">
<div class="bg-primary-soft stat-icon me-3" style="width: 50px; height: 50px; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: #dbeafe; color: #2563eb;">
<i class="bi bi-building" style="font-size: 1.5rem;"></i>
</div>
<div>
<h6 class="text-muted mb-1 small text-uppercase fw-bold">Toplam Firma</h6>
<h3 class="fw-bold mb-0 text-primary">{{ toplam_firma }}</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card stat-card border-0 shadow-sm">
<div class="card-body d-flex align-items-center p-4">
<div class="bg-success-soft stat-icon me-3" style="width: 50px; height: 50px; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: #dcfce7; color: #15803d;">
<i class="bi bi-file-earmark-text" style="font-size: 1.5rem;"></i>
</div>
<div>
<h6 class="text-muted mb-1 small text-uppercase fw-bold">Toplam Fatura</h6>
<h3 class="fw-bold mb-0 text-success">{{ toplam_fatura }}</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card stat-card border-0 shadow-sm">
<div class="card-body d-flex align-items-center p-4">
<div class="bg-warning-soft stat-icon me-3" style="width: 50px; height: 50px; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: #fef9c3; color: #a16207;">
<i class="bi bi-cash-stack" style="font-size: 1.5rem;"></i>
</div>
<div>
<h6 class="text-muted mb-1 small text-uppercase fw-bold">Toplam Harcama</h6>
<h3 class="fw-bold mb-0 text-warning">₺{{ toplam_harcama|floatformat:2 }}</h3>
</div>
</div>
</div>
</div>
</div>
<!-- Stats Cards -->
<div class="row">
<div class="col-md-4">
<div class="card stat-card">
<div class="stat-icon bg-primary-soft">
<i class="bi bi-building"></i>
</div>
<div>
<h6 class="text-muted mb-1">Toplam Firma</h6>
<h3 class="fw-bold mb-0">{{ toplam_firma }}</h3>
</div>
<div class="row mt-2">
<!-- Recent Invoices Table -->
<div class="col-lg-8">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between border-0">
<h5 class="mb-0 fw-bold"><i class="bi bi-clock-history me-2 text-primary"></i>Son Eklenen Faturalar</h5>
<a href="{% url 'fatura_arsivi' %}" class="btn btn-sm btn-link text-decoration-none">Tümünü Gör <i class="bi bi-arrow-right"></i></a>
</div>
</div>
<div class="col-md-4">
<div class="card stat-card">
<div class="stat-icon bg-success-soft">
<i class="bi bi-file-earmark-text"></i>
</div>
<div>
<h6 class="text-muted mb-1">Toplam Fatura</h6>
<h3 class="fw-bold mb-0">{{ toplam_fatura }}</h3>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card stat-card">
<div class="stat-icon bg-warning-soft">
<i class="bi bi-currency-dollar"></i>
</div>
<div>
<h6 class="text-muted mb-1">Toplam Harcama</h6>
<h3 class="fw-bold mb-0">₺{{ toplam_harcama|floatformat:2 }}</h3>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr class="bg-light">
<th class="ps-4 border-0">Fatura No</th>
<th class="border-0">Firma</th>
<th class="border-0">Tarih</th>
<th class="border-0">Toplam</th>
<th class="text-end pe-4 border-0">İşlem</th>
</tr>
</thead>
<tbody>
{% for fatura in son_faturalar %}
<tr>
<td class="ps-4 fw-medium text-dark">{{ fatura.fatura_no }}</td>
<td>
<span class="badge bg-light text-dark fw-medium border px-2 py-1">{{ fatura.firma.ad }}</span>
</td>
<td class="text-muted">{{ fatura.tarih|date:"d.m.Y" }}</td>
<td class="fw-bold text-primary">₺{{ fatura.genel_toplam }}</td>
<td class="text-end pe-4">
<a href="{% url 'fatura_detay' fatura.pk %}" class="btn btn-sm btn-light rounded-pill">
<i class="bi bi-eye"></i> İncele
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center py-5">
<div class="opacity-50 mb-3">
<i class="bi bi-file-earmark-plus" style="font-size: 3rem;"></i>
</div>
<p class="text-muted mb-0">Henüz fatura eklenmemiş.</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<!-- Recent Invoices Table -->
<div class="col-lg-8">
<div class="card">
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between">
<h5 class="mb-0 fw-bold">Son Eklenen Faturalar</h5>
<a href="#" class="btn btn-sm btn-outline-primary">Tümünü Gör</a>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th class="ps-4">Fatura No</th>
<th>Firma</th>
<th>Tarih</th>
<th>Toplam</th>
<th>Durum</th>
<th class="text-end pe-4">İşlem</th>
</tr>
</thead>
<tbody>
{% for fatura in son_faturalar %}
<tr>
<td class="ps-4 fw-medium">{{ fatura.fatura_no }}</td>
<td>{{ fatura.firma.ad }}</td>
<td>{{ fatura.tarih|date:"d.m.Y" }}</td>
<td class="fw-bold">₺{{ fatura.genel_toplam }}</td>
<td>
{% if fatura.islenmis %}
<span class="badge bg-success-soft text-success">İşlendi</span>
{% else %}
<span class="badge bg-warning-soft text-warning">Bekliyor</span>
{% endif %}
</td>
<td class="text-end pe-4">
<button class="btn btn-sm btn-light"><i class="bi bi-eye"></i></button>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center py-5 text-muted">Henüz fatura eklenmemiş.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Quick Actions -->
<div class="col-lg-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white py-3 border-0">
<h5 class="mb-0 fw-bold"><i class="bi bi-lightning-fill me-2 text-warning"></i>Hızlı İşlemler</h5>
</div>
</div>
<!-- Quick Actions -->
<div class="col-lg-4">
<div class="card">
<div class="card-header bg-white py-3">
<h5 class="mb-0 fw-bold">Hızlı İşlemler</h5>
<div class="card-body">
<div class="d-grid gap-3">
<a href="{% url 'fatura_ekle' %}" class="btn btn-primary text-start p-3 rounded-4 d-flex align-items-center shadow-sm border-0 text-decoration-none" style="background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);">
<div class="bg-white bg-opacity-25 rounded-3 p-2 me-3">
<i class="bi bi-plus-circle-fill fs-4"></i>
</div>
<div>
<div class="fw-bold text-white">Fatura Yükle</div>
<div class="small text-white opacity-75">PDF dosyasını seçin</div>
</div>
</a>
<a href="{% url 'firma_ekle' %}" class="btn btn-outline-light text-dark text-start p-3 rounded-4 d-flex align-items-center border bg-light bg-opacity-50 text-decoration-none">
<div class="bg-white rounded-3 p-2 me-3 shadow-sm text-primary">
<i class="bi bi-folder-plus fs-4"></i>
</div>
<div>
<div class="fw-bold text-dark">Yeni Firma Ekle</div>
<div class="small text-muted">Sisteme yeni tedarikçi kaydet</div>
</div>
</a>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button class="btn btn-primary text-start py-3 px-4 rounded-3 d-flex align-items-center">
<i class="bi bi-plus-circle-fill me-3 fs-4"></i>
<div>
<div class="fw-bold">Fatura Yükle</div>
<div class="small opacity-75">PDF dosyasını sürükle bırak</div>
</div>
</button>
<button class="btn btn-outline-secondary text-start py-3 px-4 rounded-3 d-flex align-items-center">
<i class="bi bi-folder-plus me-3 fs-4"></i>
<div>
<div class="fw-bold">Klasör Tara</div>
<div class="small opacity-75">Yerel dizindeki faturaları bul</div>
</div>
</button>
<button class="btn btn-outline-secondary text-start py-3 px-4 rounded-3 d-flex align-items-center">
<i class="bi bi-building-add me-3 fs-4"></i>
<div>
<div class="fw-bold">Yeni Firma Ekle</div>
<div class="small opacity-75">Firma bilgilerini manuel tanımla</div>
</div>
</button>
</div>
<div class="mt-4 p-4 rounded-4 bg-primary bg-opacity-10 border border-primary border-opacity-10">
<h6 class="fw-bold text-primary mb-2"><i class="bi bi-info-circle-fill me-2"></i>Biliyor muydunuz?</h6>
<p class="small text-muted mb-0">Sistemimiz OCR teknolojisi sayesinde faturadaki tüm kalemleri otomatik olarak ayrıştırabilir.</p>
</div>
</div>
</div>

View File

@ -0,0 +1,68 @@
{% extends "base.html" %}
{% block title %}İstatistik & Raporlar - FaturaYol{% endblock %}
{% block page_title %}İstatistik & Raporlar{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col-md-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="fw-bold mb-4">Aylık Harcama Trendi</h5>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Ay</th>
<th>Fatura Adedi</th>
<th class="text-end">Toplam Tutar</th>
</tr>
</thead>
<tbody>
{% for harcama in aylik_harcama %}
<tr>
<td>{{ harcama.tarih__month }}. Ay</td>
<td>{{ harcama.adet }}</td>
<td class="text-end fw-bold">{{ harcama.toplam }} ₺</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-center py-4">Veri bulunamadı</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="fw-bold mb-4">KDV Özeti</h5>
<div class="p-4 bg-light rounded-4 text-center mb-4">
<small class="text-muted d-block mb-1">Toplam Ödenen KDV</small>
<h2 class="fw-bold text-success mb-0">{{ kdv_ozet.toplam_kdv|default:0 }} ₺</h2>
</div>
<div class="p-4 bg-light rounded-4 text-center">
<small class="text-muted d-block mb-1">Fatura Başı Ortalama KDV</small>
<h2 class="fw-bold text-primary mb-0">{{ kdv_ozet.ortalama_kdv|default:0|floatformat:2 }} ₺</h2>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body py-5 text-center">
<i class="bi bi-graph-up-arrow text-primary opacity-25" style="font-size: 4rem;"></i>
<h4 class="mt-3 fw-bold">Gelişmiş Raporlama Yakında</h4>
<p class="text-muted">Grafikler, Excel dışa aktarma ve kategori bazlı analizler bir sonraki güncellemede eklenecektir.</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,61 @@
{% extends "base.html" %}
{% block title %}Arama Sonuçları - FaturaYol{% endblock %}
{% block page_title %}"{{ query }}" için Arama Sonuçları{% endblock %}
{% block content %}
<div class="mb-5">
<h5 class="fw-bold mb-3"><i class="bi bi-building me-2 text-primary"></i>Firmalar ({{ firmalar|length }})</h5>
<div class="row">
{% for firma in firmalar %}
<div class="col-md-4 mb-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="fw-bold mb-1">{{ firma.ad }}</h6>
<p class="small text-muted mb-2">VN: {{ firma.vergi_no }}</p>
<a href="{% url 'firma_detay' firma.pk %}" class="btn btn-sm btn-outline-primary rounded-pill">Detaya Git</a>
</div>
</div>
</div>
{% empty %}
<div class="col-12 text-muted">Eşleşen firma bulunamadı.</div>
{% endfor %}
</div>
</div>
<div>
<h5 class="fw-bold mb-3"><i class="bi bi-file-earmark-text me-2 text-primary"></i>Faturalar ({{ faturalar|length }})</h5>
<div class="card border-0 shadow-sm">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="ps-4">Fatura No</th>
<th>Firma</th>
<th>Tarih</th>
<th>Genel Toplam</th>
<th class="text-end pe-4">İşlem</th>
</tr>
</thead>
<tbody>
{% for fatura in faturalar %}
<tr>
<td class="ps-4 fw-medium">{{ fatura.fatura_no }}</td>
<td>{{ fatura.firma.ad }}</td>
<td>{{ fatura.tarih|date:"d.m.Y" }}</td>
<td class="fw-bold">₺{{ fatura.genel_toplam }}</td>
<td class="text-end pe-4">
<a href="{% url 'fatura_detay' fatura.pk %}" class="btn btn-sm btn-light rounded-pill">İncele</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center py-4 text-muted">Eşleşen fatura bulunamadı.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,7 +1,18 @@
from django.urls import path
from .views import home
from .views import (
home, fatura_arsivi, firma_detay, fatura_detay, raporlar,
firma_ekle, fatura_ekle, search, firma_sil, fatura_sil
)
urlpatterns = [
path("", home, name="home"),
]
path("arsiv/", fatura_arsivi, name="fatura_arsivi"),
path("firma/<int:pk>/", firma_detay, name="firma_detay"),
path("fatura/<int:pk>/", fatura_detay, name="fatura_detay"),
path("raporlar/", raporlar, name="raporlar"),
path("firma/ekle/", firma_ekle, name="firma_ekle"),
path("fatura/ekle/", fatura_ekle, name="fatura_ekle"),
path("arama/", search, name="search"),
path("firma/<int:pk>/sil/", firma_sil, name="firma_sil"),
path("fatura/<int:pk>/sil/", fatura_sil, name="fatura_sil"),
]

View File

@ -1,13 +1,14 @@
from django.shortcuts import render
from django.db.models import Sum, Count
from .models import Firma, Fatura
from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Sum, Count, Avg, Q
from .models import Firma, Fatura, FaturaKalemi
from .forms import FirmaForm, FaturaForm
def home(request):
"""Fatura Yönetimi Gösterge Paneli."""
toplam_firma = Firma.objects.count()
toplam_fatura = Fatura.objects.count()
toplam_harcama = Fatura.objects.aggregate(Sum('genel_toplam'))['genel_toplam__sum'] or 0
son_faturalar = Fatura.objects.select_related('firma').all()[:10]
son_faturalar = Fatura.objects.select_related('firma').all().order_by('-olusturulma_tarihi')[:10]
context = {
"toplam_firma": toplam_firma,
@ -16,4 +17,123 @@ def home(request):
"son_faturalar": son_faturalar,
"active_menu": "dashboard"
}
return render(request, "core/index.html", context)
return render(request, "core/index.html", context)
def fatura_arsivi(request):
"""Firmalara göre gruplanmış fatura arşivi."""
firmalar = Firma.objects.annotate(
fatura_sayisi=Count('faturalar'),
toplam_tutar=Sum('faturalar__genel_toplam')
).order_by('ad')
context = {
"firmalar": firmalar,
"active_menu": "archive"
}
return render(request, "core/fatura_arsivi.html", context)
def firma_detay(request, pk):
"""Belirli bir firmanın fatura listesi."""
firma = get_object_or_404(Firma, pk=pk)
faturalar = firma.faturalar.all().order_by('-tarih')
context = {
"firma": firma,
"faturalar": faturalar,
"active_menu": "archive"
}
return render(request, "core/firma_detay.html", context)
def fatura_detay(request, pk):
"""Fatura detay önizleme ve kalemleri."""
fatura = get_object_or_404(Fatura.objects.select_related('firma'), pk=pk)
kalemler = fatura.kalemler.all()
context = {
"fatura": fatura,
"kalemler": kalemler,
"active_menu": "archive"
}
return render(request, "core/fatura_detay.html", context)
def raporlar(request):
"""İstatistik ve Raporlar."""
# Aylık harcama trendi
aylik_harcama = Fatura.objects.values('tarih__month').annotate(
toplam=Sum('genel_toplam'),
adet=Count('id')
).order_by('tarih__month')
# KDV dağılımı
kdv_ozet = Fatura.objects.aggregate(
toplam_kdv=Sum('kdv_toplam'),
ortalama_kdv=Avg('kdv_toplam')
)
context = {
"aylik_harcama": aylik_harcama,
"kdv_ozet": kdv_ozet,
"active_menu": "reports"
}
return render(request, "core/raporlar.html", context)
def firma_ekle(request):
if request.method == 'POST':
form = FirmaForm(request.POST)
if form.is_valid():
form.save()
return redirect('fatura_arsivi')
else:
form = FirmaForm()
return render(request, 'core/firma_form.html', {'form': form, 'title': 'Yeni Firma Ekle'})
def fatura_ekle(request):
firma_id = request.GET.get('firma')
initial = {}
if firma_id:
initial['firma'] = firma_id
if request.method == 'POST':
form = FaturaForm(request.POST, request.FILES)
if form.is_valid():
fatura = form.save()
return redirect('firma_detay', pk=fatura.firma.pk)
else:
form = FaturaForm(initial=initial)
return render(request, 'core/fatura_form.html', {'form': form, 'title': 'Yeni Fatura Yükle'})
def search(request):
query = request.GET.get('q', '')
faturalar = []
firmalar = []
if query:
faturalar = Fatura.objects.filter(
Q(fatura_no__icontains=query) |
Q(firma__ad__icontains=query)
).select_related('firma')
firmalar = Firma.objects.filter(
Q(ad__icontains=query) |
Q(vergi_no__icontains=query)
)
context = {
'query': query,
'faturalar': faturalar,
'firmalar': firmalar,
}
return render(request, 'core/search_results.html', context)
def firma_sil(request, pk):
firma = get_object_or_404(Firma, pk=pk)
if request.method == 'POST':
firma.delete()
return redirect('fatura_arsivi')
return render(request, 'core/confirm_delete.html', {'object': firma, 'type': 'Firma'})
def fatura_sil(request, pk):
fatura = get_object_or_404(Fatura, pk=pk)
firma_pk = fatura.firma.pk
if request.method == 'POST':
fatura.delete()
return redirect('firma_detay', pk=firma_pk)
return render(request, 'core/confirm_delete.html', {'object': fatura, 'type': 'Fatura'})