From f52acbf5c3c78d67ac0dbb3739b8b465839aca42 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 6 Feb 2026 00:30:42 +0000 Subject: [PATCH] Auto commit: 2026-02-06T00:30:42.683Z --- ai/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 404 bytes ai/__pycache__/local_ai_api.cpython-311.pyc | Bin 0 -> 19874 bytes core/__pycache__/urls.cpython-311.pyc | Bin 1233 -> 1353 bytes core/__pycache__/utils.cpython-311.pyc | Bin 0 -> 3078 bytes core/__pycache__/views.cpython-311.pyc | Bin 7593 -> 14226 bytes core/templates/base.html | 8 +- core/templates/core/fatura_form_otomatik.html | 148 ++++++++++++++++++ core/templates/core/firma_detay.html | 4 +- core/templates/core/index.html | 16 +- core/urls.py | 5 +- core/utils.py | 75 +++++++++ core/views.py | 118 +++++++++++++- 12 files changed, 361 insertions(+), 13 deletions(-) create mode 100644 ai/__pycache__/__init__.cpython-311.pyc create mode 100644 ai/__pycache__/local_ai_api.cpython-311.pyc create mode 100644 core/__pycache__/utils.cpython-311.pyc create mode 100644 core/templates/core/fatura_form_otomatik.html create mode 100644 core/utils.py diff --git a/ai/__pycache__/__init__.cpython-311.pyc b/ai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9beeae7341d797e2eac5af07416a24e9ee781f92 GIT binary patch literal 404 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 delta 184 zcmX@fb&*qjIWI340}$+1ZOwen#K70D8~DU!hqno=)8 za+-{{SPBwLGF~!*I9!ugFosMPW}2(amROXSo2XyJ0~D?jPRlF;a%?j5N~{X9?evQ? zbM%XNCx2j)Vh35^r^z>2klA~3BC{`-IFJuAsyJiv4(7>|Ls_OwVzuHI<>qH<;0D1W HQJ^FM)Wa#? diff --git a/core/__pycache__/utils.cpython-311.pyc b/core/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ee45704e1243f91670d7dcb78301d7fd0fd180b GIT binary patch literal 3078 zcmZ`*O>7&-6`ox#DUy;*$)=0gu8o1BG-g9lR$`}xPzO*;t4N_r^dI@rYkHS?JPXW6h$5<)Xj#5>Q?vACU6Xvw zj9JFAp?J-VFUP6zJG7jjaiBv~E1_KCYiVdPrv<5Z=*qi{P$u9b{gzW9=G>ceHD1xW zQ^{8xo)|ojzXkFUN+Iunw23~4uP^&OEsbg|y{X;k_Y~3lKWxRCdNX#n7Xa89w9kS4 zvj0i-2Jnqhgj)KCruQ`2jX#isr1zU}$b>^7_To+GAjIil*;NQF&T906jFn{J_24~`#n zp)5Tu!(8GR*h|3U$~>;4P#=wFAB~^<_@{g0ll$Y78;MS4e8cEuPyXet5NWAtMK+3^ zk+D0)e`jC%SN5g7?92Pvm%o_Y%NF)V3LC|Z;#Zkte{TG#@yF(e%@D;>FN6t{ec{ge zPUhIg@OO`fvmw&s2gsx3=1u?VbTwKbT9+ zWzfS>P0~zy?)BKi6G!J}Vh?BHz#jsw3{QdfAHcv~(VFNJ*e9RGTG~PQ4q!@S)S&TC zb(&~u^1Z6lA)2Hqu5pc~KYa=^0CD~KaHhkD!6mYX=Ig_``IJQoJlLBfi~dhwxzlpB zOU%YK;jA#X?%2%4*SWwe#PbEgH<{p=)Z1HClUM}1-1V5nU2Yff+|DLxZ??B6sq&t> z#XOtaFv2D#Z(!zu0WfE-a#I?rG8k#{s>$%LN^dXX6-QXaYj4?Bg-g;{rM-pcIT*%2?@c_5=V*H?kFPSZ z%JCI$fl<8ZTSO2Sw9j6>vY5-`g-h?2+V@KXK^}$)a5-{m3eQJ5IbgQiv*_gU5)ph2 zMnKY*oEl^+kFON3;w9fBLeBk5CQ}=UdIpuh0$(8}vt)v1@nt4d`XLe94{XRDM7hQr zkXLSFqJn@bN_k#taSN~Tx{NuR%Mrxx@m@5q%)h7H!QGxhS|zKu z%$&n(1RUm3aw11&X8N5=zkrls$0V@0yt`&o{S4X;UX|fSYd7yJ%Z0VfEZZ$D(2hwxUC!kl>vr&VW~yo z_!_jSg@vhNaR51q9(Hr!(Ix4J%DuuoK+LxR@(q_@+c&|{K0Z~-+V^26*syU90XYKV zuaySI6DtyiWkUJHES@M73KRLB1m;-fW!rg1U@-xk?0FI*vG|OBv`?R>54huNQhyNr zV17V;Il={KoGY9=*Y~VSZ+?$NE$R2js$B?!wfDL)V%*DvtT7Y5@a^ZNLJ8%1!_b+Y z7phYsysQxZl#@y<9hHce$$ho&e_y1)BxMJqQ_?8zvQp^E`!2V--23X?iuOu(moLcT z7Oik2uH-?&t;>Sw{}zhBtfF1GDopeM!|XiZA5dG9mg06S1~Wxz$d-7Z?vXg+HiBP(&NQZB0l1DwVKL5WZ4UL`L&*Z*3emX?x6U3%6_^B@v zyCc(Zhw<3abeKd(PkuaqcVYL`tGi>bg~N$g5+MTe4N8m+{a2Fp%i$PGj{TweLG#X= zdqZdTht9xRB>u*u%!&QXYxghjWiIY#F775Tc7`(>=O2TID3=U$$A!aHU7mDgfI)`v zf+^3oWx~s(#-l76GEwR%q+4DGs~gA$=(n@hCX?ebt^!^n5JnRlv-C; l2c6nIe0R`~b`RejG`y~^&^V&O@uyGl1#K)u&l2Ut{{cmRYqJ0V literal 0 HcmV?d00001 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index cac8e1766671182d1dc9bc96f5e716ac02a5335c..91dbf8642c44c16d4a3fb484c8180dc9be4f0aa1 100644 GIT binary patch literal 14226 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 delta 1613 zcmZuxO>7fa5cc!W+Fq}J*Cck*7DI?4BN8_u392AY6Ck05G@#I`*i~Tht{W%oY?OzI)PlpY3Dbr~?NM*w%|3SfsG1?<#gfEhgwnAH<=o_w;&CdCRH zYdvPSy&Vxq!UriI73aiog6bfjzL4TCW9O_p&bW zVQ{B}0eH9gI+!_>!=(=(@8i30-H)&vz(#8>m*YL~bfQ(QvC6fx8nJG3MnkL4-61Og$M6L=n|iXcfU<7h5@gITzQM{Y6g7`OaM2g|TDKU}E^-%+l!Mp9H7;fui z_X3S2)8I2;KPj#yFSB0pD4u2K#jeyjHZML&l_A_8sY&mmEHdgC>lE*(dlPiMQ%Hap z&j-bIb!=@KmU+eTO&>k&_}(`f{7v-55pb&$n0n*H(K&t`4JQz=p>C6@-EB>9LT+v~ zozLPX%$;LT?B_(_$`6WA41OARlyFx|%%$h|jz8Fu{pw8f%trY}`P$TDwfpAGch$S* zUF)7&yss92Q;YYM;*)?kJT4xl3sCtLCEIHC-(t)WrwT(%6;}%*5~;Lb{8Y%Toy0Uw zA+)u%S~D94=V3IHtVr8!#qjsg<%Yr4ixef%H`;BP+!_}KG6Q9nQa#q4LZ zHxGZ4x-ZJF{v!2)qv92R#?xYa@P{?hICTO??`fuDaidVNmht{zHcX>o3@kM2CdaJq zcpb_8JUITwr4=JDaTFE#s9TQY9&uYkHEfRm)B$D>2AU|s1nMhWaY^-8?G~~_k#H1EHaCTk1 zJ8T8<=HAM8iC>40tx0tYDz)9pi93oB167OHZBC;@!rcQ_`lz`=$5E$wqk$p4QOl@y x;DRgmaO9y0dm1_fG^?gjGösterge Paneli +
  • + + + Otomatik Yükle + +
  • @@ -201,4 +207,4 @@ {% block extra_js %}{% endblock %} - \ No newline at end of file + diff --git a/core/templates/core/fatura_form_otomatik.html b/core/templates/core/fatura_form_otomatik.html new file mode 100644 index 0000000..005cd08 --- /dev/null +++ b/core/templates/core/fatura_form_otomatik.html @@ -0,0 +1,148 @@ +{% extends "base.html" %} + +{% block title %}Otomatik Fatura Yükle - FaturaYol{% endblock %} +{% block page_title %}Otomatik Fatura Yükle{% endblock %} + +{% block content %} + + + + + +{% endblock %} diff --git a/core/templates/core/firma_detay.html b/core/templates/core/firma_detay.html index daee070..1288faa 100644 --- a/core/templates/core/firma_detay.html +++ b/core/templates/core/firma_detay.html @@ -16,7 +16,7 @@ @@ -73,4 +73,4 @@ -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index d8cbf0d..472250b 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -113,13 +113,13 @@
    -
    Biliyor muydunuz?
    -

    Sistemimiz OCR teknolojisi sayesinde faturadaki tüm kalemleri otomatik olarak ayrıştırabilir.

    +
    Akıllı Analiz
    +

    Sistemimiz Mersis, VKN ve TCKN bilgilerini otomatik tanıyarak firmaları eşleştirir.

    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 8421686..a9b68fb 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,7 @@ from django.urls import path from .views import ( home, fatura_arsivi, firma_detay, fatura_detay, raporlar, - firma_ekle, fatura_ekle, search, firma_sil, fatura_sil + firma_ekle, fatura_ekle, fatura_otomatik_yukle, search, firma_sil, fatura_sil ) urlpatterns = [ @@ -12,7 +12,8 @@ urlpatterns = [ path("raporlar/", raporlar, name="raporlar"), path("firma/ekle/", firma_ekle, name="firma_ekle"), path("fatura/ekle/", fatura_ekle, name="fatura_ekle"), + path("fatura/otomatik-yukle/", fatura_otomatik_yukle, name="fatura_otomatik_yukle"), path("arama/", search, name="search"), path("firma//sil/", firma_sil, name="firma_sil"), path("fatura//sil/", fatura_sil, name="fatura_sil"), -] \ No newline at end of file +] diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 0000000..007ceb9 --- /dev/null +++ b/core/utils.py @@ -0,0 +1,75 @@ +import os +from pypdf import PdfReader +from ai.local_ai_api import LocalAIApi +import json + +def extract_text_from_pdf(pdf_path): + try: + reader = PdfReader(pdf_path) + text = "" + for page in reader.pages: + text += page.extract_text() + "\n" + + if not text.strip(): + # If no text extracted, maybe it's an image-based PDF + # In a real environment we would use OCR here + return None + return text + except Exception as e: + print(f"Error extracting text from PDF: {e}") + return None + +def analyze_invoice_text(text): + prompt = f""" + Sen profesyonel bir fatura veri ayıklama sistemisin. Aşağıdaki fatura metnini analiz et ve bilgileri kesinlikle JSON formatında döndür. + + Özellikle şu bilgileri bulmaya çalış: + - Satıcı Firma Bilgileri: Adı, Vergi Kimlik Numarası (VKN), MERSİS Numarası, Adresi. + - Fatura Bilgileri: Fatura No, Tarih, Ara Toplam, KDV Tutarı, Genel Toplam. + - Satır Kalemleri: Her bir ürün veya hizmetin adı, adedi, birim fiyatı, KDV oranı, KDV tutarı ve toplam tutarı. + + Dikkat: + - Vergi numarası 10 haneli (VKN) veya 11 haneli (TCKN) olabilir. + - MERSİS no genellikle 16 hanelidir. + - Sayısal değerleri (tutarlar, adetler) sadece rakam ve nokta (ondalık için) olarak döndür. + - Tarihi YYYY-MM-DD formatına çevir. + - Eğer bir veri bulunamazsa null döndür. + + İstenen JSON formatı: + {{ + "firma_adi": "...", + "vergi_no": "...", + "mersis_no": "...", + "adres": "...", + "fatura_no": "...", + "tarih": "YYYY-MM-DD", + "ara_toplam": 0.00, + "kdv_toplam": 0.00, + "genel_toplam": 0.00, + "kalemler": [ + {{ + "urun_adi": "...", + "adet": 1, + "birim_fiyat": 0.00, + "kdv_orani": 20, + "kdv_tutari": 0.00, + "toplam_tutar": 0.00 + }} + ] + }} + + Fatura Metni: + {text} + """ + + response = LocalAIApi.create_response({ + "input": [ + {"role": "system", "content": "Sen sadece JSON döndüren, hata payı düşük bir fatura analiz uzmanısın. Yanıtında asla JSON dışında metin bulundurma."}, + {"role": "user", "content": prompt}, + ], + "text": {"format": {"type": "json_object"}}, + }) + + if response.get("success"): + return LocalAIApi.decode_json_from_response(response) + return None diff --git a/core/views.py b/core/views.py index 54fe897..c94a1d7 100644 --- a/core/views.py +++ b/core/views.py @@ -1,7 +1,12 @@ from django.shortcuts import render, get_object_or_404, redirect from django.db.models import Sum, Count, Avg, Q +from django.db import transaction +from django.core.files.storage import default_storage +from django.contrib import messages from .models import Firma, Fatura, FaturaKalemi from .forms import FirmaForm, FaturaForm +from .utils import extract_text_from_pdf, analyze_invoice_text +import os def home(request): """Fatura Yönetimi Gösterge Paneli.""" @@ -97,11 +102,120 @@ def fatura_ekle(request): form = FaturaForm(request.POST, request.FILES) if form.is_valid(): fatura = form.save() - return redirect('firma_detay', pk=fatura.firma.pk) + return redirect('fatura_detay', pk=fatura.pk) else: form = FaturaForm(initial=initial) return render(request, 'core/fatura_form.html', {'form': form, 'title': 'Yeni Fatura Yükle'}) +def fatura_otomatik_yukle(request): + if request.method == 'POST' and request.FILES.get('pdf_dosyasi'): + pdf_file = request.FILES['pdf_dosyasi'] + + # Save temporary file to extract text + temp_name = 'temp_' + pdf_file.name + path = default_storage.save('temp/' + temp_name, pdf_file) + full_path = default_storage.path(path) + + try: + text = extract_text_from_pdf(full_path) + if not text: + messages.error(request, "PDF dosyasından metin okunamadı. Dosya taranmış bir resim olabilir veya şifreli olabilir.") + else: + data = analyze_invoice_text(text) + if not data: + messages.error(request, "Yapay zeka faturayı analiz edemedi. Lütfen dosyanın geçerli bir fatura olduğundan emin olun.") + else: + with transaction.atomic(): + # Smarter Firma matching + vergi_no = str(data.get('vergi_no', '')).strip() + mersis_no = str(data.get('mersis_no', '')).strip() + firma_adi = data.get('firma_adi', '').strip() or 'Bilinmeyen Firma' + + firma = None + + # 1. Try by Vergi No / TCKN + if vergi_no and vergi_no != 'None' and vergi_no != 'null': + firma = Firma.objects.filter(vergi_no=vergi_no).first() + + # 2. Try by Mersis No + if not firma and mersis_no and mersis_no != 'None' and mersis_no != 'null': + firma = Firma.objects.filter(mersis_no=mersis_no).first() + + # 3. Try by Name (Exact) + if not firma and firma_adi != 'Bilinmeyen Firma': + firma = Firma.objects.filter(ad__iexact=firma_adi).first() + + # 4. Create if not found + if not firma: + # Ensure we have a unique vergi_no even if AI failed + if not vergi_no or vergi_no == 'None' or vergi_no == 'null': + vergi_no = f"AUTO-{os.urandom(4).hex()}" + + firma = Firma.objects.create( + ad=firma_adi, + vergi_no=vergi_no, + mersis_no=mersis_no if (mersis_no != 'None' and mersis_no != 'null') else None, + adres=data.get('adres', '') + ) + else: + # Update missing info if found existing firma + updated = False + if not firma.mersis_no and mersis_no and mersis_no != 'None' and mersis_no != 'null': + firma.mersis_no = mersis_no + updated = True + if not firma.adres and data.get('adres'): + firma.adres = data.get('adres') + updated = True + if updated: + firma.save() + + # Create Fatura + fatura_no = data.get('fatura_no') or f"AUTO-{os.urandom(4).hex()}" + + # Check if this invoice already exists for this firm + fatura = Fatura.objects.filter(fatura_no=fatura_no, firma=firma).first() + if not fatura: + fatura = Fatura.objects.create( + firma=firma, + fatura_no=fatura_no, + tarih=data.get('tarih') or '2026-01-01', + ara_toplam=data.get('ara_toplam') or 0, + kdv_toplam=data.get('kdv_toplam') or 0, + genel_toplam=data.get('genel_toplam') or 0, + pdf_dosyasi=pdf_file, + islenmis=True + ) + + # Add Items + kalemler = data.get('kalemler', []) + if isinstance(kalemler, list): + for k in kalemler: + FaturaKalemi.objects.create( + fatura=fatura, + urun_adi=k.get('urun_adi') or 'Ürün', + adet=k.get('adet') or 1, + birim_fiyat=k.get('birim_fiyat') or 0, + kdv_orani=k.get('kdv_orani') or 20, + kdv_tutari=k.get('kdv_tutari') or 0, + toplam_tutar=k.get('toplam_tutar') or 0 + ) + + # Clean up temp file + if default_storage.exists(path): + default_storage.delete(path) + + messages.success(request, f"Fatura başarıyla analiz edildi ve {firma.ad} firmasına eklendi.") + return redirect('fatura_detay', pk=fatura.pk) + except Exception as e: + messages.error(request, f"Fatura işlenirken bir hata oluştu: {str(e)}") + print(f"Error processing automatic invoice: {e}") + finally: + # Clean up temp file + if default_storage.exists(path): + default_storage.delete(path) + + return render(request, 'core/fatura_form_otomatik.html', {'title': 'Otomatik Fatura Yükle', 'active_menu': 'dashboard'}) + def search(request): query = request.GET.get('q', '') faturalar = [] @@ -136,4 +250,4 @@ def fatura_sil(request, 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'}) \ No newline at end of file + return render(request, 'core/confirm_delete.html', {'object': fatura, 'type': 'Fatura'})