nodejs, express kullanılmak istenirse. Bir önceki örnekte exec kullanmıştım. Gpt spawn önerdi.
Hem sunucu hem de react projenizi konsolda takip edebilirsiniz. exec sadece sunucu bilgilerini döküyor.
import { spawn } from "child_process";
app.use((req, res, next) => {
const origin = req.headers.origin
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
// Önbelleğe alma süresi (saniye)
res.setHeader('Access-Control-Max-Age', '600');
return res.sendStatus(204);
}
next();
})
app.listen(PORT, () => {
const vite = spawn("npx", ["vite"], { stdio: "inherit", shell: true });
vite.on("close", (code) => {
console.log(`Vite kapandı (kod: ${code})`);
});
console.log(`Servers is running on http://localhost:${PORT}`);
});
Kendimce bir çözüm ürettim sadece yapılandırma aşamalarında bilmediğim teknik konularda yapay zeka yardımı aldım.
package.json
"script": {
"dev": "node ws.js"
}
ws.js
express(cors ayarına dikkat) veya ws çalıştırma dosyasına ekle.
import { WebSocketServer } from 'ws';
import { exec } from "child_process";
// 8080 portunda basit bir WebSocket sunucusu oluştur
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Mesaj alındı:', message.toString());
ws.send(`Sunucudan yanıt: ${message.toString().toUpperCase()}`);
});
ws.on('close', () => {
console.log('İstemci bağlantısı kapandı.');
});
});
// React Projesini çalıştır
exec("npx vite", (error, stdout, stderr) => {
if (error) {
console.error("Hata:", error.message);
return;
}
if (stderr) {
console.error("stderr:", stderr);
}
console.log("stdout:", stdout);
});
console.log('WebSocket sunucusu 8080 portunda çalışıyor.');
Ö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>
)
}