`); win.document.close(); }; // ═══════════════════════════════════════════════════════════════ // MİZAN (TRIAL BALANCE) — Türkiye Tekdüzen Hesap Planı // Mevcut ERP verilerinden otomatik türetilir. // ═══════════════════════════════════════════════════════════════ function renderTrialBalance() { // ── Dönem state ────────────────────────────────────────────── if (!AppState.mizanPeriod) { const now = new Date(); AppState.mizanPeriod = { type: 'month', year: now.getFullYear(), month: now.getMonth()+1, startDate: null, endDate: null }; } const p = AppState.mizanPeriod; // Dönem başlangıç/bitiş tarihlerini hesapla let periodStart, periodEnd, periodLabel; if (p.type === 'month') { periodStart = new Date(p.year, p.month-1, 1); periodEnd = new Date(p.year, p.month, 0); periodLabel = periodStart.toLocaleDateString('tr-TR', {month:'long', year:'numeric'}); } else if (p.type === 'quarter') { const qStart = Math.floor((p.month-1)/3)*3; periodStart = new Date(p.year, qStart, 1); periodEnd = new Date(p.year, qStart+3, 0); periodLabel = `${p.year} ${Math.floor(qStart/3)+1}. Çeyrek (${periodStart.toLocaleDateString('tr-TR',{month:'short'})}–${periodEnd.toLocaleDateString('tr-TR',{month:'short'})})`; } else if (p.type === 'year') { periodStart = new Date(p.year, 0, 1); periodEnd = new Date(p.year, 11, 31); periodLabel = `${p.year} Yılı`; } else { periodStart = new Date(p.startDate); periodEnd = new Date(p.endDate); periodLabel = `${periodStart.toLocaleDateString('tr-TR')} – ${periodEnd.toLocaleDateString('tr-TR')}`; } const inPeriod = dateStr => { if (!dateStr) return false; const d = new Date(dateStr); return d >= periodStart && d <= periodEnd; }; const fmtM = v => Math.abs(v||0).toLocaleString('tr-TR', {minimumFractionDigits:2}); const sym = c => ({TRY:'₺',USD:'$',EUR:'€',GBP:'£'})[c]||c; // ── Hesap Planı Tanımları ──────────────────────────────────── const ACCOUNTS = { '100': { code:'100', name:'Kasa', group:'Dönen Varlıklar' }, '102': { code:'102', name:'Bankalar', group:'Dönen Varlıklar' }, '120': { code:'120', name:'Alıcılar', group:'Dönen Varlıklar' }, '150': { code:'150', name:'İlk Mad. ve Malzeme', group:'Dönen Varlıklar' }, '152': { code:'152', name:'Mamuller', group:'Dönen Varlıklar' }, '153': { code:'153', name:'Ticari Mallar', group:'Dönen Varlıklar' }, '190': { code:'190', name:'Devreden KDV', group:'Dönen Varlıklar' }, '300': { code:'300', name:'Banka Kredileri', group:'Kısa Vad. Yab. Kaynaklar' }, '301': { code:'301', name:'Finansal Kiralama Borç.',group:'Kısa Vad. Yab. Kaynaklar' }, '320': { code:'320', name:'Satıcılar', group:'Kısa Vad. Yab. Kaynaklar' }, '360': { code:'360', name:'Ödenecek Vergiler', group:'Kısa Vad. Yab. Kaynaklar' }, '391': { code:'391', name:'Hesaplanan KDV', group:'Kısa Vad. Yab. Kaynaklar' }, '500': { code:'500', name:'Sermaye', group:'Özkaynaklar' }, '600': { code:'600', name:'Yurt İçi Satışlar', group:'Gelirler' }, '601': { code:'601', name:'Yurt Dışı Satışlar', group:'Gelirler' }, '621': { code:'621', name:'Satılan Mamul Maliyeti', group:'Satışların Maliyeti' }, '660': { code:'660', name:'Kısa Vad. Borçlanma Gid.',group:'Finansman Giderleri' }, '710': { code:'710', name:'DİMM Giderleri', group:'Üretim Giderleri' }, '720': { code:'720', name:'DİŞÇİLİK Giderleri', group:'Üretim Giderleri' }, '730': { code:'730', name:'Genel Üretim Giderleri', group:'Üretim Giderleri' }, '740': { code:'740', name:'Hizmet Üretim Maliyeti', group:'Üretim Giderleri' }, '760': { code:'760', name:'Paz. Satış Dağ. Gid.', group:'Faaliyet Giderleri' }, '770': { code:'770', name:'Genel Yönetim Giderleri',group:'Faaliyet Giderleri' }, '771': { code:'771', name:'Personel Giderleri', group:'Faaliyet Giderleri' }, '772': { code:'772', name:'Kira Giderleri', group:'Faaliyet Giderleri' }, '773': { code:'773', name:'Enerji Giderleri', group:'Faaliyet Giderleri' }, '774': { code:'774', name:'Bakım Onarım Giderleri', group:'Faaliyet Giderleri' }, '780': { code:'780', name:'Finansman Giderleri', group:'Finansman Giderleri' }, }; // Hesap bakiyelerini tutan nesne { code: { borc, alacak } } const bal = {}; const addBorc = (code, amt) => { if(!bal[code]) bal[code]={borc:0,alacak:0}; bal[code].borc += (amt||0); }; const addAlacak = (code, amt) => { if(!bal[code]) bal[code]={borc:0,alacak:0}; bal[code].alacak += (amt||0); }; // ── 1. SATIŞ FATURALARI ────────────────────────────────────── (AppState.invoices||[]).filter(i => i.type==='Satış' && inPeriod(i.invoiceDate||i.date)).forEach(inv => { const tryAmt = inv.totalTRY || inv.grandTotal || 0; const vatAmt = (inv.vatAmount||0) * (inv.exchangeRate||1); const netAmt = tryAmt - vatAmt; const isExport = (inv.currency && inv.currency !== 'TRY') || (inv.deliveryTerms||'').includes('CIF') || (AppState.customers.find(c=>c.id===inv.customerId)||{}).type === 'İhracat'; const satAcct = isExport ? '601' : '600'; // 120 Alıcılar BORÇ / 600-601 Satışlar ALACAK / 391 KDV ALACAK addBorc('120', tryAmt); addAlacak(satAcct, netAmt); if (vatAmt > 0) addAlacak('391', vatAmt); }); // ── 2. TAHSİLATLAR ────────────────────────────────────────── (AppState.collections||[]).filter(c => c.status==='Tahsil Edildi' && inPeriod(c.date)).forEach(col => { const tryAmt = col.currency==='TRY' ? col.amount : col.amount*(AppState.liveRates||{})[col.currency]||col.amount; // 102 Bankalar BORÇ / 120 Alıcılar ALACAK addBorc('102', tryAmt); addAlacak('120', tryAmt); }); // ── 3. ALIŞ FATURALARI ─────────────────────────────────────── (AppState.invoices||[]).filter(i => i.type==='Alış' && inPeriod(i.invoiceDate||i.date)).forEach(inv => { const tryAmt = inv.totalTRY || inv.grandTotal || 0; const vatAmt = (inv.vatAmount||0) * (inv.exchangeRate||1); const netAmt = tryAmt - vatAmt; // 150 İlk Madde BORÇ / 190 KDV BORÇ / 320 Satıcılar ALACAK addBorc('150', netAmt); if (vatAmt > 0) addBorc('190', vatAmt); addAlacak('320', tryAmt); }); // ── 4. ÖDEMELER ────────────────────────────────────────────── (AppState.payments||[]).filter(pay => pay.status==='Onaylandı' && inPeriod(pay.date)).forEach(pay => { const tryAmt = pay.currency==='TRY' ? pay.amount : pay.amount*((AppState.liveRates||{})[pay.currency]||1); // 320 Satıcılar BORÇ / 102 Bankalar ALACAK addBorc('320', tryAmt); addAlacak('102', tryAmt); }); // ── 5. GİDER FATURALARI ────────────────────────────────────── const giderMap = { 'Elektrik':'773','Su':'773','Doğalgaz':'773','Enerji':'773', 'Kira':'772','Aidat':'772', 'Maaşlar':'771','SGK Primi':'771','Personel':'771','Yemek':'771','Servis':'771', 'Nakliye':'760','Kargo':'760','Yakıt':'760', 'Makine Bakım':'774','Bina Onarım':'774','Bakım':'774', 'Muhasebe Danışmanlık':'770','Hukuki Danışmanlık':'770','Kırtasiye':'770', 'Sigorta':'770','Vergi':'770', }; (AppState.expenseInvoices||[]).filter(e => e.status!=='İptal' && inPeriod(e.invoiceDate||e.date)).forEach(exp => { const tryAmt = exp.totalTRY || exp.total || 0; const vatAmt = exp.vatAmount || 0; const netAmt = tryAmt - vatAmt; const subcat = exp.subcategory || ''; let acct = '770'; for (const [key, code] of Object.entries(giderMap)) { if (subcat.includes(key)) { acct = code; break; } } addBorc(acct, netAmt); if (vatAmt > 0) addBorc('190', vatAmt); addAlacak('320', tryAmt); }); // ── 6. ÜRETİM (Hammadde → Mamul) ──────────────────────────── (AppState.workOrders||[]).filter(wo => wo.status==='Tamamlandı' && inPeriod(wo.startDate)).forEach(wo => { const inputCost = (wo.rawMaterials||[]).reduce((s, rm) => { const lot = (AppState.rawMaterialStock||[]).find(r => r.lotNumber === rm.lotNumber); return s + ((lot?.unitCost||0) * (rm.quantity||0)); }, 0); const outputAmt = (wo.totalOutput||0) * ((wo.unitCost)||inputCost/(wo.totalOutput||1)); if (inputCost > 0) { // 710 DİMM Giderleri BORÇ / 150 İlk Mad. ALACAK addBorc('710', inputCost); addAlacak('150', inputCost); // 152 Mamuller BORÇ / 710 DİMM ALACAK addBorc('152', inputCost); addAlacak('710', inputCost); } }); // ── 7. KREDİ TAKSİTLERİ ───────────────────────────────────── (AppState.loans||[]).forEach(loan => { // Bu dönemde ödenen taksitleri bul (AppState.payments||[]).filter(p => p.referenceType==='loan' && p.referenceId===loan.id && inPeriod(p.date)).forEach(p => { const tryAmt = p.currency==='TRY' ? p.amount : p.amount*((AppState.liveRates||{})[p.currency]||1); addBorc('300', tryAmt); addAlacak('102', tryAmt); }); }); // ── 8. LEASİNG TAKSİTLERİ ─────────────────────────────────── (AppState.leasings||[]).forEach(ls => { (AppState.payments||[]).filter(p => p.referenceType==='leasing' && p.referenceId===ls.id && inPeriod(p.date)).forEach(p => { const tryAmt = p.currency==='TRY' ? p.amount : p.amount*((AppState.liveRates||{})[p.currency]||1); addBorc('301', tryAmt); addAlacak('102', tryAmt); }); }); // ── Hesap bakiyelerini grupla ve sırala ───────────────────── const rows = Object.keys(ACCOUNTS).sort().map(code => { const acct = ACCOUNTS[code]; const entry = bal[code] || {borc:0, alacak:0}; const borc = entry.borc; const alacak = entry.alacak; const bakiye = borc - alacak; return { ...acct, borc, alacak, bakiye }; }).filter(r => r.borc > 0 || r.alacak > 0); // Sıfır satırları gizle const totalBorc = rows.reduce((s,r) => s+r.borc, 0); const totalAlacak = rows.reduce((s,r) => s+r.alacak, 0); const diff = Math.abs(totalBorc - totalAlacak); // Gruplar const groups = [...new Set(rows.map(r => r.group))]; // ── UI ────────────────────────────────────────────────────── const now = new Date(); const months = ['Ocak','Şubat','Mart','Nisan','Mayıs','Haziran','Temmuz','Ağustos','Eylül','Ekim','Kasım','Aralık']; return `
LUCAS INGREDIENTS GIDA SANAYİ TİCARET A.Ş.
GENEL MİZAN — ${periodLabel.toUpperCase()}
Düzenlenme Tarihi: ${now.toLocaleDateString('tr-TR')} | Türkiye Tekdüzen Hesap Planı (TDHP)
Dönem:
Toplam Borç
₺ ${fmtM(totalBorc)}
Toplam Alacak
₺ ${fmtM(totalAlacak)}
Fark
₺ ${fmtM(diff)}
${diff < 1 ? '✅ Dengeli' : '⚠️ Kontrol Et'}
Hesap Sayısı
${rows.length}
${groups.map(grp => { const grpRows = rows.filter(r => r.group === grp); const grpBorc = grpRows.reduce((s,r)=>s+r.borc,0); const grpAlacak = grpRows.reduce((s,r)=>s+r.alacak,0); const grpBakiye = grpBorc - grpAlacak; return ` ${grpRows.map((r,i) => ` `).join('')}`; }).join('')}
Hesap No Hesap Adı Borç (₺) Alacak (₺) Bakiye (₺)
${grp} ${fmtM(grpBorc)} ${fmtM(grpAlacak)} ${grpBakiye>=0?'':'-'}${fmtM(Math.abs(grpBakiye))}
${r.code} ${r.name} ${r.borc>0?fmtM(r.borc):'—'} ${r.alacak>0?fmtM(r.alacak):'—'} ${r.bakiye>=0?'':'-'}${fmtM(Math.abs(r.bakiye))}
GENEL TOPLAM ₺ ${fmtM(totalBorc)} ₺ ${fmtM(totalAlacak)} ${diff<1?'✓ DENGELİ':'₺ '+fmtM(diff)+' FARK'}
⚠️ Önemli Not: Bu mizan, LUCAS ERP sistemindeki operasyonel verilerden otomatik türetilmiştir. Amortisman, kur farkı, dönem sonu ayarlamaları ve vergi hesaplamaları dahil değildir. Resmi raporlama, banka/finans kurumu sunumu ve yasal beyanname öncesinde yetkili muhasebeci tarafından doğrulanması zorunludur.
`; } // ── Mizan kontrol fonksiyonları ────────────────────────────────── window.mizanSetType = function(type) { if (!AppState.mizanPeriod) AppState.mizanPeriod = {}; AppState.mizanPeriod.type = type; // Çeyreklik için ayı çeyrek başına ayarla if (type === 'quarter') { const m = AppState.mizanPeriod.month || new Date().getMonth()+1; AppState.mizanPeriod.month = Math.floor((m-1)/3)*3+1; } render(); }; window.mizanSetMonth = function(m) { if (!AppState.mizanPeriod) AppState.mizanPeriod = {}; AppState.mizanPeriod.month = parseInt(m); render(); }; window.mizanSetYear = function(y) { if (!AppState.mizanPeriod) AppState.mizanPeriod = {}; AppState.mizanPeriod.year = parseInt(y); render(); }; // ── Mizan Excel Export ─────────────────────────────────────────── window.exportMizanExcel = function() { if (typeof XLSX === 'undefined') { alert('Excel kütüphanesi yüklenmedi.'); return; } const p = AppState.mizanPeriod || {}; const wb = XLSX.utils.book_new(); // Tabloyu al const table = document.querySelector('#mizanPrintArea table'); if (!table) { alert('Mizan tablosu bulunamadı.'); return; } const ws = XLSX.utils.table_to_sheet(table); // Kolon genişlikleri ws['!cols'] = [{wch:10},{wch:35},{wch:18},{wch:18},{wch:18}]; XLSX.utils.book_append_sheet(wb, ws, 'Mizan'); const fname = `LUCAS_Mizan_${p.year||'2026'}_${String(p.month||1).padStart(2,'0')}.xlsx`; XLSX.writeFile(wb, fname); }; // ── Mizan Yazdır ──────────────────────────────────────────────── window.printMizan = function() { const content = document.getElementById('mizanPrintArea'); if (!content) return; const win = window.open('', '_blank', 'width=1000,height=800'); win.document.write(` Lucas Mizan ${content.innerHTML} `); win.document.close(); }; function renderProfitLoss() { // Dönem state'i yoksa varsayılan oluştur if (!AppState.plPeriod) { const now = new Date(); AppState.plPeriod = { type: 'month', year: now.getFullYear(), month: now.getMonth() + 1, startDate: null, endDate: null }; } const period = AppState.plPeriod; // Dönem filtresi uygula function isInPeriod(dateStr) { if (!dateStr) return false; const date = new Date(dateStr); if (period.type === 'month') { return date.getFullYear() === period.year && (date.getMonth() + 1) === period.month; } else if (period.type === 'year') { return date.getFullYear() === period.year; } else if (period.type === 'custom') { const start = new Date(period.startDate); const end = new Date(period.endDate); return date >= start && date <= end; } return true; } // Dönem başlığı let periodTitle = ''; if (period.type === 'month') { const monthNames = ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık']; periodTitle = `${monthNames[period.month - 1]} ${period.year}`; } else if (period.type === 'year') { periodTitle = `${period.year} Yılı`; } else if (period.type === 'custom') { periodTitle = `${new Date(period.startDate).toLocaleDateString('tr-TR')} - ${new Date(period.endDate).toLocaleDateString('tr-TR')}`; } // ── GELİRLER: Satış Faturaları + Manuel Gelir Kayıtları ────────── const salesInvoices = (AppState.invoices || []).filter(i => i.type === 'Satış' && isInPeriod(i.date || i.invoiceDate)); const totalRevenue = salesInvoices.reduce((sum, i) => sum + (i.totalTRY || 0), 0); // Manuel gelir kayıtları (dönem filtreli) const manualRevenues = (AppState.revenueEntries || []).filter(e => isInPeriod(e.date)); const totalManualRevenue = manualRevenues.reduce((sum, e) => sum + (e.totalTRY || 0), 0); // Kategori bazlı manuel gelirler (P&L satırları için) const manualByCategory = {}; manualRevenues.forEach(e => { const cat = e.category || 'Diğer Gelirler'; if (!manualByCategory[cat]) manualByCategory[cat] = 0; manualByCategory[cat] += (e.totalTRY || 0); }); // Müşteri bazlı gelir breakdown const revenueByCustomer = {}; salesInvoices.forEach(inv => { const name = inv.customerName || 'Bilinmeyen'; if (!revenueByCustomer[name]) revenueByCustomer[name] = 0; revenueByCustomer[name] += (inv.totalTRY || 0); }); // Ürün bazlı gelir breakdown const revenueByProduct = {}; salesInvoices.forEach(inv => { (inv.items || []).forEach(item => { const name = item.productName || 'Bilinmeyen'; if (!revenueByProduct[name]) revenueByProduct[name] = { revenue: 0, quantity: 0 }; revenueByProduct[name].revenue += item.subtotal * (inv.exchangeRate || 1); revenueByProduct[name].quantity += (item.quantity || 0); }); }); // Teklif fallback (fatura yoksa proformalar) const totalRevenueFallback = totalRevenue === 0 ? AppState.sales.filter(s => isInPeriod(s.date)).reduce((sum, s) => sum + (s.total || 0), 0) : 0; const effectiveRevenue = totalRevenue + totalRevenueFallback + totalManualRevenue; // ── SATIŞ MALİYETİ: Alış Faturaları (dönem filtreli) ───────────── const purchaseInvoices = (AppState.invoices || []).filter(i => i.type === 'Alış' && isInPeriod(i.date || i.invoiceDate)); const rawMaterialCostInv = purchaseInvoices.reduce((sum, i) => sum + (i.totalTRY || 0), 0); // Hammadde stok maliyeti (fallback dönem filtresiyle) const rawMaterialCost = rawMaterialCostInv > 0 ? rawMaterialCostInv : AppState.rawMaterialStock.filter(s => isInPeriod(s.receivedDate)).reduce((sum, s) => sum + (s.totalCost || 0), 0); const consumableCost = AppState.consumableStock ? AppState.consumableStock.filter(s => isInPeriod(s.receivedDate)).reduce((sum, s) => sum + (s.totalCost || 0), 0) : 0; const totalDirectMaterials = rawMaterialCost + consumableCost; // ── GİDER FATURALARI: Kategorilere Göre Grupla ─────────────────── const expensesByCategory = {}; if (AppState.expenseInvoices) { AppState.expenseInvoices .filter(exp => isInPeriod(exp.invoiceDate)) .forEach(exp => { const categoryName = exp.categoryName || 'Diğer'; const subcategory = exp.subcategory || '-'; if (!expensesByCategory[categoryName]) { expensesByCategory[categoryName] = { total: 0, items: {} }; } if (!expensesByCategory[categoryName].items[subcategory]) { expensesByCategory[categoryName].items[subcategory] = 0; } expensesByCategory[categoryName].items[subcategory] += (exp.totalTRY || 0); expensesByCategory[categoryName].total += (exp.totalTRY || 0); }); } // ── TOPLAM GİDER & KAR ─────────────────────────────────────────── const totalExpenses = Object.values(expensesByCategory).reduce((sum, cat) => sum + cat.total, 0); const totalCosts = totalDirectMaterials + totalExpenses; const totalRevFinal = effectiveRevenue; const grossProfit = totalRevFinal - totalDirectMaterials; const grossMargin = totalRevFinal > 0 ? (grossProfit / totalRevFinal * 100) : 0; const netProfit = totalRevFinal - totalCosts; const profitMargin = totalRevFinal > 0 ? ((netProfit / totalRevFinal) * 100) : 0; // ── GELİR TABLOSU HTML YARDIMCILARI ───────────────────────────── const fmtTRY = v => v.toLocaleString('tr-TR', {minimumFractionDigits: 2}); const pct = (v, base) => base > 0 ? ((v / base) * 100).toFixed(1) + '%' : '0%'; return `

📅 Rapor Dönemi

${periodTitle}
💰 Net Satış
${fmtTRY(totalRevFinal)} ₺
${salesInvoices.length} fatura
🏭 COGS
${fmtTRY(totalDirectMaterials)} ₺
${pct(totalDirectMaterials, totalRevFinal)}
📊 Brüt Kar
${fmtTRY(grossProfit)} ₺
${grossMargin.toFixed(1)}% marj
📉 Faaliyet Gideri
${fmtTRY(totalExpenses)} ₺
${pct(totalExpenses, totalRevFinal)}
${netProfit >= 0 ? '✅ Net Kar' : '⚠️ Net Zarar'}
${fmtTRY(netProfit)} ₺
${profitMargin.toFixed(1)}% marj
🧾 Toplam Gider
${fmtTRY(totalCosts)} ₺
COGS + Faaliyet

📊 P&L Tablosu — ${periodTitle}

${totalRevenueFallback > 0 ? ` ` : ''} ${totalManualRevenue > 0 ? ` ${Object.entries(manualByCategory).map(([cat, val]) => ` `).join('')}` : ''} ${rawMaterialCost > 0 ? ` ` : ''} ${consumableCost > 0 ? ` ` : ''} ${Object.keys(expensesByCategory).length > 0 ? ` ${Object.entries(expensesByCategory).map(([cat, data]) => ` ${Object.entries(data.items).map(([sub, amt]) => ` `).join('')} `).join('')}` : ''}
Kalem Tutar (₺) %
📈 NET SATIŞ GELİRİ ${fmtTRY(totalRevFinal)} 100%
Satış Faturaları (${salesInvoices.length} adet) ${fmtTRY(totalRevenue)} ${pct(totalRevenue, totalRevFinal)}
Teklifler (fatura bekleniyor) ${fmtTRY(totalRevenueFallback)} ${pct(totalRevenueFallback, totalRevFinal)}
📋 Diğer Gelirler (${manualRevenues.length} kayıt) ${fmtTRY(totalManualRevenue)} ${pct(totalManualRevenue, totalRevFinal)}
${cat} ${fmtTRY(val)} ${pct(val, totalRevFinal)}
🏭 SATIŞ MALİYETİ (COGS) (${fmtTRY(totalDirectMaterials)}) ${pct(totalDirectMaterials, totalRevFinal)}
Hammadde / Alış Faturaları ${fmtTRY(rawMaterialCost)} ${pct(rawMaterialCost, totalRevFinal)}
Sarf Malzeme ${fmtTRY(consumableCost)} ${pct(consumableCost, totalRevFinal)}
📊 BRÜT KAR ${fmtTRY(grossProfit)} ${grossMargin.toFixed(1)}%
📉 FAALİYET GİDERLERİ (${fmtTRY(totalExpenses)}) ${pct(totalExpenses, totalRevFinal)}
${cat} ${fmtTRY(data.total)} ${pct(data.total, totalRevFinal)}
${sub} ${fmtTRY(amt)} ${pct(amt, totalRevFinal)}
${netProfit >= 0 ? '✅ NET KAR' : '⚠️ NET ZARAR'} ${fmtTRY(netProfit)} ${profitMargin.toFixed(1)}%

👤 Müşteri Bazlı Gelir

