Dépenses et Revenus !
This commit is contained in:
parent
f09ef9e226
commit
f899b04567
|
@ -15,6 +15,13 @@
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>2024-jan-07</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Ajout à "Dépenses" de la partie "Revenus".</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<h2>2023-oct-20</h2>
|
<h2>2023-oct-20</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -46,6 +46,9 @@
|
||||||
<section id="expenses">
|
<section id="expenses">
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="incomes">
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="export-button">
|
<section id="export-button">
|
||||||
<!-- Export button will be added here dynamically -->
|
<!-- Export button will be added here dynamically -->
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -263,9 +263,9 @@ export const fetchExpenses = async (pubkey, minTime, limit = MAX_NB_TX) => {
|
||||||
transactions.push({
|
transactions.push({
|
||||||
date: new Date(tx.medianTime).toLocaleDateString(),
|
date: new Date(tx.medianTime).toLocaleDateString(),
|
||||||
walletId: pubkey,
|
walletId: pubkey,
|
||||||
income: tx.amount > 0 ? tx.amount / 100 : 0,
|
income: 0,
|
||||||
outcome: tx.amount < 0 ? -tx.amount / 100 : 0,
|
outcome: tx.amount,
|
||||||
comment: "Transaction Comment", // Add your logic to get the comment
|
comment: "Expense Comment", // Add your logic to get the comment
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,102 @@ export const fetchExpenses = async (pubkey, minTime, limit = MAX_NB_TX) => {
|
||||||
throw new Error("Failed to fetch data from all nodes");
|
throw new Error("Failed to fetch data from all nodes");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const query__incomes = (walletPk, minTime, size = MAX_NB_TX) => {
|
||||||
|
|
||||||
|
return {
|
||||||
|
_source: ["amount", "issuer"]
|
||||||
|
,sort: [
|
||||||
|
{
|
||||||
|
"medianTime": "desc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
,size: size
|
||||||
|
,query: {
|
||||||
|
bool: {
|
||||||
|
filter: [
|
||||||
|
{
|
||||||
|
range: {
|
||||||
|
"medianTime": {
|
||||||
|
gte: minTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,{
|
||||||
|
term: {
|
||||||
|
"recipient": walletPk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchIncomes = async (pubkey, minTime, limit = MAX_NB_TX) => {
|
||||||
|
|
||||||
|
shuffle(CESIUM_G1_NODES); // Mélanger la liste des noeuds
|
||||||
|
|
||||||
|
for (let node of CESIUM_G1_NODES) {
|
||||||
|
try {
|
||||||
|
const url = `${node}/g1/movement/_search`;
|
||||||
|
let queryBody = query__incomes(pubkey, minTime, limit);
|
||||||
|
|
||||||
|
console.log('incomes queryBody : \n', JSON.stringify(queryBody));
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(queryBody)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
console.log('node for the incomes of :\n', pubkey, '\n', node);
|
||||||
|
console.log('incomes data :\n', data);
|
||||||
|
|
||||||
|
let incomesByIssuer = {};
|
||||||
|
let totalAmount = 0;
|
||||||
|
let transactions = [];
|
||||||
|
|
||||||
|
for (const hit of data.hits.hits) {
|
||||||
|
|
||||||
|
const tx = hit._source;
|
||||||
|
|
||||||
|
if (!(tx.issuer in incomesByIssuer)) {
|
||||||
|
|
||||||
|
incomesByIssuer[tx.issuer] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
incomesByIssuer[tx.issuer] += tx.amount/100;
|
||||||
|
|
||||||
|
totalAmount += tx.amount;
|
||||||
|
|
||||||
|
transactions.push({
|
||||||
|
date: new Date(tx.medianTime).toLocaleDateString(),
|
||||||
|
walletId: pubkey,
|
||||||
|
income: tx.amount,
|
||||||
|
outcome: 0,
|
||||||
|
comment: "Income Comment", // Add your logic to get the comment
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
totalAmount = totalAmount/100;
|
||||||
|
|
||||||
|
return {
|
||||||
|
incomesTotalAmount: totalAmount
|
||||||
|
,incomesByIssuer: incomesByIssuer
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch data from ${node}: ${error}`);
|
||||||
|
// Si une erreur se produit, passez simplement au noeud suivant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Failed to fetch data from all nodes");
|
||||||
|
};
|
||||||
|
|
||||||
export const query__cesium_profile = (pubkey) => {
|
export const query__cesium_profile = (pubkey) => {
|
||||||
|
|
||||||
|
@ -444,40 +540,6 @@ const exportToPDF = (transactions) => {
|
||||||
|
|
||||||
export const displayExpenses = (expensesByRecipient, expensesTotalAmount, recipientsCesiumProfiles, chartColors, currentPubkey, currentProfile) => {
|
export const displayExpenses = (expensesByRecipient, expensesTotalAmount, recipientsCesiumProfiles, chartColors, currentPubkey, currentProfile) => {
|
||||||
|
|
||||||
let exportButtonContainer = document.querySelector("#export-button");
|
|
||||||
let exportButton = document.createElement("button");
|
|
||||||
exportButton.textContent = "Export to PDF";
|
|
||||||
exportButton.addEventListener("click", async () => {
|
|
||||||
try {
|
|
||||||
const { expensesByRecipient, expensesTotalAmount } = await fetchExpenses(
|
|
||||||
currentPubkey,
|
|
||||||
minTime,
|
|
||||||
txLimit
|
|
||||||
);
|
|
||||||
|
|
||||||
let transactions = [];
|
|
||||||
|
|
||||||
for (const recipient in expensesByRecipient) {
|
|
||||||
transactions.push({
|
|
||||||
date: new Date().toLocaleDateString(),
|
|
||||||
walletId: currentPubkey,
|
|
||||||
income: expensesByRecipient[recipient],
|
|
||||||
outcome: 0,
|
|
||||||
comment: "Transaction Comment", // Add your logic to get the comment
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export transactions to PDF
|
|
||||||
exportToPDF(transactions);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error exporting to PDF: ${error}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Append the export button to the container
|
|
||||||
exportButtonContainer.innerHTML = '';
|
|
||||||
exportButtonContainer.appendChild(exportButton);
|
|
||||||
|
|
||||||
let screenElt = document.querySelector('#expenses');
|
let screenElt = document.querySelector('#expenses');
|
||||||
screenElt.innerHTML = '';
|
screenElt.innerHTML = '';
|
||||||
|
|
||||||
|
@ -539,6 +601,69 @@ export const displayExpenses = (expensesByRecipient, expensesTotalAmount, recipi
|
||||||
screenElt.scrollIntoView({behavior: 'smooth'}, true);
|
screenElt.scrollIntoView({behavior: 'smooth'}, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const displayIncomes = (incomesByIssuer, incomesTotalAmount, issuersCesiumProfiles, chartColors, currentPubkey, currentProfile) => {
|
||||||
|
|
||||||
|
let screenElt = document.querySelector('#incomes');
|
||||||
|
screenElt.innerHTML = '';
|
||||||
|
|
||||||
|
let currentProfileTitleElt = document.createElement('h2');
|
||||||
|
screenElt.append(currentProfileTitleElt);
|
||||||
|
let title = null;
|
||||||
|
if (currentProfile == undefined) {
|
||||||
|
title = 'Revenus du portefeuille <code>' + currentPubkey.substr(0, 8) + '</code>';
|
||||||
|
} else {
|
||||||
|
title = 'Revenus de <q>' + currentProfile.title + '</q>';
|
||||||
|
}
|
||||||
|
currentProfileTitleElt.innerHTML = title;
|
||||||
|
|
||||||
|
let svgContainer = document.createElement('article');
|
||||||
|
screenElt.append(svgContainer);
|
||||||
|
svgContainer.classList.add("svg-container");
|
||||||
|
|
||||||
|
let chartData = [];
|
||||||
|
|
||||||
|
// Formatting data
|
||||||
|
for (const issuer in incomesByIssuer) {
|
||||||
|
|
||||||
|
let issuerObj = {};
|
||||||
|
|
||||||
|
issuerObj.pk = issuer;
|
||||||
|
issuerObj.amount = incomesByIssuer[issuer];
|
||||||
|
let numberOptions = { roundingMode: 'ceil', minimumFractionDigits: 0, maximumFractionDigits: 0 };
|
||||||
|
issuerObj.displayedAmount = G12DU(issuerObj.amount).toLocaleString('fr-FR', numberOptions) + ' DU';
|
||||||
|
|
||||||
|
if (issuersCesiumProfiles[issuer] != undefined
|
||||||
|
&& issuersCesiumProfiles[issuer].title != undefined
|
||||||
|
) {
|
||||||
|
issuerObj.title = issuersCesiumProfiles[issuer].title;
|
||||||
|
} else {
|
||||||
|
issuerObj.title = issuer.substr(0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
chartData.push(issuerObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
let chart = Treemap(chartData, {
|
||||||
|
path: d => d.title,
|
||||||
|
value: d => d.amount,
|
||||||
|
group: d => d.title,
|
||||||
|
label: (d, n) => d.title,
|
||||||
|
title: (d, n) => d.displayedAmount,
|
||||||
|
link: (d, n) => '#' + d.pk + '',
|
||||||
|
linkTarget: '_self',
|
||||||
|
tile: d3.treemapSquarify,
|
||||||
|
width: 1280,
|
||||||
|
height: 720,
|
||||||
|
padding: 0,
|
||||||
|
colors: chartColors,
|
||||||
|
fillOpacity: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
svgContainer.append(chart);
|
||||||
|
|
||||||
|
screenElt.scrollIntoView({behavior: 'smooth'}, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
let formElt = document.querySelector('form#explore');
|
let formElt = document.querySelector('form#explore');
|
||||||
|
|
||||||
|
@ -550,18 +675,25 @@ const treemapIt = async (pubkey, minTime, maxNbTx = MAX_NB_TX) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let { expensesTotalAmount, expensesByRecipient } = await fetchExpenses(pubkey, minTime, maxNbTx);
|
let { expensesTotalAmount, expensesByRecipient } = await fetchExpenses(pubkey, minTime, maxNbTx);
|
||||||
|
let { incomesTotalAmount, incomesByIssuer } = await fetchIncomes(pubkey, minTime, maxNbTx);
|
||||||
|
|
||||||
let nbRecipients = Object.keys(expensesByRecipient).length;
|
let nbRecipients = Object.keys(expensesByRecipient).length;
|
||||||
|
let nbIssuers = Object.keys(incomesByIssuer).length;
|
||||||
|
|
||||||
console.log("nb recipients :\n", nbRecipients);
|
console.log("nb recipients :\n", nbRecipients);
|
||||||
|
console.log("nb Issuers :\n", nbIssuers);
|
||||||
|
|
||||||
let recipientsList = Object.keys(expensesByRecipient);
|
let recipientsList = Object.keys(expensesByRecipient);
|
||||||
let recipientsCesiumProfiles = await fetchCesiumProfiles(recipientsList, maxNbTx);
|
let recipientsCesiumProfiles = await fetchCesiumProfiles(recipientsList, maxNbTx);
|
||||||
|
|
||||||
|
let issuersList = Object.keys(incomesByIssuer);
|
||||||
|
let issuersCesiumProfiles = await fetchCesiumProfiles(issuersList, maxNbTx);
|
||||||
|
|
||||||
let currentProfile = await fetchCesiumProfile(pubkey);
|
let currentProfile = await fetchCesiumProfile(pubkey);
|
||||||
console.log('currentProfile :\n', currentProfile);
|
console.log('currentProfile :\n', currentProfile);
|
||||||
|
|
||||||
displayExpenses(expensesByRecipient, expensesTotalAmount, recipientsCesiumProfiles, chartColors, pubkey, currentProfile);
|
displayExpenses(expensesByRecipient, expensesTotalAmount, recipientsCesiumProfiles, chartColors, pubkey, currentProfile);
|
||||||
|
displayIncomes(incomesByIssuer, incomesTotalAmount, issuersCesiumProfiles, chartColors, pubkey, currentProfile);
|
||||||
|
|
||||||
let svg = document.querySelector('#expenses svg');
|
let svg = document.querySelector('#expenses svg');
|
||||||
let links = svg.querySelectorAll("a");
|
let links = svg.querySelectorAll("a");
|
||||||
|
@ -571,7 +703,7 @@ const treemapIt = async (pubkey, minTime, maxNbTx = MAX_NB_TX) => {
|
||||||
link.addEventListener('click', (linkEvent) => {
|
link.addEventListener('click', (linkEvent) => {
|
||||||
|
|
||||||
// linkEvent.currentTarget.preventDefault();
|
// linkEvent.currentTarget.preventDefault();
|
||||||
console.log('linkEvent.currentTarget :\n', linkEvent.currentTarget);
|
console.log('linkEvent.currentTarget :\n', linkEvent.currentTarget);
|
||||||
let pubkey = linkEvent.currentTarget.getAttribute('href').substr(1);
|
let pubkey = linkEvent.currentTarget.getAttribute('href').substr(1);
|
||||||
|
|
||||||
// treemapIt(pubkey, minDate);
|
// treemapIt(pubkey, minDate);
|
||||||
|
@ -589,7 +721,7 @@ const getPkInHash = () => {
|
||||||
window.addEventListener("popstate", (popEvent) => {
|
window.addEventListener("popstate", (popEvent) => {
|
||||||
|
|
||||||
let pk = getPkInHash();
|
let pk = getPkInHash();
|
||||||
console.log('\n\n\npubkey :\n', pk);
|
console.log('\n\n\npubkey :\n', pk);
|
||||||
|
|
||||||
if (pk != '') {
|
if (pk != '') {
|
||||||
treemapIt(pk, minTime, txLimit);
|
treemapIt(pk, minTime, txLimit);
|
||||||
|
@ -605,7 +737,7 @@ formElt.addEventListener('submit', (formEvent) => {
|
||||||
let minDateStr = formEvent.target.querySelector('input[name="minDate"]').value;
|
let minDateStr = formEvent.target.querySelector('input[name="minDate"]').value;
|
||||||
let minDate = new Date(minDateStr);
|
let minDate = new Date(minDateStr);
|
||||||
minTime = Math.floor(minDate.valueOf()/1000);
|
minTime = Math.floor(minDate.valueOf()/1000);
|
||||||
console.log('minTime :\n', minTime);
|
console.log('minTime :\n', minTime);
|
||||||
|
|
||||||
let pubkey = formEvent.target.querySelector('input[name="pubkey"]').value;
|
let pubkey = formEvent.target.querySelector('input[name="pubkey"]').value;
|
||||||
|
|
||||||
|
@ -625,13 +757,12 @@ window.addEventListener('DOMContentLoaded', (loadEvent) => {
|
||||||
let aMonthAgo = new Date(now);
|
let aMonthAgo = new Date(now);
|
||||||
aMonthAgo.setMonth(aMonthAgo.getMonth() - 1);
|
aMonthAgo.setMonth(aMonthAgo.getMonth() - 1);
|
||||||
|
|
||||||
console.log('aMonthAgo : ', aMonthAgo);
|
console.log('aMonthAgo : ', aMonthAgo);
|
||||||
console.log('aMonthAgo.getMonth() :\n', aMonthAgo.getMonth());
|
console.log('aMonthAgo.getMonth() :\n', aMonthAgo.getMonth());
|
||||||
|
|
||||||
let dateStr = aMonthAgo.getFullYear() + '-' + (aMonthAgo.getMonth()+1).toString().padStart(2,0) + '-' + aMonthAgo.getDate().toString().padStart(2,0);
|
let dateStr = aMonthAgo.getFullYear() + '-' + (aMonthAgo.getMonth()+1).toString().padStart(2,0) + '-' + aMonthAgo.getDate().toString().padStart(2,0);
|
||||||
|
|
||||||
console.log('dateStr : ', dateStr);
|
console.log('dateStr : ', dateStr);
|
||||||
|
|
||||||
minDateElt.value = dateStr;
|
minDateElt.value = dateStr;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue