This commit is contained in:
ilkeral
2025-08-08 07:24:25 +03:00
parent 342f1314c7
commit f4ee7a2d0b
29 changed files with 5189 additions and 1140 deletions

View File

@ -0,0 +1,888 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse
from django.contrib import messages
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.db import models
from django.db.models import Sum, F, Value, FloatField, Count
from django.db.models.functions import Coalesce
from django.utils import timezone
from django.core.paginator import Paginator
from django.views.decorators.csrf import csrf_exempt
from .models import Invoice, InvoiceItem, Customer, Project
import json
from datetime import datetime, timedelta
from decimal import Decimal
@login_required
def invoices(request):
"""Faturalar sayfası - tüm faturaların listesi"""
invoices = Invoice.objects.all().select_related('customer').order_by('-issue_date')
customers = Customer.objects.filter(is_active=True).order_by('name')
# Fatura özeti istatistikleri
stats = {
'total': invoices.count(),
'paid': invoices.filter(status='paid').count(),
'pending': invoices.filter(status__in=['draft', 'sent']).count(),
'overdue': invoices.filter(status='overdue').count(),
'total_amount': invoices.aggregate(Sum('total_amount'))['total_amount__sum'] or 0,
'paid_amount': invoices.filter(status='paid').aggregate(Sum('total_amount'))['total_amount__sum'] or 0,
}
context = {
'invoices': invoices,
'customers': customers,
'stats': stats,
}
return render(request, 'ssh_manager/faturalar.html', context)
@login_required
def invoice_detail(request, invoice_id):
"""Fatura detay sayfası"""
try:
invoice = get_object_or_404(Invoice, id=invoice_id)
# Decimal değerleri güvenli bir şekilde almak için
from decimal import Decimal, InvalidOperation, ROUND_DOWN
# Invoice total_amount değerini güvenli bir şekilde al
try:
if invoice.total_amount is None:
invoice.total_amount = Decimal('0.00')
elif isinstance(invoice.total_amount, str):
# Virgül varsa noktaya çevir
if ',' in invoice.total_amount:
invoice.total_amount = invoice.total_amount.replace(',', '.')
invoice.total_amount = Decimal(invoice.total_amount).quantize(Decimal('0.01'), rounding=ROUND_DOWN)
except (InvalidOperation, TypeError) as e:
print(f"Total amount dönüşüm hatası: {str(e)}")
invoice.total_amount = Decimal('0.00')
# Invoice items'ları güvenli bir şekilde al
items = invoice.items.all().select_related('project')
safe_items = []
for item in items:
try:
if item.amount is None:
item.amount = Decimal('0.00')
elif isinstance(item.amount, str):
# Virgül varsa noktaya çevir
if ',' in item.amount:
item.amount = item.amount.replace(',', '.')
item.amount = Decimal(item.amount).quantize(Decimal('0.01'), rounding=ROUND_DOWN)
safe_items.append(item)
except (InvalidOperation, TypeError) as e:
print(f"Item amount dönüşüm hatası: {str(e)}")
item.amount = Decimal('0.00')
safe_items.append(item)
# Status ve payment_method display metodlarını önceden çağır
try:
status_display = invoice.get_status_display()
except Exception:
status_display = invoice.status
try:
payment_method_display = invoice.get_payment_method_display()
except Exception:
payment_method_display = invoice.payment_method
context = {
'invoice': invoice,
'items': safe_items,
'status_display': status_display,
'payment_method_display': payment_method_display
}
return render(request, 'ssh_manager/fatura_detay.html', context)
except Exception as e:
import traceback
print(f"Fatura detayı gösterilirken hata oluştu: {str(e)}")
print(traceback.format_exc())
messages.error(request, f"Fatura görüntülenirken bir hata oluştu: {str(e)}")
return redirect('faturalar')
@login_required
@require_http_methods(["POST"])
def create_invoice(request):
"""Yeni fatura oluştur"""
try:
print("---------- YENİ FATURA OLUŞTURMA İSTEĞİ ----------")
print("REQUEST METHOD:", request.method)
print("CONTENT TYPE:", request.content_type)
print("REQUEST HEADERS:", request.headers)
try:
request_body = request.body.decode('utf-8')
print("REQUEST BODY:", request_body)
# JSON data'yı parse et
data = json.loads(request_body)
print("PARSED DATA:", data)
except Exception as e:
print(f"JSON parse hatası: {str(e)}")
return JsonResponse({'success': False, 'message': f'JSON veri hatası: {str(e)}'})
# Verileri al
customer_id = data.get('customer_id')
invoice_type = data.get('invoice_type', 'income') # Varsayılan olarak gelir
issue_date = data.get('issue_date')
due_date = data.get('due_date')
payment_method = data.get('payment_method', 'bank_transfer')
description = data.get('description', '')
project_id = data.get('project_id')
amount = data.get('amount', 0)
print(f"ALANLAR:")
print(f"- customer_id: {customer_id}")
print(f"- invoice_type: {invoice_type}")
print(f"- issue_date: {issue_date}")
print(f"- due_date: {due_date}")
print(f"- payment_method: {payment_method}")
print(f"- description: {description}")
print(f"- project_id: {project_id}")
print(f"- amount: {amount}")
# Zorunlu alanları kontrol et
if not customer_id:
print("HATA: customer_id eksik")
return JsonResponse({'success': False, 'message': 'Müşteri seçilmesi zorunludur.'})
if not issue_date:
print("HATA: issue_date eksik")
return JsonResponse({'success': False, 'message': 'Düzenleme tarihi zorunludur.'})
if not due_date:
print("HATA: due_date eksik")
return JsonResponse({'success': False, 'message': 'Son ödeme tarihi zorunludur.'})
if not description:
print("HATA: description eksik")
return JsonResponse({'success': False, 'message': 'ıklama zorunludur.'})
try:
# Tutarı doğru şekilde kontrol et
if amount is None or amount == '':
print("HATA: amount boş")
return JsonResponse({'success': False, 'message': 'Tutar boş olamaz.'})
# Virgül olan sayıları nokta ile değiştir
if isinstance(amount, str) and ',' in amount:
amount = amount.replace(',', '.')
# Decimal dönüşümü için her zaman string kullan
amount_decimal = Decimal(str(amount))
if amount_decimal <= 0:
print("HATA: amount negatif veya sıfır")
return JsonResponse({'success': False, 'message': 'Geçerli bir tutar giriniz.'})
print(f"Tutar başarıyla dönüştürüldü: {amount_decimal}")
except Exception as e:
print(f"HATA: amount geçersiz: {str(e)}")
return JsonResponse({'success': False, 'message': f'Geçerli bir tutar giriniz. Hata: {str(e)}'})
# Müşteriyi bul
try:
customer = Customer.objects.get(id=customer_id)
print(f"Müşteri bulundu: {customer}")
except Customer.DoesNotExist:
print(f"HATA: customer_id={customer_id} için müşteri bulunamadı")
return JsonResponse({'success': False, 'message': 'Geçersiz müşteri.'})
# Tarihleri doğru formata çevir
try:
issue_date_obj = datetime.strptime(issue_date, '%Y-%m-%d').date()
due_date_obj = datetime.strptime(due_date, '%Y-%m-%d').date()
print(f"Tarihler başarıyla parse edildi: {issue_date_obj}, {due_date_obj}")
except ValueError as e:
print(f"HATA: Tarih formatı hatalı: {str(e)}")
return JsonResponse({'success': False, 'message': 'Geçersiz tarih formatı.'})
print("Fatura oluşturuluyor...")
# Fatura oluştur
invoice = Invoice.objects.create(
customer=customer,
invoice_type=invoice_type,
issue_date=issue_date_obj,
due_date=due_date_obj,
payment_method=payment_method,
status='draft'
)
print(f"Fatura oluşturuldu: ID={invoice.id}, Numara={invoice.invoice_number}, Tip={invoice_type}")
# Projeyi bul
project = None
if project_id:
try:
project = Project.objects.get(id=project_id)
print(f"Proje bulundu: {project}")
except Project.DoesNotExist:
print(f"UYARI: project_id={project_id} için proje bulunamadı")
pass
print("Fatura kalemi oluşturuluyor...")
# Tek fatura kalemi olarak ekle - amount_decimal zaten oluşturuldu, tekrar dönüştürmeye gerek yok
item = InvoiceItem.objects.create(
invoice=invoice,
project=project,
description=description,
amount=amount_decimal
)
print(f"Fatura kalemi oluşturuldu: ID={item.id}")
# Başarılı yanıt döndür
response_data = {
'success': True,
'message': 'Fatura başarıyla oluşturuldu.',
'invoice_id': invoice.id,
'invoice_number': invoice.invoice_number
}
print("Başarılı yanıt:", response_data)
return JsonResponse(response_data)
except Exception as e:
import traceback
print(f"KRİTİK HATA: Fatura oluşturulamadı: {str(e)}")
print(traceback.format_exc())
# Daha detaylı hata mesajı oluştur
error_type = type(e).__name__
error_msg = str(e)
if error_type == 'InvalidOperation':
# Decimal dönüşüm hatası
return JsonResponse({'success': False, 'message': f'Geçersiz tutar formatı. Lütfen sayısal bir değer girin.'})
else:
# Diğer hatalar
return JsonResponse({'success': False, 'message': f'Beklenmeyen bir hata oluştu: {error_type} - {error_msg}'})
@login_required
@require_http_methods(["POST"])
def update_invoice(request, invoice_id):
"""Fatura güncelle"""
try:
from decimal import Decimal, InvalidOperation
invoice = get_object_or_404(Invoice, id=invoice_id)
data = json.loads(request.body)
# Temel fatura bilgilerini güncelle
if 'issue_date' in data:
try:
invoice.issue_date = datetime.strptime(data['issue_date'], '%Y-%m-%d').date()
except ValueError as e:
return JsonResponse({'success': False, 'message': f'Geçersiz düzenleme tarihi formatı: {str(e)}'})
if 'due_date' in data:
try:
invoice.due_date = datetime.strptime(data['due_date'], '%Y-%m-%d').date()
except ValueError as e:
return JsonResponse({'success': False, 'message': f'Geçersiz son ödeme tarihi formatı: {str(e)}'})
if 'payment_method' in data:
invoice.payment_method = data['payment_method']
if 'status' in data:
invoice.status = data['status']
# Müşteri değişikliği
if 'customer_id' in data:
try:
customer = Customer.objects.get(id=data['customer_id'])
invoice.customer = customer
except Customer.DoesNotExist:
return JsonResponse({'success': False, 'message': 'Geçersiz müşteri.'})
invoice.save()
# Fatura açıklama ve tutarını güncelle
description = data.get('description')
amount = data.get('amount')
project_id = data.get('project_id')
# Mevcut kalemleri temizle
invoice.items.all().delete()
# Projeyi bul
project = None
if project_id:
try:
project = Project.objects.get(id=project_id)
except Project.DoesNotExist:
pass
# Tutarı güvenli bir şekilde Decimal'e dönüştür
if description is not None and amount is not None:
try:
# Virgül olan sayıları nokta ile değiştir
if isinstance(amount, str) and ',' in amount:
amount = amount.replace(',', '.')
# Decimal dönüşümü için her zaman string kullan
amount_decimal = Decimal(str(amount))
if amount_decimal <= 0:
return JsonResponse({'success': False, 'message': 'Geçerli bir tutar giriniz (0\'dan büyük olmalı).'})
# Yeni kalemi ekle
InvoiceItem.objects.create(
invoice=invoice,
project=project,
description=description,
amount=amount_decimal
)
except (InvalidOperation, ValueError, TypeError) as e:
return JsonResponse({'success': False, 'message': f'Geçersiz tutar formatı: {str(e)}'})
else:
return JsonResponse({'success': False, 'message': 'ıklama ve tutar alanları gereklidir.'})
return JsonResponse({
'success': True,
'message': 'Fatura başarıyla güncellendi.',
})
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(f"Fatura güncelleme hatası: {str(e)}")
print(f"Hata ayrıntıları: {error_trace}")
# Decimal hatası için özel mesaj
if 'decimal.InvalidOperation' in str(e):
return JsonResponse({
'success': False,
'message': 'Fatura tutarı geçersiz format içeriyor. Lütfen geçerli bir sayısal değer girin.'
})
else:
return JsonResponse({
'success': False,
'message': f'Fatura güncelleme hatası: {str(e)}'
})
@login_required
@require_http_methods(["DELETE"])
def delete_invoice(request, invoice_id):
"""Fatura sil"""
try:
invoice = get_object_or_404(Invoice, id=invoice_id)
invoice_number = invoice.invoice_number
invoice.delete()
return JsonResponse({
'success': True,
'message': f'{invoice_number} numaralı fatura silindi.'
})
except Exception as e:
return JsonResponse({
'success': False,
'message': f'Fatura silme hatası: {str(e)}'
})
@login_required
def get_invoice_details(request, invoice_id):
"""Fatura detay bilgilerini JSON olarak döndür - API endpoint"""
try:
from decimal import Decimal, InvalidOperation
import traceback
print(f"get_invoice_details fonksiyonu çağrıldı: Invoice ID = {invoice_id}")
try:
invoice = get_object_or_404(Invoice, id=invoice_id)
print(f"Invoice {invoice_id} bulundu")
except Exception as e:
print(f"Invoice {invoice_id} bulunamadı: {str(e)}")
return JsonResponse({
'success': False,
'message': f'Fatura bulunamadı (ID: {invoice_id})'
}, status=404)
# Decimal değerlerini güvenli bir şekilde dizeye dönüştür
def safe_decimal_to_str(value):
if value is None:
return "0.00"
try:
# Eğer string ise ve virgül içeriyorsa, nokta ile değiştir
if isinstance(value, str) and ',' in value:
value = value.replace(',', '.')
# Decimal'e dönüştür
decimal_value = Decimal(str(value))
return str(decimal_value)
except (InvalidOperation, ValueError, TypeError) as e:
print(f"Decimal dönüşüm hatası: {value} - {str(e)}")
return "0.00"
# Müşteri bilgisini güvenli bir şekilde al
customer_data = {'id': 0, 'name': 'Bilinmeyen Müşteri'}
try:
if invoice.customer:
customer_data = {
'id': invoice.customer.id,
'name': invoice.customer.name # Direkt olarak name özelliğini kullan
}
except Exception as e:
print(f"Müşteri bilgisi alınamadı: {str(e)}")
# Temel fatura bilgilerini güvenli şekilde al
invoice_data = {
'id': invoice.id,
'invoice_number': invoice.invoice_number or f"FATURA-{invoice.id}",
'status': invoice.status or 'draft',
'payment_method': invoice.payment_method or 'bank_transfer',
'total_amount': safe_decimal_to_str(invoice.total_amount),
'notes': invoice.notes or '',
'payment_notes': invoice.payment_notes or '',
'customer': customer_data,
'items': []
}
# Tarihleri güvenli şekilde ekle
try:
invoice_data['issue_date'] = invoice.issue_date.strftime('%Y-%m-%d') if invoice.issue_date else ""
except Exception as e:
print(f"Issue date formatlanırken hata: {str(e)}")
invoice_data['issue_date'] = ""
try:
invoice_data['due_date'] = invoice.due_date.strftime('%Y-%m-%d') if invoice.due_date else ""
except Exception as e:
print(f"Due date formatlanırken hata: {str(e)}")
invoice_data['due_date'] = ""
try:
invoice_data['created_at'] = invoice.created_at.strftime('%Y-%m-%d %H:%M:%S') if invoice.created_at else ""
invoice_data['updated_at'] = invoice.updated_at.strftime('%Y-%m-%d %H:%M:%S') if invoice.updated_at else ""
except Exception as e:
print(f"Oluşturma/güncelleme tarihi formatlanırken hata: {str(e)}")
invoice_data['created_at'] = ""
invoice_data['updated_at'] = ""
# Fatura kalemlerini güvenli şekilde al
try:
items = invoice.items.all()
print(f"{len(items)} kalem bulundu")
# Kalem bulunamadıysa bile en azından bir tane boş kalem ekle
if not items.exists():
print("Fatura kalemi bulunamadı, varsayılan boş kalem ekleniyor")
invoice_data['items'].append({
'id': 0,
'description': '',
'amount': '0.00',
'project_id': None,
'project_name': None
})
for item in items:
try:
# Proje bilgisini güvenli bir şekilde al
project_id = None
project_name = None
try:
if item.project:
project_id = item.project.id
project_name = item.project.name
except Exception as proj_err:
print(f"Proje bilgisi alınamadı: {str(proj_err)}")
# Kalem açıklama bilgisini mutlaka ekle
description = item.description
if not description or description.strip() == '':
description = 'ıklama yok'
print(f"Kalem {item.id} için açıklama bulunamadı, varsayılan değer kullanıldı")
else:
print(f"Kalem {item.id}ıklaması: '{description}'")
# Tutarı güvenli şekilde al
amount = safe_decimal_to_str(item.amount)
print(f"Kalem {item.id} tutarı: {amount}")
item_data = {
'id': item.id,
'description': description,
'amount': amount,
'project_id': project_id,
'project_name': project_name
}
invoice_data['items'].append(item_data)
print(f"Kalem eklendi: {item_data}")
except Exception as item_err:
print(f"Kalem serileştirilirken hata: {str(item_err)}")
# Hataya neden olan kalemi atla ve devam et
except Exception as items_err:
print(f"Kalemler alınırken hata: {str(items_err)}")
# Başarılı yanıt döndür
print("Fatura detayları başarıyla alındı")
return JsonResponse({
'success': True,
'invoice': invoice_data
})
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(f"Fatura detayları alınamadı hata: {str(e)}")
print(f"Hata ayrıntıları: {error_trace}")
# Özel hata mesajları
error_message = str(e)
if 'decimal.InvalidOperation' in error_message:
error_message = 'Fatura tutarı geçersiz format içeriyor.'
elif 'DoesNotExist' in error_message:
error_message = f'Fatura (ID: {invoice_id}) bulunamadı.'
elif 'NoneType' in error_message:
error_message = 'Bazı veriler eksik veya geçersiz.'
# Daha fazla tanısal bilgi ekle
debug_info = f"[Hata tipi: {type(e).__name__}]"
return JsonResponse({
'success': False,
'message': f'Fatura detayları alınamadı: {error_message} {debug_info}'
}, status=500)
@login_required
def get_projects_by_customer(request, customer_id):
"""Müşteriye ait projeleri getir"""
try:
projects = Project.objects.filter(customer_id=customer_id)
data = [{
'id': project.id,
'name': project.name,
'folder_name': project.folder_name,
'url': project.url
} for project in projects]
return JsonResponse({'success': True, 'projects': data})
except Exception as e:
return JsonResponse({'success': False, 'message': str(e)})
@login_required
def invoice_reports(request):
"""Fatura raporları sayfası"""
# Tarih filtreleri
today = timezone.now().date()
start_date = request.GET.get('start_date', (today - timedelta(days=30)).strftime('%Y-%m-%d'))
end_date = request.GET.get('end_date', today.strftime('%Y-%m-%d'))
# String tarihleri datetime'a çevir
try:
start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
except ValueError:
start_date = today - timedelta(days=30)
end_date = today
# Filtreler
invoices = Invoice.objects.filter(issue_date__range=[start_date, end_date])
# İstatistikler
stats = {
'total_invoices': invoices.count(),
'total_amount': invoices.aggregate(Sum('total_amount'))['total_amount__sum'] or 0,
'paid_amount': invoices.filter(status='paid').aggregate(Sum('total_amount'))['total_amount__sum'] or 0,
'unpaid_amount': invoices.filter(status__in=['draft', 'sent', 'overdue']).aggregate(Sum('total_amount'))['total_amount__sum'] or 0,
'paid_count': invoices.filter(status='paid').count(),
'unpaid_count': invoices.filter(status__in=['draft', 'sent', 'overdue']).count(),
'overdue_count': invoices.filter(status='overdue').count(),
}
# Müşterilere göre dağılım
customers_data = invoices.values('customer__name').annotate(
total=Sum('total_amount')
).order_by('-total')[:10]
# Durumlara göre dağılım
status_data = invoices.values('status').annotate(
count=models.Count('id'),
total=Sum('total_amount')
).order_by('status')
context = {
'stats': stats,
'customers_data': customers_data,
'status_data': status_data,
'start_date': start_date.strftime('%Y-%m-%d'),
'end_date': end_date.strftime('%Y-%m-%d'),
'today': today.strftime('%Y-%m-%d'),
}
return render(request, 'ssh_manager/fatura_raporlari.html', context)
@login_required
def test_fatura(request):
"""Test fatura sayfası"""
customers = Customer.objects.filter(is_active=True).order_by('name')
context = {
'customers': customers
}
return render(request, 'ssh_manager/test_fatura.html', context)
@login_required
@require_http_methods(["POST"])
def update_invoice_status(request, invoice_id):
"""Fatura durumunu günceller"""
try:
# JSON veriyi parse et
data = json.loads(request.body)
new_status = data.get('status')
if not new_status:
return JsonResponse({
'success': False,
'message': 'Durum bilgisi gerekli'
}, status=400)
# Geçerli durum kontrolü
valid_statuses = ['draft', 'sent', 'paid', 'overdue', 'cancelled']
if new_status not in valid_statuses:
return JsonResponse({
'success': False,
'message': f'Geçersiz durum: {new_status}'
}, status=400)
# Faturayı bul ve güncelle
invoice = get_object_or_404(Invoice, id=invoice_id)
old_status = invoice.status
invoice.status = new_status
invoice.save()
# Log mesajı
print(f"Fatura {invoice_id} durumu '{old_status}' -> '{new_status}' olarak güncellendi")
return JsonResponse({
'success': True,
'message': f'Fatura durumu başarıyla güncellendi',
'old_status': old_status,
'new_status': new_status
})
except json.JSONDecodeError:
return JsonResponse({
'success': False,
'message': 'Geçersiz JSON verisi'
}, status=400)
except Exception as e:
print(f"Fatura durum güncelleme hatası: {str(e)}")
return JsonResponse({
'success': False,
'message': f'Durum güncellenirken hata oluştu: {str(e)}'
}, status=500)
@login_required
@require_http_methods(["POST"])
def bulk_update_invoice_status(request):
"""Toplu fatura durum güncelleme"""
try:
data = json.loads(request.body)
invoice_ids = data.get('invoice_ids', [])
new_status = data.get('status')
if not invoice_ids or not new_status:
return JsonResponse({
'success': False,
'message': 'Fatura ID\'leri ve durum bilgisi gerekli'
}, status=400)
# Geçerli durum kontrolü
valid_statuses = ['draft', 'sent', 'paid', 'overdue', 'cancelled']
if new_status not in valid_statuses:
return JsonResponse({
'success': False,
'message': f'Geçersiz durum: {new_status}'
}, status=400)
# Faturaları toplu güncelle
updated_count = Invoice.objects.filter(id__in=invoice_ids).update(status=new_status)
print(f"{updated_count} fatura durumu '{new_status}' olarak güncellendi")
return JsonResponse({
'success': True,
'message': f'{updated_count} fatura durumu başarıyla güncellendi',
'updated_count': updated_count,
'new_status': new_status
})
except json.JSONDecodeError:
return JsonResponse({
'success': False,
'message': 'Geçersiz JSON verisi'
}, status=400)
except Exception as e:
print(f"Toplu durum güncelleme hatası: {str(e)}")
return JsonResponse({
'success': False,
'message': f'Durumlar güncellenirken hata oluştu: {str(e)}'
}, status=500)
@login_required
@require_http_methods(["DELETE"])
def bulk_delete_invoices(request):
"""Toplu fatura silme"""
try:
data = json.loads(request.body)
invoice_ids = data.get('invoice_ids', [])
if not invoice_ids:
return JsonResponse({
'success': False,
'message': 'Fatura ID\'leri gerekli'
}, status=400)
# Faturaları toplu sil
deleted_count, _ = Invoice.objects.filter(id__in=invoice_ids).delete()
print(f"{deleted_count} fatura silindi")
return JsonResponse({
'success': True,
'message': f'{deleted_count} fatura başarıyla silindi',
'deleted_count': deleted_count
})
except json.JSONDecodeError:
return JsonResponse({
'success': False,
'message': 'Geçersiz JSON verisi'
}, status=400)
except Exception as e:
print(f"Toplu silme hatası: {str(e)}")
return JsonResponse({
'success': False,
'message': f'Faturalar silinirken hata oluştu: {str(e)}'
}, status=500)
@login_required
def profit_loss_report(request):
"""Kar/Zarar raporu"""
# Tarih filtreleri
today = timezone.now().date()
start_date = request.GET.get('start_date', (today - timedelta(days=30)).strftime('%Y-%m-%d'))
end_date = request.GET.get('end_date', today.strftime('%Y-%m-%d'))
customer_id = request.GET.get('customer_id', '')
# String tarihleri datetime'a çevir
try:
start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
except ValueError:
start_date = today - timedelta(days=30)
end_date = today
# Temel sorgu
invoices = Invoice.objects.filter(
issue_date__range=[start_date, end_date],
status='paid' # Sadece ödenmiş faturalar
)
# Müşteri filtresi
if customer_id:
try:
customer = Customer.objects.get(id=customer_id)
invoices = invoices.filter(customer=customer)
except Customer.DoesNotExist:
customer = None
else:
customer = None
# Gelir ve gider hesaplamaları
income_invoices = invoices.filter(invoice_type='income')
expense_invoices = invoices.filter(invoice_type='expense')
total_income = income_invoices.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
total_expense = expense_invoices.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
net_profit = total_income - total_expense
# Genel özet
summary = {
'total_income': total_income,
'total_expense': total_expense,
'net_profit': net_profit,
'profit_margin': (net_profit / total_income * 100) if total_income > 0 else 0,
'income_count': income_invoices.count(),
'expense_count': expense_invoices.count(),
}
# Müşteri bazında kar/zarar (sadece genel rapor için)
customer_stats = []
if not customer_id:
customers = Customer.objects.filter(
invoices__issue_date__range=[start_date, end_date],
invoices__status='paid'
).distinct()
for cust in customers:
cust_invoices = invoices.filter(customer=cust)
cust_income = cust_invoices.filter(invoice_type='income').aggregate(Sum('total_amount'))['total_amount__sum'] or 0
cust_expense = cust_invoices.filter(invoice_type='expense').aggregate(Sum('total_amount'))['total_amount__sum'] or 0
cust_profit = cust_income - cust_expense
customer_stats.append({
'customer': cust,
'income': cust_income,
'expense': cust_expense,
'profit': cust_profit,
'profit_margin': (cust_profit / cust_income * 100) if cust_income > 0 else 0,
})
# Kar'a göre sırala
customer_stats.sort(key=lambda x: x['profit'], reverse=True)
# Aylık trend analizi
monthly_data = []
current_date = start_date.replace(day=1) # Ay başına ayarla
while current_date <= end_date:
month_end = current_date.replace(day=28) + timedelta(days=4) # Sonraki ay
month_end = month_end - timedelta(days=month_end.day) # Ayın son günü
month_invoices = invoices.filter(
issue_date__range=[current_date, month_end]
)
month_income = month_invoices.filter(invoice_type='income').aggregate(Sum('total_amount'))['total_amount__sum'] or 0
month_expense = month_invoices.filter(invoice_type='expense').aggregate(Sum('total_amount'))['total_amount__sum'] or 0
monthly_data.append({
'month': current_date.strftime('%Y-%m'),
'month_name': current_date.strftime('%B %Y'),
'income': month_income,
'expense': month_expense,
'profit': month_income - month_expense,
})
# Sonraki aya geç
if current_date.month == 12:
current_date = current_date.replace(year=current_date.year + 1, month=1)
else:
current_date = current_date.replace(month=current_date.month + 1)
# Son faturalar (gelir/gider ayrımı ile)
recent_income = income_invoices.order_by('-issue_date')[:5]
recent_expense = expense_invoices.order_by('-issue_date')[:5]
context = {
'summary': summary,
'customer_stats': customer_stats,
'monthly_data': monthly_data,
'recent_income': recent_income,
'recent_expense': recent_expense,
'start_date': start_date.strftime('%Y-%m-%d'),
'end_date': end_date.strftime('%Y-%m-%d'),
'today': today.strftime('%Y-%m-%d'),
'selected_customer': customer,
'customers': Customer.objects.filter(is_active=True).order_by('name'),
}
return render(request, 'ssh_manager/kar_zarar_raporu.html', context)