${Object.keys(revenueByCustomer).length === 0 ? '

Bu dönemde fatura yok

' : `
${Object.entries(revenueByCustomer) .sort((a,b) => b[1]-a[1]) .map(([name, val]) => { const barPct = totalRevFinal > 0 ? (val/totalRevFinal*100) : 0; return `
${name} ${fmtTRY(val)} ₺ (${barPct.toFixed(0)}%)
`; }).join('')} ${totalManualRevenue > 0 ? `
📋 Diğer Gelirler ${fmtTRY(totalManualRevenue)} ₺ (${pct(totalManualRevenue, totalRevFinal)})
` : ''}
Toplam: ${fmtTRY(totalRevFinal)} ₺
`}

📦 Ürün Bazlı Gelir

${Object.keys(revenueByProduct).length === 0 ? '

Bu dönemde fatura yok

' : ` ${Object.entries(revenueByProduct) .sort((a,b) => b[1].revenue - a[1].revenue) .map(([name, data]) => ` `).join('')}
Ürün Miktar Gelir (₺) Pay
${name} ${data.quantity.toLocaleString('tr-TR')} KG ${fmtTRY(data.revenue)} ${pct(data.revenue, totalRevFinal)}
`}
${Object.keys(expensesByCategory).length > 0 ? `

💸 Faaliyet Gider Detayı

${Object.entries(expensesByCategory).map(([cat, data]) => `
${cat}
${fmtTRY(data.total)} ₺
${Object.entries(data.items).map(([sub, amt]) => `
${sub} ${fmtTRY(amt)} ₺
`).join('')}
`).join('')}
` : ''}
`; } // ── HAFTALIK NAKİT AKIŞ DETAY MODALi ────────────────────────── window.openWeekDetailModal = function(weekIdx) { const wk = (AppState._cfWeeks||[])[weekIdx]; if (!wk) return; const weekLabel = wk.label; const events = wk.events || []; const inflowEvents = events.filter(e => e.origAmt > 0); const outflowEvents = events.filter(e => e.origAmt < 0); const inflow = wk.inTRY.toLocaleString('tr-TR',{minimumFractionDigits:2}); const outflow = wk.outTRY.toLocaleString('tr-TR',{minimumFractionDigits:2}); const net = wk.netTRY.toLocaleString('tr-TR',{minimumFractionDigits:2}); const balance = wk.closeTRY.toLocaleString('tr-TR',{minimumFractionDigits:2}); const fmtM = v => Math.abs(v).toLocaleString('tr-TR', {minimumFractionDigits:2}); const curSym = c => ({TRY:'₺',USD:'$',EUR:'€',GBP:'£'})[c]||c; const sourceBadge = src => { const map = { 'Satış Faturası':'bg-green-100 text-green-700', 'Alış Faturası':'bg-red-100 text-red-700', 'Gider Faturası':'bg-orange-100 text-orange-700', 'Kredi Taksiti':'bg-blue-100 text-blue-700', 'Leasing Taksiti':'bg-purple-100 text-purple-700', 'Alınan Çek':'bg-teal-100 text-teal-700', 'Verilen Çek':'bg-indigo-100 text-indigo-700', 'Alınan Senet':'bg-cyan-100 text-cyan-700', 'Verilen Senet':'bg-amber-100 text-amber-700', }; return `${src}`; }; const eventRow = e => { const isIn = e.origAmt > 0; const dateStr = e.date ? new Date(e.date).toLocaleDateString('tr-TR') : ''; return ` ${dateStr} ${sourceBadge(e.source)} ${e.label}
${isIn?'+':''}${fmtM(Math.abs(e.origAmt))} ${curSym(e.origCur||'TRY')}
${e.origCur && e.origCur !== 'TRY' ? `
≈ ${fmtM(Math.abs(e.tryAmt||0))} ₺
` : ''} `; }; const modalHtml = `
📥 Toplam Giriş
+${inflow} ₺
📤 Toplam Çıkış
-${outflow} ₺
📊 Net
${net} ₺
🏦 Bakiye
${balance} ₺
${inflowEvents.length > 0 ? `

Tahsilatlar / Girişler (${inflowEvents.length} kalem)

${inflowEvents.map(eventRow).join('')}
Tarih Kaynak Açıklama Tutar
Toplam Giriş: +${inflow} ₺
` : ''} ${outflowEvents.length > 0 ? `

Ödemeler / Çıkışlar (${outflowEvents.length} kalem)

${outflowEvents.map(eventRow).join('')}
Tarih Kaynak Açıklama Tutar
Toplam Çıkış: -${outflow} ₺
` : ''}
`; document.getElementById('weekDetailModalTitle').textContent = `📅 ${weekLabel} — Haftalık Nakit Akış Detayı`; document.getElementById('weekDetailModalContent').innerHTML = modalHtml; openModal('weekDetailModal'); }; window.printWeekDetail = function() { const content = document.getElementById('weekDetailPrintArea'); const printTitle = document.querySelector('#weekDetailPrintArea .print-only'); if (printTitle) printTitle.style.display = 'block'; const win = window.open('', '_blank', 'width=900,height=700'); win.document.write(` Nakit Akış Detayı ${content.innerHTML} `); win.document.close(); if (printTitle) printTitle.style.display = 'none'; }; window.setPLPeriod = function(type, offset) { const now = new Date(); if (!AppState.plPeriod) AppState.plPeriod = {}; if (type === 'month') { if (offset === 0) { AppState.plPeriod.type = 'month'; AppState.plPeriod.year = now.getFullYear(); AppState.plPeriod.month = now.getMonth() + 1; } else { const current = new Date(AppState.plPeriod.year || now.getFullYear(), (AppState.plPeriod.month || now.getMonth() + 1) - 1, 1); current.setMonth(current.getMonth() + offset); AppState.plPeriod.type = 'month'; AppState.plPeriod.year = current.getFullYear(); AppState.plPeriod.month = current.getMonth() + 1; } } else if (type === 'year') { AppState.plPeriod.type = 'year'; AppState.plPeriod.year = now.getFullYear(); } render(); }; window.openCustomPeriodModal = function() { const content = `

📊 Özel Tarih Aralığı

`; document.getElementById('customPeriodModalContent').innerHTML = content; openModal('customPeriodModal'); }; window.setCustomPLPeriod = function(event) { event.preventDefault(); const startDate = document.getElementById('plStartDate').value; const endDate = document.getElementById('plEndDate').value; if (new Date(startDate) > new Date(endDate)) { alert('❌ Başlangıç tarihi bitiş tarihinden sonra olamaz!'); return; } AppState.plPeriod = { type: 'custom', startDate: startDate, endDate: endDate }; closeModal('customPeriodModal'); render(); }; // Reports Component window.Shipping = function() { try { // Get all products in stock const allProducts = AppState.finishedProductStock || []; const readyProducts = allProducts.filter(s => s.available && s.qualityStatus === 'Onaylandı' && !s.isOffspec); const pendingProducts = allProducts.filter(s => s.qualityStatus === 'Beklemede'); const rejectedProducts = allProducts.filter(s => s.qualityStatus === 'Reddedildi'); const offspecProducts = allProducts.filter(s => s.isOffspec && s.available); console.log('Shipping module loaded'); console.log('Ready products:', readyProducts.length); console.log('Shipments:', AppState.shipments); return `

🚚 Sevkiyat Yönetimi

${readyProducts.length > 0 ? '' : ''}

Sevkiyat Kayıtları

${AppState.shipments && AppState.shipments.length > 0 ? AppState.shipments.map(ship => ` `).join('') : ''}
Sevkiyat No İrsaliye No Müşteri Ürün Sayısı Toplam Miktar Sevk Tarihi Taşıyıcı Fatura Durumu İşlemler
${ship.shipmentNumber || ship.waybillNumber || '—'} ${ship.waybillNumber || ship.shipmentNumber || '—'} ${ship.customerName} ${ship.items ? ship.items.length : '1'} ürün ${((ship.totalWeight || ship.totalQuantity || (ship.items && ship.items.reduce((s,i)=>s+(i.quantity||0),0)) || ship.quantity || 0)).toLocaleString('tr-TR')} KG ${new Date(ship.shipmentDate || ship.date).toLocaleDateString('tr-TR')} ${ship.carrierCompany || ship.carrier || '—'} ${ship.invoiceId ? ` ✅ Faturalandı
${ship.invoiceNumber || ''}
` : ` ⏳ Fatura Bekleniyor
Finans → Fatura Kes
`}
Henüz sevkiyat kaydı yok

✅ Sevke Hazır Ürünler (Kalite Onaylı)

