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': 'Açı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': 'Açı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 = 'Açıklama yok' print(f"Kalem {item.id} için açıklama bulunamadı, varsayılan değer kullanıldı") else: print(f"Kalem {item.id} açı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)