- aggiornamento cataloghi.

possibilità di estrapolare i dati da GM direttamente
- migrazione delle tabelle di GM in locale
- corretto l'ordinamento del Catalogo
This commit is contained in:
Surya Paolo
2025-04-18 13:23:59 +02:00
parent fba2ebd710
commit ad45ce60ee
13 changed files with 988 additions and 106 deletions

View File

@@ -13,48 +13,306 @@ const shared_consts = require('../tools/shared_nodejs'); // Assicurati di avere
const { getTableContent } = require('../controllers/articleController');
class Macro {
constructor(idapp) {
constructor(idapp, options) {
this.idapp = idapp;
this.localoptions = options;
}
async updateLocalDbFromGM_T_Web_Articoli(params) {
const options = {
idapp: params.idapp,
nameTable: 'T_Web_Articoli',
campispeciali: true,
recordraw: true,
query: '',
showQtaDisponibile: true,
outhtml: false,
cmd: shared_consts.CmdQueryMs.GET,
inputdaGM: true,
}
let mylog = ''
let numrec = 0;
try {
if (params.caricatutti) {
options.where = '';
} else {
// Singolo
options.where = 'T.IdArticolo =' + params.sku;
}
let updated = 0,
imported = 0,
errors = 0;
const recproducts = await getTableContent(options);
if (Array.isArray(recproducts)) {
for (const recproduct of recproducts) {
await this.elaboraProdotto(recproduct, { updated, imported, errors, inputdaGM: options.inputdaGM, idapp: options.idapp });
const options = {
idapp: params.idapp,
nameTable: 'T_Web_Articoli',
campispeciali: true,
recordraw: true,
query: '',
showQtaDisponibile: true,
outhtml: false,
cmd: shared_consts.CmdQueryMs.GET,
inputdaGM: true,
...params,
}
let opt = {
updated: 0,
imported: 0,
errors: 0,
inputdaGM: options.inputdaGM,
idapp: options.idapp,
}
let miomatch = {};
let miolimit = 0;
if (options.caricatutti) {
mylog = '*** CaricaTutti ***\n';
if (options.usaDBGMLocale) {
mylog += '*** usaDBGMLocale ***\n';
miomatch = { IdStatoProdotto: { $in: [1, 4, 34, 45, 46] } };
// options.where = { IdStatoProdotto: { $in: [1, 4, 34, 45, 46] } };
} else {
options.where = `
(DescrizioneStatoProdotto = 'In commercio' OR
DescrizioneStatoProdotto = '2023 in commercio' OR
DescrizioneStatoProdotto = 'Vendita sito' OR
DescrizioneStatoProdotto = 'In prevendita' OR
DescrizioneStatoProdotto = 'Prossima uscita') AND
(DescrizioneTipologia = 'Libri')
`;
}
} else {
miolimit = 1;
miomatch = {
IdArticolo: Number(options.sku),
Ean13: options.isbn,
};
}
if (options.usaDBGMLocale) {
mylog += '*** usaDBGMLocale ***\n';
options.aggregation = [
{
$match: miomatch
},
{
$sort: {
DataOra: -1,
},
},
{
$group: {
_id: "$IdArticolo",
lastRecord: { $first: "$$ROOT" } // prendi il record più recente
}
},
{
$replaceRoot: { newRoot: "$lastRecord" }
},
...(miolimit > 0 ? [{ $limit: miolimit }] : []),
{
$lookup: {
from: 't_web_statiprodottos',
localField: 'IdStatoProdotto',
foreignField: 'IdStatoProdotto',
as: 'StatoProdotto',
}
},
{
$addFields: {
DescrizioneStatoProdotto: { $arrayElemAt: ['$StatoProdotto.Descrizione', 0] },
}
},
{
$lookup: {
from: 't_web_tipologies',
localField: 'idTipologia',
foreignField: 'idTipologia',
as: 'DescrizioneTipologia',
}
},
{
$addFields: {
DescrizioneTipologia: { $arrayElemAt: ['$DescrizioneTipologia.Descrizione', 0] },
}
},
{
$lookup: {
from: 't_web_tipiformatos',
localField: 'idFormato',
foreignField: 'idFormato',
as: 'DescrizioneFormato',
}
},
{
$addFields: {
DescrizioneFormato: { $arrayElemAt: ['$DescrizioneFormato.Descrizione', 0] },
}
},
{
$lookup: {
from: 't_web_collanes',
localField: 'idCollana',
foreignField: 'idCollana',
as: 'DescrizioneCollana',
}
},
{
$addFields: {
DescrizioneCollana: { $arrayElemAt: ['$DescrizioneCollana.Descrizione', 0] },
}
},
{
$lookup: {
from: 't_web_edizionis',
localField: 'IdEdizione',
foreignField: 'CodEdizione',
as: 'editore',
}
},
{
$addFields: {
editore: { $arrayElemAt: ['$editore.Descrizione', 0] },
}
},
{
$addFields: {
ListaAutoriArray: {
$map: {
input: { $split: ['$ListaAutori', ','] },
as: 'id',
in: {
$convert: {
input: { $trim: { input: '$$id' } },
to: "int",
onError: null,
onNull: null
}
}
}
}
}
},
{
$lookup: {
from: 't_web_autoris', // assicurati che il nome della collezione sia corretto
localField: 'ListaAutoriArray',
foreignField: 'IdAutore',
as: 'AutoriDettagliati'
}
},
{
$addFields: {
AutoriCompleti: {
$reduce: {
input: '$AutoriDettagliati',
initialValue: '',
in: {
$cond: [
{ $eq: ['$$value', ''] },
{ $concat: ['$$this.Nome', ' ', '$$this.Cognome'] },
{ $concat: ['$$value', ', ', '$$this.Nome', ' ', '$$this.Cognome'] }
]
}
}
}
}
},
{
$addFields: {
ListaArgomentiArray: {
$map: {
input: { $split: ['$ListaArgomenti', ','] },
as: 'id',
in: { $toInt: { $trim: { input: '$$id' } } }
}
}
}
},
// Step: Lookup verso collezione degli argomenti, ordinando per DataOra
{
$lookup: {
from: 't_web_argomentis',
let: { argomentiArray: '$ListaArgomentiArray' },
pipeline: [
{
$match: {
$expr: {
$in: ['$IdArgomento',
{ $ifNull: ["$$argomentiArray", []] }
]
}
}
},
{ $sort: { DataOra: -1 } },
{ $limit: 1 }
],
as: 'ArgomentiDettagliati'
}
},
// Step: Genera campo DescrArgomento concatenando tutte le descrizioni
{
$addFields: {
DescrArgomento: {
$reduce: {
input: '$ArgomentiDettagliati',
initialValue: '',
in: {
$cond: [
{ $eq: ['$$value', ''] },
'$$this.Descrizione',
{ $concat: ['$$value', ', ', '$$this.Descrizione'] }
]
}
}
}
}
},
// Step: Pulisci i campi temporanei
{
$project: {
ListaArgomentiArray: 0,
// ArgomentiDettagliati: 0
}
},
{
$lookup: {
from: 't_web_marchieditorialis',
localField: 'IdMarchioEditoriale',
foreignField: 'IdMarchioEditoriale',
as: 'CasaEditrice',
}
},
{
$addFields: {
CasaEditrice: { $arrayElemAt: ['$CasaEditrice.Descrizione', 0] },
}
},
];
} else {
if (!options.caricatutti) {
// Singolo
options.where = 'T.IdArticolo =' + options.sku + ' AND T.Ean13 = ' + options.isbn;
}
}
const recproducts = await getTableContent(options);
if (!tools.isArray(recproducts)) {
console.error('Error: ', recproducts);
mylog += recproducts + '\n';
} else {
numrec = recproducts?.length || 0;
console.log('numrec', numrec);
}
if (Array.isArray(recproducts)) {
for (const recproduct of recproducts) {
if (!options.caricatutti) {
await this.elaboraProdotto(recproduct, opt);
}
}
}
mylog += ' *** IMPORTATI: ' + opt.imported + ' AGGIORNATI = ' + opt.updated + ' (su ' + numrec + ' RECORD)';
console.log(mylog);
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog };
} catch (e) {
mylog += 'ERRORE ! *** IMPORTATI: ' + opt.imported + ' AGGIORNATI = ' + opt.updated + ' (su ' + numrec + ' RECORD)';
console.error(e.message);
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog };
}
console.log(
'*** IMPORTATI: ',
imported,
'AGGIORNATI = ' + updated + ' (su ' + dataObjects.length + ' RECORD)'
);
return { updated, imported, errors };
}
@@ -151,7 +409,7 @@ class Macro {
/**
* Elabora un singolo prodotto.
*/
async elaboraProdotto(productInput, { updated, imported, errors, inputdaGM, idapp }) {
async elaboraProdotto(productInput, options) {
let isnuovo = false,
setta = false,
importa = true;
@@ -159,17 +417,19 @@ class Macro {
let product = { ...productInput };
if (inputdaGM)
product = this.convertiDaCampiGMACampoFDV_ProductInfo(idapp, product);
if (options.inputdaGM)
product = this.convertiDaCampiGMACampoFDV_ProductInfo(options.idapp, product);
if (!product.title || !product.sku) importa = false;
if (importa) {
const productInfo = this.preparaProductInfo(product);
const recrankingisbn = await ImportaIsbn.findOne({ sku: product.sku }).lean();
if (recrankingisbn) {
this.aggiornaCampiDaIsbn(productInfo, recrankingisbn);
if (this.localoptions?.importadaFDV) {
const recrankingisbn = await ImportaIsbn.findOne({ sku: product.sku }).lean();
if (recrankingisbn) {
this.aggiornaCampiDaIsbn(product, recrankingisbn);
}
}
if (!product.hasOwnProperty('active')) {
@@ -189,10 +449,10 @@ class Macro {
if (risrecInfo) {
await this.aggiornaImmagineSeNecessario(risrecInfo);
await this.gestisciGasOrdine(product, risrecInfo);
await this.gestisciVariazioni(product, risrecInfo, { updated, imported, errors });
await this.gestisciVariazioni(product, risrecInfo, options);
} else {
console.error('Errore ProductInfo:', product.code);
errors++;
options.errors++;
}
}
}
@@ -223,6 +483,8 @@ class Macro {
if (!recProductExist) {
product.idGasordine = recGas ? recGas._id : null;
await Product.findOneAndUpdate({ _id: product._id }, { $set: { idGasordine: product.idGasordine } });
}
}
@@ -248,10 +510,10 @@ class Macro {
checkout_link: product.checkout_link || undefined,
idStatoProdotto: product.idStatoProdotto || undefined,
date_pub: product.date_pub || undefined,
sottotitolo: product.Sottotitolo || undefined,
sottotitolo: product.sottotitolo || undefined,
};
} catch (e) {
console.error('Errore preparaProductInfo:', e);
console.error('Errore preparaProductInfo :', e);
return {};
}
}
@@ -328,8 +590,8 @@ class Macro {
short_descr: '',
editore: productGM.CasaEditrice,
collezione: productGM.DescrizioneCollana,
editore: productGM.CasaEditrice,
Autore: productGM.AutoriCompleti,
publisher: productGM.CasaEditrice,
DescrArgomento: productGM.DescrArgomento,
idStatoProdotto: productGM.IdStatoProdotto,
Stato: this.getStatusByIdStatoProdotto(productGM.IdStatoProdotto),
@@ -491,8 +753,8 @@ class Macro {
* Gestisce l'editore del prodotto.
*/
async gestisciEditore(productInfo, product) {
if (product.publisher) {
const publisher = product.publisher.trim();
if (productInfo.publisher) {
const publisher = productInfo.publisher.trim();
const recpublisher = await Publisher.findOne({ idapp: this.idapp, name: publisher }).lean();
if (!recpublisher) {
@@ -551,7 +813,7 @@ class Macro {
/**
* Gestisce le variazioni del prodotto.
*/
async gestisciVariazioni(product, risrecInfo, { updated, imported, errors }) {
async gestisciVariazioni(product, risrecInfo, options) {
const queryprod = { idProductInfo: risrecInfo._id };
const recold = await Product.findOne(queryprod).lean();
const variazione = this.preparaVariazione(product);
@@ -561,13 +823,14 @@ class Macro {
if (recold) {
const arrvariazioni = this.aggiornaVariazioni(recold.arrvariazioni, variazione);
risultupdate = await Product.findOneAndUpdate(queryprod, { $set: { arrvariazioni } });
options.updated++;
} else {
const myproduct = { ...queryprod, arrvariazioni: [variazione] };
risultupdate = await Product.findOneAndUpdate(queryprod, { $set: myproduct }, { new: true, upsert: true });
imported++;
options.imported++;
}
console.log('risultupdate', risultupdate);
// console.log('risultupdate', risultupdate);
}
}

View File

@@ -0,0 +1,122 @@
const axios = require('axios');
const mongoose = require('mongoose').set('debug', false)
const Schema = mongoose.Schema;
const tools = require('../tools/general');
mongoose.Promise = global.Promise;
mongoose.level = "F";
class MssqlMigrator {
/**
* Costruttore della classe MssqlMigrator
* @param {string} serverUrl - URL del server che esegue le query MSSQL
* @param {string} apiKey - API Key per l'autenticazione
*/
constructor() {
this.serverUrl = process.env.SERVER_A_URL;
this.apiKey = process.env.API_KEY_MSSQL;
}
/**
* Migra una lista di tabelle MSSQL in MongoDB usando Mongoose
* @param {string[]} tableNames - Lista dei nomi delle tabelle MSSQL
* @returns {string} logs - Lista dei log di esecuzione
*/
async migrateTables(tableNames) {
try {
const logs = [];
for (const tableName of tableNames) {
try {
logs.push(`\n>> Recupero dati da MSSQL per la tabella: ${tableName}`);
console.log(logs[logs.length - 1]);
const dataQuery = `SELECT * FROM [${tableName}]`;
let dataResponse = null;
try {
dataResponse = await axios.post(
`${this.serverUrl}/query`,
{ query: dataQuery },
{ headers: { 'x-api-key': this.apiKey } },
null,
{ timeout: 300000 });
} catch (error) {
console.error('Error: ', error);
if (error.message === 'socket hang up') {
console.log('Error: hangup, waiting 5 seconds and retrying...');
await new Promise(resolve => setTimeout(resolve, 5000));
dataResponse = await axios.post(
`${this.serverUrl}/query`,
{ query: dataQuery },
{ headers: { 'x-api-key': this.apiKey } },
null,
{ timeout: 300000 });
} else {
throw error;
}
}
const records = dataResponse?.data;
if (!records || records.length === 0) {
logs.push(`⚠️ Nessun record trovato per la tabella: ${tableName}`);
console.log(logs[logs.length - 1]);
continue;
}
const sample = records[0];
const schemaDef = {};
for (const key of Object.keys(sample)) {
const value = sample[key];
if (typeof value === 'string' && key.startsWith('Data') && !isNaN(Date.parse(value))) {
schemaDef[key] = Date;
} else if (typeof value === 'number') {
schemaDef[key] = Number;
} else if (typeof value === 'boolean') {
schemaDef[key] = Boolean;
} else if (value instanceof Date) {
schemaDef[key] = Date;
} else {
schemaDef[key] = mongoose.Schema.Types.Mixed;
}
}
const dynamicSchema = new mongoose.Schema(schemaDef, { strict: false });
const modelName = tableName.charAt(0).toUpperCase() + tableName.slice(1);
// Se il modello esiste già, lo riutilizziamo
const DynamicModel = mongoose.models[modelName] || mongoose.model(modelName, dynamicSchema);
// ❗ Elimina tutti i documenti esistenti prima dell'inserimento
await DynamicModel.deleteMany({});
logs.push(`✅ Inserimento di ${records.length} record nella collezione MongoDB: ${modelName}`);
console.log(logs[logs.length - 1]);
await DynamicModel.insertMany(records);
} catch (error) {
logs.push(`❌ Errore con la tabella ${tableName}:`, error.message);
console.log(logs[logs.length - 1]);
}
}
logs.push('\n🎉 Tutte le tabelle sono state migrate.');
console.log(logs[logs.length - 1]);
return logs.join('\n');
} catch (error) {
logs.push(`❌ Errore migrateTables:`, error.message);
console.log(logs[logs.length - 1]);
}
}
}
module.exports = MssqlMigrator;