${readyProducts.length > 0 ? ` ${readyProducts.map(product => ` `).join('')}
Lot No Ürün Adı Miktar (KG) Torba Üretim Tarihi Kalite Sonucu İşlem
${product.lotNumber} ${product.productName} ${product.quantity.toLocaleString('tr-TR')} ${product.bags} ${new Date(product.productionDate).toLocaleDateString('tr-TR')} ✅ ${product.qualityResult || 'UYGUN'}
` : '

Kalite onaylı, sevke hazır ürün bulunmamaktadır

'}
${pendingProducts.length > 0 ? `

⏳ Kalite Onayı Bekleyen Ürünler

${pendingProducts.map(product => ` `).join('')}
Lot No Ürün Adı Miktar (KG) Torba Kalite Durumu Üretim Tarihi
${product.lotNumber} ${product.productName} ${product.quantity.toLocaleString('tr-TR')} ${product.bags} ${product.qualityStatus || 'Beklemede'} ${new Date(product.productionDate).toLocaleDateString('tr-TR')}
ℹ️ Bilgi: Bu ürünler kalite onayı beklemektedir. Sevk edilebilmesi için Kalite & Laboratuvar modülünden COA doldurulup onaylanması gerekmektedir.
` : ''} ${rejectedProducts.length > 0 ? `

❌ Kalite Reddedilen Ürünler

${rejectedProducts.map(product => ` `).join('')}
Lot No Ürün Adı Miktar (KG) Üretim Tarihi Kalite Sonucu Hatalı Parametreler
${product.lotNumber} ${product.productName} ${product.quantity.toLocaleString('tr-TR')} ${new Date(product.productionDate).toLocaleDateString('tr-TR')} ❌ ${product.qualityResult || 'UYGUN DEĞİL'} ${product.failedParameters ? product.failedParameters.map(p => `
• ${p.name}: ${p.result} (Limit: ${p.limit})
`).join('') : 'Detay yok'}
⚠️ Uyarı: Bu ürünler kalite testlerinden geçememiştir ve sevk edilemez. Kalite & Laboratuvar modülünden işlem seçiniz: Rework, Yeniden Analiz veya Offspec.
` : ''} ${offspecProducts.length > 0 ? `

🟠 Offspec Ürünler (Gıda Dışı Kullanım)

${offspecProducts.map(product => ` `).join('')}
Lot No Ürün Adı Miktar (KG) Torba Üretim Tarihi Kalite Sonucu Hatalı Parametreler İşlem
${product.lotNumber} ${product.productName} ${product.quantity.toLocaleString('tr-TR')} ${product.bags} ${new Date(product.productionDate).toLocaleDateString('tr-TR')} 🟠 ${product.qualityResult} ${product.failedParameters ? product.failedParameters.map(p => `
• ${p.name}: ${p.result} (Limit: ${p.limit})
`).join('') : 'Detay yok'}
ℹ️ Bilgi: Bu ürünler gıda dışı kullanım için satılabilir (kozmetik, endüstriyel vb.). Müşteriye offspec/gıda dışı olduğu belirtilmelidir.
` : ''}
`; } catch (error) { console.error('Shipping module error:', error); return `

⚠️ Sevkiyat Modülü Hatası

Hata: ${error.message}

F12 → Console'u kontrol edin

`; } } // Placeholder declarations for Sales modal functions // These will be overridden later in the file window.openQuotationModal = function() { alert('Teklif modal yükleniyor... Lütfen bekleyin.'); }; window.openSampleRequestModal = function() { alert('Numune modal yükleniyor... Lütfen bekleyin.'); }; window.openProformaInvoiceModal = function() { alert('Proforma modal yükleniyor... Lütfen bekleyin.'); }; // Maintenance & Repair function Maintenance() { return `

🔧 Bakım & Onarım

Makine bakımı, onarım kayıtları ve sarf malzeme çıkışları

🔧

Bakım Planı

Periyodik bakım takibi

Yakında...

⚠️

Arıza Kayıtları

Onarım geçmişi

Yakında...

📦

Sarf Malzeme Çıkışı

Civata, kaynak, vb.

Yakında...

🚧

Yakında Gelecek

Bu modül şu anda yapılandırılmaktadır.
Bakım planları, arıza kayıtları ve sarf malzeme çıkışları burada takip edilecektir.

`; } function Reports() { return `

Raporlama & Audit Trail

📜

Audit Trail (Kim Ne Yaptı)

Tüm sistem aktivitelerinin detaylı kaydı

${AppState.auditLog.length} kayıt
📊

Üretim Raporları

Aylık/yıllık üretim detayları, ürün bazlı analiz

📦

Stok Durum Raporları

Hammadde ve bitmiş ürün stok seviyeleri

💰

Parti Bazlı Maliyet

Lot bazında maliyet analizi ve karlılık

💼

Satış Raporları

Müşteri ve ürün bazlı satış analizleri

🚚

Tedarikçi Raporları

Tedarikçi bazlı alım ve kalite analizi

⚠️

Fire Raporları

Üretim fire oranları ve trend analizi

🔬

Kalite Analiz Raporları

Bloom, pH, viskozite trendleri

📈

Cari Hesap Raporları

Müşteri bazlı alacak takibi

📋

P&L Özet Raporları

Gelir-gider analizi ve karlılık

`; } // Excel Upload Functions function uploadCustomersExcel(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(firstSheet); let imported = 0; rows.forEach(row => { const customerName = row['MÜŞTERİ ADI'] || row['Müşteri Adı'] || row['Customer Name']; if (customerName) { const newCustomer = { id: AppState.customers.length + 1 + imported, name: customerName, type: row['TİP'] || row['Tip'] || row['Type'] || 'Yerli', sector: row['Sektör'] || row['Sector'] || '', phone: row['Telefon'] || row['Phone'] || '', email: row['E -mail'] || row['Email'] || row['E-mail'] || '', address: row['Adres'] || row['Address'] || '', taxNo: row['vergi no'] || row['Vergi No'] || row['Tax No'] || '', taxOffice: row['Vergi dairesi'] || row['Vergi Dairesi'] || row['Tax Office'] || '', notes: row['Notlar'] || row['Notes'] || '' }; AppState.customers.push(newCustomer); imported++; } }); alert(`${imported} müşteri başarıyla yüklendi!`); render(); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); } }; reader.readAsArrayBuffer(file); event.target.value = ''; } function uploadSuppliersExcel(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(firstSheet); let imported = 0; rows.forEach(row => { const supplierName = row['TEDARİKÇİ ADI'] || row['Tedarikçi Adı'] || row['Supplier Name']; if (supplierName) { const newSupplier = { id: AppState.suppliers.length + 1 + imported, name: supplierName, type: row['TİP'] || row['Tip'] || row['Type'] || 'Yerli', country: row['Ülke'] || row['Country'] || 'Türkiye', phone: row['Telefon'] || row['Phone'] || '', email: row['E -mail'] || row['Email'] || row['E-mail'] || '', address: row['Adres'] || row['Address'] || '', taxNo: row['vergi no'] || row['Vergi No'] || row['Tax No'] || '', taxOffice: row['Vergi dairesi'] || row['Vergi Dairesi'] || row['Tax Office'] || '', notes: row['Notlar'] || row['Notes'] || '' }; AppState.suppliers.push(newSupplier); imported++; } }); alert(`${imported} tedarikçi başarıyla yüklendi!`); render(); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); } }; reader.readAsArrayBuffer(file); event.target.value = ''; } function uploadRawMaterialsExcel(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(firstSheet); let imported = 0; rows.forEach(row => { if (row['Hammadde Adı'] || row['Material Name']) { const newMaterial = { id: AppState.rawMaterials.length + 1 + imported, code: row['Hammadde Kodu'] || row['Material Code'] || `HM-${(AppState.rawMaterials.length + 1 + imported).toString().padStart(3, '0')}`, name: row['Hammadde Adı'] || row['Material Name'], mainCategory: row['Ana Kategori'] || row['Main Category'] || 'Diğer', type: row['Tip'] || row['Type'] || '', origin: row['Köken/Kaynak'] || row['Origin'] || '', unit: row['Birim'] || row['Unit'] || 'KG', notes: row['Notlar'] || row['Notes'] || '' }; AppState.rawMaterials.push(newMaterial); imported++; } }); alert(`${imported} hammadde başarıyla yüklendi!`); render(); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); } }; reader.readAsArrayBuffer(file); event.target.value = ''; } function uploadFinishedProductsExcel(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(firstSheet); let imported = 0; let errors = []; rows.forEach((row, index) => { if (row['Ürün Adı'] || row['Product Name']) { const rmCode = row['Hammadde Kodu'] || row['Raw Material Code']; const rawMaterial = AppState.rawMaterials.find(rm => rm.code === rmCode); if (!rawMaterial) { errors.push(`Satır ${index + 2}: Hammadde kodu "${rmCode}" bulunamadı`); return; } const newProduct = { id: AppState.finishedProducts.length + 1 + imported, name: row['Ürün Adı'] || row['Product Name'], rawMaterialId: rawMaterial.id, rawMaterialCode: rawMaterial.code, rawMaterialName: rawMaterial.name, bloomRange: row['Bloom Aralığı'] || row['Bloom Range'] || '', category: row['Kategori'] || row['Category'] || '', unit: row['Birim'] || row['Unit'] || '25KG Kraft', code: '', notes: row['Notlar'] || row['Notes'] || '' }; AppState.finishedProducts.push(newProduct); imported++; } }); if (errors.length > 0) { alert(`${imported} ürün yüklendi.\n\nHatalar:\n${errors.join('\n')}`); } else { alert(`${imported} ürün başarıyla yüklendi!`); } render(); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); } }; reader.readAsArrayBuffer(file); event.target.value = ''; } function uploadRawMaterialStockExcel(event) { const file = event.target.files[0]; if (!file) return; console.log('Excel yükleniyor:', file.name); const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); let totalImported = 0; let errors = []; // Process first sheet const sheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet); console.log(`Excel'de ${rows.length} satır bulundu`); rows.forEach((row, index) => { console.log(`Satır ${index + 2} işleniyor:`, row); try { // Get required fields const supplierName = row['Tedarikçi']; const materialCode = row['Hammadde Kodu']; const lotNumber = row['Parti/Lot No']; const quantity = parseFloat(row['Tartılan Miktar (KG)']); const unitCost = parseFloat(row['Birim Fiyat']); const currency = row['Para Birimi'] || 'TRY'; const exchangeRate = parseFloat(row['Döviz Kuru (TRY)']) || 1; const vatRate = parseFloat(row['KDV (%)']) || 0; console.log(' - Para Birimi:', currency, 'Kur:', exchangeRate, 'KDV:', vatRate); if (!supplierName || !materialCode || !lotNumber || !quantity || !unitCost) { const missing = []; if (!supplierName) missing.push('Tedarikçi'); if (!materialCode) missing.push('Hammadde Kodu'); if (!lotNumber) missing.push('Parti/Lot No'); if (!quantity) missing.push('Tartılan Miktar'); if (!unitCost) missing.push('Birim Fiyat'); errors.push(`Satır ${index + 2}: Eksik alanlar: ${missing.join(', ')}`); console.log(' ❌ Eksik alanlar:', missing.join(', ')); return; } // Find supplier const supplier = AppState.suppliers.find(s => s.name.toLowerCase().includes(supplierName.toLowerCase()) ); if (!supplier) { errors.push(`Satır ${index + 2}: Tedarikçi "${supplierName}" bulunamadı`); console.log(` ❌ Tedarikçi "${supplierName}" bulunamadı`); console.log(' Mevcut tedarikçiler:', AppState.suppliers.map(s => s.name)); return; } // Find material by code OR name (flexible) let material = AppState.rawMaterials.find(m => m.code === materialCode); // If not found by code, try by name if (!material) { const materialName = row['Hammadde Adı']; if (materialName) { material = AppState.rawMaterials.find(m => m.name.toLowerCase().includes(materialName.toLowerCase()) || materialName.toLowerCase().includes(m.name.toLowerCase()) ); if (material) { console.log(` ⚠️ Kod "${materialCode}" bulunamadı ama isim "${materialName}" ile eşleşti: ${material.name} (${material.code})`); } } } if (!material) { errors.push(`Satır ${index + 2}: Hammadde kodu "${materialCode}" ve adı bulunamadı`); console.log(` ❌ Hammadde kodu "${materialCode}" bulunamadı`); console.log(' Mevcut kodlar:', AppState.rawMaterials.map(m => `${m.code} - ${m.name}`).join(', ')); return; } console.log(' ✓ Tedarikçi bulundu:', supplier.name); console.log(' ✓ Hammadde bulundu:', material.name, `(${material.code})`); // Hesaplamalar const subtotal = quantity * unitCost; const vatAmount = subtotal * (vatRate / 100); const total = subtotal + vatAmount; const totalTRY = total * exchangeRate; const newStock = { id: AppState.rawMaterialStock.length + 1 + totalImported, materialId: material.id, materialCode: material.code, materialName: material.name, supplierId: supplier.id, supplierName: supplier.name, waybillNo: row['İrsaliye No'] || '', invoiceNo: row['Fatura No'] || '', lotNumber: lotNumber, depotNo: row['Bigbag/Depo No'] || '', quantity: quantity, unitCost: unitCost, currency: currency, exchangeRate: exchangeRate, vatRate: vatRate, vatAmount: vatAmount * exchangeRate, totalCost: totalTRY, bloom: parseFloat(row['Bloom']) || null, mesh: parseInt(row['Mesh']) || null, clarity: parseFloat(row['Berraklık (%T 620nm)']) || null, color: parseFloat(row['Renk (%T 450nm)']) || null, ph: parseFloat(row['pH']) || null, viscosity: parseFloat(row['Viskozite (mPa.s)']) || null, productionDate: row['Üretim Tarihi'] || '', expiryDate: row['Son Kullanım Tarihi'] || '', receivedDate: row['Kabul Tarihi'] || new Date().toISOString().split('T')[0], checkedBy: row['Kontrolü Yapan'] || 'Admin' }; AppState.rawMaterialStock.push(newStock); totalImported++; } catch (err) { errors.push(`Satır ${index + 2}: ${err.message}`); } }); if (errors.length > 0) { alert(`${totalImported} hammadde girişi yüklendi.\n\nHatalar:\n${errors.slice(0, 10).join('\n')}${errors.length > 10 ? '\n...' : ''}`); } else { alert(`✅ ${totalImported} hammadde girişi başarıyla yüklendi!`); } render(); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); console.error(error); } }; reader.readAsArrayBuffer(file); event.target.value = ''; } function excelDateToJSDate(excelDate) { // Excel stores dates as numbers (days since 1900-01-01) if (typeof excelDate === 'number') { const date = new Date((excelDate - 25569) * 86400 * 1000); return date.toISOString().split('T')[0]; } // If it's already a date object if (excelDate instanceof Date) { return excelDate.toISOString().split('T')[0]; } // If it's a string, return as is return excelDate; } function downloadExcelTemplate(type) { // This is the old function, kept for backward compatibility downloadTemplate(type); } window.downloadTemplate = function(type) { let data = []; let filename = ''; switch(type) { case 'customers': data = [ { 'MÜŞTERİ ADI': 'Örnek Müşteri A.Ş.', 'TİP': 'Yerli', 'Ülke': 'Türkiye', 'Telefon': '+90 212 555 0000', 'E -mail': 'info@ornek.com', 'vergi no': '1234567890', 'Vergi dairesi': 'Kadıköy', 'Notlar': 'Gıda sanayii müşterisi' } ]; filename = 'Musteri_Tanimlama_Sablonu.xlsx'; break; case 'suppliers': data = [ { 'TEDARİKÇİ ADI': 'Örnek Tedarikçi Ltd.', 'TİP': 'Yerli', 'Ülke': 'Türkiye', 'Telefon': '+90 232 555 0000', 'E -mail': 'satis@tedarikci.com', 'vergi no': '0987654321', 'Vergi dairesi': 'İzmir', 'Notlar': '30 gün vadeli ödeme' } ]; filename = 'Tedarikci_Tanimlama_Sablonu.xlsx'; break; case 'rawMaterials': data = [ { 'Hammadde Adı': 'Sığır Jelatini', 'Ana Kategori': 'Hayvansal', 'Tip': 'Jelatin', 'Köken/Kaynak': 'Sığır', 'Birim': 'KG', 'Notlar': '' }, { 'Hammadde Adı': 'Balık Jelatini', 'Ana Kategori': 'Hayvansal', 'Tip': 'Jelatin', 'Köken/Kaynak': 'Balık', 'Birim': 'KG', 'Notlar': '' }, { 'Hammadde Adı': 'Acacia Senegal', 'Ana Kategori': 'Bitkisel', 'Tip': 'Gum Arabic', 'Köken/Kaynak': 'Acacia Senegal', 'Birim': 'KG', 'Notlar': '' } ]; filename = 'Hammadde_Tanimlama_Sablonu.xlsx'; break; case 'finishedProducts': data = [ { 'Ürün Adı': '240-260 Bloom Yenilebilir Sığır Jelatini 20 MESH', 'Hammadde Kodu': 'AN-GEL-BV-0001', 'Bloom Aralığı': '240-260', 'Kategori': 'Jelatin', 'Birim': '25KG Kraft', 'Notlar': '' }, { 'Ürün Adı': '180-220 Bloom Balık Jelatini', 'Hammadde Kodu': 'AN-GEL-FS-0001', 'Bloom Aralığı': '180-220', 'Kategori': 'Jelatin', 'Birim': '20KG Kraft', 'Notlar': '' } ]; filename = 'Bitmis_Urun_Tanimlama_Sablonu.xlsx'; break; case 'rawMaterialStock': data = [ { 'Tedarikçi': 'Global Gelatin Supplier', 'İrsaliye No': 'IRS-2026-001', 'Fatura No': 'FAT-2026-001', 'Hammadde Kodu': 'AN-GEL-BV-0001', 'Hammadde Adı': 'Sığır Jelatini', 'Parti/Lot No': 'LOT-2026-001', 'Bigbag/Depo No': 'BB-A-001', 'Tartılan Miktar (KG)': 1000, 'Birim Fiyat': 8.50, 'Para Birimi': 'USD', 'Döviz Kuru (TRY)': 34.5678, 'KDV (%)': 20, 'Bloom': 255, 'Mesh': 20, 'Berraklık (%T 620nm)': 96.5, 'Renk (%T 450nm)': 83.2, 'pH': 5.8, 'Viskozite (mPa.s)': 4.5, 'Üretim Tarihi': '2025-12-15', 'Son Kullanım Tarihi': '2030-12-15', 'Kabul Tarihi': '2026-02-11', 'Kontrolü Yapan': 'Admin' } ]; filename = 'Hammadde_Girisi_Sablonu.xlsx'; break; case 'consumableStock': data = [ { 'Tedarikçi': 'Ambalaj Tedarikçi A.Ş.', 'İrsaliye No': 'IRS-2026-100', 'Fatura No': 'FAT-2026-100', 'Sarf Malzeme Adı': 'Kraft Ambalaj 25KG', 'Miktar': 15000, 'Birim': 'Adet', 'Birim Fiyat': 0.45, 'Para Birimi': 'EUR', 'Döviz Kuru (TRY)': 36.8000, 'KDV (%)': 20, 'Depo No': 'DEPO-C-01', 'Kabul Tarihi': '2026-02-11' } ]; filename = 'Sarf_Malzeme_Girisi_Sablonu.xlsx'; break; default: alert('Geçersiz şablon tipi!'); return; } const ws = XLSX.utils.json_to_sheet(data); // Set column widths based on type if (type === 'rawMaterialStock') { ws['!cols'] = [ {wch: 25}, // Tedarikçi {wch: 18}, // İrsaliye No {wch: 18}, // Fatura No {wch: 20}, // Hammadde Kodu {wch: 30}, // Hammadde Adı {wch: 18}, // Parti/Lot No {wch: 18}, // Bigbag/Depo No {wch: 18}, // Tartılan Miktar {wch: 15}, // Birim Fiyat {wch: 15}, // Para Birimi {wch: 18}, // Döviz Kuru (TRY) {wch: 12}, // KDV (%) {wch: 10}, // Bloom {wch: 10}, // Mesh {wch: 18}, // Berraklık {wch: 18}, // Renk {wch: 10}, // pH {wch: 18}, // Viskozite {wch: 15}, // Üretim Tarihi {wch: 18}, // SKT {wch: 15}, // Kabul Tarihi {wch: 18} // Kontrolü Yapan ]; } else if (type === 'consumableStock') { ws['!cols'] = [ {wch: 25}, // Tedarikçi {wch: 18}, // İrsaliye No {wch: 18}, // Fatura No {wch: 30}, // Sarf Malzeme Adı {wch: 12}, // Miktar {wch: 12}, // Birim {wch: 15}, // Birim Fiyat {wch: 15}, // Para Birimi {wch: 18}, // Döviz Kuru (TRY) {wch: 12}, // KDV (%) {wch: 18}, // Depo No {wch: 15} // Kabul Tarihi ]; } else if (type === 'customers' || type === 'suppliers') { ws['!cols'] = [ {wch: 30}, {wch: 10}, {wch: 15}, {wch: 18}, {wch: 25}, {wch: 15}, {wch: 20}, {wch: 40} ]; } else { ws['!cols'] = [{wch: 30}, {wch: 20}, {wch: 20}, {wch: 15}]; } const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Şablon'); XLSX.writeFile(wb, filename); }; function exportRawMaterialStockToExcel() { if (AppState.rawMaterialStock.length === 0) { alert('Henüz kaydedilmiş hammadde girişi yok!'); return; } const data = AppState.rawMaterialStock.map(stock => ({ 'Tedarikçi': stock.supplierName || '', 'İrsaliye No': stock.invoiceNo || '', 'Malzeme/Hizmetin Tanımı': stock.materialName, 'KG fiyat': stock.unitCost, 'Etikette Yazan Miktar(KG)': stock.labelQuantity || stock.quantity, 'Tartılan Miktar(KG)': stock.quantity, 'Bigbag/ DEPO No': stock.bigbagNo || '', 'Parti / Lot No': stock.lotNumber, 'Berraklık (620)': stock.clarity || '', 'Renk (450)': stock.color || '', 'Bloom': stock.bloom || '', 'Mesh': stock.mesh || '', 'Üretim Tarihi': stock.productionDate || '', 'Son Kullanım Tarihi': stock.expiryDate || '', 'Ürün Analiz Sertifikası (Var/Yok, No)': stock.hasCOA ? '✓' : '✗', 'Helal Sertifikası (Var/Yok)': stock.hasHalalCert ? '✓' : '✗', 'Alım Şartnamesine uygun mu?': '✓', 'Ürün Görünüşü Ve Kokusu Normal Mi?': '✓', 'Taşıyıcılar Hijyen Kurallarına Uyuyor Mu?': '✓', 'Ürüne Temas Eden Malzemeler Temiz Mi?': '✓', 'Ürünler Aracın Tabanına Temas Ediyor Mu?': '✓', 'Aracın İç Yüzeyi Temiz Mi?': '✓', 'Ürün Ambalajı Sağlam Mı? Temiz Ve Uygun Mu?': '✓', 'Ürün Kabul Edilerek Teslim Alındı Mı?': '✓', 'Açıklama': stock.notes || '', 'Kontrolü Yapan': stock.checkedBy || '', 'TARİH': stock.receivedDate })); const ws = XLSX.utils.json_to_sheet(data); // Set column widths ws['!cols'] = [ {wch: 25}, {wch: 15}, {wch: 30}, {wch: 10}, {wch: 15}, {wch: 15}, {wch: 15}, {wch: 15}, {wch: 12}, {wch: 12}, {wch: 10}, {wch: 8}, {wch: 15}, {wch: 15}, {wch: 20}, {wch: 15}, {wch: 15}, {wch: 20}, {wch: 20}, {wch: 20}, {wch: 20}, {wch: 15}, {wch: 20}, {wch: 15}, {wch: 30}, {wch: 20}, {wch: 12} ]; const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Girdi Kontrol'); const filename = `Girdi_Kontrol_Formu_${new Date().toISOString().split('T')[0]}.xlsx`; XLSX.writeFile(wb, filename); } // Calculate customer balance (sales - payments) function calculateCustomerBalance(customerId) { const customerSales = AppState.sales.filter(s => s.customerId === customerId); const totalSales = customerSales.reduce((sum, s) => sum + s.total, 0); // TODO: subtract payments when payment system is implemented return totalSales; } // Calculate supplier balance (purchases - payments) function calculateSupplierBalance(supplierId) { const supplierPurchases = AppState.rawMaterialStock.filter(s => s.supplierId === supplierId); const totalPurchases = supplierPurchases.reduce((sum, s) => sum + s.totalCost, 0); // TODO: subtract payments when payment system is implemented return totalPurchases; } // Print current page window.printPage = function() { window.print(); }; // Print specific element window.printElement = function(elementId) { const element = document.getElementById(elementId); if (!element) return; const printWindow = window.open('', '', 'height=600,width=800'); printWindow.document.write('LUCAS ERP - Yazdır'); printWindow.document.write(''); printWindow.document.write('
'); printWindow.document.write('

LUCAS INGREDIENTS GIDA A.Ş.

'); printWindow.document.write('

Tarih: ' + new Date().toLocaleDateString('tr-TR') + '

'); printWindow.document.write('
'); printWindow.document.write(element.innerHTML); printWindow.document.write(''); printWindow.document.close(); printWindow.print(); }; // Open customer detail modal window.openCustomerDetail = function(customerId) { const customer = AppState.customers.find(c => c.id === customerId); if (!customer) return; const customerSales = AppState.sales.filter(s => s.customerId === customerId); const balance = calculateCustomerBalance(customerId); const content = `

${customer.name}

${customer.type} ${customer.sector ? `${customer.sector}` : ''}
Toplam Satış
${customerSales.reduce((sum, s) => sum + s.total, 0).toLocaleString('tr-TR')} ₺
Güncel Bakiye
${balance.toLocaleString('tr-TR')} ₺
${balance > 0 ? 'Alacak' : 'Ödenmiş'}
Fatura Sayısı
${customerSales.length}

İletişim Bilgileri

${customer.phone ? `
Telefon: ${customer.phone}
` : ''} ${customer.email ? `
Email: ${customer.email}
` : ''} ${customer.address ? `
Adres: ${customer.address}
` : ''}

Vergi Bilgileri

${customer.taxNo ? `
Vergi No: ${customer.taxNo}
` : ''} ${customer.taxOffice ? `
Vergi Dairesi: ${customer.taxOffice}
` : ''}

Satış Geçmişi (${customerSales.length})

${customerSales.length > 0 ? `
${customerSales.map(sale => ` `).join('')}
Belge No Tip Tarih Tutar Durum
${sale.number} ${sale.type} ${new Date(sale.date).toLocaleDateString('tr-TR')} ${sale.total.toLocaleString('tr-TR')} ₺ ${sale.status}
` : '

Henüz satış yapılmamış

'}
`; document.getElementById('customerDetailContent').innerHTML = content; openModal('customerDetailModal'); }; // Open supplier detail modal window.openSupplierDetail = function(supplierId) { const supplier = AppState.suppliers.find(s => s.id === supplierId); if (!supplier) return; const supplierPurchases = AppState.rawMaterialStock.filter(s => s.supplierId === supplierId); const balance = calculateSupplierBalance(supplierId); const content = `

${supplier.name}

${supplier.type} ${supplier.country}
Toplam Alım
${supplierPurchases.reduce((sum, s) => sum + s.totalCost, 0).toLocaleString('tr-TR')} ₺
Güncel Borç
${balance.toLocaleString('tr-TR')} ₺
${balance > 0 ? 'Ödeme Bekliyor' : 'Ödenmiş'}
Hammadde Girişi
${supplierPurchases.length}

İletişim Bilgileri

${supplier.phone ? `
Telefon: ${supplier.phone}
` : ''} ${supplier.email ? `
Email: ${supplier.email}
` : ''} ${supplier.address ? `
Adres: ${supplier.address}
` : ''}

Diğer Bilgiler

${supplier.taxNo ? `
Vergi No: ${supplier.taxNo}
` : ''} ${supplier.taxOffice ? `
Vergi Dairesi: ${supplier.taxOffice}
` : ''} ${supplier.notes ? `
Notlar: ${supplier.notes}
` : ''}

Alım Geçmişi (${supplierPurchases.length})

${supplierPurchases.length > 0 ? `
${supplierPurchases.map(purchase => ` `).join('')}
Tarih Lot No Hammadde Miktar (KG) Birim Fiyat Toplam
${new Date(purchase.receivedDate).toLocaleDateString('tr-TR')} ${purchase.lotNumber} ${purchase.materialName} ${purchase.quantity.toLocaleString('tr-TR')} ${purchase.unitCost.toLocaleString('tr-TR')} ₺ ${purchase.totalCost.toLocaleString('tr-TR')} ₺
` : '

Henüz alım yapılmamış

'}
`; document.getElementById('supplierDetailContent').innerHTML = content; openModal('supplierDetailModal'); }; // Main render function function render() { // Check if user is logged in if (!AppState.currentUser) { renderLoginPage(); return; } // Get user permissions const userRole = AppState.roles.find(r => r.name === AppState.currentUser.role); const permissions = userRole ? userRole.permissions : []; const tabs = [ { name: 'dashboard', label: 'Dashboard', icon: '📊' }, { name: 'definitions', label: 'Tanımlamalar', icon: '📝' }, { name: 'warehouse', label: 'Mal Kabul', icon: '📦' }, { name: 'production', label: 'Üretim', icon: '⚙️' }, { name: 'quality', label: 'Kalite & Laboratuvar', icon: '🔬' }, { name: 'stock', label: 'Stok', icon: '📋' }, { name: 'sales', label: 'Satış', icon: '💼' }, { name: 'shipping', label: 'Sevkiyat', icon: '🚚' }, { name: 'finance', label: 'Finans', icon: '💰' }, { name: 'accounting', label: 'Muhasebe', icon: '📊' }, { name: 'maintenance', label: 'Bakım & Onarım', icon: '🔧' }, { name: 'reports', label: 'Raporlama', icon: '📈' }, { name: 'users', label: 'Kullanıcılar', icon: '👥' } ].filter(tab => permissions.includes(tab.name)); // Filter by permissions let content = ''; switch(AppState.currentTab) { case 'dashboard': content = Dashboard(); break; case 'definitions': content = Definitions(); break; case 'warehouse': content = WarehouseManagement(); break; case 'production': content = Production(); break; case 'quality': content = Quality(); break; case 'stock': content = Stock(); break; case 'sales': content = Sales(); break; case 'finance': content = Finance(); break; case 'accounting': content = Accounting(); break; case 'maintenance': content = Maintenance(); break; case 'reports': content = Reports(); break; case 'users': content = UsersManagement(); break; case 'shipping': content = Shipping(); break; } document.getElementById('app').innerHTML = `

LUCAS ERP

Entegre Yönetim Sistemi

LUCAS INGREDIENTS GIDA A.Ş.
${AppState.currentUser.name}
${AppState.currentUser.role} - ${AppState.currentUser.department}
${tabs.map(tab => Tab(tab)).join('')}
${content}
${renderModals()} `; } // Render modals function renderModals() { return ` `; } // Tab switching function switchTab(tabName) { AppState.currentTab = tabName; render(); } // Modal functions function openModal(modalId) { document.getElementById(modalId).classList.add('active'); } function closeModal(modalId) { document.getElementById(modalId).classList.remove('active'); } // Open dynamic modal with custom content window.openDynamicModal = function(content) { document.getElementById('dynamicModalContent').innerHTML = content; document.getElementById('dynamicModal').classList.add('active'); }; function openRawMaterialReceiptModal() { openModal('rawMaterialReceiptModal'); document.getElementById('rmDate').value = new Date().toISOString().split('T')[0]; } function openWorkOrderModal() { openModal('workOrderModal'); document.getElementById('woStartDate').value = new Date().toISOString().split('T')[0]; document.getElementById('woStartTime').value = new Date().toTimeString().slice(0,5); } function openProformaModal() { openModal('proformaModal'); document.getElementById('pfDate').value = new Date().toISOString().split('T')[0]; } function openInvoiceModal() { alert('Fatura modülü yakında eklenecek!'); } function openCustomerModal() { openModal('customerModal'); } function openSupplierModal() { openModal('supplierModal'); } function openRawMaterialModal() { openModal('rawMaterialModal'); } function openFinishedProductModal() { openModal('finishedProductModal'); } function openConsumableModal() { openModal('consumableModal'); } window.submitRawMaterial = function(event) { event.preventDefault(); // Auto-generate code if empty if (!document.getElementById('rmCode').value) { window.generateRawMaterialCode(); } const newRawMaterial = { id: AppState.rawMaterials.length + 1, code: document.getElementById('rmCode').value, name: document.getElementById('rmName').value, mainCategory: document.getElementById('rmMainCategory').value, type: document.getElementById('rmType').value, origin: document.getElementById('rmOrigin').value || '', unit: document.getElementById('rmUnit').value, notes: document.getElementById('rmNotes').value || '' }; AppState.rawMaterials.push(newRawMaterial); closeModal('rawMaterialModal'); render(); alert(`Hammadde "${newRawMaterial.code} - ${newRawMaterial.name}" başarıyla eklendi!`); }; // Update raw material subcategories based on main category window.updateRawMaterialSubCategories = function() { const mainCategory = document.getElementById('rmMainCategory').value; const typeSelect = document.getElementById('rmType'); const originSelect = document.getElementById('rmOrigin'); // Clear existing options typeSelect.innerHTML = ''; originSelect.innerHTML = ''; if (mainCategory === 'Hayvansal') { typeSelect.innerHTML = ` `; } else if (mainCategory === 'Bitkisel') { typeSelect.innerHTML = ` `; } else if (mainCategory === 'Diğer') { typeSelect.innerHTML = ` `; } }; // Update origins based on type window.updateRawMaterialOrigins = function() { const type = document.getElementById('rmType').value; const originSelect = document.getElementById('rmOrigin'); originSelect.innerHTML = ''; if (type === 'Jelatin') { originSelect.innerHTML = ` `; } else if (type === 'Kolajen') { originSelect.innerHTML = ` `; } else if (type === 'Gum Arabic') { originSelect.innerHTML = ` `; } else { originSelect.innerHTML = ''; } }; // Generate raw material code automatically window.generateRawMaterialCode = function() { const mainCategory = document.getElementById('rmMainCategory').value; const type = document.getElementById('rmType').value; const origin = document.getElementById('rmOrigin').value; if (!mainCategory || !type || !origin) return; // Category codes const categoryCode = { 'Hayvansal': 'AN', 'Bitkisel': 'VG', 'Diğer': 'OT' }[mainCategory]; // Type codes const typeCode = { 'Jelatin': 'GEL', 'Kolajen': 'COL', 'Gum Arabic': 'GUM', 'Diğer Hayvansal': 'OAN', 'Diğer Bitkisel': 'OVG', 'Katkı Maddesi': 'ADD', 'Enzim': 'ENZ', 'Diğer': 'OTH' }[type]; // Origin codes const originCode = { 'Sığır': 'BV', 'Domuz': 'PR', 'Balık': 'FS', 'Tavuk': 'CK', 'Acacia Senegal': 'SEN', 'Acacia Seyal': 'SEY', 'Genel': 'GEN' }[origin]; // Find the next sequence number for this combination const prefix = `${categoryCode}-${typeCode}-${originCode}`; const existingCodes = AppState.rawMaterials .filter(rm => rm.code && rm.code.startsWith(prefix)) .map(rm => { const match = rm.code.match(/-(\d{4})$/); return match ? parseInt(match[1]) : 0; }); const nextNumber = existingCodes.length > 0 ? Math.max(...existingCodes) + 1 : 1; const sequenceNumber = nextNumber.toString().padStart(4, '0'); const generatedCode = `${categoryCode}-${typeCode}-${originCode}-${sequenceNumber}`; document.getElementById('rmCode').value = generatedCode; }; // Toggle technical details based on material type (raw material vs consumable) window.toggleTechnicalDetails = function() { const materialValue = document.getElementById('rmMaterial').value; const technicalSection = document.getElementById('technicalDetailsSection'); if (!technicalSection) return; // Determine material type and unit let materialUnit = 'KG'; // default if (materialValue.startsWith('con-')) { // Consumable selected - hide technical details technicalSection.style.display = 'none'; // Find consumable to get unit const conId = parseInt(materialValue.replace('con-', '')); const consumable = AppState.consumables.find(c => c.id === conId); if (consumable) { materialUnit = consumable.unit; } // Clear all technical fields document.getElementById('rmBloom').value = ''; document.getElementById('rmMesh').value = ''; document.getElementById('rmClarity').value = ''; document.getElementById('rmColor').value = ''; document.getElementById('rmPH').value = ''; document.getElementById('rmViscosity').value = ''; } else if (materialValue.startsWith('rm-')) { // Raw material selected - show technical details technicalSection.style.display = 'block'; materialUnit = 'KG'; } // Update field labels and visibility based on unit updateFieldsByUnit(materialUnit); }; // Update fields based on unit type function updateFieldsByUnit(unit) { const kgFields = document.getElementById('kgFieldsSection'); const adetFields = document.getElementById('adetFieldsSection'); const priceLabel = document.getElementById('unitPriceLabel'); if (unit === 'KG' || unit === 'TON' || unit === 'LT') { // Show KG-based fields if (kgFields) kgFields.style.display = 'contents'; if (adetFields) adetFields.style.display = 'none'; if (priceLabel) priceLabel.textContent = 'KG Fiyat (₺) *'; } else { // Show ADET-based fields (for Adet, Rulo, Paket etc.) if (kgFields) kgFields.style.display = 'none'; if (adetFields) adetFields.style.display = 'contents'; if (priceLabel) priceLabel.textContent = 'Adet Fiyat (₺) *'; } } // Update product name and code automatically window.updateProductName = function() { const rawMaterialId = document.getElementById('fpRawMaterial').value; const bloomRange = document.getElementById('fpBloomRange').value; const meshSize = document.getElementById('fpMeshSize').value; const category = document.getElementById('fpCategory').value; const unit = document.getElementById('fpUnit').value; const productType = document.getElementById('fpProductType').value; if (!rawMaterialId || !bloomRange || !meshSize) return; const rawMaterial = AppState.rawMaterials.find(rm => rm.id === parseInt(rawMaterialId)); if (!rawMaterial) return; // Generate product name: "240-260 Bloom 20 Mesh Yenilebilir Sığır Jelatini" const productName = `${bloomRange} Bloom ${meshSize} Mesh ${productType} ${rawMaterial.name}`; document.getElementById('fpName').value = productName; // Generate product code: LCS-240-GEL-20-25 const bloomStart = bloomRange.split('-')[0]; // "240" from "240-260" // Category code const categoryCode = { 'Jelatin': 'GEL', 'Kolajen': 'COL', 'Gum Arabic': 'GUM', 'Diğer': 'OTH' }[category]; // Package code let packageCode = 'BB'; // Default Bigbag if (unit.includes('25KG')) packageCode = '25'; else if (unit.includes('20KG')) packageCode = '20'; else if (unit.includes('15KG')) packageCode = '15'; else if (unit.includes('10KG')) packageCode = '10'; else if (unit.includes('1KG')) packageCode = '1'; else if (unit.includes('500G')) packageCode = '0.5'; const productCode = `LCS-${bloomStart}-${categoryCode}-${meshSize}-${packageCode}`; document.getElementById('fpCode').value = productCode; }; window.submitFinishedProduct = function(event) { event.preventDefault(); // Auto-generate if not already done if (!document.getElementById('fpName').value) { window.updateProductName(); } const rawMaterialId = parseInt(document.getElementById('fpRawMaterial').value); const rawMaterial = AppState.rawMaterials.find(rm => rm.id === rawMaterialId); const newProduct = { id: AppState.finishedProducts.length + 1, name: document.getElementById('fpName').value, code: document.getElementById('fpCode').value, rawMaterialId: rawMaterialId, rawMaterialCode: rawMaterial.code, rawMaterialName: rawMaterial.name, bloomRange: document.getElementById('fpBloomRange').value, meshSize: document.getElementById('fpMeshSize').value, category: document.getElementById('fpCategory').value, unit: document.getElementById('fpUnit').value, productType: document.getElementById('fpProductType').value || 'Yenilebilir', notes: document.getElementById('fpNotes').value || '' }; AppState.finishedProducts.push(newProduct); closeModal('finishedProductModal'); render(); alert(`Ürün "${newProduct.code} - ${newProduct.name}" başarıyla eklendi!`); }; window.submitConsumable = function(event) { event.preventDefault(); const newConsumable = { id: AppState.consumables.length + 1, name: document.getElementById('conName').value, unit: document.getElementById('conUnit').value }; AppState.consumables.push(newConsumable); closeModal('consumableModal'); render(); alert(`Sarf malzeme "${newConsumable.name}" başarıyla eklendi!`); }; // Submit Raw Material Receipt - SADECE HAMMADDE // Submit Consumable Receipt - SADECE SARF MALZEME window.submitConsumableReceipt = function(event) { event.preventDefault(); // Fatura durumunu kontrol et const invoiceStatus = document.querySelector('input[name="conInvoiceStatus"]:checked').value; const hasInvoice = (invoiceStatus === 'with_invoice'); const consumableId = parseInt(document.getElementById('conMaterial').value); const consumable = AppState.consumables.find(c => c.id === consumableId); const supplierId = parseInt(document.getElementById('conSupplier').value); const supplier = AppState.suppliers.find(s => s.id === supplierId); if (!consumable || !supplier) { alert('Sarf malzeme veya tedarikçi bulunamadı!'); return; } const quantity = parseFloat(document.getElementById('conQuantity').value); const waybillNo = document.getElementById('conWaybillNo').value; const coaNo = document.getElementById('conCoaNo').value || ''; // Fiyat bilgileri (sadece fatura varsa) let unitCost = 0; let currency = 'TRY'; let exchangeRate = 1; let vatRate = 0; let invoiceNo = ''; let subtotal = 0; let vatAmount = 0; let total = 0; let totalTRY = 0; if (hasInvoice) { unitCost = parseFloat(document.getElementById('conUnitCost').value); currency = document.getElementById('conCurrency').value; exchangeRate = parseFloat(document.getElementById('conExchangeRate').value) || 1; vatRate = parseFloat(document.getElementById('conVatRate').value) || 0; invoiceNo = document.getElementById('conInvoiceNo').value || ''; // Hesaplamalar subtotal = quantity * unitCost; vatAmount = subtotal * (vatRate / 100); total = subtotal + vatAmount; totalTRY = total * exchangeRate; console.log('Sarf Malzeme Kabul Debug:'); console.log('Para Birimi:', currency); console.log('Döviz Kuru:', exchangeRate); console.log('Birim Fiyat:', unitCost, currency); console.log('Miktar:', quantity); console.log('Genel Toplam:', total, currency); console.log('TL Karşılığı:', totalTRY, '₺'); } // Onay iste let confirmMsg = `🔧 SARF MALZEME KABUL EDİLECEK\n\n` + `Sarf Malzeme: ${consumable.name}\n` + `Tedarikçi: ${supplier.name}\n` + `Miktar: ${quantity} ${consumable.unit}\n` + `İrsaliye No: ${waybillNo}\n` + (coaNo ? `COA No: ${coaNo}\n` : ''); if (hasInvoice) { confirmMsg += `\nFATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNo}\n` + `Birim Fiyat: ${unitCost.toFixed(2)} ${currency}\n` + `Ara Toplam: ${subtotal.toFixed(2)} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toFixed(2)} ${currency}\n` + `Genel Toplam: ${total.toFixed(2)} ${currency}\n` + `Kur: ${exchangeRate.toFixed(4)}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Alış faturası oluşturulacak\n` + `✓ Tedarikçi carisine borç yazılacak`; } else { confirmMsg += `\n⏳ SADECE İRSALİYE KABUL\n` + `✓ Stok girilecek\n` + `⏳ Fatura bekleniyor (Finans'tan girilecek)`; } confirmMsg += `\n\nOnaylıyor musunuz?`; if (!confirm(confirmMsg)) return; const newStock = { id: AppState.consumableStock.length + 1, consumableId: consumableId, consumableName: consumable.name, supplierId: supplierId, supplierName: supplier.name, waybillNo: waybillNo, coaNo: coaNo, invoiceNo: invoiceNo, quantity: quantity, unit: consumable.unit, unitCost: unitCost, totalCost: totalTRY, depotNo: document.getElementById('conDepotNo').value || '', receivedDate: document.getElementById('conDate').value, currency: currency, exchangeRate: exchangeRate, vatRate: vatRate, vatAmount: vatAmount * exchangeRate, // YENİ ALANLAR invoiceStatus: hasInvoice ? 'Faturalandı' : 'Fatura Bekleniyor', invoiceId: null }; AppState.consumableStock.push(newStock); // ALIŞ FATURASI OLUŞTUR (Sadece fatura varsa) if (hasInvoice && invoiceNo) { if (!AppState.invoices) AppState.invoices = []; const purchaseInvoice = { id: (AppState.invoices.length || 0) + 1, type: 'Alış', invoiceNumber: invoiceNo, invoiceDate: newStock.receivedDate, supplierId: supplierId, supplierName: supplier.name, receiptId: newStock.id, receiptType: 'Sarf Malzeme', waybillNumber: waybillNo, items: [{ productName: consumable.name, quantity: quantity, unit: consumable.unit, unitPrice: unitCost, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: total }], subtotal: subtotal, vatTotal: vatAmount, grandTotal: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, status: 'Ödenmedi', notes: `Sarf Malzeme Girişi: ${consumable.name} - İrsaliye: ${waybillNo}`, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(purchaseInvoice); // Stok kaydına fatura ID'yi ekle newStock.invoiceId = purchaseInvoice.id; // Tedarikçi carisine borç yaz if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: supplierId, supplierName: supplier.name, invoiceId: purchaseInvoice.id, invoiceNumber: invoiceNo, amount: total, currency: currency, date: newStock.receivedDate, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('invoice', 'finance', `Alış faturası (Sarf Malzeme): ${invoiceNo} - ${supplier.name} - ${totalTRY.toLocaleString('tr-TR')} ₺`, purchaseInvoice.id, 'invoice'); } closeModal('consumableReceiptModal'); render(); const message = hasInvoice ? `✅ SARF MALZEME KABUL EDİLDİ!\n\n` + `Sarf Malzeme: ${consumable.name}\n` + `Miktar: ${quantity} ${consumable.unit}\n\n` + `FATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNo}\n` + `Ara Toplam: ${subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Stok girildi\n` + `✓ Alış faturası oluşturuldu\n` + `✓ Tedarikçi carisine borç yazıldı` : `✅ SARF MALZEME KABUL EDİLDİ!\n\n` + `Sarf Malzeme: ${consumable.name}\n` + `Miktar: ${quantity} ${consumable.unit}\n` + `İrsaliye No: ${waybillNo}\n\n` + `✓ Stok girildi\n` + `⏳ Fatura Bekleniyor\n\n` + `→ Finans & P&L → Fatura Kabul Et'ten faturayı girebilirsiniz`; alert(message); }; // Open modal functions window.openRawMaterialReceiptModal = function() { openModal('rawMaterialReceiptModal'); document.getElementById('rmDate').value = new Date().toISOString().split('T')[0]; }; window.openConsumableReceiptModal = function() { openModal('consumableReceiptModal'); document.getElementById('conDate').value = new Date().toISOString().split('T')[0]; }; // Toggle RM Invoice Fields window.toggleRMInvoiceFields = function(showInvoice) { const invoiceFields = document.getElementById('rmInvoiceFields'); const priceFields = document.getElementById('rmPriceFields'); const invoiceNoInput = document.getElementById('rmInvoiceNo'); const unitCostInput = document.getElementById('rmUnitCost'); const currencyInput = document.getElementById('rmCurrency'); const exchangeRateInput = document.getElementById('rmExchangeRate'); const vatRateInput = document.getElementById('rmVatRate'); if (showInvoice) { // İrsaliye + Fatura Birlikte invoiceFields.classList.remove('hidden'); priceFields.classList.remove('hidden'); invoiceNoInput.required = true; unitCostInput.required = true; currencyInput.required = true; exchangeRateInput.required = true; vatRateInput.required = true; } else { // Sadece İrsaliye invoiceFields.classList.add('hidden'); priceFields.classList.add('hidden'); invoiceNoInput.required = false; invoiceNoInput.value = ''; unitCostInput.required = false; unitCostInput.value = ''; currencyInput.required = false; exchangeRateInput.required = false; vatRateInput.required = false; } }; // Toggle Con Invoice Fields window.toggleConInvoiceFields = function(showInvoice) { const invoiceFields = document.getElementById('conInvoiceFields'); const priceFields = document.getElementById('conPriceFields'); const invoiceNoInput = document.getElementById('conInvoiceNo'); const unitCostInput = document.getElementById('conUnitCost'); const currencyInput = document.getElementById('conCurrency'); const exchangeRateInput = document.getElementById('conExchangeRate'); const vatRateInput = document.getElementById('conVatRate'); if (showInvoice) { // İrsaliye + Fatura Birlikte invoiceFields.classList.remove('hidden'); priceFields.classList.remove('hidden'); invoiceNoInput.required = true; unitCostInput.required = true; currencyInput.required = true; exchangeRateInput.required = true; vatRateInput.required = true; } else { // Sadece İrsaliye invoiceFields.classList.add('hidden'); priceFields.classList.add('hidden'); invoiceNoInput.required = false; invoiceNoInput.value = ''; unitCostInput.required = false; unitCostInput.value = ''; currencyInput.required = false; exchangeRateInput.required = false; vatRateInput.required = false; } }; // Make these functions global so they work from modals window.submitCustomer = function(event) { event.preventDefault(); const newCustomer = { id: AppState.customers.length + 1, name: document.getElementById('custName').value, type: document.getElementById('custType').value, sector: document.getElementById('custSector').value || '', phone: document.getElementById('custPhone').value || '', email: document.getElementById('custEmail').value || '', address: document.getElementById('custAddress').value || '', taxNo: document.getElementById('custTaxNo').value || '', taxOffice: document.getElementById('custTaxOffice').value || '' }; AppState.customers.push(newCustomer); closeModal('customerModal'); render(); alert(`Müşteri "${newCustomer.name}" başarıyla eklendi!`); }; window.submitSupplier = function(event) { event.preventDefault(); const newSupplier = { id: AppState.suppliers.length + 1, name: document.getElementById('suppName').value, type: document.getElementById('suppType').value, country: document.getElementById('suppCountry').value || 'Türkiye', phone: document.getElementById('suppPhone').value || '', email: document.getElementById('suppEmail').value || '', address: document.getElementById('suppAddress').value || '', taxNo: document.getElementById('suppTaxNo').value || '', taxOffice: document.getElementById('suppTaxOffice').value || '', notes: document.getElementById('suppNotes').value || '' }; AppState.suppliers.push(newSupplier); closeModal('supplierModal'); render(); alert(`Tedarikçi "${newSupplier.name}" başarıyla eklendi!`); }; // Form submissions // Fetch Exchange Rate from turkiye.gov.tr (for Raw Materials) window.fetchRMExchangeRate = function() { const currency = document.getElementById('rmCurrency').value; const rateInput = document.getElementById('rmExchangeRate'); if (currency === 'TRY') { rateInput.value = '1.0000'; return; } // Try turkiye.gov.tr first fetch('https://www.turkiye.gov.tr/doviz-kurlari') .then(response => { if (!response.ok) throw new Error('Turkiye.gov.tr erişilemedi'); return response.text(); }) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); let rate = null; const rows = doc.querySelectorAll('tr'); for (let row of rows) { const text = row.textContent; if (text.includes(currency)) { const cells = row.querySelectorAll('td'); if (cells.length >= 3) { const sellValue = cells[2].textContent.trim().replace(',', '.'); rate = parseFloat(sellValue); break; } } } if (rate && !isNaN(rate)) { rateInput.value = rate.toFixed(4); } else { throw new Error('Kur bulunamadı'); } }) .catch(error => { console.log('turkiye.gov.tr CORS hatası, TCMB kullanılıyor...'); // Fallback: TCMB XML const today = new Date(); const dateStr = String(today.getDate()).padStart(2, '0') + String(today.getMonth() + 1).padStart(2, '0') + today.getFullYear(); const url = `https://www.tcmb.gov.tr/kurlar/${today.getFullYear()}${String(today.getMonth() + 1).padStart(2, '0')}/${dateStr}.xml`; return fetch(url) .then(response => response.text()) .then(str => new window.DOMParser().parseFromString(str, "text/xml")) .then(data => { let rate = null; const currencies = data.getElementsByTagName('Currency'); for (let i = 0; i < currencies.length; i++) { const currencyCode = currencies[i].getAttribute('CurrencyCode'); if (currencyCode === currency) { const forexSelling = currencies[i].getElementsByTagName('ForexSelling')[0]; if (forexSelling) { rate = parseFloat(forexSelling.textContent); break; } } } if (rate) { rateInput.value = rate.toFixed(4); } else { throw new Error('Kur bulunamadı'); } }); }) .catch(error => { console.error('Kur çekme hatası:', error); alert('⚠️ Kur çekilemedi.\n\nLütfen kuru manuel olarak girin.'); }); }; // Bulk Upload Functions window.openBulkUploadModal = function() { const content = `

📋 Nasıl Kullanılır?

  1. 1. Excel şablonunu indirin
  2. 2. Lot bilgilerini doldurun (Lot No ve Miktar zorunlu)
  3. 3. Excel'i yükleyin
  4. 4. Önizlemeyi kontrol edin
  5. 5. Fatura bilgilerini girin ve kaydedin
Şablon kolonları: Lot No*, Bigbag No, Miktar (KG)*, Bloom, pH, Color, Clarity, Viscosity, Mesh
* Zorunlu alanlar
`; document.getElementById('bulkUploadContent').innerHTML = content; openModal('bulkUploadModal'); }; window.downloadBulkTemplate = function() { const template = `Lot No,Bigbag No,Miktar (KG),Bloom,pH,Color,Clarity,Viscosity,Mesh LOT-ABC-001,BB-001,1000,245,5.2,85,95,3.2,40 LOT-ABC-002,BB-002,1000,250,5.3,87,96,3.1,40 LOT-ABC-003,BB-003,1000,248,5.1,86,94,3.3,40`; const blob = new Blob([template], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'hammadde_toplu_yukleme_sablonu.csv'; link.click(); }; window.parseBulkExcel = function(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { const text = e.target.result; const rows = text.split('\n').map(row => row.split(',')); // Skip header row const headers = rows[0]; const dataRows = rows.slice(1).filter(row => row[0] && row[0].trim()); if (dataRows.length === 0) { alert('❌ Excel dosyasında veri bulunamadı!'); return; } // Parse data const lots = dataRows.map((row, index) => ({ lotNumber: row[0]?.trim() || '', depotNo: row[1]?.trim() || '', quantity: parseFloat(row[2]) || 0, bloom: parseFloat(row[3]) || null, ph: parseFloat(row[4]) || null, color: parseFloat(row[5]) || null, clarity: parseFloat(row[6]) || null, viscosity: parseFloat(row[7]) || null, mesh: row[8]?.trim() || null })); // Validate const invalid = lots.filter(lot => !lot.lotNumber || !lot.quantity); if (invalid.length > 0) { alert(`❌ Geçersiz satırlar bulundu! Her satırda Lot No ve Miktar olmalı.\n\nGeçersiz satır sayısı: ${invalid.length}`); return; } // Show preview showBulkPreview(lots); }; reader.readAsText(file); }; function showBulkPreview(lots) { const totalQty = lots.reduce((sum, lot) => sum + lot.quantity, 0); const content = `

✅ Excel Başarıyla Yüklendi

Toplam Lot: ${lots.length}
Toplam Miktar: ${totalQty.toLocaleString('tr-TR')} KG
Ortalama: ${(totalQty / lots.length).toFixed(0)} KG/lot
${lots.slice(0, 10).map((lot, i) => ` `).join('')} ${lots.length > 10 ? ` ` : ''}
# Lot No Bigbag Miktar (KG) Bloom pH
${i + 1} ${lot.lotNumber} ${lot.depotNo || '-'} ${lot.quantity.toLocaleString('tr-TR')} ${lot.bloom || '-'} ${lot.ph || '-'}
... ve ${lots.length - 10} lot daha

📋 Fatura Bilgileri

`; // Store lots in a global variable for submission window.bulkUploadLots = lots; document.getElementById('bulkPreviewArea').innerHTML = content; } window.submitBulkUpload = function(event) { event.preventDefault(); if (!window.bulkUploadLots || window.bulkUploadLots.length === 0) { alert('❌ Yüklenmiş lot bulunamadı!'); return; } const lots = window.bulkUploadLots; const supplierId = parseInt(document.getElementById('bulkSupplier').value); const materialId = parseInt(document.getElementById('bulkMaterial').value); const waybillNo = document.getElementById('bulkWaybillNo').value; const invoiceNo = document.getElementById('bulkInvoiceNo').value; const date = document.getElementById('bulkDate').value; const unitCost = parseFloat(document.getElementById('bulkUnitCost').value); const currency = document.getElementById('bulkCurrency').value; const exchangeRate = parseFloat(document.getElementById('bulkExchangeRate').value) || 1; const vatRate = parseFloat(document.getElementById('bulkVatRate').value) || 0; const supplier = AppState.suppliers.find(s => s.id === supplierId); const material = AppState.rawMaterials.find(m => m.id === materialId); if (!supplier || !material) { alert('❌ Hata: Tedarikçi veya hammadde bulunamadı!'); return; } const totalQty = lots.reduce((sum, lot) => sum + lot.quantity, 0); const subtotal = totalQty * unitCost; const vatAmount = subtotal * (vatRate / 100); const total = subtotal + vatAmount; const totalTRY = total * exchangeRate; // Confirm const confirmMsg = `📦 TOPLU HAMMADDE KABUL EDİLECEK\n\n` + `Hammadde: ${material.name}\n` + `Tedarikçi: ${supplier.name}\n` + `Lot Sayısı: ${lots.length}\n` + `Toplam Miktar: ${totalQty.toLocaleString('tr-TR')} KG\n` + `İrsaliye: ${waybillNo}\n` + `Fatura: ${invoiceNo}\n\n` + `Birim Fiyat: ${unitCost.toFixed(2)} ${currency}/KG\n` + `Ara Toplam: ${subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `Onaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // Create stock entries for each lot lots.forEach(lot => { const stockEntry = { id: AppState.rawMaterialStock.length + 1, lotNumber: lot.lotNumber, materialId: materialId, materialName: material.name, quantity: lot.quantity, bloom: lot.bloom, ph: lot.ph, color: lot.color, clarity: lot.clarity, viscosity: lot.viscosity, mesh: lot.mesh, unitCost: unitCost, totalCost: lot.quantity * unitCost * exchangeRate, receivedDate: date, supplierId: supplierId, supplierName: supplier.name, invoiceNo: invoiceNo, waybillNo: waybillNo, coaNo: '', depotNo: lot.depotNo || '', productionDate: null, expiryDate: null, checkedBy: AppState.currentUser.name, currency: currency, exchangeRate: exchangeRate, vatRate: vatRate, vatAmount: (lot.quantity * unitCost * vatRate / 100) * exchangeRate, invoiceStatus: 'Faturalandı', invoiceId: null }; AppState.rawMaterialStock.push(stockEntry); }); // Create single purchase invoice if (!AppState.invoices) AppState.invoices = []; const purchaseInvoice = { id: (AppState.invoices.length || 0) + 1, type: 'Alış', invoiceNumber: invoiceNo, invoiceDate: date, supplierId: supplierId, supplierName: supplier.name, receiptType: 'Toplu Hammadde', waybillNumber: waybillNo, items: [{ productName: material.name, quantity: totalQty, unit: 'KG', unitPrice: unitCost, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: total }], subtotal: subtotal, vatTotal: vatAmount, grandTotal: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, status: 'Ödenmedi', notes: `${lots.length} lot toplu yükleme`, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(purchaseInvoice); // Update invoice ID in stock entries const invoiceId = purchaseInvoice.id; AppState.rawMaterialStock .filter(s => s.invoiceNo === invoiceNo) .forEach(s => s.invoiceId = invoiceId); // Add to accounts payable if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: supplierId, supplierName: supplier.name, invoiceId: purchaseInvoice.id, invoiceNumber: invoiceNo, amount: total, currency: currency, date: date, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('create', 'warehouse', `Toplu Hammadde Kabul: ${lots.length} lot - ${totalQty} KG - ${invoiceNo}`, invoiceId, 'invoice'); closeModal('bulkUploadModal'); closeModal('rawMaterialReceiptModal'); render(); alert(`✅ TOPLU HAMMADDE KABUL EDİLDİ!\n\n` + `${lots.length} adet lot kaydedildi\n` + `Toplam: ${totalQty.toLocaleString('tr-TR')} KG\n` + `Fatura: ${invoiceNo}\n` + `Tutar: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}`); }; window.submitRawMaterialReceipt = function(event) { event.preventDefault(); // Fatura durumunu kontrol et const invoiceStatus = document.querySelector('input[name="rmInvoiceStatus"]:checked').value; const hasInvoice = (invoiceStatus === 'with_invoice'); const materialId = parseInt(document.getElementById('rmMaterial').value); const material = AppState.rawMaterials.find(m => m.id === materialId); const supplierId = parseInt(document.getElementById('rmSupplier').value); const supplier = AppState.suppliers.find(s => s.id === supplierId); const quantity = parseFloat(document.getElementById('rmQuantity').value); const waybillNo = document.getElementById('rmWaybillNo').value; const coaNo = document.getElementById('rmCoaNo').value || ''; // Fiyat bilgileri (sadece fatura varsa) let unitCost = 0; let currency = 'TRY'; let exchangeRate = 1; let vatRate = 0; let invoiceNo = ''; let subtotal = 0; let vatAmount = 0; let total = 0; let totalTRY = 0; if (hasInvoice) { unitCost = parseFloat(document.getElementById('rmUnitCost').value); currency = document.getElementById('rmCurrency').value; exchangeRate = parseFloat(document.getElementById('rmExchangeRate').value) || 1; vatRate = parseFloat(document.getElementById('rmVatRate').value) || 0; invoiceNo = document.getElementById('rmInvoiceNo').value || ''; // Hesaplamalar subtotal = quantity * unitCost; vatAmount = subtotal * (vatRate / 100); total = subtotal + vatAmount; totalTRY = total * exchangeRate; console.log('Hammadde Kabul Debug:'); console.log('Para Birimi:', currency); console.log('Döviz Kuru:', exchangeRate); console.log('Birim Fiyat:', unitCost, currency); console.log('Miktar:', quantity); console.log('Genel Toplam:', total, currency); console.log('TL Karşılığı:', totalTRY, '₺'); } // Onay iste let confirmMsg = `📦 HAMMADDE KABUL EDİLECEK\n\n` + `Hammadde: ${material.name}\n` + `Tedarikçi: ${supplier.name}\n` + `Miktar: ${quantity} KG\n` + `İrsaliye No: ${waybillNo}\n` + (coaNo ? `COA No: ${coaNo}\n` : ''); if (hasInvoice) { confirmMsg += `\nFATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNo}\n` + `Birim Fiyat: ${unitCost.toFixed(2)} ${currency}\n` + `Ara Toplam: ${subtotal.toFixed(2)} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toFixed(2)} ${currency}\n` + `Genel Toplam: ${total.toFixed(2)} ${currency}\n` + `Kur: ${exchangeRate.toFixed(4)}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Alış faturası oluşturulacak\n` + `✓ Tedarikçi carisine borç yazılacak`; } else { confirmMsg += `\n⏳ SADECE İRSALİYE KABUL\n` + `✓ Stok girilecek\n` + `⏳ Fatura bekleniyor (Finans'tan girilecek)`; } confirmMsg += `\n\nOnaylıyor musunuz?`; if (!confirm(confirmMsg)) return; const newStock = { id: AppState.rawMaterialStock.length + 1, lotNumber: document.getElementById('rmLotNo').value || `LOT-${AppState.nextLotNumber++}`, materialId: materialId, materialName: material.name, quantity: quantity, bloom: parseFloat(document.getElementById('rmBloom').value) || null, ph: parseFloat(document.getElementById('rmPH').value) || null, color: parseFloat(document.getElementById('rmColor').value) || null, clarity: parseFloat(document.getElementById('rmClarity').value) || null, viscosity: parseFloat(document.getElementById('rmViscosity').value) || null, mesh: document.getElementById('rmMesh').value || null, unitCost: unitCost, totalCost: totalTRY, // TL karşılığı receivedDate: document.getElementById('rmDate').value, supplierId: supplierId, supplierName: supplier.name, invoiceNo: invoiceNo, waybillNo: waybillNo, coaNo: coaNo, depotNo: document.getElementById('rmDepotNo').value || '', productionDate: document.getElementById('rmProductionDate').value || null, expiryDate: document.getElementById('rmExpiryDate').value || null, checkedBy: AppState.currentUser.name, currency: currency, exchangeRate: exchangeRate, vatRate: vatRate, vatAmount: vatAmount * exchangeRate, // YENİ ALANLAR invoiceStatus: hasInvoice ? 'Faturalandı' : 'Fatura Bekleniyor', invoiceId: null // Fatura oluşturulunca ID eklenecek }; AppState.rawMaterialStock.push(newStock); // ALIŞ FATURASI OLUŞTUR (Sadece fatura varsa) if (hasInvoice && invoiceNo) { if (!AppState.invoices) AppState.invoices = []; const purchaseInvoice = { id: (AppState.invoices.length || 0) + 1, type: 'Alış', invoiceNumber: invoiceNo, invoiceDate: document.getElementById('rmDate').value, supplierId: supplierId, supplierName: supplier.name, receiptId: newStock.id, waybillNumber: waybillNo, items: [{ productName: material.name, lotNumber: newStock.lotNumber, quantity: quantity, unit: 'KG', unitPrice: unitCost, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: total }], subtotal: subtotal, vatTotal: vatAmount, grandTotal: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, paymentTerms: 'Hammadde Kabul', status: 'Ödenmedi', notes: `Hammadde Girişi: ${material.name} - Lot: ${newStock.lotNumber}`, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(purchaseInvoice); // Stok kaydına fatura ID'yi ekle newStock.invoiceId = purchaseInvoice.id; // Tedarikçi carisine borç yaz if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: supplierId, supplierName: supplier.name, invoiceId: purchaseInvoice.id, invoiceNumber: invoiceNo, amount: total, currency: currency, date: document.getElementById('rmDate').value, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('invoice', 'finance', `Alış faturası (Hammadde): ${invoiceNo} - ${supplier.name} - ${totalTRY.toLocaleString('tr-TR')} ₺`, purchaseInvoice.id, 'invoice'); } closeModal('rawMaterialReceiptModal'); render(); const message = hasInvoice ? `✅ HAMMADDE KABUL EDİLDİ!\n\n` + `Hammadde: ${material.name}\n` + `Lot: ${newStock.lotNumber}\n` + `Miktar: ${quantity} KG\n\n` + `FATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNo}\n` + `Ara Toplam: ${subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Stok girildi\n` + `✓ Alış faturası oluşturuldu\n` + `✓ Tedarikçi carisine borç yazıldı` : `✅ HAMMADDE KABUL EDİLDİ!\n\n` + `Hammadde: ${material.name}\n` + `Lot: ${newStock.lotNumber}\n` + `Miktar: ${quantity} KG\n` + `İrsaliye No: ${waybillNo}\n\n` + `✓ Stok girildi\n` + `⏳ Fatura Bekleniyor\n\n` + `→ Finans & P&L → Fatura Kabul Et'ten faturayı girebilirsiniz`; alert(message); }; function addRawMaterialToWO() { const container = document.getElementById('woRawMaterials'); // Daha önce seçilmiş lot'ları al const selectedLots = Array.from(document.querySelectorAll('.wo-rm-lot')) .map(select => select.value) .filter(val => val); // Boş olanları filtrele // Sadece stokta olan VE daha önce seçilmemiş lotları göster const availableLots = AppState.rawMaterialStock.filter(s => s.quantity > 0 && !selectedLots.includes(s.lotNumber) ); if (availableLots.length === 0) { alert('❌ Stokta uygun hammadde kalmadı veya tüm lotlar zaten seçildi!'); return; } const div = document.createElement('div'); div.className = 'grid grid-cols-2 gap-2'; div.innerHTML = ` `; container.appendChild(div); } // Hammadde dropdown'larını güncelle (seçilmiş lotları filtrele) function updateRawMaterialDropdowns() { const allSelects = Array.from(document.querySelectorAll('.wo-rm-lot')); const selectedLots = allSelects .map(select => select.value) .filter(val => val); allSelects.forEach(select => { const currentValue = select.value; const availableLots = AppState.rawMaterialStock.filter(s => s.quantity > 0 && (s.lotNumber === currentValue || !selectedLots.includes(s.lotNumber)) ); const selectedOption = select.querySelector('option:checked'); select.innerHTML = ` ${availableLots.map(s => ` `).join('')} `; }); } function addConsumableToWO() { const container = document.getElementById('woConsumables'); const div = document.createElement('div'); div.className = 'grid grid-cols-2 gap-2'; div.innerHTML = ` `; container.appendChild(div); } function submitWorkOrder(event) { event.preventDefault(); const productId = parseInt(document.getElementById('woProduct').value); const product = AppState.finishedProducts.find(p => p.id === productId); const rawMaterialLots = Array.from(document.querySelectorAll('.wo-rm-lot')); const rawMaterialQtys = Array.from(document.querySelectorAll('.wo-rm-qty')); const rawMaterials = rawMaterialLots.map((lot, i) => ({ lotNumber: lot.value, quantity: parseFloat(rawMaterialQtys[i].value) || 0 })).filter(rm => rm.lotNumber && rm.quantity > 0); // 🎯 YENİ: Mükerrer lot kontrolü const lotCounts = {}; const duplicateLots = []; rawMaterials.forEach(rm => { lotCounts[rm.lotNumber] = (lotCounts[rm.lotNumber] || 0) + 1; if (lotCounts[rm.lotNumber] > 1 && !duplicateLots.includes(rm.lotNumber)) { duplicateLots.push(rm.lotNumber); } }); if (duplicateLots.length > 0) { alert(`❌ HATA: Aynı lot birden fazla kez seçilmiş!\n\n${duplicateLots.map(lot => `• ${lot}`).join('\n')}\n\nLütfen her lot'u sadece bir kez seçin.`); return; } const consumableNames = Array.from(document.querySelectorAll('.wo-consumable-name')); const consumableQtys = Array.from(document.querySelectorAll('.wo-consumable-qty')); const consumables = consumableNames.map((name, i) => ({ name: name.value, quantity: parseFloat(consumableQtys[i].value) || 0 })).filter(c => c.name && c.quantity > 0); // STOCK VALIDATION - Check if sufficient stock exists const stockErrors = []; // Check raw material stock rawMaterials.forEach(rm => { const stock = AppState.rawMaterialStock.find(s => s.lotNumber === rm.lotNumber); if (!stock) { stockErrors.push(`❌ Hammadde Lot ${rm.lotNumber} stokta bulunamadı`); } else if (stock.quantity < rm.quantity) { stockErrors.push(`❌ Hammadde Lot ${rm.lotNumber}: Yetersiz stok! (Mevcut: ${stock.quantity} KG, İstenen: ${rm.quantity} KG)`); } }); // Check consumable stock consumables.forEach(con => { // 🎯 Kraft Ambalaj standart, validasyon yapma (paketlemede otomatik çekilir) if (con.name && con.name.toLowerCase().includes('kraft')) { return; // Skip kraft validation } const stock = AppState.consumableStock.find(s => s.consumableName === con.name); if (!stock) { stockErrors.push(`❌ Sarf Malzeme "${con.name}" stokta bulunamadı`); } else if (stock.quantity < con.quantity) { stockErrors.push(`❌ Sarf Malzeme "${con.name}": Yetersiz stok! (Mevcut: ${stock.quantity}, İstenen: ${con.quantity})`); } }); // If stock errors exist, show error and don't create work order if (stockErrors.length > 0) { alert(`İş Emri Oluşturulamadı!\n\n${stockErrors.join('\n')}\n\nLütfen stok miktarlarını kontrol edin.`); return; } const totalInput = rawMaterials.reduce((sum, rm) => sum + rm.quantity, 0); const startDate = document.getElementById('woStartDate').value; const startTime = document.getElementById('woStartTime').value; const palletCount = parseInt(document.getElementById('woPalletCount').value) || 0; const stretchFilm = parseInt(document.getElementById('woStretchFilm').value) || 0; // Calculate expected bags based on product unit weight const unitWeight = product.unitWeight || 25; // Default 25KG const expectedBags = Math.ceil(totalInput / unitWeight); // Generate LUCAS work order number const workOrderNumber = generateLucasWorkOrderNumber('gelatin'); // Get quotation from pending batch (if coming from customer order) or manual selection let quotationId = null; let quotation = null; // Check for pending batch from customer order const pendingBatch = sessionStorage.getItem('pendingBatch'); if (pendingBatch) { const batchData = JSON.parse(pendingBatch); quotationId = batchData.quotationId; quotation = AppState.quotations.find(q => q.id === quotationId); // Clear after use sessionStorage.removeItem('pendingBatch'); } else { // Manual quotation selection (if woQuotation field exists) const woQuotationField = document.getElementById('woQuotation'); if (woQuotationField && woQuotationField.value) { quotationId = parseInt(woQuotationField.value); quotation = AppState.quotations.find(q => q.id === quotationId); } } const newWorkOrder = { id: AppState.workOrders.length + 1, workOrderNumber: workOrderNumber, productId: productId, productName: product.name + (product.bloomRange ? ' ' + product.bloomRange : ''), meshSize: document.getElementById('woMeshSize').value, status: 'Devam Ediyor', startDate: startDate, startTime: startTime, endDate: null, endTime: null, rawMaterials: rawMaterials, consumables: consumables, totalInput: totalInput, totalOutput: null, expectedBags: expectedBags, unitWeight: unitWeight, palletCount: palletCount, stretchFilm: stretchFilm, waste: null, wastePercentage: null, quotationId: quotation ? quotation.id : null, quotationNumber: quotation ? quotation.number : null, customerName: quotation ? quotation.customerName : null, // GELATIN PRODUCTION STAGES stages: { mixing: { mixes: rawMaterials.map((rm, index) => ({ mixNumber: index + 1, lotNumber: rm.lotNumber, quantity: rm.quantity, connectedAt: null, connectedBy: null, notes: '' })), processCompleted: false, processCompletedAt: null, processCompletedBy: null }, electricMagnetControl: { completed: false, completedAt: null, completedBy: null, photoUrl: null, notes: '' }, packaging: { started: false, startedAt: null, completed: false, completedAt: null, bags: null, totalKg: null, notes: '' }, downtimes: [] }, analysis: { bloom: null, ph: null, color: null, clarity: null, viscosity: null, analysisComplete: false }, lotNumber: null, notes: '' }; AppState.workOrders.push(newWorkOrder); // Deduct raw materials from stock rawMaterials.forEach(rm => { const stock = AppState.rawMaterialStock.find(s => s.lotNumber === rm.lotNumber); if (stock) { const oldQty = stock.quantity; stock.quantity -= rm.quantity; console.log(`📦 STOK DÜŞÜMÜ: ${rm.lotNumber}`); console.log(` Eski: ${oldQty} KG`); console.log(` Kullanılan: ${rm.quantity} KG`); console.log(` Yeni: ${stock.quantity} KG`); } }); // Deduct consumables from stock consumables.forEach(con => { // 🎯 Kraft Ambalaj paketlemede düşülür if (con.name && con.name.toLowerCase().includes('kraft')) { return; // Skip kraft deduction } const stock = AppState.consumableStock.find(s => s.consumableName === con.name); if (stock) { stock.quantity -= con.quantity; } }); closeModal('workOrderModal'); render(); const successMessage = quotation ? `✅ İş emri ${workOrderNumber} başarıyla oluşturuldu!\n\nMüşteri Siparişi: ${quotation.number}\nMüşteri: ${quotation.customerName}\n\nToplam Girdi: ${totalInput} KG\nStoklar güncellendi.\n\nŞimdi üretim aşamalarını takip edebilirsiniz.` : `✅ İş emri ${workOrderNumber} başarıyla oluşturuldu!\n\nToplam Girdi: ${totalInput} KG\nStoklar güncellendi.\n\nŞimdi üretim aşamalarını takip edebilirsiniz.`; alert(successMessage); } // Edit Work Order (only if not completed) window.editWorkOrder = function(workOrderId) { const workOrder = AppState.workOrders.find(wo => wo.id === workOrderId); if (!workOrder) return; if (workOrder.status === 'Tamamlandı') { alert('❌ Tamamlanmış iş emirleri düzenlenemez!'); return; } // TODO: Open edit modal with current values alert('İş emri düzenleme özelliği yakında eklenecek!'); }; // Delete Work Order (only if not completed) window.deleteWorkOrder = function(workOrderId) { const workOrder = AppState.workOrders.find(wo => wo.id === workOrderId); if (!workOrder) return; if (workOrder.status === 'Tamamlandı') { alert('❌ Tamamlanmış iş emirleri silinemez!'); return; } if (!confirm(`${workOrder.workOrderNumber} nolu iş emrini silmek istediğinizden emin misiniz?\n\nKullanılan hammaddeler stoka geri eklenecektir.`)) { return; } // Return materials to stock workOrder.rawMaterials.forEach(rm => { const stock = AppState.rawMaterialStock.find(s => s.lotNumber === rm.lotNumber); if (stock) { stock.quantity += rm.quantity; } }); // Return consumables to stock workOrder.consumables.forEach(con => { const stock = AppState.consumableStock.find(s => s.consumableName === con.name); if (stock) { stock.quantity += con.quantity; } }); // Remove work order const index = AppState.workOrders.findIndex(wo => wo.id === workOrderId); if (index > -1) { AppState.workOrders.splice(index, 1); } render(); alert(`✅ İş emri silindi ve stoklar geri yüklendi.`); }; function addProformaItem() { const container = document.getElementById('pfItems'); const div = document.createElement('div'); div.className = 'grid grid-cols-4 gap-2'; div.innerHTML = ` `; // Auto-calculate total const qtyInput = div.querySelector('.pf-qty'); const priceInput = div.querySelector('.pf-price'); const totalInput = div.querySelector('.pf-total'); qtyInput.addEventListener('input', () => { totalInput.value = (parseFloat(qtyInput.value) || 0) * (parseFloat(priceInput.value) || 0); }); priceInput.addEventListener('input', () => { totalInput.value = (parseFloat(qtyInput.value) || 0) * (parseFloat(priceInput.value) || 0); }); container.appendChild(div); } function submitProforma(event) { event.preventDefault(); const customerId = parseInt(document.getElementById('pfCustomer').value); const customer = AppState.customers.find(c => c.id === customerId); const products = Array.from(document.querySelectorAll('.pf-product')); const quantities = Array.from(document.querySelectorAll('.pf-qty')); const prices = Array.from(document.querySelectorAll('.pf-price')); const items = products.map((prod, i) => { const option = prod.options[prod.selectedIndex]; return { productName: option.dataset.product, lotNumber: prod.value, quantity: parseFloat(quantities[i].value), unitPrice: parseFloat(prices[i].value), total: parseFloat(quantities[i].value) * parseFloat(prices[i].value) }; }).filter(item => item.lotNumber); const subtotal = items.reduce((sum, item) => sum + item.total, 0); const vat = subtotal * 0.2; // 20% KDV const newProforma = { id: AppState.sales.length + 1, type: 'Proforma', number: `PRF-${AppState.nextProformaNumber++}`, date: document.getElementById('pfDate').value, customerId: customerId, customerName: customer.name, items: items, subtotal: subtotal, vat: vat, total: subtotal + vat, status: 'Beklemede', purchaseOrder: '' }; AppState.sales.push(newProforma); closeModal('proformaModal'); render(); alert(`Proforma fatura ${newProforma.number} oluşturuldu!`); } function completeWorkOrder(woNumber) { const output = prompt('Üretim çıktısını (KG) girin:'); if (!output) return; const wo = AppState.workOrders.find(w => w.workOrderNumber === woNumber); if (!wo) return; wo.totalOutput = parseFloat(output); wo.waste = wo.totalInput - wo.totalOutput; wo.wastePercentage = ((wo.waste / wo.totalInput) * 100).toFixed(2); wo.endDate = new Date().toISOString().split('T')[0]; wo.endTime = new Date().toTimeString().slice(0,5); // Prompt for analysis const bloom = prompt('Bloom değeri:'); const ph = prompt('pH değeri:'); const color = prompt('Renk:'); const clarity = prompt('Berraklık:'); const viscosity = prompt('Viskozite:'); wo.analysis = { bloom: parseFloat(bloom), ph: parseFloat(ph), color: color, clarity: clarity, viscosity: parseFloat(viscosity), analysisComplete: true }; wo.status = 'Tamamlandı'; wo.lotNumber = `FP-LOT-${2000 + AppState.finishedProductStock.length + 1}`; // Add to finished product stock const bags = Math.floor(wo.totalOutput / 25); const product = AppState.finishedProducts.find(p => p.id === wo.productId); // Calculate weighted average cost const rawMaterialCosts = wo.rawMaterials.map(rm => { const stock = AppState.rawMaterialStock.find(s => s.lotNumber === rm.lotNumber); return stock ? stock.unitCost * rm.quantity : 0; }); const totalCost = rawMaterialCosts.reduce((sum, c) => sum + c, 0); const unitCost = Math.round(totalCost / wo.totalOutput); const finishedStock = { id: AppState.finishedProductStock.length + 1, lotNumber: wo.lotNumber, productId: wo.productId, productName: wo.productName, meshSize: wo.meshSize, quantity: wo.totalOutput, bags: bags, productionDate: wo.endDate, workOrderNumber: wo.workOrderNumber, workOrderId: wo.id, analysis: wo.analysis, unitCost: unitCost, available: false, // Will be true after quality approval qualityStatus: 'Beklemede', rawMaterials: wo.rawMaterials || [], consumables: wo.consumables || [], // 🎯 YENİ: Sipariş referansı quotationId: wo.quotationId || null, quotationNumber: wo.quotationNumber || null }; AppState.finishedProductStock.push(finishedStock); // Change status to Kalitede instead of Tamamlandı wo.status = 'Kalitede'; render(); alert(`✅ İş emri tamamlandı!\n\nLot No: ${wo.lotNumber}\nÜrün kalite kontrolünde.\n\nKalite & Laboratuvar modülünden analiz yapılabilir.`); } function printWorkOrder(woNumber) { alert(`İş emri ${woNumber} yazdırılıyor...`); } function downloadCOA(lotNumber) { const stock = AppState.finishedProductStock.find(s => s.lotNumber === lotNumber); if (!stock) return; const coa = ` CERTIFICATE OF ANALYSIS (COA) ======================================== LUCAS INGREDIENTS GIDA A.Ş. Ürün: ${stock.productName} Lot No: ${stock.lotNumber} Üretim Tarihi: ${new Date(stock.productionDate).toLocaleDateString('tr-TR')} ANALIZ SONUÇLARI: ---------------- Bloom: ${stock.analysis.bloom} pH: ${stock.analysis.ph} Renk: ${stock.analysis.color} Berraklık: ${stock.analysis.clarity} Viskozite: ${stock.analysis.viscosity} Bu ürün belirtilen spesifikasyonlara uygundur. Tarih: ${new Date().toLocaleDateString('tr-TR')} `; const blob = new Blob([coa], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `COA_${lotNumber}.txt`; a.click(); } function viewSaleDetails(saleNumber) { alert(`Satış detayları ${saleNumber} görüntüleniyor...`); } function printSale(saleNumber) { alert(`Belge ${saleNumber} yazdırılıyor...`); } function generateReport(reportType) { alert(`${reportType} raporu oluşturuluyor...\n\nRapor Excel formatında indirilebilir olacak.`); } // COA FUNCTIONS window.openCOAForm = function(woId) { const wo = AppState.workOrders.find(w => w.id === woId); if (!wo) return; // Initialize COA data if not exists if (!wo.coaData) { const now = new Date(); const day = String(now.getDate()).padStart(2, '0'); const month = String(now.getMonth() + 1).padStart(2, '0'); const year = now.getFullYear(); // Gerçek lot numarasını kullan (iş emrinin lot numarası) const lotNumber = wo.lotNumber || wo.workOrderNumber || 'BATCH-' + Date.now(); wo.coaData = { batchNumber: lotNumber, analysisDate: day + '.' + month + '.' + year, expiryDate: day + '.' + month + '.' + (year + 5), completed: false, parameters: [ { name: 'Protein İçeriği', nameEn: 'Protein Content', unit: '%', limitValue: '≥92', result: '', method: 'INHOUSE' }, { name: 'Viskozite', nameEn: 'Viscosity (20% at 25°C mpa.s)', unit: 'mpa.s', limitValue: '2.0-4.0', result: '', method: 'GME' }, { name: 'Berraklık', nameEn: 'Clarity (% Transmittance 620 nm)', unit: '%', limitValue: '≥80', result: '', method: 'GME' }, { name: 'Renk', nameEn: 'Colour (% Transmittance 450 nm)', unit: '%', limitValue: '≥30', result: '', method: 'GME' }, { name: 'Kül', nameEn: 'Ash', unit: '%', limitValue: '≤2', result: '', method: 'GMIE' }, { name: 'Nem', nameEn: 'Moisture', unit: '%', limitValue: '≤8', result: '', method: 'GME' }, { name: 'pH', nameEn: 'pH', unit: '', limitValue: '5.0-7.5', result: '', method: 'GME' }, { name: 'SO₂', nameEn: 'SO₂', unit: 'ppm', limitValue: '≤10', result: '', method: 'GME' }, { name: 'H₂O₂', nameEn: 'H₂O₂', unit: 'ppm', limitValue: '≤10', result: '', method: 'GME' }, { name: 'İletkenlik', nameEn: 'Conductivity', unit: 'µs/cm', limitValue: '≤1', result: '', method: 'Ph.Eur' }, { name: 'Arsenik', nameEn: 'Arsenic (As)*', unit: 'ppm', limitValue: '≤1', result: '', method: 'GME' }, { name: 'Kadmiyum', nameEn: 'Cadmium (Cd)*', unit: 'ppm', limitValue: '≤0.5', result: '', method: 'GME' }, { name: 'Bakır', nameEn: 'Copper (Cu)*', unit: 'ppm', limitValue: '≤30', result: '', method: 'GME' }, { name: 'Civa', nameEn: 'Mercury (Hg)*', unit: 'ppm', limitValue: '≤0.15', result: '', method: 'GME' }, { name: 'Kurşun', nameEn: 'Lead', unit: 'ppm', limitValue: '≤5', result: '', method: 'GME' }, { name: 'Çinko', nameEn: 'Zinc', unit: 'ppm', limitValue: '≤50', result: '', method: 'GME' }, { name: 'Krom', nameEn: 'Chrome (Cr)*', unit: 'ppm', limitValue: '≤10', result: '', method: 'GME' }, { name: 'Toplam Aerobik Koloni Sayısı', nameEn: 'Total Aerobic count', unit: 'CFU/g', limitValue: '≤10³', result: '', method: 'GME' }, { name: 'Escherichia coli', nameEn: 'Escherichia coli', unit: 'CFU/10g', limitValue: 'ABSENT', result: 'ABSENT', method: 'GME' }, { name: 'Koagülaz Pozitif Stafilokoklar', nameEn: 'Coagulase Positive Staphylococci', unit: 'CFU/g', limitValue: '≤10⁴', result: '', method: 'TS EN ISO 6888-1' }, { name: 'Bacillus cereus', nameEn: 'Bacillus cereus', unit: 'CFU/g', limitValue: '≤10⁴', result: '', method: 'TS EN ISO 7932' }, { name: 'Salmonella species', nameEn: 'Salmonella species', unit: 'CFU/25g', limitValue: 'ABSENT', result: 'ABSENT', method: 'ISO 6579-1' }, { name: 'Sulfide Reducing anaerobes', nameEn: 'Sulfide Reducing anaerobes', unit: 'cfu/g', limitValue: '≤10', result: '', method: 'ISO 15213' } ] }; } let content = '
'; // Header info content += '
'; content += '
Parti: ' + wo.coaData.batchNumber + '
'; content += '
Analiz Tarihi: ' + wo.coaData.analysisDate + '
'; content += '
SKT: ' + wo.coaData.expiryDate + '
'; content += '
'; // Info message content += '
'; content += '💡 Bilgi: Limit değerleri müşteriye özel değiştirilebilir. Standart değerler otomatik doldurulmuştur.'; content += '
'; // Parameters table content += '
'; content += ''; content += ''; content += ''; content += ''; content += ''; content += ''; for (let i = 0; i < wo.coaData.parameters.length; i++) { const p = wo.coaData.parameters[i]; content += ''; content += ''; // Editable limit value content += ''; content += ''; content += ''; content += ''; } content += '
ParametreLimit DeğerSonuçMetod
' + p.name + '
' + p.nameEn + '
' + p.unit + '
'; content += '
'; content += ''; content += ''; content += '
'; document.getElementById('coaModalContent').innerHTML = content; openModal('coaModal'); }; window.saveCOA = function(event, woId) { event.preventDefault(); const wo = AppState.workOrders.find(w => w.id === woId); if (!wo) return; // Save all parameter results AND limit values let failedParameters = []; for (let i = 0; i < wo.coaData.parameters.length; i++) { const param = wo.coaData.parameters[i]; param.limitValue = document.getElementById('limit_' + i).value; param.result = document.getElementById('param_' + i).value; param.method = document.getElementById('method_' + i).value; // Check if result is within limits const isValid = checkParameterInLimits(param); param.isValid = isValid; if (!isValid) { failedParameters.push({ name: param.name, limit: param.limitValue, result: param.result }); } } // Determine quality status based on test results const allParametersPass = failedParameters.length === 0; // CONFIRMATION DIALOG BEFORE SAVING let confirmMessage = ''; if (allParametersPass) { confirmMessage = `✅ ANALİZ SONUÇLARI UYGUN\n\n`; confirmMessage += `Tüm ${wo.coaData.parameters.length} parametre limit değerler içinde.\n\n`; confirmMessage += `Parti: ${wo.coaData.batchNumber}\n`; confirmMessage += `Ürün: ${wo.productName}\n\n`; confirmMessage += `❓ Analiz sonuçlarını onaylayıp KAYDETMEK istediğinize emin misiniz?\n\n`; confirmMessage += `✅ EVET → Ürün "Sevk Edilebilir" olacak\n`; confirmMessage += `❌ HAYIR → Listeye dön, sonuçları kontrol et`; } else { confirmMessage = `⚠️ UYARI: ANALİZ SONUÇLARI UYGUN DEĞİL!\n\n`; confirmMessage += `Limit dışı ${failedParameters.length} parametre tespit edildi:\n\n`; failedParameters.forEach(p => { confirmMessage += `❌ ${p.name}\n Sonuç: ${p.result} | Limit: ${p.limit}\n\n`; }); confirmMessage += `Parti: ${wo.coaData.batchNumber}\n`; confirmMessage += `Ürün: ${wo.productName}\n\n`; confirmMessage += `⚠️ Bu sonuçlar DOĞRU mu? Kontrol ediniz!\n\n`; confirmMessage += `✅ EVET, DOĞRU → Ürün "Reddedildi" olarak işaretlenecek\n`; confirmMessage += `❌ HAYIR → Listeye dön, sonuçları düzelt`; } if (!confirm(confirmMessage)) { // User cancelled - stay on the form return; } wo.coaData.completed = true; wo.coaData.completedAt = new Date().toISOString(); wo.coaData.responsible = AppState.currentUser.name; wo.coaData.failedParameters = failedParameters; // Update work order status wo.status = 'Tamamlandı'; // Update finished product stock if (AppState.finishedProductStock) { // Try to find by workOrderId first, then by workOrderNumber let stock = AppState.finishedProductStock.find(s => s.workOrderId === wo.id); if (!stock) { // Try finding by work order number stock = AppState.finishedProductStock.find(s => s.workOrderNumber === wo.workOrderNumber); } console.log('Updating stock for work order:', wo.id, wo.workOrderNumber); console.log('Found stock:', stock); console.log('All parameters pass:', allParametersPass); if (stock) { if (allParametersPass) { // All tests passed - APPROVE stock.qualityStatus = 'Onaylandı'; stock.available = true; stock.qualityResult = 'UYGUN'; } else { // Some tests failed - REJECT stock.qualityStatus = 'Reddedildi'; stock.available = false; stock.qualityResult = 'UYGUN DEĞİL'; stock.failedParameters = failedParameters; } stock.coaData = wo.coaData; stock.qualityCompletedBy = AppState.currentUser.name; stock.qualityCompletedAt = new Date().toISOString(); console.log('Updated stock successfully:', stock); } else { console.error('Stock not found! Work Order ID:', wo.id, 'Work Order Number:', wo.workOrderNumber); console.log('Available stocks:', AppState.finishedProductStock.map(s => ({ id: s.id, workOrderId: s.workOrderId, workOrderNumber: s.workOrderNumber, lotNumber: s.lotNumber }))); } } closeModal('coaModal'); render(); if (allParametersPass) { // Add audit log for approval addAuditLog('approved', 'quality', `Kalite onaylandı: ${wo.workOrderNumber} - ${wo.productName} (Parti: ${wo.coaData.batchNumber})`, wo.id, 'workOrder'); alert(`✅ KALİTE ONAYLANDI!\n\nParti: ${wo.coaData.batchNumber}\n\nTüm test sonuçları limit değerler içinde.\nÜrün sevk edilebilir durumda.`); } else { // Add audit log for rejection addAuditLog('rejected', 'quality', `Kalite reddedildi: ${wo.workOrderNumber} - ${wo.productName} (${failedParameters.length} parametre limit dışı)`, wo.id, 'workOrder'); // Show rejection actions modal openRejectionActionsModal(stock.id); } }; // Check if parameter result is within limits function checkParameterInLimits(param) { const result = param.result.trim().toLowerCase(); const limit = param.limitValue.trim().toLowerCase(); // Handle ABSENT values if (limit === 'absent') { return result === 'absent'; } // Handle ≥ (greater than or equal) if (limit.includes('≥')) { const limitValue = parseFloat(limit.replace('≥', '').trim()); const resultValue = parseFloat(result); if (!isNaN(limitValue) && !isNaN(resultValue)) { return resultValue >= limitValue; } } // Handle ≤ (less than or equal) if (limit.includes('≤')) { const limitValue = parseFloat(limit.replace('≤', '').trim()); const resultValue = parseFloat(result); if (!isNaN(limitValue) && !isNaN(resultValue)) { return resultValue <= limitValue; } } // Handle range (e.g., "2.0-4.0" or "5.0-7.5") if (limit.includes('-') && !limit.startsWith('≥') && !limit.startsWith('≤')) { const parts = limit.split('-'); if (parts.length === 2) { const minValue = parseFloat(parts[0].trim()); const maxValue = parseFloat(parts[1].trim()); const resultValue = parseFloat(result); if (!isNaN(minValue) && !isNaN(maxValue) && !isNaN(resultValue)) { return resultValue >= minValue && resultValue <= maxValue; } } } // If we can't parse it, assume it's valid (manual check needed) return true; } // ==================== // REJECTION ACTIONS (Rework, Re-analyze, Offspec) // ==================== // Open Rejection Actions Modal window.openRejectionActionsModal = function(stockId) { const stock = AppState.finishedProductStock.find(s => s.id === stockId); if (!stock) return; const content = `

❌ Kalite Reddedildi - İşlem Seçiniz

${stock.productName}
Lot: ${stock.lotNumber} | Miktar: ${stock.quantity} KG
Hatalı Parametreler: ${stock.failedParameters ? stock.failedParameters.map(p => `
• ${p.name}: ${p.result} (Limit: ${p.limit})
`).join('') : ''}
🔄 Rework (Yeniden İşleme)
Ürün hammadde stoğuna geri alınır. Lot numarası RW-${stock.lotNumber} olarak güncellenir. Yeni üretimlerde hammadde olarak kullanılabilir.
🔬 Yeniden Analiz
COA formu sıfırlanır ve yeniden doldurulur. Yeni test sonuçlarına göre tekrar değerlendirilir.
📦 Offspec (Gıda Dışı Kullanım)
Ürün OS-${stock.lotNumber} olarak işaretlenir. Gıda dışı sektörlerde (kozmetik, endüstriyel vb.) kullanım için satılabilir. Ayrı stokta turuncu ile gösterilir.
`; document.getElementById('rejectionActionsModalContent').innerHTML = content; openModal('rejectionActionsModal'); }; // Select Rejection Action window.selectRejectionAction = function(action, stockId) { const stock = AppState.finishedProductStock.find(s => s.id === stockId); if (!stock) return; switch(action) { case 'rework': processRework(stock); break; case 'reanalyze': processReanalyze(stock); break; case 'offspec': processOffspec(stock); break; } }; // Process Rework function processRework(stock) { if (!confirm(`🔄 REWORK İŞLEMİ\n\nÜrün hammadde stoğuna geri alınacak.\n\nYeni Lot No: RW-${stock.lotNumber}\nMiktar: ${stock.quantity} KG\n\nDevam edilsin mi?`)) { return; } // Create rework raw material const reworkMaterial = { id: AppState.rawMaterialStock.length + 1, lotNumber: `RW-${stock.lotNumber}`, materialCode: 'REWORK', materialName: `Rework - ${stock.productName}`, quantity: stock.quantity, unitCost: stock.unitCost || 0, totalCost: (stock.unitCost || 0) * stock.quantity, receivedDate: new Date().toISOString().split('T')[0], supplierName: 'İç Üretim - Rework', notes: `Kalite reddi sebebiyle rework. Orijinal Lot: ${stock.lotNumber}`, reworkSource: stock.lotNumber, isRework: true }; AppState.rawMaterialStock.push(reworkMaterial); // Remove from finished product stock const index = AppState.finishedProductStock.findIndex(s => s.id === stock.id); if (index > -1) { AppState.finishedProductStock.splice(index, 1); } // Add audit log addAuditLog('rework', 'quality', `Rework işlemi: ${stock.lotNumber} → RW-${stock.lotNumber} (${stock.quantity} KG hammadde stoğuna alındı)`, stock.id, 'stock'); closeModal('rejectionActionsModal'); render(); alert(`✅ Rework işlemi tamamlandı!\n\nYeni hammadde lot: RW-${stock.lotNumber}\nMiktar: ${stock.quantity} KG\n\nHammadde stoğunda görüntüleyebilirsiniz.`); } // Process Reanalyze function processReanalyze(stock) { if (!confirm(`🔬 YENİDEN ANALİZ\n\nCOA formu sıfırlanacak ve yeniden doldurulacak.\n\nDevam edilsin mi?`)) { return; } // Find work order and reset COA const wo = AppState.workOrders.find(w => w.id === stock.workOrderId || w.workOrderNumber === stock.workOrderNumber); if (wo && wo.coaData) { // Reset parameters wo.coaData.parameters.forEach(p => { p.result = ''; p.isValid = null; }); wo.coaData.completed = false; wo.coaData.failedParameters = []; delete wo.coaData.completedAt; } // Reset stock status stock.qualityStatus = 'Beklemede'; stock.available = false; stock.qualityResult = null; stock.failedParameters = []; delete stock.qualityCompletedBy; delete stock.qualityCompletedAt; // Add audit log addAuditLog('reanalyze', 'quality', `Yeniden analiz: ${stock.lotNumber} - COA sıfırlandı`, stock.id, 'stock'); closeModal('rejectionActionsModal'); render(); alert(`✅ Yeniden analiz için hazırlandı!\n\nKalite & Laboratuvar modülünden COA'yı yeniden doldurabilirsiniz.`); } // Process Offspec function processOffspec(stock) { if (!confirm(`📦 OFFSPEC İŞLEMİ\n\nÜrün gıda dışı kullanım için işaretlenecek.\n\nYeni Lot No: OS-${stock.lotNumber}\n\nKozmetik, endüstriyel vb. sektörlerde satılabilir.\n\nDevam edilsin mi?`)) { return; } // Update stock for offspec stock.lotNumber = `OS-${stock.lotNumber.replace('OS-', '')}`; // Avoid double OS- prefix stock.qualityStatus = 'Offspec'; stock.available = true; // Can be sold as offspec stock.qualityResult = 'OFFSPEC - GIDA DIŞI KULLANIM'; stock.isOffspec = true; // Add audit log addAuditLog('offspec', 'quality', `Offspec işlemi: ${stock.lotNumber.replace('OS-', '')} → ${stock.lotNumber} (Gıda dışı kullanım)`, stock.id, 'stock'); closeModal('rejectionActionsModal'); render(); alert(`✅ Offspec işlemi tamamlandı!\n\nYeni lot: ${stock.lotNumber}\n\nÜrün gıda dışı kullanım için hazır.\nSevkiyat modülünde ayrı gösterilecektir.`); } window.viewCOA = function(woId) { alert('COA görüntüleme özelliği yakında eklenecek!'); }; window.printCOA = function(woId) { const wo = AppState.workOrders.find(w => w.id === woId); if (!wo || !wo.coaData) return; const printWindow = window.open('', '_blank'); let html = 'COA - ' + wo.coaData.batchNumber + ''; html += ''; html += ''; html += '

LUCAS ANALİZ SERTİFİKASI / CERTIFICATE OF ANALYSES

'; html += ''; html += ''; html += ''; html += '
Ürün: ' + wo.productName + '
Parti No: ' + wo.coaData.batchNumber + '
Analiz Tarihi: ' + wo.coaData.analysisDate + '
SKT: ' + wo.coaData.expiryDate + '
'; html += ''; for (let i = 0; i < wo.coaData.parameters.length; i++) { const p = wo.coaData.parameters[i]; html += ''; html += ''; html += ''; html += ''; } html += '
ParametreLimitSonuçMetod
' + p.name + ' / ' + p.nameEn + '' + p.limitValue + ' ' + p.unit + '' + p.result + '' + p.method + '
'; html += '

Kalite Sorumlusu: ' + (wo.coaData.responsible || 'Admin') + '

'; html += ''; printWindow.document.write(html); printWindow.document.close(); printWindow.print(); }; // ==================== BULK LOT UPLOAD ==================== window.openBulkLotUploadModal = function() { // Hammadde dropdown'unu doldur const materialSelect = document.getElementById('bulkMaterial'); materialSelect.innerHTML = '' + AppState.rawMaterials.map(m => ``).join(''); // Tedarikçi dropdown'unu doldur const supplierSelect = document.getElementById('bulkSupplier'); supplierSelect.innerHTML = '' + AppState.suppliers.map(s => ``).join(''); // Tarihi bugün olarak ayarla document.getElementById('bulkReceivedDate').value = new Date().toISOString().split('T')[0]; // Fatura alanlarını göster document.getElementById('bulkInvoiceFields').classList.remove('hidden'); document.querySelector('input[name="bulkInvoiceStatus"][value="with_invoice"]').checked = true; openModal('bulkLotUploadModal'); }; window.toggleBulkInvoiceFields = function(show) { const fields = document.getElementById('bulkInvoiceFields'); if (show) { fields.classList.remove('hidden'); document.getElementById('bulkInvoiceNo').required = true; document.getElementById('bulkUnitCost').required = true; } else { fields.classList.add('hidden'); document.getElementById('bulkInvoiceNo').required = false; document.getElementById('bulkUnitCost').required = false; } }; window.updateBulkDueDate = function() { const terms = document.getElementById('bulkPaymentTerms')?.value || ''; const receivedDateEl = document.getElementById('bulkReceivedDate'); const dueEl = document.getElementById('bulkDueDate'); if (!dueEl) return; const baseDate = new Date(receivedDateEl?.value || new Date()); let days = 30; if (terms.includes('Peşin')) days = 0; else if (terms.includes('7')) days = 7; else if (terms.includes('14')) days = 14; else if (terms.includes('30')) days = 30; else if (terms.includes('45')) days = 45; else if (terms.includes('60')) days = 60; else if (terms.includes('90')) days = 90; else if (terms.includes('Çek') || terms.includes('Senet')) days = 30; const due = new Date(baseDate); due.setDate(due.getDate() + days); dueEl.value = due.toISOString().split('T')[0]; }; window.downloadBulkLotTemplate = function() { const data = [ { 'Parti/Lot No': 'LOT-ABC-001', 'Bigbag/Depo No': 'BB-001', 'Tartılan Miktar (KG)': 1000, 'Bloom': 245, 'pH': 5.2, 'Renk (%T 450nm)': 85, 'Berraklık (%T 620nm)': 92, 'Viskozite (mPa.s)': 3.5, 'Mesh': 8 }, { 'Parti/Lot No': 'LOT-ABC-002', 'Bigbag/Depo No': 'BB-002', 'Tartılan Miktar (KG)': 1000, 'Bloom': 248, 'pH': 5.3, 'Renk (%T 450nm)': 87, 'Berraklık (%T 620nm)': 93, 'Viskozite (mPa.s)': 3.6, 'Mesh': 8 } ]; const ws = XLSX.utils.json_to_sheet(data); ws['!cols'] = [ {wch: 18}, // Parti/Lot No {wch: 18}, // Bigbag/Depo No {wch: 20}, // Tartılan Miktar {wch: 12}, // Bloom {wch: 10}, // pH {wch: 18}, // Renk {wch: 18}, // Berraklık {wch: 18}, // Viskozite {wch: 10} // Mesh ]; const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Lot Detayları'); XLSX.writeFile(wb, 'Toplu_Lot_Yukleme_Sablonu.xlsx'); }; window.submitBulkLotUpload = function(event) { event.preventDefault(); // Genel bilgileri al const materialId = parseInt(document.getElementById('bulkMaterial').value); const material = AppState.rawMaterials.find(m => m.id === materialId); const supplierId = parseInt(document.getElementById('bulkSupplier').value); const supplier = AppState.suppliers.find(s => s.id === supplierId); const waybillNo = document.getElementById('bulkWaybillNo').value; const receivedDate = document.getElementById('bulkReceivedDate').value; const coaNo = document.getElementById('bulkCoaNo').value || ''; const invoiceStatus = document.querySelector('input[name="bulkInvoiceStatus"]:checked').value; const hasInvoice = (invoiceStatus === 'with_invoice'); let invoiceNo = ''; let unitCost = 0; let currency = 'TRY'; let exchangeRate = 1; let vatRate = 0; let paymentTerms = '30 Gün Vadeli'; let dueDate = null; if (hasInvoice) { invoiceNo = document.getElementById('bulkInvoiceNo').value; unitCost = parseFloat(document.getElementById('bulkUnitCost').value); currency = document.getElementById('bulkCurrency').value; exchangeRate = parseFloat(document.getElementById('bulkExchangeRate').value) || 1; vatRate = parseFloat(document.getElementById('bulkVatRate').value) || 0; paymentTerms = document.getElementById('bulkPaymentTerms')?.value || '30 Gün Vadeli'; dueDate = document.getElementById('bulkDueDate')?.value || null; if (!invoiceNo || !unitCost) { alert('❌ Fatura numarası ve birim fiyat gerekli!'); return; } } // Excel dosyasını al const fileInput = document.getElementById('bulkExcelFile'); const file = fileInput.files[0]; if (!file) { alert('❌ Lütfen Excel dosyası seçin!'); return; } const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const sheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet); if (rows.length === 0) { alert('❌ Excel dosyası boş!'); return; } // Hesaplamalar let totalQuantity = 0; let totalAmount = 0; const lots = []; // Her lot için stok kaydı oluştur rows.forEach((row, index) => { const lotNumber = row['Parti/Lot No']; const quantity = parseFloat(row['Tartılan Miktar (KG)']); if (!lotNumber || !quantity) { alert(`❌ Satır ${index + 2}: Lot No veya Miktar eksik!`); throw new Error('Eksik alan'); } totalQuantity += quantity; const newStock = { id: AppState.rawMaterialStock.length + lots.length + 1, lotNumber: lotNumber, materialId: materialId, materialName: material.name, quantity: quantity, bloom: parseFloat(row['Bloom']) || null, ph: parseFloat(row['pH']) || null, color: parseFloat(row['Renk (%T 450nm)']) || null, clarity: parseFloat(row['Berraklık (%T 620nm)']) || null, viscosity: parseFloat(row['Viskozite (mPa.s)']) || null, mesh: row['Mesh'] || null, unitCost: unitCost, totalCost: hasInvoice ? (quantity * unitCost * exchangeRate * (1 + vatRate/100)) : 0, receivedDate: receivedDate, supplierId: supplierId, supplierName: supplier.name, invoiceNo: invoiceNo, waybillNo: waybillNo, coaNo: coaNo, depotNo: row['Bigbag/Depo No'] || '', productionDate: null, expiryDate: null, checkedBy: AppState.currentUser.name, currency: currency, exchangeRate: exchangeRate, vatRate: vatRate, vatAmount: hasInvoice ? (quantity * unitCost * (vatRate / 100) * exchangeRate) : 0, invoiceStatus: hasInvoice ? 'Faturalandı' : 'Fatura Bekleniyor', invoiceId: null }; lots.push(newStock); }); // Onay iste const confirmMsg = `📦 TOPLU LOT KABUL EDİLECEK\n\n` + `Hammadde: ${material.name}\n` + `Tedarikçi: ${supplier.name}\n` + `Lot Sayısı: ${lots.length} adet\n` + `Toplam Miktar: ${totalQuantity.toLocaleString('tr-TR')} KG\n` + `İrsaliye No: ${waybillNo}\n` + (coaNo ? `COA No: ${coaNo}\n` : '') + (hasInvoice ? `\nFATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNo}\n` + `Birim Fiyat: ${unitCost.toFixed(2)} ${currency}/KG\n` + `Ara Toplam: ${(totalQuantity * unitCost).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${(totalQuantity * unitCost * vatRate / 100).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${(totalQuantity * unitCost * (1 + vatRate/100)).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Kur: ${exchangeRate.toFixed(4)}\n` + `TL Karşılığı: ${(totalQuantity * unitCost * (1 + vatRate/100) * exchangeRate).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ TEK alış faturası oluşturulacak\n` + `✓ Tedarikçi carisine borç yazılacak` : `\n⏳ SADECE İRSALİYE KABUL\n` + `✓ Tüm lotlar stoka girilecek\n` + `⏳ Fatura bekleniyor (Finans'tan girilecek)` ) + `\n\nOnaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // Tüm lotları kaydet lots.forEach(lot => { AppState.rawMaterialStock.push(lot); }); // TEK FATURA OLUŞTUR (Faturalı ise) if (hasInvoice && invoiceNo) { if (!AppState.invoices) AppState.invoices = []; const subtotal = totalQuantity * unitCost; const vatAmount = subtotal * (vatRate / 100); const total = subtotal + vatAmount; const totalTRY = total * exchangeRate; const purchaseInvoice = { id: (AppState.invoices.length || 0) + 1, type: 'Alış', invoiceNumber: invoiceNo, invoiceDate: receivedDate, supplierId: supplierId, supplierName: supplier.name, waybillNumber: waybillNo, items: [{ productName: material.name, quantity: totalQuantity, unit: 'KG', unitPrice: unitCost, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: total, lotCount: lots.length, lotNumbers: lots.map(l => l.lotNumber).join(', ') }], subtotal: subtotal, vatTotal: vatAmount, grandTotal: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, paymentTerms: paymentTerms, dueDate: dueDate, status: 'Ödenmedi', notes: `${lots.length} lot toplu yükleme`, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(purchaseInvoice); // Tüm lotlara fatura ID'sini ekle lots.forEach(lot => { lot.invoiceId = purchaseInvoice.id; }); // Tedarikçi carisine borç yaz if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: supplierId, supplierName: supplier.name, invoiceId: purchaseInvoice.id, invoiceNumber: invoiceNo, amount: total, currency: currency, date: receivedDate, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('create', 'finance', `Toplu Alış Faturası: ${invoiceNo} - ${lots.length} lot - ${totalTRY.toLocaleString('tr-TR')} ₺`, purchaseInvoice.id, 'invoice'); } closeModal('bulkLotUploadModal'); render(); const message = hasInvoice ? `✅ TOPLU LOT KABUL EDİLDİ!\n\n` + `${lots.length} lot kaydedildi\n` + `Toplam: ${totalQuantity.toLocaleString('tr-TR')} KG\n\n` + `✓ TEK alış faturası oluşturuldu\n` + `✓ Tedarikçi carisine borç yazıldı` : `✅ TOPLU LOT KABUL EDİLDİ!\n\n` + `${lots.length} lot kaydedildi\n` + `Toplam: ${totalQuantity.toLocaleString('tr-TR')} KG\n\n` + `✓ Stok girildi\n` + `⏳ Fatura Bekleniyor\n\n` + `→ Finans & P&L → Fatura Kabul Et'ten faturayı girebilirsiniz`; alert(message); } catch (error) { alert('Excel dosyası okunurken hata oluştu: ' + error.message); console.error(error); } }; reader.readAsArrayBuffer(file); }; // Initialize app initializeData(); render(); `); printWindow.document.close(); printWindow.print(); }; // Show Audit Trail window.showAuditTrail = function() { const logs = getAuditLogs(); const content = `

📜 Audit Trail - Sistem Aktivite Geçmişi

Toplam ${logs.length} aktivite kaydı
Son 30 gün içindeki tüm sistem aktiviteleri
${logs.length > 0 ? logs.map(log => `
${getActionIcon(log.action)} ${log.details}
${log.userName} (${log.userRole}) • ${formatAuditTimestamp(log.timestamp)} • ${log.category}
`).join('') : '

Henüz aktivite kaydı yok

'}
`; document.getElementById('auditTrailModalContent').innerHTML = content; openModal('auditTrailModal'); }; // Export Audit Trail to Excel (placeholder) window.exportAuditTrail = function() { alert('Excel export özelliği yakında eklenecek!'); }; // ==================== // QUOTATION (TEKLIF) SYSTEM // ==================== // Open Quotation Modal window.openQuotationModal = function() { console.log('✅ GERÇEK openQuotationModal fonksiyonu çalışıyor!'); const content = `

💼 Fiyat Teklifi Oluştur

Ürünler

Ödeme Koşulları

`; document.getElementById('quotationModalContent').innerHTML = content; openModal('quotationModal'); // Add first item automatically addQuotItem(); }; // Update customer info display window.updateQuotCustomerInfo = function() { const select = document.getElementById('quotCustomer'); const option = select.options[select.selectedIndex]; if (option.value) { document.getElementById('quotCustomerInfo').classList.remove('hidden'); document.getElementById('quotCustomerName').textContent = option.dataset.name; document.getElementById('quotCustomerAddress').textContent = option.dataset.address + ', ' + option.dataset.country; } else { document.getElementById('quotCustomerInfo').classList.add('hidden'); } }; let quotItemCounter = 0; // Add quotation item window.addQuotItem = function() { quotItemCounter++; const itemsContainer = document.getElementById('quotItems'); // Get all products const allProducts = AppState.finishedProducts || []; const itemHtml = `
`; itemsContainer.insertAdjacentHTML('beforeend', itemHtml); // Add event listener for custom product const selectElement = document.getElementById('quotItemName' + quotItemCounter); const customRow = document.getElementById('customProductRow' + quotItemCounter); const customInput = document.getElementById('quotItemCustomName' + quotItemCounter); selectElement.addEventListener('change', function() { if (this.value === 'CUSTOM') { customRow.classList.remove('hidden'); customInput.required = true; } else { customRow.classList.add('hidden'); customInput.required = false; customInput.value = ''; } }); }; // Remove quotation item window.removeQuotItem = function(itemId) { const item = document.getElementById('quotItem' + itemId); if (item) item.remove(); }; // Submit Quotation window.submitQuotation = function(event) { event.preventDefault(); const customerId = parseInt(document.getElementById('quotCustomer').value); const customer = AppState.customers.find(c => c.id === customerId); // Collect items const items = []; const itemsContainer = document.getElementById('quotItems'); const itemDivs = itemsContainer.querySelectorAll('[id^="quotItem"]'); itemDivs.forEach(div => { const id = div.id.replace('quotItem', ''); let name = document.getElementById('quotItemName' + id)?.value; // Check if custom product if (name === 'CUSTOM') { name = document.getElementById('quotItemCustomName' + id)?.value; } const qty = parseFloat(document.getElementById('quotItemQty' + id)?.value || 0); const unit = document.getElementById('quotItemUnit' + id)?.value; const price = parseFloat(document.getElementById('quotItemPrice' + id)?.value || 0); if (name && name !== 'CUSTOM' && qty > 0 && price >= 0) { items.push({ name: name, quantity: qty, unit: unit, unitPrice: price, total: qty * price }); } }); if (items.length === 0) { alert('❌ En az bir ürün eklemelisiniz!'); return; } // Generate quotation number const year = new Date().getFullYear(); const month = String(new Date().getMonth() + 1).padStart(2, '0'); const quotNumber = `QT-${year}${month}-${String(AppState.quotations.length + 1).padStart(4, '0')}`; const subtotal = items.reduce((sum, item) => sum + item.total, 0); const currency = document.getElementById('quotCurrency').value; const newQuotation = { id: AppState.quotations.length + 1, number: quotNumber, date: document.getElementById('quotDate').value, validityDays: parseInt(document.getElementById('quotValidity').value), validUntil: new Date(new Date(document.getElementById('quotDate').value).getTime() + parseInt(document.getElementById('quotValidity').value) * 24 * 60 * 60 * 1000).toISOString().split('T')[0], customerId: customerId, customerName: customer.name, customerAddress: customer.address, customerCountry: customer.country || 'Türkiye', items: items, subtotal: subtotal, currency: currency, paymentTerms: document.getElementById('quotPaymentTerms').value, incoterms: document.getElementById('quotIncoterms').value, notes: document.getElementById('quotNotes').value || '', status: 'Beklemede', createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.quotations.push(newQuotation); // Add audit log addAuditLog('created', 'sales', `Teklif oluşturuldu: ${quotNumber} - ${customer.name} (${subtotal.toFixed(2)} ${currency})`, newQuotation.id, 'quotation'); closeModal('quotationModal'); render(); // Ask if user wants to print if (confirm(`✅ Teklif oluşturuldu!\n\nTeklif No: ${quotNumber}\nMüşteri: ${customer.name}\n\nTeklifi PDF olarak yazdırmak ister misiniz?`)) { printQuotation(newQuotation.id); } }; // Print Quotation (PDF-like format) window.printQuotation = function(quotId) { const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) return; const printWindow = window.open('', '_blank'); const currencySymbol = quot.currency === 'EUR' ? '€' : quot.currency === 'USD' ? '$' : quot.currency === 'GBP' ? '£' : '₺'; printWindow.document.write(` Teklif - ${quot.number}
LUCAS INGREDIENTS GIDA A.Ş.
Adres: İstanbul, Türkiye
Tel: +90 XXX XXX XX XX
Email: info@lucas.com
Teklif No:${quot.number}
Tarih:${new Date(quot.date).toLocaleDateString('tr-TR')}
Geçerlilik:${new Date(quot.validUntil).toLocaleDateString('tr-TR')} tarihine kadar
Para Birimi:${quot.currency}
MÜŞTERİ BİLGİLERİ
${quot.customerName}
${quot.customerAddress}
${quot.customerCountry}
ÜRÜN LİSTESİ
${quot.items.map((item, index) => ` `).join('')}
Sıra Ürün Adı Miktar Birim Birim Fiyat Toplam
${index + 1} ${item.name} ${item.quantity.toLocaleString('tr-TR')} ${item.unit} ${item.unitPrice.toFixed(2)} ${currencySymbol} ${item.total.toFixed(2)} ${currencySymbol}
Ara Toplam: ${quot.subtotal.toFixed(2)} ${currencySymbol}
TOPLAM: ${quot.subtotal.toFixed(2)} ${currencySymbol}
TİCARİ KOŞULLAR
Ödeme Şekli: ${quot.paymentTerms}
Sevkiyat Şekli: ${quot.incoterms}
Geçerlilik: ${quot.validityDays} gün (${new Date(quot.validUntil).toLocaleDateString('tr-TR')} tarihine kadar)
${quot.notes ? `
Özel Notlar:
${quot.notes}
` : ''}

BANKA BİLGİLERİMİZ / BANK DETAILS

QNB Finans Bankası
IBAN TRY: TR20 0011 1000 0000 0089 4945 79
IBAN USD: TR88 0011 1000 0000 0089 4953 92
IBAN EUR: TR26 0011 1000 0000 0089 4957 32
SWIFT: FINTURXX
Garanti Bankası BBVA
IBAN TRY: TR33000620001669000009299110
IBAN USD: TR69000620001669000009099531
IBAN EUR: TR96000620001669000009099530
SWIFT: TGBATRISXXX
`); printWindow.document.close(); printWindow.print(); }; // Convert Quotation to Proforma window.convertQuotToProforma = function(quotId) { const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) return; if (confirm(`Teklif ${quot.number} proforma faturaya dönüştürülsün mü?`)) { // This would open proforma modal with pre-filled data alert('Teklif → Proforma dönüşümü yakında eklenecek!'); } }; // ==================== // SAMPLE REQUEST SYSTEM // ==================== // Open Sample Request Modal window.openSampleRequestModal = function() { console.log('✅ GERÇEK openSampleRequestModal fonksiyonu çalışıyor!'); const allProducts = AppState.finishedProducts || []; const content = `

🧪 Numune Talebi Oluştur

`; document.getElementById('sampleRequestModalContent').innerHTML = content; openModal('sampleRequestModal'); }; // Submit Sample Request window.submitSampleRequest = function(event) { event.preventDefault(); const customerId = parseInt(document.getElementById('sampleCustomer').value); const customer = AppState.customers.find(c => c.id === customerId); const year = new Date().getFullYear(); const month = String(new Date().getMonth() + 1).padStart(2, '0'); const requestNumber = `NS-${year}${month}-${String(AppState.sampleRequests.length + 1).padStart(4, '0')}`; const newSample = { id: AppState.sampleRequests.length + 1, requestNumber: requestNumber, customerId: customerId, customerName: customer.name, productName: document.getElementById('sampleProduct').value, quantity: parseInt(document.getElementById('sampleQuantity').value), unit: document.getElementById('sampleUnit').value, specification: document.getElementById('sampleSpecification').value || '', notes: document.getElementById('sampleNotes').value || '', status: 'Hazırlanıyor', stages: { preparation: { started: false, completed: false, notes: '' }, ready: { completed: false, notes: '' }, shipped: { completed: false, carrier: '', trackingNumber: '', notes: '' } }, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.sampleRequests.push(newSample); // Add audit log addAuditLog('created', 'sales', `Numune talebi oluşturuldu: ${requestNumber} - ${customer.name} (${newSample.productName})`, newSample.id, 'sampleRequest'); closeModal('sampleRequestModal'); render(); alert(`✅ Numune talebi oluşturuldu!\n\nTalep No: ${requestNumber}\nMüşteri: ${customer.name}\nÜrün: ${newSample.productName}\n\nKalite & Lab modülünden işlem yapılabilir.`); }; // ==================== window.openSalesInvoiceModal = function() { // Kalite onaylı ürünler const availableStock = AppState.finishedProductStock ? AppState.finishedProductStock.filter(s => s.available && s.qualityStatus === 'Onaylandı') : []; // Faturalanmamış sevkiyatlar const pendingShipments = AppState.shipments ? AppState.shipments.filter(s => !s.invoiceId) : []; const content = `

📄 Satış Faturası Kes

Ürünler *

${availableStock.length === 0 ? `
⚠️ Stokta kalite onaylı ürün bulunmamaktadır!
` : ''}

Fatura Detayları

TRY için 1 girin
`; document.getElementById('salesInvoiceModalContent').innerHTML = content; openModal('salesInvoiceModal'); // İlk ürün satırını ekle if (availableStock.length > 0) { addInvoiceItem(); } }; // Open Purchase Invoice Modal (Alış Faturası Kabul Et) window.openPurchaseInvoiceModal = function() { // Bugünün tarihi const today = new Date().toISOString().split('T')[0]; // Bekleyen irsaliyeleri bul (faturalanmamış hammadde ve sarf malzeme) const pendingRMReceipts = AppState.rawMaterialStock ? AppState.rawMaterialStock.filter(s => s.invoiceStatus === 'Fatura Bekleniyor') : []; const pendingConReceipts = AppState.consumableStock ? AppState.consumableStock.filter(s => s.invoiceStatus === 'Fatura Bekleniyor') : []; const allPendingReceipts = [ ...pendingRMReceipts.map(r => ({...r, type: 'Hammadde'})), ...pendingConReceipts.map(r => ({...r, type: 'Sarf Malzeme'})) ]; const content = `

📥 Alış Faturası Kabul Et

${allPendingReceipts.length === 0 ? `
⚠️ Faturalanmamış irsaliye bulunmamaktadır!
` : ''}

💰 Fiyat Bilgileri

TRY için 1 girin

📊 Fatura Özeti

Ara Toplam
0.00
KDV
0.00
Genel Toplam
0.00
Fatura tarihine göre otomatik hesaplanır
`; document.getElementById('purchaseInvoiceModalContent').innerHTML = content; openModal('purchaseInvoiceModal'); }; // Toggle Purchase Invoice Type window.updatePurInvDueDate = function() { const terms = document.getElementById('purInvPaymentTerms')?.value || ''; const dateEl = document.getElementById('purInvDate'); const dueEl = document.getElementById('purInvDueDate'); if (!dateEl || !dueEl) return; const invoiceDate = new Date(dateEl.value || new Date()); let days = 30; if (terms.includes('Peşin')) days = 0; else if (terms.includes('7')) days = 7; else if (terms.includes('14')) days = 14; else if (terms.includes('30')) days = 30; else if (terms.includes('45')) days = 45; else if (terms.includes('60')) days = 60; else if (terms.includes('90')) days = 90; else if (terms.includes('Çek') || terms.includes('Senet')) days = 30; const due = new Date(invoiceDate); due.setDate(due.getDate() + days); dueEl.value = due.toISOString().split('T')[0]; }; window.togglePurchaseInvoiceType = function(type) { const receiptSection = document.getElementById('receiptSection'); const receiptInfoBox = document.getElementById('receiptInfoBox'); const expenseSection = document.getElementById('expenseSection'); const normalPriceFields = document.getElementById('normalPriceFields'); const expensePriceFields = document.getElementById('expensePriceFields'); const supplierSelect = document.getElementById('purInvSupplier'); if (type === 'receipt') { // İrsaliyeye fatura kes receiptSection.classList.remove('hidden'); expenseSection.classList.add('hidden'); normalPriceFields.classList.remove('hidden'); expensePriceFields.classList.add('hidden'); supplierSelect.disabled = true; supplierSelect.classList.add('bg-gray-100'); } else if (type === 'normal') { // Normal alış faturası receiptSection.classList.add('hidden'); receiptInfoBox.classList.add('hidden'); expenseSection.classList.add('hidden'); normalPriceFields.classList.remove('hidden'); expensePriceFields.classList.add('hidden'); supplierSelect.disabled = false; supplierSelect.classList.remove('bg-gray-100'); supplierSelect.value = ''; } else if (type === 'expense') { // Gider faturası receiptSection.classList.add('hidden'); receiptInfoBox.classList.add('hidden'); expenseSection.classList.remove('hidden'); normalPriceFields.classList.add('hidden'); expensePriceFields.classList.remove('hidden'); supplierSelect.disabled = false; supplierSelect.classList.remove('bg-gray-100'); supplierSelect.value = ''; } }; // Load Expense Subcategories window.loadExpenseSubcategories = function() { const categoryId = parseInt(document.getElementById('expenseCategory').value); const subcategorySelect = document.getElementById('expenseSubcategory'); if (!categoryId) { subcategorySelect.innerHTML = ''; return; } const category = AppState.expenseCategories.find(c => c.id === categoryId); if (!category) return; subcategorySelect.innerHTML = '' + category.subcategories.map(sub => ``).join(''); }; // Calculate Expense Invoice Totals window.calculateExpenseInvoiceTotals = function() { const amount = parseFloat(document.getElementById('expenseAmount').value) || 0; const vatRate = parseFloat(document.getElementById('expenseVatRate').value) || 0; const currency = document.getElementById('expenseCurrency').value; const vatAmount = amount * (vatRate / 100); const total = amount + vatAmount; document.getElementById('purInvSubtotal').textContent = amount.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; document.getElementById('purInvVatAmount').textContent = vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; document.getElementById('purInvGrandTotal').textContent = total.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; }; // Load Receipt Data window.loadReceiptData = function() { const receiptSelect = document.getElementById('purInvReceipt'); const selectedOption = receiptSelect.selectedOptions[0]; if (!receiptSelect.value) { document.getElementById('receiptInfoBox').classList.add('hidden'); return; } // İrsaliye bilgilerini göster const infoBox = document.getElementById('receiptInfoBox'); infoBox.classList.remove('hidden'); document.getElementById('receiptWaybill').textContent = selectedOption.dataset.waybill || '-'; document.getElementById('receiptMaterial').textContent = selectedOption.dataset.materialName || '-'; document.getElementById('receiptQuantity').textContent = selectedOption.dataset.quantity + ' ' + (selectedOption.dataset.type === 'Hammadde' ? 'KG' : 'Adet'); document.getElementById('receiptType').textContent = selectedOption.dataset.type || '-'; // Tedarikçiyi otomatik seç document.getElementById('purInvSupplier').value = selectedOption.dataset.supplierId; // Fiyat hesaplamasını tetikle calculatePurchaseInvoiceTotals(); }; // Calculate Purchase Invoice Totals window.calculatePurchaseInvoiceTotals = function() { const receiptSelect = document.getElementById('purInvReceipt'); const selectedOption = receiptSelect.selectedOptions[0]; if (!receiptSelect.value) return; const quantity = parseFloat(selectedOption.dataset.quantity) || 0; const unitPrice = parseFloat(document.getElementById('purInvUnitPrice').value) || 0; const vatRate = parseFloat(document.getElementById('purInvVatRate').value) || 0; const currency = document.getElementById('purInvCurrency').value; const subtotal = quantity * unitPrice; const vatAmount = subtotal * (vatRate / 100); const grandTotal = subtotal + vatAmount; document.getElementById('purInvSubtotal').textContent = subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; document.getElementById('purInvVatAmount').textContent = vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; document.getElementById('purInvGrandTotal').textContent = grandTotal.toLocaleString('tr-TR', {minimumFractionDigits: 2}) + ' ' + currency; }; // Submit Purchase Invoice window.submitPurchaseInvoice = function(event) { event.preventDefault(); const invoiceType = document.querySelector('input[name="purchaseInvoiceType"]:checked').value; if (invoiceType === 'receipt') { submitPurchaseInvoiceFromReceipt(); } else if (invoiceType === 'expense') { submitExpenseInvoice(); } else { alert('Normal alış faturası yakında eklenecek!'); } }; // Submit Purchase Invoice From Receipt function submitPurchaseInvoiceFromReceipt() { const receiptValue = document.getElementById('purInvReceipt').value; if (!receiptValue) { alert('❌ Lütfen irsaliye seçin!'); return; } const [receiptType, receiptId] = receiptValue.split('-'); const receiptIdNum = parseInt(receiptId); let receipt; if (receiptType === 'Hammadde') { receipt = AppState.rawMaterialStock.find(s => s.id === receiptIdNum); } else { receipt = AppState.consumableStock.find(s => s.id === receiptIdNum); } if (!receipt || receipt.invoiceStatus !== 'Fatura Bekleniyor') { alert('⚠️ Geçersiz irsaliye veya zaten faturalanmış!'); return; } // Form verilerini al const invoiceNumber = document.getElementById('purInvNumber').value; const invoiceDate = document.getElementById('purInvDate').value; const unitPrice = parseFloat(document.getElementById('purInvUnitPrice').value); const currency = document.getElementById('purInvCurrency').value; const exchangeRate = parseFloat(document.getElementById('purInvExchangeRate').value) || 1; const vatRate = parseFloat(document.getElementById('purInvVatRate').value) || 0; const notes = document.getElementById('purInvNotes').value; if (unitPrice <= 0) { alert('❌ Geçerli bir birim fiyat girin!'); return; } // Hesaplamalar const quantity = receipt.quantity; const subtotal = quantity * unitPrice; const vatAmount = subtotal * (vatRate / 100); const total = subtotal + vatAmount; const totalTRY = total * exchangeRate; // Onay iste const confirmMsg = `💰 ALIŞ FATURASI KAYDEDİLECEK\n\n` + `İrsaliye: ${receipt.waybillNo}\n` + `Tedarikçi: ${receipt.supplierName}\n` + `Ürün: ${receipt.materialName || receipt.consumableName}\n` + `Miktar: ${quantity.toLocaleString('tr-TR')} ${receiptType === 'Hammadde' ? 'KG' : 'Adet'}\n\n` + `FATURA BİLGİLERİ:\n` + `Fatura No: ${invoiceNumber}\n` + `Birim Fiyat: ${unitPrice.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Ara Toplam: ${subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `⚠️ Bu işlem:\n` + `✓ Alış faturası oluşturacak\n` + `✓ Tedarikçi carisine borç yazacak\n` + `✓ İrsaliyeyi kapatacak\n\n` + `Onaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // Fatura oluştur if (!AppState.invoices) AppState.invoices = []; const newInvoice = { id: AppState.invoices.length + 1, type: 'Alış', invoiceNumber: invoiceNumber, invoiceDate: invoiceDate, supplierId: receipt.supplierId, supplierName: receipt.supplierName, receiptId: receipt.id, receiptType: receiptType, waybillNumber: receipt.waybillNo, items: [{ productName: receipt.materialName || receipt.consumableName, quantity: quantity, unit: receiptType === 'Hammadde' ? 'KG' : 'Adet', unitPrice: unitPrice, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: total }], subtotal: subtotal, vatTotal: vatAmount, grandTotal: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, paymentTerms: document.getElementById('purInvPaymentTerms')?.value || '30 Gün Vadeli', dueDate: document.getElementById('purInvDueDate')?.value || null, status: 'Ödenmedi', notes: notes || `${receiptType} Girişi: ${receipt.materialName || receipt.consumableName} - İrsaliye: ${receipt.waybillNo}`, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(newInvoice); // İrsaliyeyi güncelle receipt.invoiceId = newInvoice.id; receipt.invoiceNumber = invoiceNumber; receipt.invoiceStatus = 'Faturalandı'; receipt.unitCost = unitPrice; receipt.totalCost = totalTRY; receipt.currency = currency; receipt.exchangeRate = exchangeRate; receipt.vatRate = vatRate; receipt.vatAmount = vatAmount * exchangeRate; // CARİ HAREKET EKLE - Tedarikçi borçlandı if (!AppState.cariMovements) AppState.cariMovements = []; AppState.cariMovements.push({ id: AppState.cariMovements.length + 1, date: invoiceDate, cariType: 'supplier', cariId: receipt.supplierId, cariName: receipt.supplierName, type: 'Alış Faturası', amount: totalTRY, currency: 'TRY', description: `Fatura No: ${invoiceNumber} - İrsaliye: ${receipt.waybillNo}`, referenceType: 'invoice', referenceId: newInvoice.id }); // Eski sistem için - geriye dönük uyumluluk if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: receipt.supplierId, supplierName: receipt.supplierName, invoiceId: newInvoice.id, invoiceNumber: invoiceNumber, amount: total, currency: currency, date: invoiceDate, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('invoice', 'finance', `Alış faturası (${receiptType}): ${invoiceNumber} - ${receipt.supplierName} - ${totalTRY.toLocaleString('tr-TR')} ₺`, newInvoice.id, 'invoice'); closeModal('purchaseInvoiceModal'); render(); alert(`✅ ALIŞ FATURASI KAYDEDİLDİ!\n\n` + `Fatura No: ${invoiceNumber}\n` + `İrsaliye: ${receipt.waybillNo}\n` + `Tedarikçi: ${receipt.supplierName}\n\n` + `Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Fatura kaydedildi\n` + `✓ Tedarikçi carisine borç yazıldı\n` + `✓ İrsaliye kapandı`); } // Submit Expense Invoice function submitExpenseInvoice() { const categoryId = parseInt(document.getElementById('expenseCategory').value); const subcategory = document.getElementById('expenseSubcategory').value; if (!categoryId || !subcategory) { alert('❌ Lütfen gider kategorisi ve alt kategori seçin!'); return; } const category = AppState.expenseCategories.find(c => c.id === categoryId); const supplierId = parseInt(document.getElementById('purInvSupplier').value); const supplier = AppState.suppliers.find(s => s.id === supplierId); const invoiceNumber = document.getElementById('purInvNumber').value; const invoiceDate = document.getElementById('purInvDate').value; const amount = parseFloat(document.getElementById('expenseAmount').value); const currency = document.getElementById('expenseCurrency').value; const exchangeRate = parseFloat(document.getElementById('expenseExchangeRate').value) || 1; const vatRate = parseFloat(document.getElementById('expenseVatRate').value) || 0; const notes = document.getElementById('purInvNotes') ? document.getElementById('purInvNotes').value : ''; if (!amount || amount <= 0) { alert('❌ Geçerli bir tutar girin!'); return; } // Hesaplamalar const vatAmount = amount * (vatRate / 100); const total = amount + vatAmount; const totalTRY = total * exchangeRate; // Onay iste const confirmMsg = `💰 GİDER FATURASI KAYDEDİLECEK\n\n` + `Kategori: ${category.name}\n` + `Alt Kategori: ${subcategory}\n` + `Tedarikçi/Firma: ${supplier ? supplier.name : 'Belirtilmedi'}\n\n` + `Fatura No: ${invoiceNumber}\n` + `Tutar (KDV Hariç): ${amount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV (%${vatRate}): ${vatAmount.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `⚠️ Bu işlem:\n` + `✓ Gider faturası oluşturacak\n` + `✓ P&L'e gider olarak işlenecek\n` + (supplier ? `✓ Tedarikçi carisine borç yazılacak\n` : '') + `\nOnaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // Gider faturası oluştur if (!AppState.expenseInvoices) AppState.expenseInvoices = []; const expenseInvoice = { id: AppState.expenseInvoices.length + 1, type: 'Gider', categoryId: categoryId, categoryName: category.name, subcategory: subcategory, invoiceNumber: invoiceNumber, invoiceDate: invoiceDate, supplierId: supplierId || null, supplierName: supplier ? supplier.name : '', amount: amount, vatRate: vatRate, vatAmount: vatAmount, total: total, currency: currency, exchangeRate: exchangeRate, totalTRY: totalTRY, paymentTerms: document.getElementById('purInvPaymentTerms')?.value || '30 Gün Vadeli', dueDate: document.getElementById('purInvDueDate')?.value || null, status: 'Ödenmedi', notes: notes, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.expenseInvoices.push(expenseInvoice); // CARİ HAREKET EKLE - Tedarikçi varsa if (supplier) { if (!AppState.cariMovements) AppState.cariMovements = []; AppState.cariMovements.push({ id: AppState.cariMovements.length + 1, date: invoiceDate, cariType: 'supplier', cariId: supplierId, cariName: supplier.name, type: 'Gider Faturası', amount: totalTRY, currency: 'TRY', description: `Fatura No: ${invoiceNumber} - ${category.name}: ${subcategory}`, referenceType: 'expense_invoice', referenceId: expenseInvoice.id }); // Eski sistem için - geriye dönük uyumluluk if (!AppState.accountsPayable) AppState.accountsPayable = []; AppState.accountsPayable.push({ id: AppState.accountsPayable.length + 1, type: 'Borç', supplierId: supplierId, supplierName: supplier.name, invoiceId: expenseInvoice.id, invoiceNumber: invoiceNumber, invoiceType: 'Gider', amount: total, currency: currency, date: invoiceDate, status: 'Açık', category: subcategory, createdAt: new Date().toISOString() }); } // Audit log addAuditLog('expense', 'finance', `Gider Faturası: ${invoiceNumber} - ${subcategory} - ${totalTRY.toLocaleString('tr-TR')} ₺`, expenseInvoice.id, 'expense'); closeModal('purchaseInvoiceModal'); render(); alert(`✅ GİDER FATURASI KAYDEDİLDİ!\n\n` + `Fatura No: ${invoiceNumber}\n` + `Kategori: ${subcategory}\n\n` + `Toplam: ${total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL: ${totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Gider faturası kaydedildi\n` + (supplier ? `✓ Tedarikçi carisine borç yazıldı\n` : '') + `✓ P&L'e işlendi`); } // Load Receipt Data window.toggleInvoiceType = function(type) { const shipmentSection = document.getElementById('shipmentSection'); const invCustomer = document.getElementById('invCustomer'); const addItemBtn = document.getElementById('addItemBtn'); const invItems = document.getElementById('invItems'); const shipmentInfoBox = document.getElementById('shipmentInfoBox'); const noStockWarning = document.getElementById('noStockWarning'); if (type === 'shipment') { // Sevkiyat moduna geç shipmentSection.classList.remove('hidden'); invCustomer.disabled = true; invCustomer.classList.add('bg-gray-100'); addItemBtn.classList.add('hidden'); if (noStockWarning) noStockWarning.classList.add('hidden'); // Ürün listesini temizle invItems.innerHTML = ''; } else { // Normal moda geç shipmentSection.classList.add('hidden'); invCustomer.disabled = false; invCustomer.classList.remove('bg-gray-100'); invCustomer.value = ''; addItemBtn.classList.remove('hidden'); if (noStockWarning) noStockWarning.classList.remove('hidden'); shipmentInfoBox.classList.add('hidden'); // Ürün listesini temizle ve ilk satırı ekle invItems.innerHTML = ''; const availableStock = AppState.finishedProductStock ? AppState.finishedProductStock.filter(s => s.available && s.qualityStatus === 'Onaylandı') : []; if (availableStock.length > 0) { addInvoiceItem(); } } }; // Load Shipment Data window.loadShipmentData = function() { const shipmentId = parseInt(document.getElementById('invShipment').value); if (!shipmentId) return; const shipment = AppState.shipments.find(s => s.id === shipmentId); if (!shipment) return; // Müşteriyi otomatik seç document.getElementById('invCustomer').value = shipment.customerId; // Sevkiyat bilgilerini göster const shipmentInfoBox = document.getElementById('shipmentInfoBox'); shipmentInfoBox.classList.remove('hidden'); shipmentInfoBox.innerHTML = `
Sevkiyat No: ${shipment.shipmentNumber}
İrsaliye No: ${shipment.waybillNumber}
Sevk Tarihi: ${new Date(shipment.shipmentDate).toLocaleDateString('tr-TR')}
Toplam: ${shipment.totalQuantity.toLocaleString('tr-TR')} KG (${shipment.totalBags} torba)
`; // Ürünleri ekle const invItems = document.getElementById('invItems'); invItems.innerHTML = ''; shipment.items.forEach((item, idx) => { const itemHtml = `
${item.productName}
Lot: ${item.lotNumber}
${item.quantity.toLocaleString('tr-TR')} KG
${item.bags} torba
Toplam
0.00
`; invItems.insertAdjacentHTML('beforeend', itemHtml); }); }; // Submit Invoice From Shipment (Finance Module) window.submitInvoiceFromShipmentFinance = function(event) { const shipmentId = parseInt(document.getElementById('invShipment').value); if (!shipmentId) { alert('❌ Lütfen sevkiyat seçin!'); return; } const shipment = AppState.shipments.find(s => s.id === shipmentId); if (!shipment || shipment.invoiceId) { alert('⚠️ Geçersiz sevkiyat veya zaten faturalanmış!'); return; } // Form verilerini al const invoiceNumber = document.getElementById('invNumber').value; const invoiceDate = document.getElementById('invDate').value; const currency = document.getElementById('invCurrency').value; const exchangeRate = parseFloat(document.getElementById('invExchangeRate').value) || 1; const paymentTerms = document.getElementById('invPaymentTerms').value; const incoterms = document.getElementById('invIncoterms').value; const notes = document.getElementById('invNotes').value; // Ürünlerden fiyat ve KDV bilgilerini topla const invoiceItems = []; let subtotal = 0; let vatTotal = 0; try { shipment.items.forEach((item, idx) => { const priceInput = document.getElementById(`invItemPrice_${idx}`); const vatInput = document.getElementById(`invItemVat_${idx}`); if (!priceInput || !vatInput) { alert(`❌ ${item.productName} için fiyat bilgileri eksik!`); throw new Error('Price info missing'); } const unitPrice = parseFloat(priceInput.value) || 0; const vatRate = parseFloat(vatInput.value) || 0; if (unitPrice <= 0) { alert(`❌ ${item.productName} için geçerli bir fiyat girin!`); throw new Error('Invalid price'); } const amount = item.quantity * unitPrice; const vat = amount * (vatRate / 100); invoiceItems.push({ productName: item.productName, lotNumber: item.lotNumber, quantity: item.quantity, unitPrice: unitPrice, vatRate: vatRate, subtotal: amount, vatAmount: vat, total: amount + vat }); subtotal += amount; vatTotal += vat; }); } catch (error) { return; } const grandTotal = subtotal + vatTotal; // Onay iste const confirmMsg = `💰 FATURA KESİLECEK\n\n` + `Sevkiyat: ${shipment.shipmentNumber}\n` + `İrsaliye: ${shipment.waybillNumber}\n` + `Müşteri: ${shipment.customerName}\n\n` + `Ara Toplam: ${subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `KDV: ${vatTotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TOPLAM: ${grandTotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n\n` + `⚠️ Bu işlem:\n` + `✓ Fatura oluşturacak\n` + `✓ Stokları düşecek\n` + `✓ Müşteri carisine alacak yazacak\n\n` + `Onaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // Fatura oluştur const newInvoice = { id: AppState.invoices.length + 1, invoiceNumber: invoiceNumber, type: 'Satış', customerId: shipment.customerId, customerName: shipment.customerName, shipmentId: shipment.id, shipmentNumber: shipment.shipmentNumber, waybillNumber: shipment.waybillNumber, items: invoiceItems, subtotal: subtotal, vatTotal: vatTotal, grandTotal: grandTotal, currency: currency, exchangeRate: exchangeRate, totalTRY: grandTotal * exchangeRate, paymentTerms: paymentTerms, incoterms: incoterms, invoiceDate: invoiceDate, notes: notes, status: 'Ödenmedi', createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(newInvoice); // Sevkiyatı güncelle shipment.invoiceId = newInvoice.id; shipment.invoiceNumber = invoiceNumber; shipment.status = 'Faturalandı'; // STOKLAR DÜŞÜR! shipment.items.forEach(item => { const stock = AppState.finishedProductStock.find(s => s.id === item.stockId); if (stock) { stock.quantity -= item.quantity; stock.bags -= item.bags; if (stock.quantity <= 0) { stock.available = false; } } }); // Müşteri carisine alacak yaz if (!AppState.accountsReceivable) { AppState.accountsReceivable = []; } AppState.accountsReceivable.push({ id: AppState.accountsReceivable.length + 1, type: 'Alacak', customerId: shipment.customerId, customerName: shipment.customerName, invoiceId: newInvoice.id, invoiceNumber: invoiceNumber, amount: grandTotal, currency: currency, date: invoiceDate, status: 'Açık', createdAt: new Date().toISOString() }); // Audit log addAuditLog('invoice', 'finance', `Sevkiyattan fatura kesildi: ${invoiceNumber} - ${shipment.customerName} (${grandTotal.toLocaleString('tr-TR')} ${currency}) - Stoklar düşüldü`, newInvoice.id, 'invoice'); closeModal('salesInvoiceModal'); render(); alert(`✅ FATURA KESİLDİ!\n\n` + `Fatura No: ${invoiceNumber}\n` + `Sevkiyat: ${shipment.shipmentNumber}\n` + `İrsaliye: ${shipment.waybillNumber}\n\n` + `Toplam: ${grandTotal.toLocaleString('tr-TR')} ${currency}\n\n` + `✓ Stoklar düşüldü\n` + `✓ Müşteri carisine alacak yazıldı`); }; let invoiceItemCounter = 0; // Add Invoice Item window.addInvoiceItem = function() { invoiceItemCounter++; const itemsContainer = document.getElementById('invItems'); const availableStock = AppState.finishedProductStock ? AppState.finishedProductStock.filter(s => s.available && s.qualityStatus === 'Onaylandı') : []; if (availableStock.length === 0) { alert('⚠️ Stokta kalite onaylı ürün bulunmamaktadır!'); return; } const itemHtml = `
`; itemsContainer.insertAdjacentHTML('beforeend', itemHtml); }; // Update Stock Info window.updateStockInfo = function(itemId) { const select = document.getElementById('invProduct' + itemId); const option = select.options[select.selectedIndex]; const available = option.getAttribute('data-available'); const stockInfo = document.getElementById('stockInfo' + itemId); if (available) { stockInfo.innerHTML = `📦 Mevcut Stok: ${parseFloat(available).toLocaleString('tr-TR')} KG`; } }; // Remove Invoice Item window.removeInvoiceItem = function(itemId) { const item = document.getElementById('invItem' + itemId); if (item) item.remove(); }; // Fetch Exchange Rate from turkiye.gov.tr window.fetchExchangeRate = function() { const currency = document.getElementById('invCurrency').value; const rateInput = document.getElementById('invExchangeRate'); const rateInfo = document.getElementById('rateInfo'); if (currency === 'TRY') { rateInput.value = '1.0000'; rateInfo.innerHTML = 'TRY seçili - Kur: 1.0000'; return; } rateInfo.innerHTML = '⏳ Kur çekiliyor...'; // Try turkiye.gov.tr first fetch('https://www.turkiye.gov.tr/doviz-kurlari') .then(response => { if (!response.ok) throw new Error('Turkiye.gov.tr erişilemedi'); return response.text(); }) .then(html => { // HTML'den kur bilgisini parse et const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // Bu kısım sitenin HTML yapısına göre ayarlanmalı // Örnek: Tablodaki döviz satış değerini bul let rate = null; // Genel bir arama yapalım const rows = doc.querySelectorAll('tr'); for (let row of rows) { const text = row.textContent; if (text.includes(currency)) { // Satış kuru kolonunu bul const cells = row.querySelectorAll('td'); if (cells.length >= 3) { // Genellikle: Döviz | Alış | Satış formatında const sellValue = cells[2].textContent.trim().replace(',', '.'); rate = parseFloat(sellValue); break; } } } if (rate && !isNaN(rate)) { rateInput.value = rate.toFixed(4); rateInfo.innerHTML = `✅ ${currency} Satış: ${rate.toFixed(4)} ₺ (turkiye.gov.tr)`; } else { throw new Error('Kur bulunamadı'); } }) .catch(error => { console.error('turkiye.gov.tr hatası:', error); console.log('TCMB XML kullanılıyor...'); // Fallback: TCMB XML const today = new Date(); const dateStr = String(today.getDate()).padStart(2, '0') + String(today.getMonth() + 1).padStart(2, '0') + today.getFullYear(); const url = `https://www.tcmb.gov.tr/kurlar/${today.getFullYear()}${String(today.getMonth() + 1).padStart(2, '0')}/${dateStr}.xml`; return fetch(url) .then(response => response.text()) .then(str => new window.DOMParser().parseFromString(str, "text/xml")) .then(data => { let rate = null; const currencies = data.getElementsByTagName('Currency'); for (let i = 0; i < currencies.length; i++) { const currencyCode = currencies[i].getAttribute('CurrencyCode'); if (currencyCode === currency) { const forexSelling = currencies[i].getElementsByTagName('ForexSelling')[0]; if (forexSelling) { rate = parseFloat(forexSelling.textContent); break; } } } if (rate) { rateInput.value = rate.toFixed(4); rateInfo.innerHTML = `✅ TCMB ${currency} Satış: ${rate.toFixed(4)} ₺ (${today.toLocaleDateString('tr-TR')})`; } else { throw new Error('Kur bulunamadı'); } }); }) .catch(error => { console.error('Kur çekme hatası:', error); rateInfo.innerHTML = '❌ Kur çekilemedi. Manuel girin.'; alert('⚠️ Kur çekilemedi.\n\nLütfen kuru manuel olarak girin.'); }); }; // Submit Sales Invoice window.submitSalesInvoice = function(event) { event.preventDefault(); console.log('submitSalesInvoice called'); // Check invoice type const invoiceType = document.querySelector('input[name="invoiceType"]:checked')?.value || 'normal'; if (invoiceType === 'shipment') { // Sevkiyattan fatura kes submitInvoiceFromShipmentFinance(event); return; } // NORMAL FATURA - Mevcut kod devam ediyor // Initialize invoices array if not exists if (!AppState.invoices) { AppState.invoices = []; } // Form verilerini al const customerId = parseInt(document.getElementById('invCustomer').value); const customerOption = document.getElementById('invCustomer').selectedOptions[0]; const customerName = customerOption.getAttribute('data-name'); const invoiceNumber = document.getElementById('invNumber').value; const date = document.getElementById('invDate').value; const currency = document.getElementById('invCurrency').value; const exchangeRate = parseFloat(document.getElementById('invExchangeRate').value) || 1; const paymentTerms = document.getElementById('invPaymentTerms').value; const incoterms = document.getElementById('invIncoterms').value; const notes = document.getElementById('invNotes').value; // Ürünleri topla const items = []; const productSelects = document.querySelectorAll('.inv-product'); if (productSelects.length === 0) { alert('❌ En az bir ürün eklemelisiniz!'); return; } for (let i = 0; i < productSelects.length; i++) { const select = productSelects[i]; const stockId = parseInt(select.value); if (!stockId) continue; const option = select.selectedOptions[0]; const productName = option.getAttribute('data-product'); const lotNumber = option.getAttribute('data-lot'); const availableQty = parseFloat(option.getAttribute('data-available')); const qtyInput = select.closest('.p-3').querySelector('.inv-quantity'); const priceInput = select.closest('.p-3').querySelector('.inv-price'); const vatInput = select.closest('.p-3').querySelector('.inv-vat'); const unitSelect = select.closest('.p-3').querySelector('select[id^="invUnit"]'); const quantity = parseFloat(qtyInput.value); const unitPrice = parseFloat(priceInput.value); const vatRate = parseFloat(vatInput.value) || 0; const unit = unitSelect.value; // Stok kontrolü if (quantity > availableQty) { alert(`❌ ${productName} için yetersiz stok!\n\nTalep: ${quantity} KG\nMevcut: ${availableQty} KG`); return; } const subtotal = quantity * unitPrice; const vatAmount = subtotal * (vatRate / 100); const itemTotal = subtotal + vatAmount; items.push({ stockId: stockId, productName: productName, lotNumber: lotNumber, quantity: quantity, unit: unit, unitPrice: unitPrice, subtotal: subtotal, vatRate: vatRate, vatAmount: vatAmount, total: itemTotal }); } if (items.length === 0) { alert('❌ Lütfen ürün seçiniz!'); return; } // Toplam hesapla const totalSubtotal = items.reduce((sum, item) => sum + item.subtotal, 0); const totalVat = items.reduce((sum, item) => sum + item.vatAmount, 0); const grandTotal = items.reduce((sum, item) => sum + item.total, 0); const grandTotalTRY = grandTotal * exchangeRate; // Onay iste const confirmMsg = `📄 SATIŞ FATURASI KESİLECEK\n\n` + `Müşteri: ${customerName}\n` + `Fatura No: ${invoiceNumber}\n` + `Ürün Sayısı: ${items.length}\n\n` + `Ara Toplam: ${totalSubtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Toplam KDV: ${totalVat.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${grandTotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `TL Karşılığı: ${grandTotalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ Stoktan düşülecek\n` + `✓ Müşteri carisine borç yazılacak\n\n` + `Onaylıyor musunuz?`; if (!confirm(confirmMsg)) return; // STOKTAN DÜŞ for (let i = 0; i < items.length; i++) { const item = items[i]; const stock = AppState.finishedProductStock.find(s => s.id === item.stockId); if (stock) { stock.quantity -= item.quantity; if (stock.quantity <= 0) { stock.available = false; stock.quantity = 0; } } } // FATURA OLUŞTUR const newInvoice = { id: (AppState.invoices.length || 0) + 1, type: 'Satış', invoiceNumber: invoiceNumber, date: date, customerId: customerId, customerName: customerName, items: items, subtotal: totalSubtotal, vatAmount: totalVat, grandTotal: grandTotal, currency: currency, exchangeRate: exchangeRate, totalTRY: grandTotalTRY, paymentTerms: paymentTerms, incoterms: incoterms, notes: notes, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; AppState.invoices.push(newInvoice); // CARİ HAREKET EKLE - Müşteri borçlandı if (!AppState.cariMovements) AppState.cariMovements = []; AppState.cariMovements.push({ id: AppState.cariMovements.length + 1, date: date, cariType: 'customer', cariId: customerId, cariName: customerName, type: 'Satış Faturası', amount: grandTotalTRY, currency: 'TRY', description: `Fatura No: ${invoiceNumber}`, referenceType: 'invoice', referenceId: newInvoice.id }); // Audit log addAuditLog('invoice', 'finance', `Satış faturası kesildi: ${invoiceNumber} - ${customerName} - ${grandTotalTRY.toLocaleString('tr-TR')} ₺`, newInvoice.id, 'invoice'); closeModal('salesInvoiceModal'); render(); alert(`✅ FATURA KESİLDİ!\n\n` + `Fatura No: ${invoiceNumber}\n` + `Müşteri: ${customerName}\n` + `Ara Toplam: ${totalSubtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Toplam KDV: ${totalVat.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${currency}\n` + `Genel Toplam: ${grandTotalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺\n\n` + `✓ ${items.length} ürün stoktan düşüldü\n` + `✓ Cari hesaba kaydedildi`); }; // View Invoice Details window.viewInvoiceDetails = function(invoiceId) { const invoice = AppState.invoices.find(inv => inv.id === invoiceId); if (!invoice) return; const content = `

📄 Fatura Detayı

Fatura No
${invoice.invoiceNumber}
Tarih
${new Date(invoice.date).toLocaleDateString('tr-TR')}
${invoice.type === 'Satış' ? 'Müşteri' : 'Tedarikçi'}
${invoice.customerName || invoice.supplierName}

Ürünler

${invoice.items.map(item => ` `).join('')}
Ürün Lot Miktar Birim Fiyat Toplam
${item.productName} ${item.lotNumber} ${item.quantity.toLocaleString('tr-TR')} ${item.unit} ${item.unitPrice.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${invoice.currency} ${item.total.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${invoice.currency}
Ara Toplam
${invoice.subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${invoice.currency}
KDV (%${invoice.vatRate || 0})
${(invoice.vatAmount || 0).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${invoice.currency}
Genel Toplam
${(invoice.grandTotal || invoice.subtotal).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${invoice.currency}
${invoice.currency !== 'TRY' ? `
TL Karşılığı (Kur: ${invoice.exchangeRate})
${invoice.totalTRY.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ₺
` : ''}
Ödeme Şekli
${invoice.paymentTerms}
Sevkiyat
${invoice.incoterms}
${invoice.notes ? `
Notlar
${invoice.notes}
` : ''}
Oluşturan: ${invoice.createdBy} - ${new Date(invoice.createdAt).toLocaleString('tr-TR')}
`; document.getElementById('invoiceDetailModalContent').innerHTML = content; openModal('invoiceDetailModal'); }; // Update Quotation Status window.updateQuotationStatus = function(quotId) { const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) return; const content = `

📝 Teklif Durumu Güncelle

${quot.number} - ${quot.customerName}
${quot.subtotal.toFixed(2)} ${quot.currency}
Mevcut Durum: ${quot.status || 'Beklemede'}
`; document.getElementById('quotationStatusModalContent').innerHTML = content; openModal('quotationStatusModal'); }; // Submit Quotation Status window.submitQuotationStatus = function(event, quotId) { event.preventDefault(); const quot = AppState.quotations.find(q => q.id === quotId); const newStatus = document.getElementById('quotStatus').value; const oldStatus = quot.status || 'Beklemede'; quot.status = newStatus; quot.statusUpdatedBy = AppState.currentUser.name; quot.statusUpdatedAt = new Date().toISOString(); if (newStatus === 'Onaylanmadı') { const reason = document.getElementById('quotRejectionReason').value; const notes = document.getElementById('quotRejectionNotes').value; quot.rejectionReason = reason; quot.rejectionNotes = notes; if (!reason) { alert('❌ Red gerekçesi seçmelisiniz!'); return; } } else { delete quot.rejectionReason; delete quot.rejectionNotes; } // Add audit log addAuditLog('updated', 'sales', `Teklif durumu güncellendi: ${quot.number} (${oldStatus} → ${newStatus})`, quot.id, 'quotation'); closeModal('quotationStatusModal'); render(); if (newStatus === 'Onaylandı') { alert(`✅ Teklif Onaylandı!\n\n${quot.number} - ${quot.customerName}\n\nMüşteri onaylı sipariş olarak kaydedilmiştir.`); } else if (newStatus === 'Onaylanmadı') { alert(`❌ Teklif Onaylanmadı\n\nGerekçe: ${quot.rejectionReason}${quot.rejectionNotes ? '\n\nNotlar: ' + quot.rejectionNotes : ''}`); } }; // ==================== SATIŞ SEVKİYAT YÖNETİMİ ==================== // Open Sales Shipment Modal window.openSalesShipmentModal = function(quotId) { const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) { alert('❌ Sipariş bulunamadı!'); return; } // Güvenlik kontrolleri if (!quot.shipments) quot.shipments = []; if (!quot.items || !Array.isArray(quot.items)) quot.items = []; const totalQuantity = quot.items.reduce((sum, item) => sum + (item.quantity || 0), 0); const shippedQuantity = quot.shipments.reduce((sum, ship) => sum + (ship.quantity || 0), 0); const remainingQuantity = totalQuantity - shippedQuantity; if (remainingQuantity <= 0) { alert('✅ Bu sipariş tamamen sevk edilmiştir!'); return; } const content = `

🚚 Yeni Sevkiyat

${quot.number} - ${quot.customerName}
Toplam Sipariş
${totalQuantity.toLocaleString('tr-TR')} KG
Sevk Edildi
${shippedQuantity.toLocaleString('tr-TR')} KG
Kalan
${remainingQuantity.toLocaleString('tr-TR')} KG
Max: ${remainingQuantity.toLocaleString('tr-TR')} KG
`; document.getElementById('salesShipmentModalContent').innerHTML = content; openModal('salesShipmentModal'); }; // Submit Sales Shipment window.submitSalesShipment = function(event, quotId) { event.preventDefault(); const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) return; // Güvenlik kontrolleri if (!quot.shipments) quot.shipments = []; if (!quot.items || !Array.isArray(quot.items)) quot.items = []; const quantity = parseFloat(document.getElementById('shipQuantity').value); const date = document.getElementById('shipDate').value; const container = document.getElementById('shipContainer').value; const waybill = document.getElementById('shipWaybill').value; const notes = document.getElementById('shipNotes').value; const totalQuantity = quot.items.reduce((sum, item) => sum + (item.quantity || 0), 0); const shippedQuantity = quot.shipments.reduce((sum, ship) => sum + (ship.quantity || 0), 0); const remainingQuantity = totalQuantity - shippedQuantity; if (quantity > remainingQuantity) { alert(`❌ Sevk miktarı kalan miktardan fazla olamaz!\n\nKalan: ${remainingQuantity.toLocaleString('tr-TR')} KG`); return; } if (!confirm(`🚚 SEVKİYAT ONAY\n\nSipariş: ${quot.number}\nMüşteri: ${quot.customerName}\n\nSevk Miktarı: ${quantity.toLocaleString('tr-TR')} KG\nKonteyner: ${container || '-'}\nİrsaliye: ${waybill || '-'}\n\nOnaylıyor musunuz?`)) { return; } // Yeni sevkiyat kaydı const newShipment = { id: quot.shipments.length + 1, date: date, quantity: quantity, container: container, waybill: waybill, notes: notes, createdBy: AppState.currentUser.name, createdAt: new Date().toISOString() }; quot.shipments.push(newShipment); const newShippedQuantity = shippedQuantity + quantity; const newRemainingQuantity = totalQuantity - newShippedQuantity; const shipmentProgress = (newShippedQuantity / totalQuantity) * 100; // Audit log addAuditLog('shipment', 'sales', `Sevkiyat yapıldı: ${quot.number} - ${quantity.toLocaleString('tr-TR')} KG (${shipmentProgress.toFixed(0)}%)`, quot.id, 'quotation'); closeModal('shipmentModal'); render(); if (shipmentProgress === 100) { alert(`✅ SEVKİYAT TAMAMLANDI!\n\nSipariş: ${quot.number}\nMüşteri: ${quot.customerName}\n\nToplam ${quot.shipments.length} sevkiyatta ${totalQuantity.toLocaleString('tr-TR')} KG sevk edilmiştir.\n\n🎉 Sipariş %100 tamamlandı!`); } else { alert(`✅ Sevkiyat Kaydedildi!\n\nSevk Edilen: ${quantity.toLocaleString('tr-TR')} KG\nToplam Sevk: ${newShippedQuantity.toLocaleString('tr-TR')} / ${totalQuantity.toLocaleString('tr-TR')} KG\nKalan: ${newRemainingQuantity.toLocaleString('tr-TR')} KG\n\nİlerleme: ${shipmentProgress.toFixed(0)}%`); } }; // View Sales Shipments window.viewSalesShipments = function(quotId) { const quot = AppState.quotations.find(q => q.id === quotId); if (!quot) { alert('❌ Sipariş bulunamadı!'); return; } // Güvenlik kontrolleri if (!quot.shipments) quot.shipments = []; if (!quot.items || !Array.isArray(quot.items)) quot.items = []; const totalQuantity = quot.items.reduce((sum, item) => sum + (item.quantity || 0), 0); const shippedQuantity = quot.shipments.reduce((sum, ship) => sum + (ship.quantity || 0), 0); const shipmentProgress = totalQuantity > 0 ? (shippedQuantity / totalQuantity) * 100 : 0; const remainingQuantity = totalQuantity - shippedQuantity; const content = `

📦 Sipariş Detayları

${quot.number} - ${quot.customerName}
Sipariş Tarihi
${new Date(quot.date).toLocaleDateString('tr-TR')}
Tutar
${quot.subtotal.toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${quot.currency}
Durum
${shipmentProgress === 100 ? '✅ Tamamlandı' : shipmentProgress > 0 ? '🚚 Sevkiyat Devam Ediyor' : '⏳ Sevkiyat Bekliyor'}
Sevkiyat İlerlemesi
${shipmentProgress.toFixed(0)}%
Toplam Sipariş
${totalQuantity.toLocaleString('tr-TR')} KG
Sevk Edildi
${shippedQuantity.toLocaleString('tr-TR')} KG
Kalan
${remainingQuantity.toLocaleString('tr-TR')} KG
📋 Sipariş Kalemleri
${quot.items.map(item => ` `).join('')}
Ürün Miktar Birim Fiyat Toplam
${item.productName||'—'} ${(item.quantity||0).toLocaleString('tr-TR')} KG ${(item.unitPrice||0).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${quot.currency} ${(item.subtotal||item.total||0).toLocaleString('tr-TR', {minimumFractionDigits: 2})} ${quot.currency}
🚚 Sevkiyat Geçmişi (${quot.shipments.length})
${quot.shipments.length === 0 ? `
📭

Henüz sevkiyat yapılmamıştır

` : ` ${quot.shipments.map((ship, index) => { const previousShipped = quot.shipments.slice(0, index).reduce((sum, s) => sum + s.quantity, 0); const remaining = totalQuantity - previousShipped - ship.quantity; return ` ${ship.notes ? ` ` : ''} `; }).join('')}
Tarih Konteyner İrsaliye Miktar Kalan Yapan
${new Date(ship.date).toLocaleDateString('tr-TR')} ${ship.container || '-'} ${ship.waybill || '-'} ${ship.quantity.toLocaleString('tr-TR')} KG ${remaining.toLocaleString('tr-TR')} KG ${remaining === 0 ? '✅' : ''} ${ship.createdBy}
📝 ${ship.notes}
`}
${shipmentProgress < 100 ? ` ` : ''}
`; document.getElementById('salesShipmentDetailModalContent').innerHTML = content; openModal('salesShipmentDetailModal'); }; // Users Management Page function UsersManagement() { return `

👥 Kullanıcı Yönetimi

${AppState.users.map(user => ` `).join('')}
Ad Soyad Kullanıcı Adı Email Rol Departman Durum İşlemler
${user.name} ${user.username} ${user.email} ${user.role} ${user.department} ${user.active ? 'Aktif' : 'Pasif'}

🔐 Rol Yetkileri

${AppState.roles.map(role => `

${role.name}

${role.description}

${role.permissions.map(p => ` ${p} `).join('')}
`).join('')}
`; } // Open User Modal window.openUserModal = function(userId = null) { const user = userId ? AppState.users.find(u => u.id === userId) : null; const isEdit = !!user; const content = `

${isEdit ? 'Kullanıcı Düzenle' : 'Yeni Kullanıcı Ekle'}

`; document.getElementById('userModalContent').innerHTML = content; openModal('userModal'); }; // Submit User window.submitUser = function(event, userId) { event.preventDefault(); if (userId) { // Edit existing user const user = AppState.users.find(u => u.id === userId); user.name = document.getElementById('userName').value; user.email = document.getElementById('userEmail').value; user.role = document.getElementById('userRole').value; user.department = document.getElementById('userDepartment').value; const password = document.getElementById('userPassword').value; if (password) user.password = password; alert('✅ Kullanıcı güncellendi!'); } else { // Add new user const newUser = { id: AppState.users.length + 1, username: document.getElementById('userUsername').value, password: document.getElementById('userPassword').value, name: document.getElementById('userName').value, email: document.getElementById('userEmail').value, role: document.getElementById('userRole').value, department: document.getElementById('userDepartment').value, active: true, createdAt: new Date().toISOString().split('T')[0] }; AppState.users.push(newUser); alert('✅ Yeni kullanıcı eklendi!'); } closeModal('userModal'); render(); }; // Edit User window.editUser = function(userId) { openUserModal(userId); }; // Toggle User Status window.toggleUserStatus = function(userId) { const user = AppState.users.find(u => u.id === userId); if (user.id === AppState.currentUser.id) { alert('❌ Kendi hesabınızı pasifleştiremezsiniz!'); return; } user.active = !user.active; render(); alert(`✅ Kullanıcı ${user.active ? 'aktifleştirildi' : 'pasifleştirildi'}!`); };