Kaynak:
docs/knowledge-base/architecture/database-access.md— KB-docs (tam çözüm taraması, 1.029 .vb).
Veri Erişim Mimarisi (ERPPRO → ERPGOLDV2)
Gerçeklik türü: AS-IMPLEMENTED (kod). Depo kökü:
/home/claude/erppro/ERPPRO; yollarERPPRO/...veDijitalFatura/...projeye görelidir. Sayısal değerlerkb_src/erppro/scan.json(statik tarama; 1029 dosya, 3426 sınıf, 16043 metot, 1468 SQL ifadesi) üzerinden Python ile üretildi; kod satırları grep ile doğrulandı. Gizlilik: bağlantı dizesi, parola, lisans ve sunucu adresi değerleri kopyalanmamıştır; yalnız yapı ve konum verilir. İlgili dokümanlar: hotspots (büyüklük/çiftleme/boş catch tabloları), technical-debt (borç kaydı), application-flow.
1. Özet
Uygulama tek bir SQL Server veritabanına (ERPGOLDV2) doğrudan ADO.NET (System.Data.SqlClient) ile bağlanır; ORM yoktur. Üç farklı erişim yolu bir arada yaşar:
| Yol | Nerede | Kaç sınıf kullanıyor | Etiket |
|---|---|---|---|
Elle yazılmış SQL metni + InternalDb sarmalayıcısı |
ERPPRO/Class/Module/InternalDb.vb |
293 sınıf InternalDb kullanıyor; 364 sınıfta SQL ifadesi var (scan uses/sql_count) |
VERIFIED |
Doğrudan SqlConnection/SqlCommand (sarmalayıcısız) |
39 dosya, 129 satır (grep "New SqlClient.SqlConnection") — özellikle Class/Ext/*, Sistem.KurListesi, DijitalFatura/Common/ERPFatura.vb |
39 dosya | VERIFIED |
Attribute tabanlı mini-ORM (SQLBase, DBBaseClass) |
Class/Dao/_SQLBase/*, DataLayout/DBBaseClass.vb |
1 + 1 sınıf (aşağıda) | VERIFIED |
Runtime derlenen dinamik formlar (AIO.Dynamic.*) |
Class/Dao/Dynamic/* |
Yalnız DynamicWizard üzerinden |
VERIFIED |
Typed DataSet (ERPGOLDV2DataSet) |
ERPPRO/ERPGOLDV2DataSet.Designer.vb |
1 tablo adaptörü; tüketici yalnız SchedulerView tasarımcısı |
VERIFIED |
Çok adımlı işlemlerde hiçbir yerde uygulama katmanı transaction'ı yoktur (bkz. §8).
2. Bağlantı dizesi — nerede tutuluyor (yapı, değer yok)
- Varsayılan sabit değer (kodda):
InternalDb.ConnStrShared Property'sinin başlangıç değeri bir yerel sunucu,sakullanıcı adı ve düz metin parola içerir — VERIFIEDERPPRO/Class/Module/InternalDb.vb:11(değer kopyalanmadı;AGENT_RULES §4). Uygulama açılışında üzerine yazılır ama derlenmiş binaride sabit olarak kalır. - Çalışma zamanı kaynağı:
Program.ConnectDatabase→InternalDb.ConnStr = Program.Config.ConnectionStringve aynı değerAifaDev.Common.ConnectionString'e — VERIFIEDERPPRO/Class/Module/Program.vb:78-87. Diğer kütüphanelere aktarım:TCMB.Currency.ConnStr(Program.vb:209),DijitalFatura.ERPFatura.ConnStr(FrmEFaturaBas.vb:8,FrmEFaturaListesi.vb:132),NetEntegretor.Common.ConnStr(Form/Siparis/FrmNetSiparis.vb:4). Configuration.Configurationsınıfı (ERPPRO/Configuration/ConfigurationForm.vb:97-329):SQLServer,SQLPort,IntegratedSecurity,SQLUserName,SQLPassword,PersistSecurityInfo,SQLDatabase,ApplicationName,WorkstationIDözelliklerindenSqlConnectionStringBuilderile dize üretir (:226-246). Kalıcı depo: uygulama dizinindekiConfig.cfgINI dosyası,CONNECTION/ConnectionStringanahtarı; içerikAES_Encrypt/AES_Decryptile şifreli (:297-328). Şifreleme: Rijndael ECB modu, MD5'ten türetilmiş anahtar, koda gömülü sabit parola (:301,:325,:331-365) — VERIFIED (değer kopyalanmadı).app.config:connectionStringsbölümünde üç giriş (ERPGOLDV2ConnectionString,localhost_Connection[XpoProvider],ERPGOLDV2ConnectionString1) — VERIFIEDERPPRO/app.config:9-13. Bunlar yalnız tasarımcı/typed dataset içindir; çalışma zamanıInternalDb.ConnStr'ı kullanır.- Typed DataSet tasarımcısı:
GetEfaturaListesiTableAdapter.InitConnectioniçinde bir sunucu IP'si vesakullanıcı adı sabit kodludur — VERIFIEDERPPRO/ERPGOLDV2DataSet.Designer.vb:1730(değer kopyalanmadı). - SQL editörü:
FrmSqlEditorkullanıcıya serbest bağlantı dizesi ve serbest SQL yürütme imkânı verir — VERIFIEDERPPRO/Form/Tanımlamalar/Sistem/FrmSqlEditor.vb:77-85.
3. InternalDb — bağlantı yaşam döngüsü
Kaynak: ERPPRO/Class/Module/InternalDb.vb (159 satır). VERIFIED.
| Üye | Satır | Davranış |
|---|---|---|
Shared Property ConnStr |
11 | Tüm örnekler için ortak bağlantı dizesi. |
Property SqlConnection As New SqlConnection(ConnStr) |
12 | Her InternalDb örneği kendi SqlConnection nesnesini yaratır. |
Property SqlCommand As New SqlCommand("", SqlConnection) |
13 | Tek komut nesnesi; parametreler db.SqlCommand.Parameters.AddWithValue ile dışarıdan eklenir. |
Private Da As SqlDataAdapter, Private Ds As DataSet |
14-15 | DataSet/DataTable için; aynı Ds yeniden doldurulur, temizlenmez. |
New(CommandText_) |
18-20 | Sorgu metni kurucuya verilir (Using db As New InternalDb("Select ...") kalıbı). |
SqlConnectionState (set) |
36-52 | Open: kapalıysa açar; Close: açıksa kapatır. Hata → bir kez MessageBox "Sunucuya Ulaşılamıyor…" (UI çağrısı veri katmanında). |
ExecuteNonQuery(SqlQuery) |
53-65 | Bağlantıyı açar, komutu çalıştırır; hata → MsgBox(ex.Message); Finally boş, bağlantı kapatılmaz. |
ExecuteScalar(SqlQuery) |
69-81 | Aynı kalıp. |
DataReader(SqlQuery) |
85-96 | ExecuteReader — CommandBehavior.CloseConnection yok; okuyucuyu kapatmak çağıranın sorumluluğu. |
DataSet / DataTable |
100-123 | Da.Fill(Ds); Ds.Tables(0) döner. Aynı örnekte ikinci çağrı aynı Ds'ye ekler. |
Dispose(disposing) |
131-141 | Gövde boş — SqlConnection.Close/Dispose çağrılmaz. |
Sonuçlar:
- Using db As New InternalDb(...) (866 kullanım, grep "Using db As New InternalDb") bloğu bittiğinde bağlantı kapanmaz; açık bağlantı ancak çağıran açıkça db.SqlConnectionState = InternalDb.IDbConnectionState.Close derse (231 yer) veya GC SqlConnection'ı toplayınca havuza döner — VERIFIED (InternalDb.vb:131-141 + grep sayıları). Havuz tükenmesi riski INFERRED (üretimde gözlemlenmedi).
- Satır içi New InternalDb("...").ExecuteScalar kalıbı (196 yer, grep "(New InternalDb(\|= New InternalDb(") hiçbir zaman dispose edilmez.
- DataReader dönen kod çoğunlukla Dim Dr As Object ile geç bağlı çalışır (184 satır) ve Dr.Close/Dr.Dispose elle çağrılır (Database.vb:76-77 örneği).
- Hatalar MsgBox ile kullanıcıya gösterilir ve yutulur; ExecuteNonQuery Nothing→0 döner, çağıran "başarısız" sanır ama sebep kaybolur (InternalDb.vb:60-61).
Diğer sarmalayıcı: Class/Module/SQLServers.vb yalnız ağdaki SQL Server örneklerini SqlDataSourceEnumerator ile ayrı bir Thread üzerinde listeler (yapılandırma ekranı için) — VERIFIED SQLServers.vb:4-25; boş Catch (:22-24).
4. DAO temel sınıfları
4.1 AIO.DBBase (ERPPRO/Class/Dao/Base.vb, 124 satır)
118 dosyada 119 sınıf Inherits DBBase (grep). Sağladıkları — VERIFIED:
- ActiveSirketId/ActiveSubeId → Program.SirketId/SubeId (:6-16); ActiveKasaId kendi kendini çağırır (Return ActiveKasaId, :18-22) → çağrılırsa StackOverflowException. Çağıran yok (grep 0) — latent.
- Shared Event RecordInserted/RecordUpdated/RecordDeleted ve OnRecord… tetikleyicileri (:24-40). Tek dinleyici: Program.Shared Sub New → AfterSave (Program.vb:5-6, 8-19: Parcakarti kaydında ReceteDetay/PaketDetay miktarlarını iki ayrı UPDATE ile günceller, transaction yok).
- Sanal Save/Insert/Update/Delete (:41-55) Nothing döner; her türetilmiş sınıf kendi SQL'ini yazar.
- Shared Event Validate(Obj, ByRef Cancel) + IsValidate (:116-121): IsValid=True ile çağrılır, dinleyici yoksa True. Tek dinleyici Program.ValidateObject (Program.vb:20-30) parametreyi Validated anlamında kullanır (geçerliyse True) — yani bildirilen Cancel semantiğiyle ters; yalnız Parcakarti/Takimkarti/Paket/Modelkarti için doğrulama yapar, diğer her tip için True döner. Çağıranlar: Siparis.Parcakarti.vb:220, FrmModelKartıEkle.vb:100, FrmTakimEkle.vb:36, FrmPaketEkle.vb:39, FrmParcaKartiEkle.vb:93. (Mimari referans §9-10 "kırık" der; kodda: adlandırma ters, tek dinleyici ters kuralla uyumlu olduğu için 4 tipte çalışıyor, diğerlerinde doğrulama yok — CONTRADICTS değil, ayrıntılandırma.)
4.2 DataLayout.DBBaseClass (ERPPRO/DataLayout/DBBaseClass.vb, 385 satır)
Attribute tabanlı (TableInfo, DataField(Identity, Binding)) reflection ORM: ReadFromTable (:16-33, Id string birleştirme), Insert (:42-87, alan listesi reflection'dan, parametreli, SCOPE_IDENTITY), Update (:88-143), Delete/DeleteAt (soft delete IsDeleted='True', :144-195). Her çağrıda New SqlConnection(InternalDb.ConnStr) açılıp Using ile kapanır (doğru yaşam döngüsü). Tek türetilmiş sınıf: DataLayout.RepositoryItem (DataLayout/Repository.vb:21-23, tablo AifaDev.RepositoryItems) — VERIFIED (grep Inherits DBBaseClass = 1).
4.3 AIO.SQL.SQLBase (ERPPRO/Class/Dao/_SQLBase/SQLBase.vb, 224 satır)
SQLTableName/SQLIdentity attribute'larıyla tek MERGE … WITH (SERIALIZABLE) … OUTPUT inserted.* upsert üretir (:38-64), parametreleri reflection'la doldurur (:65-88), Load(Id)/Load(Query)/Delete/Save sağlar. Tek türetilmiş sınıf: AIO.Cari.IFatura (Class/Dao/Islem/Cari.IDetay.vb:4-6, tablo Cari.IslemDetaylari) — VERIFIED. Kusur: Private Shared _Query (:31) → MERGE metni tüm türetilmiş sınıflar arasında paylaşılır; ikinci bir türetilmiş sınıf eklenirse ilk derlenen sorgu diğerine uygulanır (bugün tek sınıf olduğu için gizli).
4.4 Türetilmiş DAO sınıfları (kalıp)
ERPPRO/Class/Dao/*.vb (127 dosya) + Class/Ext/*.vb (66 dosya, Partial uzantılar). Her sınıf aynı iskeleti taşır — VERIFIED (scan, Save gövdeleri normalize edilip kümelendi):
- New(RecordId) → Select * From <tablo> Where Id='" & RecordId & "' + DataReader + alan alan If Not Dr.Item(x) Is DBNull.Value Then … (Cari.Islem.vb:19-…).
- Save → If Id = 0 Then Return Insert Else Return Update — 105 Save metodundan 91'i birebir bu gövde (52 + 39 yazım varyantı), 2'si HasCode kontrollü, 12 farklı.
- Insert → Using db As New InternalDb("Insert Into … Values(@…) SELECT SCOPE_IDENTITY") + her alan için If X <> Nothing Then AddWithValue(@X, X) Else AddWithValue(@X, DBNull.Value) (110 Insert, 110 Update metodu Dao klasöründe).
- Delete, Delete(RecordId), Shared DeleteAt(Id) üçlüsü: 87 dosyada (soft delete).
- SetPropertyValue/GetPropertyValue reflection çifti: 121 dosyada kopya (grep "Sub SetPropertyValue(ByVal PropertyName").
Ayrıntılı çiftleme tabloları: hotspots §6.
5. Dinamik formlar — AIO.Dynamic.* (runtime derleme)
ERPPRO/Class/Dao/Dynamic/ (1309 satır sınıf + formlar). VERIFIED:
- Compiler(Of ReturnType) (Class/Compiler.vb:4-74): VBCodeProvider.CompileAssemblyFromSource ile çalışma zamanında VB kaynak derler (:59).
- DynamicForm.Execute (Class/Base/DynamicForm.vb:300-318): AifaDev.DynamicForms.Code kolonundaki kaynak kodu derleyip AIO.Dynamic.Data türetilmiş nesne üretir; hata → MsgBox.
- AIO.Dynamic.Data (Class/Base/Data.vb): reflection'la Insert/Update (parametreli, :192-240), ToList (:134); 8 boş Catch.
- AIO.Dynamic.Table (Class/Base/Table.vb:92-160): sys.columns sorgusu ve CREATE/ALTER TABLE betiği üretimi — tablo/kolon adları string birleştirme (:145,152,160), Exec ('…').
- Giriş noktası: FrmMainForm.onMenuSelect → "FORMWIZARD" → AIO.Dynamic.Design.DynamicWizard (Form/Genel/FrmMainForm.vb:98-101). Ayrıca Utils.cVBEvalProvider.Eval (Class/Module/Utils.vb:198-…) ikinci bir runtime derleyicidir; çağıranı yok (grep) — ölü kod.
- Derlenen kod uygulama ile aynı süreçte, aynı yetkiyle çalışır; kaynak veritabanı tablosundan gelir → veritabanına yazma yetkisi olan herkes uygulama içinde rastgele .NET kodu çalıştırabilir (INFERRED, güvenlik etkisi technical-debt TD-19).
6. Typed DataSet ve DevExpress XPO
ERPGOLDV2DataSet(ERPPRO/ERPGOLDV2DataSet.xsd+.Designer.vb, 2159 satır): tek tablo adaptörüGetEfaturaListesiTableAdapter(:1581, sorguSELECT Id, Tarih, Unvan, …:1739,@StartDate/@EndDateparametreli).TableAdapterManager.UpdateAll(:1917-1992) depodaki tekSqlTransactionkullanımıdır (BeginTransaction:1938,Commit:1970,Rollback:1992) — ancakUpdateAllhiçbir yerden çağrılmaz; dataset'i kullanan tek yerOrganizasyon/Task/SchedulerView.Designer.vb:108,781(BindingSourceolarak). VERIFIED: scantransaction=Trueyalnız bu sınıfta.- XPO:
ERPPRO/ERPPRO.vbproj:180DevExpress.Xpo.v14.1referansı veapp.config:11XpoProvider=MSSqlServerbağlantısı var; ancakgrep -i "XpoDefault|XPCollection|UnitOfWork|XPObject"ERPPRO kaynaklarında 0 sonuç → XPO kullanılmıyor, artık referans — VERIFIED.
7. DSX önbelleği (ERPPRO/Class/Module/Database.vb, 695 satır)
DSX.TABLE — Shared listeler (:3-26): ListOfController, ListOfCariIslemKodlari, ListOfMaden, ListOfHesap, ListOfNakit, ListOfProje, ListOfBirim, ListOfKasalar, ListOfSubeler, ListOfHizmetler, ListOfVadeliKod, ListOfAnlikFiyat, ListOfParite, ListOfTakozTipi, ListOfVariant, ListOfSSKGrup, ListOfCikisKodu, ListOfMeslekKodu, ListOfListLayout, ListOfCariIslemTipi, UrunListesi. VERIFIED.
DSX.DAO.RetriveOfAllData (:556-607) tek InternalDb ile sırayla yükler:
| Sıra | Metot | Kaynak sorgu (tablo) | Satır |
|---|---|---|---|
| 0 | ListOfCariIslemTipi ← AIO.Sistem.CariIslemTipi.TOList |
Sistem.CariIslemTipi |
558-559 |
| 1 | RetriveOfController |
Agent.Controller Where IsDeleted='False' |
54-79 |
| 2 | RetriveOfBirim |
Sistem.Birimler … SirketId=… |
110-135 |
| 3 | RetriveOfMaden |
Stok.Maden … SirketId=… |
178-220 |
| 4 | RetriveOfNakit |
Stok.Nakit … SirketId=… |
221-244 |
| 5 | RetriveOfHesap |
Cari.HesapListesi Order by HesapAdi (view; Password kolonu dahil) |
245-293 |
| 6 | RetriveOfHizmet |
Stok.Hizmet |
294-314 |
| 7 | RetriveOfVadeliKod |
Sistem.Vadeli |
315-338 |
| 8 | RetriveOfSube |
Sistem.Subeler |
360-400 |
| 9 | RetriveOfKasa |
Sistem.Kasalar … SirketId=… |
339-359 |
| 10 | RetriveOfFiyatTablosu |
Sistem.FiyatTablosu … SubeId=… |
154-177 |
| 11 | RetriveOfTakozTipi |
Sistem.TakozTipleri |
426-446 |
| 12 | RetriveOfParite |
Sistem.Pariteler |
136-153 |
| 13 | RetriveOfCariIslemKodlari |
Sistem.CariIslemKodu ∪ CariIslemTuru ∪ CariIslemTipi |
401-415 |
| 14 | RetriveOfVariant |
Stok.Varyant |
447-469 |
| 15 | RetriveOfPersonelCikisNedeni |
Personel.CikisNedenleri |
512-533 |
| 16 | RetriveOfMeslekKodu |
Personel.MeslekKodlari |
491-511 |
| 17 | RetriveOfSSKGrup |
Personel.SSKGrup |
470-490 |
| 18 | RetriveOfListLayout |
Sistem.ListLayout |
534-553 |
| 19 | RetriveOfProjeler |
Genel.Projeler |
80-109 |
| 20 | RetriveOfRibbonPage, RetriveOfThreadGrid, RetriveOfDbCombo, UrunListesi.LoadStok |
Ribbon tasarımı, popup grid, combo kaynakları, stok listesi | 598-604 |
Ne zaman yenilenir — VERIFIED:
- Girişte: Program.vb:217 (GlobalSettings.Read sonrası, KurListesi.Start öncesi).
- Elle: Ana menü "RETRIVEALLDATA" (Form/Genel/FrmMainForm.vb:329-332), "AKTARIM" (FrmHesapAktar sonrası, :372-376), FrmDevirGiris.vb:83.
- Kısmi: RetriveOfHesap yalnız FrmHesapTemizle.vb:30; RetriveOfMaden yalnız FrmZiynetIscilikleri.vb:69. ListOfHesap.RemoveAt Cari.Hesap.vb:824 (silmede). ListOfHesap.Add yalnız Database.vb'de → yeni hesap kaydından sonra önbelleğe ekleyen kod bulunamadı; INFERRED yeni hesap, RETRIVEALLDATA veya yeniden giriş yapılana kadar iHesapControl combolarında görünmez [GAP: FrmCariHesapEkle çağıranlarında yenileme aranmadı — iScreen.vb:320 salt görüntüleme].
- Olaylar: her RetriveOfX içinde RaiseEvent RetriveX var ve RetriveOfAllData aynı olayı tekrar yükseltir (:78 + :562, :134 + :564, :219 + :566 …) → dinleyiciler iki kez çalışır — VERIFIED.
- Bütün RetriveOfX sorguları Program.SirketId/SubeId değerini string birleştirmeyle gömer (:113,157,181,224,297,342,429); değer tam sayı olduğundan enjeksiyon riski düşük.
- RetriveOfProjeler/TakozTipi/Variant/SSKGrup/MeslekKodu/CikisNedeni/ListLayout boş Catch içinde (:105,443,466,487,508,530,550).
DSX.FN (:611-689): önbellek üstünde yardımcılar (GetAnaBirim, GetIdToBirim, GetCariIslemKodu, GetKarsiIslemTuru, GetHesapAdi) + iki skaler SQL fonksiyonu çağrısı (FN.GET_ERP_ISTATISTIK_KODU :613, Stok.GetTakozIsciligi :625, ikisi de string birleştirme).
Döviz kuru önbelleği ayrıdır: AIO.Kur.KurListesi (Class/Dao/Sistem.KurListesi.vb:60-127) System.Threading.Timer ile 15 sn (GlobalSettings.KurRefreshInterval) aralıkla Sistem.GetKurListesi(GETDATE) okur (:74,100); Program.GetKur(Birim) her zaman Alis döner, birim yoksa 1 döner (Program.vb:260-268). Liste UI iş parçacığı dışından değiştirilir, kilit yok — INFERRED yarış.
8. Sorgu kalıpları ve sayılar
Kaynak: scan sql girdileri (1468 ifade) + grep. VERIFIED.
| Kalıp | Sayı | Not |
|---|---|---|
| Toplam SQL ifadesi (scan) | 1468 | 793 select, 578 update, 161 insert, 27 delete, 9 exec, 1 truncate |
| Dinamik (string birleştirme içeren) | 1010 / 1468 (%69) | 311 sınıfta |
| Statik metin | 458 | çoğu Insert/Update … @param DAO kalıbı |
İfadesi olan metotlardan Parameters.AddWithValue/Add kullananlar |
290 / 1296 metot (%22) | 142 dosyada 4559 AddWithValue satırı — neredeyse tamamı DAO Insert/Update |
| Dinamik ifadelerden parametre de kullanan metot içindekiler | 32 / 1010 | karışık kullanım (ör. Cari.Islem.Insert parametreli INSERT + "Cari.RsAktar " & RecId) |
Tırnak içine gömülü '" & X & "' içeren satır |
568 | çoğu Id/SirketId gibi tam sayı; string olanlar aşağıda |
| Elle SQL yazan sınıf | 364 | sql_count > 0 |
InternalDb kullanan sınıf |
293 | |
| Saklı yordam çağrısı | ~28 çağrı, 16 dosya | EXECli 8 (Siparis.CreateUretimSiparisi, sys.sp_configure, NET.StokYaz, Cari.IslemDetayYaz, SPSort, bilanco.SaveAll×2, dinamik Exec(…)) + EXECsiz 20 (Cari.FaturaYaz×8, Cari.RsAktar, Takoz.RaporYaz, Organizasyon.GorevDagit, Sistem.GetFaturaNo×3, Sistem.GetLastFaturaNo, Cari.MadenSiparisTeslimET, Sistem.FromERP, …). CommandType.StoredProcedure 0; tümü metin komutu, parametreler birleştirilmiş. |
Tablo değerli fonksiyon (From Sema.Fn(...)) |
154 ifade, 95 sınıf | veritabanında 281 inline TVF (kb_src/db/modules_all.json); liste/rapor katmanı SQL tarafında |
Skaler fonksiyon (Select Sema.Fn(...)) |
7 ifade, 4 sınıf | FN.GetCariLimit, Cari.GetNewSiraNo, Cari.GetFisPesinKontrol, FN.ProvizyonIzin, Stok.GetTakozIsciligi, Siparis.GetLastPrice |
Grid/Liste kontrolüne .SQL = "…" ataması |
31 | SimpleList, DataListGrid sorguyu kontrol içinde çalıştırır |
8.1 String birleştirme örnekleri (SQL enjeksiyonu açısından string parametreler) — 10 adet, VERIFIED
| # | Dosya:satır | Gömülen değer | Kaynak |
|---|---|---|---|
| 1 | ERPPRO/Form/Doviz/FrmDovFaturaEkranı.vb:239-243 |
RefNo |
InputBox ile kullanıcıdan → Update Dov.Fatura Set IsDeleted='True' where RefNo='…' |
| 2 | ERPPRO/Controls/Islem Ekranı/Ekran/Fatura Helper/FindTaxInfo.vb:25 (ve FrmFaturaBas.vb:140) |
SearchText |
Metin kutusu → Select * from Cari.FindTaxInfo('…') |
| 3 | ERPPRO/Controls/Editor/CmbListLayout.vb:110,116 |
Ad, ListeTipi |
Kullanıcının girdiği düzen adı → Insert Into Sistem.Layout(...) Values('…') |
| 4 | ERPPRO/Class/Dao/Personel.Personel.vb:29 |
TcNo |
Where TcKimlikNo ='…' |
| 5 | ERPPRO/Class/Dao/Stok.Model.vb:248,300 (aynısı Stok.Pirlanta.vb:444,469, Siparis.ModelEx.vb:52, Siparis.ParcaEx.vb:56) |
Kodu, Value |
Stok kodu (kullanıcı girişi) → Where Kodu='…' |
| 6 | ERPPRO/Class/Dao/Sistem.Clients.vb:232 |
ComputerName |
My.Computer.Name → Delete From Sistem.Clients Where ClientName='…' (hard delete) |
| 7 | ERPPRO/Class/Module/Extention.vb:99 |
TableName, FieldName, Value |
Genel FindRecord — tablo/alan adı da birleştiriliyor |
| 8 | ERPPRO/Class/Ext/Dov.HaraketFoyu.vb:38 |
Kodu, StokCinsi |
Where StokKodu='…' And StokCinsi='…' |
| 9 | ERPPRO/Class/Dao/Siparis.Recete.vb:102, Class/Ext/Siparis.ReceteEx.vb:51 |
Kodu |
Reçete kodu |
| 10 | ERPPRO/Controls/Islem Ekranı/Base Control/iScreen.vb:975 |
Filter, OrderString, HesapType |
Grid filtre/sıralama metni doğrudan sorguya ekleniyor |
Ek: Class/Module/Devir.vb:39-40,141 (veritabanı adı, yedek yolu, DevirName → BACKUP/RESTORE ve Insert Into ERPGLOBAL.Dbo.Company), Class/RFID/PrintBase.vb:82 (TableName), DataLayout/DBBaseClass.vb:17, _SQLBase/SQLBase.vb:138-163 (Id değerleri).
Enjeksiyon açısından değerlendirme (INFERRED): uygulama masaüstü/iç ağ ve kullanıcılar kimlikli; ancak sa ile bağlanıldığı için (§2) tek bir ' içeren serbest metin girdisi en azından sorguyu bozar, bilinçli girdi ise veri değiştirebilir. Tam-sayı birleştirmeleri (Id, SirketId) Val/Integer tipinden geldiği için pratik risk taşımaz.
9. Transaction / atomiklik
Arama sonuçları — VERIFIED:
- SqlTransaction, BeginTransaction: yalnız ERPGOLDV2DataSet.Designer.vb:1588,1644-1665,1938-1992 (üretilmiş kod, çağıranı yok).
- TransactionScope: 0.
- BEGIN TRAN / Begin Tran uygulama SQL metinlerinde: 0 (yalnız DB tarafında trigger/SP içinde ROLLBACK).
- scan transaction bayrağı yalnız TableAdapterManager.UpdateAll.
Sonuç: uygulama katmanındaki her çok adımlı işlem, ayrı SqlConnection/SqlCommand'lerle ve ara adım başarısız olursa geri alınmadan çalışır. Beş somut örnek:
| # | İşlem | Adımlar | Dosya:satır | Başarısızlık etkisi |
|---|---|---|---|---|
| 1 | Fatura + detay + FaturaYaz |
Insert Into Cari.Faturalar … SCOPE_IDENTITY → Cari.FaturaYaz <Id> (SP) ; ayrıca her detay satırı FrmFaturaIslemEkle.FormSave ile ayrı Detay.Save → her biri yine Cari.FaturaYaz çağırır. Silme: UPDATE Faturalar → UPDATE FaturaDetay → FaturaYaz |
ERPPRO/Class/Dao/Cari.Fatura.vb:229-230 (Insert), :313-317 (DeleteAt), Cari.FaturaDetay.vb:167,209,229, Form/Fatura/FrmFaturaIslemEkle.vb:129-146 |
Başlık yazılır, FaturaYaz hata verirse (Catch boş, :231) türetilmiş kayıtlar eksik kalır; başlık ile detay farklı zamanlarda, her detay ayrı bağlantıda. |
| 2 | İşlem + RsAktar |
Insert Into Cari.Islemler … SCOPE_IDENTITY → RSAktar=True ve şube RsAktar ise db.ExecuteNonQuery("Cari.RsAktar " & RecId) (aynı InternalDb, ayrı komut) ; TAKOZ girişinde Update sonrası ek komut |
ERPPRO/Class/Dao/Islem/Cari.Islem.vb:695-696 (Insert), :989 (Update) |
RsAktar başarısız olursa Catch boş (:697), işlem kayıtlı kalır, resmî hesaba aktarım yapılmamış olur; Save True döner. |
| 4 | Dönem devri (Devir.DevirYap) |
sp_configure → BACKUP DATABASE → RESTORE DATABASE (arşiv) → sayım doğrulama → Update Subeler (arşiv) → TRUNCATE Cari.Islemler, Cari.BakiyeIslem, Cari.History, Stok.StokHaraketleri, Cari.IslemDetaylari + geçici tablolardan yeniden INSERT → ERPGLOBAL DB oluşturma → Insert Into ERPGLOBAL.Dbo.Company |
ERPPRO/Class/Module/Devir.vb:37-155 (DevirQuery :41-87, yürütme :90-147) |
DevirQuery tek ExecuteNonQuery metnidir ama BEGIN TRAN içermez; TRUNCATE'ten sonra INSERT hata verirse hareket tabloları boş kalır; öncesinde alınan yedek tek güvence. |
| 5 | Reçete / sipariş kopyalama | Recete.Save (yeni başlık) → ReceteDetay.ToList(eskiId) döngüsünde her satır itm.Save |
ERPPRO/Class/Ext/Siparis.ReceteEx.vb:49-74 (:61-68); aynı kalıp Siparis.SiparisEx.vb:39-60, Siparis.UretimSiparisiEx.vb:57 |
Döngü ortasında hata → yarım kopya (başlık + kısmi detay), dönüş değeri yine yeni Id. |
Ek örnekler: Program.AfterSave iki ayrı UPDATE (Program.vb:11-14); FrmCariHesapEkle.FormSave → K.Save + DataLayoutControl1.Save + karşı hesap UPDATE (Form/Tanımlamalar/Cari/FrmCariHesapEkle.vb:223-229); FrmHesapAktar üç Count sorgusu sonra Sistem.FromERP SP (Form/Tanımlamalar/Sistem/FrmHesapAktar.vb:8-22).
Veritabanı tarafındaki telafi: Cari.Islemler tetikleyicileri (AcceptTransaction vb.) INSERT/UPDATE'i kendi içinde ROLLBACK edebilir; ancak demo kopyasında dördü de DISABLED (kb_src/projdocs/erpgoldv2-kod-katmani-bulgulari.md §2, AS-DOCUMENTED) ve ROLLBACK sonrası RETURN bazı dallarda yok (AcceptTransaction satır 122, 184, 231, 245 — kb_src/db/modules_all.json, VERIFIED). Ayrıntı: technical-debt TD-03/TD-04.
10. Yeniden yazım için çıkarımlar (öneri, uygulanmadı)
- Tek veri erişim yolu:
InternalDb'nin yeriniUsingile açılıp kapanan,IDisposable'ı doğru uygulayan, her zaman parametreli bir komut fabrikası almalı;MsgBoxveri katmanından çıkmalı. Savezinciri (Islem + RsAktar,Fatura + Detay + FaturaYaz,SendTo,Copy) tekSqlTransaction/TransactionScopeiçine alınmalı; SP'lerCommandType.StoredProcedureveSqlParameterile çağrılmalı.DSX.TABLEönbelleği kayıt sonrası olaylarla (RecordInserted/Updated/Deleted) güncellenmeli; olaylar tek yerden yükseltilmeli.- Bağlantı dizesi ve şifreleme parolası koddan çıkarılmalı (DPAPI/Windows kimlik doğrulaması),
sayerine en az yetkili hesap. SQLBase/DBBaseClass/Dynamicüçlüsünden biri seçilip diğerleri kaldırılmalı (bugün toplam 2 üretim sınıfı kullanıyor).
