Önce bir form yardımı ile aşağıdaki örnek js değişkeni oluştur.
Sonra UUID (ETTN) isteği atıp fatura.faturaUuid
değerini gelen data ile değiştir.
En sonda post parametresini fetch isteği ile sunucuya gönder. Fatura hazır. Bir tek onaylaması kalır.
Adet * Birim Fiyat üzerine sade bir fatura json yapısı:
const fatura = {
"faturaUuid":"",
"belgeNumarasi":"",
"faturaTarihi":"09/09/2024",
"saat":"19:03:11",
"paraBirimi":"TRY",
"dovzTLkur":"0",
"faturaTipi":"SATIS",
"hangiTip":"5000/30000",
"vknTckn":"1234567890",
"aliciUnvan":"",
"aliciAdi":"Potansiyel",
"aliciSoyadi":"Tüketici",
"binaAdi":"",
"binaNo":"",
"kapiNo":"",
"kasabaKoy":"",
"vergiDairesi":"???E VERGİ DAİRESİ MÜD.",
"ulke":"Türkiye",
"bulvarcaddesokak":"??? Mah.",
"irsaliyeNumarasi":"",
"irsaliyeTarihi":"",
"mahalleSemtIlce":"",
"sehir":" ",
"postaKodu":"",
"tel":"",
"fax":"","eposta":"",
"websitesi":"",
"iadeTable":[],
"vergiCesidi":" ",
"malHizmetTable":[
{
"malHizmet":"Test",
"miktar":10,
"birim": "C62",
"birimFiyat":"500",
"fiyat":"5000",
"iskontoOrani":0,
"iskontoTutari":"0",
"iskontoNedeni":"",
"malHizmetTutari":"5000",
"kdvOrani":"10",
"vergiOrani":0,
"kdvTutari":"500",
"vergininKdvTutari":"0",
"ozelMatrahTutari":"0",
"hesaplananotvtevkifatakatkisi":"0"
}
],
"tip":"İskonto",
"matrah":"5000",
"malhizmetToplamTutari":"5000",
"toplamIskonto":"0",
"hesaplanankdv":"500",
"vergilerToplami":"500",
"vergilerDahilToplamTutar":"5500",
"odenecekTutar":"5500",
"not":"",
"siparisNumarasi":"",
"siparisTarihi":"",
"fisNo":"",
"fisTarihi":"",
"fisSaati":" ",
"fisTipi":" ",
"zRaporNo":"",
"okcSeriNo":""
}
const params = {
cmd: 'EARSIV_PORTAL_FATURA_OLUSTUR',
pageName: 'RG_BASITFATURA',
token: token,
jp: JSON.stringify(fatura);
}
https://earsivportal.efatura.gov.tr/intragiris.html
adresi fetch isteği kabul ediyor aslında.
Yani bir localhost sunucusunda html ve js yeterli aslında. Bir de gerekli şablona uygun fatura json yapısı.
Sunucuya atılan genel istekler:
- Oturum açma
- Fatura Oluşturma ve UUID (diğer adı ile ETTN) al ve depola
- Vergi veya TC numarasından şirket/şahıs sorgula ve depola
Kullanıcı tarafından girilecek veriler:
- Şirket/Şahıs bilgilerini (Sunucuya sorgulama yaptırmak istemezseniz)
- Adres bilgisi
- Fatura satır bilgileri dizi
- Satır toplam bilgileri
Oturum açtıktan sonra ETTN(UUID) değeri alma isteği:
const params = {
cmd:'EARSIV_PORTAL_UUID_GETIR',
token: token,
jp: JSON.stringify({})
}
fetch("https://earsivportal.efatura.gov.tr/earsiv-services/dispatch", {
"headers": {
"accept": "application/json, text/javascript, */*; q=0.01",
"accept-language": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrerPolicy": "strict-origin-when-cross-origin",
"body": new URLSearchParams(params),
"method": "POST",
"mode": "cors",
"credentials": "omit" // localhost üzerinden atınca include falan olabiliyor net hatırlamıyorum.
});
Vergi/TC Sorgulama
const params = {
cmd: '',
token: token,
pageName: 'RG_BASITFATURA',
jp: JSON.stringify({vknTcknn: 'vergi/tcno'})
}
fetch("https://earsivportal.efatura.gov.tr/earsiv-services/dispatch", {
"headers": {
"accept": "application/json, text/javascript, */*; q=0.01",
"accept-language": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrerPolicy": "strict-origin-when-cross-origin",
"body": new URLSearchParams(params),
"method": "POST",
"mode": "cors",
"credentials": "omit"
});
Tam anlamadım temel sayfa mı yüklenmiyor yoksa yüklenen sayfada atılan istekler mi boş geliyor?
Yeni bir çözüm...
const dizi = [
{
"GmKod": "150.20T627",
"Borclu": 15898.75,
"Alacakli": 0
},
{
"GmKod": "191.20",
"Borclu": 1589.87,
"Alacakli": 0
},
{
"GmKod": "191.20T627",
"Borclu": 1589.88,
"Alacakli": 0
},
{
"GmKod": "360.KDV2.62720",
"Borclu": 0,
"Alacakli": 1589.88
},
{
"GmKod": "150.20T627",
"Borclu": 8295,
"Alacakli": 0
},
{
"GmKod": "191.20",
"Borclu": 829.5,
"Alacakli": 0
},
{
"GmKod": "191.20T627",
"Borclu": 829.5,
"Alacakli": 0
},
{
"GmKod": "360.KDV2.62720",
"Borclu": 0,
"Alacakli": 829.5
},
{
"GmKod": "150.20",
"Borclu": 500,
"Alacakli": 0
},
{
"GmKod": "191.20",
"Borclu": 100,
"Alacakli": 0
}
];
const sum = ['Borclu', 'Alacakli'];
const diziTotal = dizi.reduce((a,b)=>{
const findObj = a.find(r=>r.GmKod==b.GmKod); // eğer boje bulunmuşsa
if(!findObj){
a.push(b);
return a;
} else {
for (const key of sum){
findObj[key] += b[key]
}
return a;
}
}, [])
Bir de react örneği hazırladım. Herhangi bir html dosyasına yapıştırıp çalıştırabilirsin.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root" class="container"></div>
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
const [users, setUsers] = useState([]);
const [userId, setUserId] = useState(1);
const [comments, setComments] = useState([]);
const handleUsers = async ()=>{
const result = await fetch("https://jsonplaceholder.typicode.com/users").then(d=>d.json())
setUsers(result);
};
const handleComments = async ()=>{
const result = await fetch(`https://jsonplaceholder.typicode.com/posts/${userId}/comments`).then(d=>d.json())
if(userId != users.length){
setComments([...comments, ...result]);
setUserId(prev=>prev+1);
}
}
useEffect(()=>{
handleUsers();
}, [])
return (
<div>
<ul>
{
comments.map((line,k)=>(
<li key={k}>{line.body}</li>
))
}
</ul>
<button onClick={handleComments}>Yükle</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />)
</script>
</body>
</html>
Tam doğru anlatabilir miyim bilmiyorum.
Php sunucu tarafında çalışıyor gelen istekleri alır ve çıktı verir.
Sayfa 1-20 arası yorum gösteriyor. Sayfa sonuna geldin 21-40 arasını otomatik eklemesini istersen Javascript ile sunucuya istek atıp gelen değerleri ekrana bastırman lazım.
Ama işlemi yine de php ile yüklemek istersen parametre aralığını 1-40 olarak değiştirmen lazım.
Bu seferde sayfa yeniden yüklenir. Pek doğru değil.
Zaten çok özel bir işlem olmadığı sürece html etiketleri ile Php çıktısı almak ve 10Kb veriyi atıyorum 100Kb veriye çıkartıyorsun.
Veri tabanından çektiğin her satır için etiket, sınıf yazmasını bekliyorsun. Tailwind içine girdi mi 100Kb olur 200Kb :)
Sunucudan bir veri çektiğin zaman hiç bir zaman html yapısı kullanma. Belki bilgilendirme sayfalarında olabilir (Anasayfa, İletişim, Hakkımızda).
O sırada hem html hem veri aynı anda yüklendiği için göz ardı edilebilir.
const rowsConfig = {
start: 1,
start: 20
}
async function movieComments(rowsConfig) {
const comments = await fetch("movie.php", {
body: new UrlSearchParams(rowsConfig),
method: "POST"
}).then(d=>d.json());
comments.forEach((line)=>{
$("ul#comment").append(`<li>${line.comment}</li>`)
})
rowsConfig.start += 20;
rowsConfig.end += 20;
}
// Şimdi tekerlekle ve aşağı ok tuşu ile sayfa sonunu yakalamak kısmını yapay zekaya sorarsın.
movieComments(rowsConfig)
Çözümü buldum form
etiketi ve useRef
hook'u kullanarak.
Okunan yeni değerler sonrası invoiceRef.current.reset()
çalıştırılırsa set edilen değerler varsayılan değer olarak tekrar okunur.
function Fatura(){
const invoiceRef = useRef(null);
const [invoiceLine, setInvoiceLine] = useState([10,20,30,40,50]);
const newInvoice = ()=>{
invoiceLine([60,70,80,90,100]);
invoiceRef.current.reset();
}
const handleChange = (e,k)=>{
invoiceLine[k] = eval(`${e.target.value.replaceAll(".","").replace(",","."}`)
setInvoiceLine([...sayilar])
}
return(
<div>
<button type="button" className="text-white border border-[orange]" onClick={newInvoice}>Sayıları Değiştir</button>
<form ref={invoiceRef} className="grid gap-2 w-[450px] p-2 bg-black mx-auto">
{invoiceLine.map((l, k) => (
<div key={k} className="text-white grid grid-cols-2 gap-2">
<input type="text" className="text-black text-right pr-2" onChange={(e) => { handleChange(e, k) }} defaultValue={l} />
<span>Çıktı: {Intl.NumberFormat("tr", { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(l)}</span>
</div>
))}
</form>
</div>
)
}
Bir kaynak buldum ve uyarladım. Şimdilik işimi çözdü.
App.svelte
<script>
import Tabs from "./Tabs.svelte";
import Tab1 from "./Tab1.svelte";
import Tab2 from "./Tab2.svelte";
const children = [
{
title: "Tab 1",
component: Tab1
},
{
title: "Tab 2",
component: Tab2
}
]
</script>
<Tabs children={[Tab1, Tab2]} defaultName="Tab1" />
Tabs.svelte
<script>
export let children = [], index=0, defaultName = "";
defaultName = children[0].component.name
</script>
{#each children as child , i (i)}
<button on:click={()=>{index=i; defaultName=child.component.name}}>{child.title}</button>
{/each}
<h2>Sekmenin Index değerine göre</h2>
{#each children as child , i (i)}
{#if i == index}
<svelte:component this={child.component} />
{/if}
{/each}
<hr>
<h2>Sekmenin Adına Göre(Benzersiz olmalı)</h2>
{#each children as child , i (i)}
{#if child.component.name == defaultName}
<svelte:component this={child.component} />
{/if}
{/each}
Tablo dışında bir yere tıklandığı zaman pencerenin gizlenmesini sağladım o yüzden kaydırma çubuğunun bir önemi kalmadı.