- scheda prodotto migliorata

- aggiornamento filtri
This commit is contained in:
Surya Paolo
2025-04-24 01:03:27 +02:00
parent 85e2df56e1
commit 4b4e3963ac
10 changed files with 393 additions and 95 deletions

View File

@@ -463,7 +463,7 @@ exports.getTableContentBase = async (options) => {
return output;
} catch (error) {
output = `${error.response.data.error || error.stack || error.message}`;
output = `${error?.response?.data?.error || error?.stack || error.message}`;
console.error("Errore nel recupero della tabella: ", `${error.response.data.error || error.stack || error.message}`);
if (options.outhtml) {
output = `

View File

@@ -58,9 +58,9 @@ CatProdSchema.statics.findAllIdApp = async function (idapp) {
return await CatProd.find(myfind).sort({ name: 1 }).lean();
};
CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp) {
CatProdSchema.statics.updateCatDeleteEmpty = async function (idapp) {
try {
const result = await CatProd.aggregate([
const toDelete = await CatProd.aggregate([
{ $match: { idapp } },
{
$lookup: {
@@ -77,9 +77,72 @@ CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp) {
quanti: { $size: '$products' } // Conta il numero di prodotti per ciascun CatProd
}
},
{ $sort: { name: 1 } } // Ordina i risultati per nome
{ $match: { quanti: 0 } },
]);
if (toDelete.length > 0) {
const ids = toDelete.map(x => x._id);
const ris = await CatProd.deleteMany({ _id: { $in: ids } });
const deletedRecs = toDelete.map(x => ({ _id: x._id, name: x.name }));
if (deletedRecs.length > 0) {
return `Lista Argomenti cancellati: ${deletedRecs.map(x => x.name).join(', ')}`;
}
}
return "Nessun argomento cancellato";
} catch (error) {
console.error('Error UpdateCatDeleteEmpty:', error);
return error;
}
};
CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp) {
try {
const myquery = [
{ $match: { idapp } },
{
$lookup: {
from: 'productinfos', // Nome della tua collezione productInfo
localField: '_id',
foreignField: 'idCatProds',
as: 'products'
}
},
{
$addFields: {
myproducts: {
$filter: {
input: "$products",
as: "prod",
cond: {
$in: ["$$prod.idStatoProdotto", [1, 4, 34, 45, 46]]
}
}
}
}
}, {
$project: {
_id: 1,
name: 1,
quanti: { $size: '$myproducts' }, // Conta il numero di prodotti per ciascun CatProd
products: {
$map: {
input: "$myproducts",
as: "prod",
in: {
name: "$$prod.name"
}
}
}
}
},
{ $sort: { name: 1 } } // Ordina i risultati per nome
];
const result = await CatProd.aggregate(myquery);
return result;
} catch (error) {
console.error('Error retrieving CatProd with title count:', error);

View File

@@ -88,6 +88,12 @@ const productSchema = new Schema({
tipologia: {
type: String,
},
idTipologia: {
type: Number,
},
idTipoFormato: {
type: Number,
},
edizione: {
type: String,
},
@@ -241,73 +247,101 @@ module.exports.executeQueryTable = function (idapp, params) {
module.exports.executeQueryPickup = async function (idapp, params) {
let strfind = tools.removeAccents(params.search.trim().toLowerCase());
let strfindInput = tools.removeAccents(params.search.trim().toLowerCase());
// Rimuove le parole "il" e "la" e gli spazi, le @ e i tabulazioni
// per non farli influire sulla ricerca
strfind = strfind.replace(/\b(il|la|gli|le|lo|un|una)\b/g, '').replace(/[-@\t]/g, '').trim();
strfindInput = strfindInput.replace(/\b(il|la|gli|le|lo|un|una)\b/g, '').replace(/[-@\t]/g, '').trim();
if (strfind === '' && !params.filter) {
if (strfindInput === '' && !params.filter) {
return [];
}
let filterfindexact = {};
if (strfind) {
filterfindexact = { comune: strfind };
if (strfindInput) {
filterfindexact = { comune: strfindInput };
}
let limit = 20;
let risexact = [];
let filterfind = {
const cleanInput = tools.removeAccents(strfindInput.trim());
const words = cleanInput.split(/\s+/);
const escapeRegex = w => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// 🔹 Pattern per productInfo.name: tutte le parole devono essere presenti
const patternAllWords = words.map(w => `(?=.*\\b${escapeRegex(w)}\\b)`).join('') + '.*';
// 🔹 Condizioni per autori
let authorConditions = [];
// Se ci sono esattamente 2 parole (es. "antonio graziano")
if (words.length === 2) {
const [w1, w2] = words.map(escapeRegex);
authorConditions = [
{
'productInfo.authors': {
$elemMatch: {
name: { $regex: `^${w1}`, $options: 'i' },
surname: { $regex: `^${w2}`, $options: 'i' }
}
}
},
{
'productInfo.authors': {
$elemMatch: {
name: { $regex: `^${w2}`, $options: 'i' },
surname: { $regex: `^${w1}`, $options: 'i' }
}
}
}
];
}
// Se c'è solo una parola (es. "antonio")
if (words.length === 1) {
const word = escapeRegex(words[0]);
authorConditions = [
{
'productInfo.authors': {
$elemMatch: {
$or: [
{ name: { $regex: `^${word}`, $options: 'i' } },
{ surname: { $regex: `^${word}`, $options: 'i' } }
]
}
}
}
];
}
// 🔹 Filtro finale
const filterfind = {
idapp,
$or: [
{
'productInfo.name': {
$regex: `(?i).*${strfind}.*`, // Cerca una o più parole che sono contenute
$options: 'i' // Rende la ricerca case-insensitive
$regex: patternAllWords,
$options: 'i'
}
},
{
'productInfo.code': {
$regex: `\\b${strfind}`, // Cerca parole che iniziano con strfind
$options: 'i' // Rende la ricerca case-insensitive
$regex: `\\b${escapeRegex(cleanInput)}`,
$options: 'i'
}
},
{
'productInfo.sku': strfind
},
{
$and: [
{
'productInfo.authors.name': {
$regex: `(?i).*${strfind.split(' ').shift()}.*`, // Cerca la prima parola
$options: 'i' // Rende la ricerca case-insensitive
}
},
{
'productInfo.authors.surname': {
$regex: `(?i).*${strfind.split(' ').pop()}.*`, // Cerca la seconda parola
$options: 'i' // Rende la ricerca case-insensitive
}
},
]
},
{
'productInfo.authors.name': {
$regex: `(?i).*${strfind}.*`, // Cerca una o più parole che sono contenute
$options: 'i' // Rende la ricerca case-insensitive
}
},
{
'productInfo.authors.surname': {
$regex: `(?i).*${strfind}.*`, // Cerca una o più parole che sono contenute
$options: 'i' // Rende la ricerca case-insensitive
}
'productInfo.sku': cleanInput
},
...authorConditions
]
};
if (params.filter) {
filterfind = { ...params.filter, ...filterfind };
limit = 200;
@@ -357,6 +391,13 @@ module.exports.executeQueryPickup = async function (idapp, params) {
},
arrvariazioni: "$arrvariazioni",
}
},
{
$sort: {
'arrvariazioni.0.quantita': -1, // Ordina per arrvariazioni[0].quantita , decrescente
'productInfo.date_pub': -1,
'productInfo.name': 1 // Ordina per name in ordine crescente
}
}
];

View File

@@ -447,6 +447,7 @@ module.exports.correggiProductTypes = async function () {
}
module.exports.updateProductInfoByStats = async function (idapp) {
let mylog = '';
try {
const ProductInfo = this;
@@ -455,7 +456,7 @@ module.exports.updateProductInfoByStats = async function (idapp) {
// Ottieni le statistiche dalla query
const statistics = await T_WEB_ArticoliFatturati.getStatistics();
let log = "Inizio Aggiornamento Statistiche... \n";
mylog = "Inizio Aggiornamento Statistiche... \n";
console.log(mylog);
// Itera sui risultati e aggiorna productInfo

View File

@@ -1,7 +1,10 @@
// t_web_tipiformato.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
mongoose.Promise = global.Promise;
mongoose.level = "F";
/**
* @typedef {Object} TipoFormato
* @property {bigint} Id
@@ -20,4 +23,32 @@ const TipoFormatoSchema = new Schema({
EnabledAlFresco: Boolean
}, { collection: 't_web_tipiformatos' });
module.exports = mongoose.model('T_WEB_TipiFormato', TipoFormatoSchema);
const T_WEB_TipiFormato = module.exports = mongoose.model('T_WEB_TipiFormato', TipoFormatoSchema);
module.exports.findAllIdApp = async function () {
const myfind = {};
const myquery = [
{
$sort: { IdTipoFormato: 1, DataOra: -1 } // ordina per ID e DataOra decrescente
},
{
$group: {
_id: "$IdTipoFormato",
IdTipoFormato: { $first: "$IdTipoFormato" },
Descrizione: { $first: "$Descrizione" },
DataOra: { $first: "$DataOra" },
// aggiungi altri campi se servono
}
},
{
$sort: { IdTipoFormato: 1 } // opzionale, per ordinare il risultato
}
];
const rec = await T_WEB_TipiFormato.aggregate(myquery);
return rec;
};

View File

@@ -0,0 +1,54 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
mongoose.Promise = global.Promise;
mongoose.level = "F";
/**
* @typedef {Object} Tipologie
* @property {bigint} Id
* @property {number} IdTipologia
* @property {string} Descrizione
* @property {Date} DataOra
* @property {boolean} Enabled
* @property {boolean} EnabledAlFresco
*/
const TipologieSchema = new Schema({
IdTipologia: Number,
Descrizione: { type: String, maxlength: 100 },
DataOra: Date,
Enabled: Boolean,
EnabledAlFresco: Boolean
}, { collection: 't_web_tipologies' });
const T_WEB_Tipologie = module.exports = mongoose.model('T_WEB_Tipologie', TipologieSchema);
module.exports.findAllIdApp = async function () {
const myfind = {};
const myquery = [
{
$sort: { IdTipologia: 1, DataOra: -1 } // ordina per ID e DataOra decrescente
},
{
$group: {
_id: "$IdTipologia",
IdTipologia: { $first: "$IdTipologia" },
Descrizione: { $first: "$Descrizione" },
DataOra: { $first: "$DataOra" },
// aggiungi altri campi se servono
}
},
{
$sort: { IdTipologia: 1 } // opzionale, per ordinare il risultato
}
];
const rec = await T_WEB_Tipologie.aggregate(myquery);
return rec;
};

View File

@@ -24,31 +24,32 @@ class Macro {
let mylog = ''
let numrec = 0;
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,
}
try {
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 miomatch2 = {};
let miolimit = 0;
if (options.caricatutti) {
@@ -56,7 +57,37 @@ class Macro {
if (options.usaDBGMLocale) {
mylog += '*** usaDBGMLocale ***\n';
miomatch = { IdStatoProdotto: { $in: [1, 4, 34, 45, 46] } };
//miomatch2 = { IdStatoProdotto: { $in: [1, 3, 4, 6, 7, 8, 9, 20, 26, 33, 34, 45, 46, 47, 48] } };
miomatch2 = {
$or: [
{ DescrizioneStatoProdotto: 'In commercio' },
{ DescrizioneStatoProdotto: '2023 in commercio' },
{ DescrizioneStatoProdotto: 'Vendita sito' },
{ DescrizioneStatoProdotto: 'In prevendita' },
{ DescrizioneStatoProdotto: 'Prossima uscita' }
]
};
/*
1 In commercio
3 Ristampa
4 Prossima uscita/pubblicazione
6 In promozione
7 In fase di valutazione
8 Titolo in esaurimento (in attesa Nuova Edizione)
9 Titolo in esaurimento
20 Titolo in esaurimento (in att N.E Ricopertinata)
26 Titolo in Esaurimento (disponibile N.E.)
33 In commercio (digitale)
34 In prevendita
45 Vendita sito
46 2023 in commercio
47 Assoluto NO Reso
48 Titolo esaurito
*/
// options.where = { IdStatoProdotto: { $in: [1, 4, 34, 45, 46] } };
} else {
@@ -66,7 +97,8 @@ class Macro {
DescrizioneStatoProdotto = 'Vendita sito' OR
DescrizioneStatoProdotto = 'In prevendita' OR
DescrizioneStatoProdotto = 'Prossima uscita') AND
(DescrizioneTipologia = 'Libri')
(DescrizioneTipologia = 'Libri' OR
DescrizioneTipologia = 'Cartonato')
`;
}
@@ -84,7 +116,7 @@ class Macro {
if (true) {
filtroTipologia = {
$match: {
DescrizioneTipologia: 'Libri',
DescrizioneTipologia: { $in: ['Libri', 'Cartolibro'] },
}
};
}
@@ -93,7 +125,9 @@ class Macro {
mylog += '*** usaDBGMLocale ***\n';
options.aggregation = [
{
$match: miomatch
$match: {
...miomatch,
}
},
{
$sort: {
@@ -130,6 +164,11 @@ class Macro {
DescrizioneStatoProdotto: { $arrayElemAt: ['$StatoProdotto.Descrizione', 0] },
}
},
{
$match: {
...miomatch2
}
},
{
$lookup: {
from: 't_web_tipologies',
@@ -154,7 +193,7 @@ class Macro {
{
$match: {
$expr: {
$eq: ["$DescrizioneTipologia", "Libri"]
$in: ["$DescrizioneTipologia", ["Libri", "Cartolibro"]]
}
}
},
@@ -356,6 +395,8 @@ class Macro {
const recproducts = await getTableContent(options);
let idRecUpdated = null;
if (!tools.isArray(recproducts)) {
console.error('Error: ', recproducts);
mylog += recproducts + '\n';
@@ -370,8 +411,7 @@ class Macro {
if (Array.isArray(recproducts)) {
for (const recproduct of recproducts) {
// if (!options.caricatutti) {
await this.
elaboraProdotto(recproduct, opt);
await this.elaboraProdotto(recproduct, opt);
count++;
if (count % 50 === 0) {
@@ -382,15 +422,22 @@ class Macro {
}
}
mylog += ' *** IMPORTATI: ' + opt.imported + ' AGGIORNATI = ' + opt.updated + ' (su ' + numrec + ' RECORD)';
if (numrec > 1) {
opt.idRecUpdated = null;
}
if (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 };
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog, idRecUpdated: opt.idRecUpdated, table: opt.table };
} catch (e) {
mylog += 'ERRORE ! *** IMPORTATI: ' + opt.imported + ' AGGIORNATI = ' + opt.updated + ' (su ' + numrec + ' RECORD)';
mylog += 'ERRORE ! *** IMPORTATI: ' + opt?.imported + ' AGGIORNATI = ' + opt?.updated + ' (su ' + numrec + ' RECORD)';
opt.logerror = e.message;
console.error(e.message);
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog };
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog, logerror: opt.logerror };
}
}
@@ -677,6 +724,8 @@ class Macro {
sale_price: productGM.PrezzoIvatoScontatoCampagna,
formato: productGM.DescrizioneFormato,
Tipologia: productGM.DescrizioneTipologia,
idTipologia: productGM.IdTipologia,
idTipoFormato: productGM.IdTipoFormato,
Edizione: productGM.Edizione,
Pagine: productGM.Pagine,
misure: productGM.Misure,
@@ -690,9 +739,9 @@ class Macro {
let vers = 0;
if (productGM.DescrizioneTipologia === 'Usato')
vers = shared_consts.PRODUCTTYPE.USATO;
else if (productGM.DescrizioneTipologia === 'Download')
//if (productGM.DescrizioneTipologia === 'Usato')
// vers = shared_consts.PRODUCTTYPE.USATO;
if (productGM.DescrizioneTipologia === 'Download')
vers = shared_consts.PRODUCTTYPE.DOWNLOAD;
else if (productGM.DescrizioneTipologia === 'DVD')
vers = shared_consts.PRODUCTTYPE.DVD;
@@ -869,8 +918,10 @@ class Macro {
status: product.Stato || null,
price: product.price ? parseFloat(tools.convertPriceEurToValue(product.price)) : null,
sale_price: product.sale_price ? parseFloat(tools.convertPriceEurToValue(product.sale_price)) : null,
formato: product.formato || '',
tipologia: product.Tipologia || '',
// formato: product.formato || '',
idTipologia: product.idTipologia || '',
idTipoFormato: product.idTipoFormato || '',
// tipologia: product.Tipologia || '',
edizione: product.Edizione || '',
pagine: tools.isValidNumber(product.Pagine) ? tools.convstrToInt(product.Pagine) : 0,
misure: product.misure || '',
@@ -902,8 +953,8 @@ class Macro {
const myproduct = {
...product,
...(!product.isbn ? [{ isbn: risrecInfo.code }]: []),
...(!product.maxbookableGASQty && risrecInfo.maxbookableGASQty ? [{ maxbookableGASQty: risrecInfo.maxbookableGASQty }]: []),
...(!product.isbn ? [{ isbn: risrecInfo.code }] : []),
...(!product.maxbookableGASQty && risrecInfo.maxbookableGASQty ? [{ maxbookableGASQty: risrecInfo.maxbookableGASQty }] : []),
idapp: this.idapp,
arrvariazioni: [variazione],
};
@@ -912,25 +963,68 @@ class Macro {
if (recold) {
const arrvariazioni = this.aggiornaVariazioni(recold.arrvariazioni, variazione);
risultupdate = await Product.updateOne(this.queryprod, { $set: { arrvariazioni } }, { new: true });
if (risultupdate && risultupdate.modifiedCount > 0) {
options.updated++;
}
const updatedDoc = await Product.findOneAndUpdate(this.queryprod, { $set: { arrvariazioni } }, {
upsert: true,
new: true, // restituisce il documento aggiornato
includeResultMetadata: true
});
if (updatedDoc && updatedDoc.lastErrorObject) {
const wasUpserted = updatedDoc.lastErrorObject.upserted !== undefined;
const wasUpdated = updatedDoc.lastErrorObject.n === 1 && !wasUpserted;
if (wasUpserted || wasUpdated) {
options.updated++;
options.table = 'products';
options.idRecUpdated = wasUpserted
? updatedDoc.lastErrorObject.upserted
: updatedDoc.value._id;
}
}
if (recold.isbn !== risrecInfo.code) {
product.isbn = risrecInfo.code;
}
}
if (!recold || recold.isbn !== myproduct.isbn || !recold.idapp || !this.recProductExist) {
risultupdate = await Product.updateOne(this.queryprod, { $set: myproduct }, { new: true, upsert: true });
if (risultupdate && risultupdate.modifiedCount > 0) {
options.imported++;
if (!recold || this.isModified(recold, myproduct) || !this.recProductExist) {
const updatedDoc = await Product.findOneAndUpdate(this.queryprod, { $set: myproduct },
{
new: true, upsert: true,
includeResultMetadata: true
}
);
if (updatedDoc && updatedDoc.lastErrorObject) {
const wasUpserted = updatedDoc.lastErrorObject.upserted !== undefined;
const wasUpdated = updatedDoc.lastErrorObject.n === 1 && !wasUpserted;
if (wasUpserted || wasUpdated) {
options.imported++;
options.table = 'products';
options.idRecUpdated = wasUpserted
? updatedDoc.lastErrorObject.upserted
: updatedDoc.value._id;
}
}
}
// console.log('risultupdate', risultupdate);
}
/**
* Verifica se i campi nella lista sono stati modificati da una versione all'altra.
* @param {Object} recordOld - record vecchio
* @param {Object} recordNew - record nuovo
* @param {string[]} listaCampi - lista dei campi da verificare
* @returns {boolean} true se i campi sono stati modificati
*/
isModified(recordOld, recordNew) {
const listaCampi = [
'idapp',
'isbn',
//++FIELD_PRODUCT
]
return listaCampi.some((campo) => recordOld[campo] !== recordNew[campo]);
}
}
module.exports = Macro;

View File

@@ -85,6 +85,8 @@ const { Category } = require('../models/category');
const Group = require('../models/group');
const T_WEB_StatiProdotto = require('../models/t_web_statiprodotto');
const T_WEB_Tipologie = require('../models/t_web_tipologie');
const T_WEB_TipiFormato = require('../models/t_web_tipiformato');
const tools = require('../tools/general');
@@ -2020,6 +2022,8 @@ async function load(req, res, version = '0') {
catalogs: version >= 91 ? Catalog.findAllIdApp(idapp) : Promise.resolve([]),
catprtotali: version >= 91 ? CatProd.getCatProdWithTitleCount(idapp) : Promise.resolve([]),
stati_prodotto: version >= 91 ? T_WEB_StatiProdotto.findAllIdApp() : Promise.resolve([]),
tipologie: version >= 91 ? T_WEB_Tipologie.findAllIdApp() : Promise.resolve([]),
tipoformato: version >= 91 ? T_WEB_TipiFormato.findAllIdApp() : Promise.resolve([]),
myuserextra: req.user
? User.addExtraInfo(idapp, req.user, version)
: Promise.resolve(null)
@@ -2134,6 +2138,8 @@ async function load(req, res, version = '0') {
catalogs: data.catalogs,
catprtotali: data.catprtotali,
stati_prodotto: data.stati_prodotto,
tipologie: data.tipologie,
tipoformato: data.tipoformato,
};
}

View File

@@ -1173,6 +1173,11 @@ async function eseguiDbOp(idapp, mydata, locale, req, res) {
}
ris = { mystr };
} else if (mydata.dbop === 'UpdateCatDeleteEmpty') {
mystr = await CatProd.updateCatDeleteEmpty(req.body.idapp);
ris = { mystr };
} else if (mydata.dbop === 'UpdateStatFatturato') {
mystr = await ProductInfo.updateProductInfoByStats(req.body.idapp);

View File

@@ -77,6 +77,7 @@ const CatAI = require('../models/catai');
const { QueryAI } = require('../models/queryai');
const SubCatProd = require('../models/subcatprod');
const T_Web_StatiProdotto = require('../models/t_web_statiprodotto');
const T_Web_Tipologie = require('../models/t_web_tipologie');
const { Category } = require('../models/category');
const ShareWithUs = require('../models/sharewithus');
const Site = require('../models/site');
@@ -146,6 +147,8 @@ module.exports = {
mytable = SubCatProd;
else if (tablename === 't_web_statiprodottos')
mytable = T_Web_StatiProdotto;
else if (tablename === 't_web_tipologies')
mytable = T_Web_Tipologie;
else if (tablename === 'sharewithus')
mytable = ShareWithUs;
else if (tablename === 'sites')