461 lines
19 KiB
JavaScript
461 lines
19 KiB
JavaScript
const axios = require('axios');
|
|
|
|
const server_constants = require('../tools/server_constants');
|
|
|
|
const shared_consts = require('../tools/shared_nodejs');
|
|
|
|
const SERVER_A_URL = process.env.SERVER_A_URL || "http://IP_DI_SERVER_A:3000";
|
|
const API_KEY = process.env.API_KEY_MSSQL;
|
|
|
|
// Funzione per ottenere i dati
|
|
const getArticlesSales = async () => {
|
|
try {
|
|
const query = `
|
|
SELECT
|
|
a.IdArticolo, a.Titolo, a.DataPubblicazione, a.Ean13 AS isbn,
|
|
a.IdCollana, y.DescrizioneCollana, i2.DescrArgomento, a.ListaArgomenti,
|
|
a.Pagine, a.IdTipoFormato, a.Misure,
|
|
COALESCE(o.totVen, 0) as totVen, COALESCE(u.totFat, 0) as totFat,
|
|
COALESCE(p.rank3M, 0) as rank3M, COALESCE(t.fatrank3M, 0) as fatrank3M,
|
|
COALESCE(q.rank6M, 0) as rank6M, COALESCE(r.rank1Y, 0) as rank1Y,
|
|
COALESCE(t.fat3mesi, 0) as fatLast3M, COALESCE(p.venduti3mesi, 0) as vLast3M,
|
|
COALESCE(q.venduti6mesi, 0) as vLast6M, COALESCE(r.venduti1anno, 0) as vLastY,
|
|
s.ultimoOrdine as dataUltimoOrdine
|
|
FROM T_WEB_Articoli a
|
|
LEFT JOIN (SELECT CodArticoloGM, SUM(Qta) as totVen FROM T_WEB_Ordini GROUP BY CodArticoloGM) o
|
|
ON a.IdArticolo = o.CodArticoloGM
|
|
LEFT JOIN (SELECT CodArticolo, SUM(TRY_CAST(Qta AS INT)) as totFat FROM T_WEB_ArticoliFatturati WHERE ISNUMERIC(Qta) = 1 GROUP BY CodArticolo) u
|
|
ON a.IdArticolo = u.CodArticolo
|
|
WHERE a.IdStatoProdotto IS NOT NULL
|
|
ORDER BY totVen DESC;
|
|
`;
|
|
|
|
const response = await axios.post(SERVER_A_URL + '/query', { query }, {
|
|
headers: { 'x-api-key': API_KEY }
|
|
});
|
|
|
|
return response.data || [];
|
|
|
|
} catch (error) {
|
|
console.error("Errore nel recupero degli articoli:", error);
|
|
throw new Error("Errore nel recupero degli articoli venduti.");
|
|
}
|
|
};
|
|
|
|
// Endpoint per ottenere i dati in formato JSON
|
|
exports.getArticlesSalesHandler = async (req, res) => {
|
|
try {
|
|
const data = await getArticlesSales();
|
|
if (!data.length) return res.status(404).json({ message: "Nessun articolo trovato." });
|
|
res.json(data);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
// Endpoint per esportare i dati come file JSON
|
|
exports.exportArticlesSalesByJSON = async (req, res) => {
|
|
try {
|
|
const data = await getArticlesSales();
|
|
if (!data.length) return res.status(404).json({ message: "Nessun articolo trovato." });
|
|
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.setHeader("Content-Disposition", `attachment; filename="ranking_articles_${new Date().toISOString().split('T')[0]}.json"`);
|
|
res.json(data);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateValue) => {
|
|
const date = new Date(dateValue);
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
return `${day}/${month}/${year}`;
|
|
};
|
|
|
|
const getTableContent = async (options) => {
|
|
try {
|
|
// Verifica se la tabella esiste
|
|
const checkTableQuery = `SELECT COUNT(*) as tableExists FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '${options.nameTable}'`;
|
|
const checkResponse = await axios.post(SERVER_A_URL + '/query', { query: checkTableQuery }, {
|
|
headers: { 'x-api-key': API_KEY }
|
|
});
|
|
if (!checkResponse.data || checkResponse.data.length === 0 || checkResponse.data[0].tableExists === 0) {
|
|
return `La tabella '${options.nameTable}' non esiste.`;
|
|
}
|
|
|
|
// Recupera le colonne della tabella principale dal catalogo
|
|
const columnsQuery = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${options.nameTable}'`;
|
|
const columnsResponse = await axios.post(SERVER_A_URL + '/query', { query: columnsQuery }, {
|
|
headers: { 'x-api-key': API_KEY }
|
|
});
|
|
const tableColumns = columnsResponse.data.map(col => col.COLUMN_NAME);
|
|
|
|
// Mappatura per unire i campi (ID e Descrizione)
|
|
const mergedMapping = {
|
|
"IdStatoProdotto": "DescrizioneStatoProdotto",
|
|
"IdTipologia": "DescrizioneTipologia",
|
|
"IdTipoFormato": "DescrizioneFormato",
|
|
"IdCollana": "DescrizioneCollana",
|
|
"ListaArgomenti": "DescrArgomento",
|
|
"ListaAutori": "AutoriCompleti",
|
|
"IdMarchioEditoriale": "CasaEditrice",
|
|
};
|
|
|
|
// Costruisce la query per recuperare i record
|
|
let dataQuery = "";
|
|
|
|
let columnsToShow = 'T.*';
|
|
if (options.fieldGM) {
|
|
columnsToShow = 'T.' + options.fieldGM;
|
|
}
|
|
|
|
if (options.nameTable.toLowerCase() === 't_web_articoli') {
|
|
if (true) {
|
|
dataQuery = `
|
|
SELECT TOP ${options.numrec}
|
|
${columnsToShow}
|
|
` + (options.campispeciali ? `
|
|
,f.DescrizioneStatoProdotto
|
|
,i.DescrizioneTipologia
|
|
,n.DescrizioneFormato
|
|
,y.DescrizioneCollana
|
|
,z.AutoriCompleti
|
|
,i2.DescrArgomento
|
|
,z3.CasaEditrice` : ``) + (options.showQtaDisponibile ? ` ,q.QtaDisponibile ` : ``) +
|
|
` FROM T_WEB_Articoli T
|
|
JOIN(
|
|
SELECT IdArticolo, MAX(DataOra) AS data
|
|
FROM T_WEB_Articoli
|
|
GROUP BY IdArticolo
|
|
) b ON T.IdArticolo = b.IdArticolo AND T.DataOra = b.data `
|
|
+ (options.campispeciali ?
|
|
` LEFT JOIN(
|
|
SELECT e.IdStatoProdotto, e.Descrizione as DescrizioneStatoProdotto
|
|
FROM T_WEB_StatiProdotto e
|
|
JOIN(
|
|
SELECT IdStatoProdotto, MAX(DataOra) as data1
|
|
FROM T_WEB_StatiProdotto
|
|
GROUP BY IdStatoProdotto
|
|
) c ON e.IdStatoProdotto = c.IdStatoProdotto AND e.DataOra = c.data1
|
|
) f ON T.IdStatoProdotto = f.IdStatoProdotto
|
|
LEFT JOIN(
|
|
SELECT g.IdTipologia, g.Descrizione as DescrizioneTipologia
|
|
FROM T_WEB_Tipologie g
|
|
JOIN(
|
|
SELECT IdTipologia, MAX(DataOra) as data1
|
|
FROM T_WEB_Tipologie
|
|
GROUP BY IdTipologia
|
|
) h ON g.IdTipologia = h.IdTipologia AND g.DataOra = h.data1
|
|
) i ON T.IdTipologia = i.IdTipologia
|
|
LEFT JOIN(
|
|
SELECT l.IdTipoFormato, l.Descrizione as DescrizioneFormato
|
|
FROM T_WEB_TipiFormato l
|
|
JOIN(
|
|
SELECT IdTipoFormato, MAX(DataOra) as data1
|
|
FROM T_WEB_TipiFormato
|
|
GROUP BY IdTipoFormato
|
|
) m ON l.IdTipoFormato = m.IdTipoFormato AND l.DataOra = m.data1
|
|
) n ON T.IdTipoFormato = n.IdTipoFormato
|
|
LEFT JOIN(
|
|
SELECT v.IdCollana, v.Descrizione as DescrizioneCollana
|
|
FROM T_WEB_Collane v
|
|
INNER JOIN(
|
|
SELECT IdCollana, MAX(ID) as MaxID
|
|
FROM T_WEB_Collane
|
|
GROUP BY IdCollana
|
|
) x ON v.IdCollana = x.IdCollana AND v.ID = x.MaxID
|
|
) y ON T.IdCollana = y.IdCollana
|
|
LEFT JOIN(
|
|
SELECT g2.IdArgomento, g2.Descrizione as DescrArgomento
|
|
FROM T_WEB_Argomenti g2
|
|
INNER JOIN(
|
|
SELECT IdArgomento, MAX(DataOra) as data12
|
|
FROM T_WEB_Argomenti
|
|
GROUP BY IdArgomento
|
|
) h ON g2.IdArgomento = h.IdArgomento AND g2.DataOra = h.data12
|
|
) i2 ON T.ListaArgomenti = i2.IdArgomento
|
|
LEFT JOIN(
|
|
SELECT
|
|
T1.IdArticolo,
|
|
STUFF((
|
|
SELECT ',' + ISNULL(A2.AutoreCompleto, '')
|
|
FROM(
|
|
SELECT CAST('<root><x>' + REPLACE(T1.ListaAutori, ',', '</x><x>') + '</x></root>' AS XML) AS DataXML
|
|
) X
|
|
CROSS APPLY X.DataXML.nodes('/root/x') AS A(x)
|
|
CROSS APPLY(
|
|
SELECT TRY_CAST(LTRIM(RTRIM(A.x.value('.', 'VARCHAR(100)'))) AS INT) AS AutoreID
|
|
) CA
|
|
JOIN(
|
|
SELECT a.IdAutore, CONCAT(a.Nome, ' ', a.Cognome) AS AutoreCompleto
|
|
FROM T_WEB_Autori a
|
|
JOIN(
|
|
SELECT IdAutore, MAX(DataOra) AS maxData
|
|
FROM T_WEB_Autori
|
|
GROUP BY IdAutore
|
|
) aa ON a.IdAutore = aa.IdAutore AND a.DataOra = aa.maxData
|
|
) A2 ON CA.AutoreID = A2.IdAutore
|
|
FOR XML PATH(''), TYPE
|
|
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS AutoriCompleti
|
|
FROM T_WEB_Articoli T1
|
|
GROUP BY T1.IdArticolo, T1.ListaAutori
|
|
) z ON T.IdArticolo = z.IdArticolo
|
|
LEFT JOIN(
|
|
SELECT a3.IdMarchioEditoriale, a3.Descrizione as CasaEditrice
|
|
FROM T_WEB_MarchiEditoriali a3
|
|
JOIN(
|
|
SELECT IdMarchioEditoriale, MAX(DataOra) as maxData
|
|
FROM T_WEB_MarchiEditoriali
|
|
GROUP BY IdMarchioEditoriale
|
|
) aa3 ON a3.IdMarchioEditoriale = aa3.IdMarchioEditoriale AND a3.DataOra = aa3.maxData
|
|
) z3 ON T.IdMarchioEditoriale = z3.IdMarchioEditoriale `
|
|
: ``)
|
|
+ (options.showQtaDisponibile ?
|
|
` LEFT JOIN(
|
|
SELECT o.Codice, o.QtaDisponibile
|
|
FROM T_WEB_Disponibile o
|
|
JOIN(
|
|
SELECT Codice, MAX(DataOra) as data1
|
|
FROM T_WEB_Disponibile
|
|
GROUP BY Codice
|
|
) p ON o.Codice = p.Codice AND o.DataOra = p.data1
|
|
) q ON T.IdArticolo = q.Codice` : ``)
|
|
} else {
|
|
dataQuery += `
|
|
SELECT TOP ${options.numrec}
|
|
T.*
|
|
FROM T_WEB_Articoli T
|
|
JOIN(
|
|
SELECT IdArticolo, MAX(DataOra) AS data
|
|
FROM T_WEB_Articoli
|
|
GROUP BY IdArticolo
|
|
) b ON T.IdArticolo = b.IdArticolo AND T.DataOra = b.data
|
|
`;
|
|
}
|
|
} else {
|
|
dataQuery = `SELECT TOP ${options.numrec} * FROM ${options.nameTable} `;
|
|
}
|
|
if (options.where && options.where.trim() !== "") {
|
|
dataQuery += ` WHERE ${options.where} `;
|
|
}
|
|
|
|
// Esegue la query per recuperare i dati
|
|
console.log('dataQuery', dataQuery);
|
|
const dataResponse = await axios.post(SERVER_A_URL + '/query', { query: dataQuery }, {
|
|
headers: { 'x-api-key': API_KEY }
|
|
});
|
|
const records = dataResponse.data;
|
|
if (!records || records.length === 0) {
|
|
return `Nessun record trovato nella tabella '${options.nameTable}'.`;
|
|
}
|
|
|
|
// Determina quali colonne visualizzare.
|
|
let displayColumns;
|
|
if (options.nameTable.toLowerCase() === 't_web_articoli') {
|
|
// Usa tutte le proprietà del record, escludendo le colonne dei campi uniti (quelle usate per il merge)
|
|
displayColumns = Object.keys(records[0]).filter(col => !Object.values(mergedMapping).includes(col));
|
|
} else {
|
|
displayColumns = tableColumns;
|
|
}
|
|
|
|
// Funzione per ottenere il valore da visualizzare, fondendo i campi se presente nella mappatura
|
|
const getDisplayValue = (record, col) => {
|
|
let value = record[col] ?? 'NULL';
|
|
// Format date solo se il nome della colonna indica una data/ora
|
|
if ((col.toLowerCase().includes("data") || col.toLowerCase().includes("ora")) && value !== 'NULL') {
|
|
if (value.includes(',')) {
|
|
// Se ci sono più valori separati da virgola, formatta ciascuno se è una data valida
|
|
value = value.split(',')
|
|
.map(item => {
|
|
const trimmed = item.trim();
|
|
const parsed = Date.parse(trimmed);
|
|
return !isNaN(parsed) ? formatDate(trimmed) : trimmed;
|
|
})
|
|
.join(', ');
|
|
} else {
|
|
const parsed = Date.parse(value);
|
|
if (!isNaN(parsed)) {
|
|
value = formatDate(value);
|
|
}
|
|
}
|
|
}
|
|
if (mergedMapping[col]) {
|
|
return `${record[mergedMapping[col]] || ''} (${value})`;
|
|
}
|
|
return value;
|
|
};
|
|
|
|
// Costruisce l'output HTML
|
|
let output = "";
|
|
if (options.outhtml) {
|
|
if (records.length === 1) {
|
|
// Se c'è un solo record, visualizza una lista di chiavi e valori
|
|
const record = records[0];
|
|
output += `
|
|
< table border = '1' style = "border-collapse: collapse; width: 50%;" >
|
|
<thead>
|
|
<tr>
|
|
<th style="padding: 8px; background-color: #f2f2f2;">Campo</th>
|
|
<th style="padding: 8px; background-color: #f2f2f2;">Valore</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
`;
|
|
displayColumns.forEach(column => {
|
|
output += `
|
|
<tr>
|
|
<td style="padding: 8px;">${column}</td>
|
|
<td style="padding: 8px;">${getDisplayValue(record, column)}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
output += `
|
|
</tbody>
|
|
</table >
|
|
`;
|
|
} else {
|
|
// Se ci sono più record, visualizza una tabella con intestazioni
|
|
output += "<table border='1' style='border-collapse: collapse; width: 100%;'><thead><tr>";
|
|
displayColumns.forEach(column => {
|
|
output += `< th style = "padding: 8px; background-color: #f2f2f2;" > ${column}</th > `;
|
|
});
|
|
output += "</tr></thead><tbody>";
|
|
records.forEach(record => {
|
|
output += "<tr>";
|
|
displayColumns.forEach(column => {
|
|
output += `< td style = "padding: 8px;" > ${getDisplayValue(record, column)}</td > `;
|
|
});
|
|
output += "</tr>";
|
|
});
|
|
output += "</tbody></table>";
|
|
}
|
|
} else {
|
|
// solo dati
|
|
output = {};
|
|
|
|
if (options.fieldGM) {
|
|
if (records && records.length === 1) {
|
|
output[options.fieldGM] = records[0][options.fieldGM];
|
|
}
|
|
} else {
|
|
output = [];
|
|
records.forEach(record => {
|
|
let myrec = {}
|
|
|
|
displayColumns.forEach(column => {
|
|
myrec[column] = `${getDisplayValue(record, column)} `;
|
|
});
|
|
output.push(myrec)
|
|
});
|
|
}
|
|
}
|
|
return output;
|
|
|
|
} catch (error) {
|
|
console.error("Errore nel recupero della tabella: ", error.message);
|
|
if (options.outhtml) {
|
|
output = `
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Errore nel Recupero della Tabella ${options.nameTable}</title>
|
|
<style>
|
|
body {
|
|
font - family: Arial, sans-serif;
|
|
background-color: #f7f7f7;
|
|
color: #333;
|
|
padding: 20px;
|
|
}
|
|
.error-container {
|
|
background - color: #ffe6e6;
|
|
border: 1px solid #ff4d4d;
|
|
padding: 20px;
|
|
border-radius: 5px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
max-width: 800px;
|
|
margin: auto;
|
|
}
|
|
.error-title {
|
|
font - size: 24px;
|
|
font-weight: bold;
|
|
color: #d8000c;
|
|
margin-bottom: 10px;
|
|
}
|
|
.error-message {
|
|
font - size: 16px;
|
|
white-space: pre-wrap;
|
|
background: #fff;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="error-container">
|
|
<div class="error-title">Errore nel Recupero della Tabella ${options.nameTable} con query: ${options.where}</div>
|
|
<div class="error-message">
|
|
${error.response.data.error || error.stack || error.message}
|
|
</div>
|
|
</div>
|
|
</body>
|
|
`;
|
|
}
|
|
return output;
|
|
// throw new Error("Errore nel recupero della tabella.");
|
|
}
|
|
};
|
|
|
|
// Endpoint per mostrare i dati della tabella
|
|
exports.viewTable = async (req, res) => {
|
|
try {
|
|
const options = req.body.options;
|
|
const tableContent = await getTableContent(options);
|
|
|
|
let out = {};
|
|
|
|
if (options.outhtml) {
|
|
out = `
|
|
<h2>Tabella: ${options.nameTable}</h2>
|
|
${tableContent}
|
|
`
|
|
} else {
|
|
out = tableContent;
|
|
}
|
|
|
|
return res.send({ code: server_constants.RIS_CODE_OK, data: out });
|
|
|
|
} catch (error) {
|
|
console.error('Error: ', error);
|
|
return res.send({ code: server_constants.RIS_CODE_ERR, error });
|
|
}
|
|
};
|
|
|
|
// Endpoint per mostrare i dati della tabella
|
|
exports.queryTable = async (req, res) => {
|
|
try {
|
|
const options = req.body.options;
|
|
const tableContent = await getTableContent(options);
|
|
|
|
let out = {};
|
|
|
|
if (options.outhtml) {
|
|
out = `
|
|
<h2>Tabella: ${options.nameTable}</h2>
|
|
${tableContent}
|
|
`
|
|
} else {
|
|
out = tableContent;
|
|
}
|
|
|
|
return res.send({ code: server_constants.RIS_CODE_OK, data: out });
|
|
|
|
} catch (error) {
|
|
console.error('Error: ', error);
|
|
return res.send({ code: server_constants.RIS_CODE_ERR, error });
|
|
}
|
|
};
|