1434 lines
34 KiB
JavaScript
Executable File
1434 lines
34 KiB
JavaScript
Executable File
mongoose = require('mongoose').set('debug', false);
|
|
const Schema = mongoose.Schema;
|
|
|
|
const tools = require('../tools/general');
|
|
|
|
const Producer = require('../models/producer');
|
|
const Storehouse = require('../models/storehouse');
|
|
const Provider = require('../models/provider');
|
|
const CatProd = require('../models/catprod');
|
|
const SubCatProd = require('../models/subcatprod');
|
|
const Gasordine = require('../models/gasordine');
|
|
const Scontistica = require('../models/scontistica');
|
|
|
|
const shared_consts = require('../tools/shared_nodejs');
|
|
|
|
const { ObjectId } = require('mongodb');
|
|
|
|
mongoose.Promise = global.Promise;
|
|
mongoose.level = 'F';
|
|
|
|
// A1P
|
|
|
|
// Resolving error Unknown modifier: $pushAll
|
|
mongoose.plugin((schema) => {
|
|
schema.options.usePushEach = true;
|
|
});
|
|
|
|
const productSchema = new Schema({
|
|
idapp: {
|
|
type: String,
|
|
},
|
|
deleted: {
|
|
type: Boolean,
|
|
},
|
|
active: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
isbn: {
|
|
type: String,
|
|
},
|
|
idProductInfo: { type: Schema.Types.ObjectId, ref: 'ProductInfo', index: true }, //@@@ DA CANCELLARE DOPO DELLA CONVERSIONE
|
|
productInfo: {
|
|
department: {
|
|
type: String, ref: 'Department'
|
|
},
|
|
sku: {
|
|
type: String,
|
|
},
|
|
code: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
codice: {
|
|
type: String,
|
|
}, // codice interno prodotto
|
|
id_wp: {
|
|
type: String,
|
|
}, // id in wordpress
|
|
codice_EAN: {
|
|
type: String,
|
|
},
|
|
barcode: {
|
|
type: String,
|
|
},
|
|
name: {
|
|
type: String,
|
|
index: true,
|
|
},
|
|
description: {
|
|
type: String,
|
|
},
|
|
short_descr: {
|
|
type: String,
|
|
},
|
|
idCatProds: [{
|
|
type: Schema.Types.ObjectId, ref: 'CatProd', index: true
|
|
}],
|
|
idSubCatProds: [{
|
|
type: Schema.Types.ObjectId, ref: 'SubCatProd'
|
|
}],
|
|
idStatoProdotto: {
|
|
type: Number, index: true
|
|
},
|
|
color: {
|
|
type: String
|
|
},
|
|
size: {
|
|
type: String // 11x4x3
|
|
},
|
|
weight: {
|
|
type: Number
|
|
},
|
|
weight_lordo: {
|
|
type: Number
|
|
},
|
|
unit: {
|
|
type: Number,
|
|
},
|
|
unit_lordo: {
|
|
type: Number,
|
|
},
|
|
sfuso: {
|
|
type: Boolean
|
|
}, // serve se moltiplicare le qta (es: 12 kg) oppure fare (2 x 20 ml)
|
|
vegan: {
|
|
type: Boolean
|
|
},
|
|
icon: {
|
|
type: String,
|
|
},
|
|
img: {
|
|
type: String,
|
|
},
|
|
imagefile: {
|
|
type: String,
|
|
},
|
|
image_not_found: {
|
|
type: Boolean,
|
|
},
|
|
vers_img: {
|
|
type: Number,
|
|
},
|
|
image_link: {
|
|
type: String,
|
|
},
|
|
link_scheda: {
|
|
type: String,
|
|
},
|
|
link: {
|
|
type: String,
|
|
},
|
|
checkout_link: {
|
|
type: String,
|
|
},
|
|
versioneGM: {
|
|
type: String,
|
|
},
|
|
img2: {
|
|
type: String,
|
|
},
|
|
img3: {
|
|
type: String,
|
|
},
|
|
img4: {
|
|
type: String,
|
|
},
|
|
ingredienti: {
|
|
type: String,
|
|
},
|
|
valori_nutrizionali: {
|
|
type: String,
|
|
},
|
|
note: {
|
|
type: String,
|
|
},
|
|
idAuthors: [{
|
|
type: Schema.Types.ObjectId, ref: 'Author', index: true
|
|
}],
|
|
idCollana: {
|
|
type: Schema.Types.ObjectId, ref: 'Collana', index: true
|
|
},
|
|
idPublisher: {
|
|
type: Schema.Types.ObjectId, ref: 'Publisher', index: true
|
|
},
|
|
collezione: {
|
|
type: String,
|
|
},
|
|
ListaArgomenti: {
|
|
type: String,
|
|
},
|
|
date_pub: {
|
|
type: Date,
|
|
index: 1,
|
|
},
|
|
date_pub_ts: {
|
|
type: Number,
|
|
},
|
|
productTypes: [{
|
|
type: Number,
|
|
}],
|
|
|
|
date_updated: {
|
|
type: Date,
|
|
},
|
|
|
|
date_updated_fromGM: {
|
|
type: Date,
|
|
},
|
|
|
|
totVen: Number,
|
|
totFat: Number,
|
|
vLast3M: Number,
|
|
fatLast3M: Number,
|
|
fatLast6M: Number,
|
|
fatLast1Y: Number,
|
|
fatLast2Y: Number,
|
|
vLast6M: {
|
|
type: Number,
|
|
index: true,
|
|
},
|
|
vLast1Y: {
|
|
type: Number, index: true
|
|
},
|
|
vLast2Y: Number,
|
|
dataUltimoOrdine: Date,
|
|
rank3M: Number,
|
|
rank6M: Number,
|
|
rank1Y: Number,
|
|
|
|
descrizione_breve_macro: String,
|
|
descrizione_completa_macro: String,
|
|
descr_trafiletto_catalogo: String,
|
|
sottotitolo: String,
|
|
link_macro: String,
|
|
},
|
|
idProducer: { type: Schema.Types.ObjectId, ref: 'Producer', index: true },
|
|
idStorehouses: [{ type: Schema.Types.ObjectId, ref: 'Storehouse', index: true }],
|
|
idGasordine: { type: Schema.Types.ObjectId, ref: 'Gasordine', index: true },
|
|
idScontisticas: [{ type: Schema.Types.ObjectId, ref: 'Scontistica', index: true }],
|
|
idProvider: { type: Schema.Types.ObjectId, ref: 'Provider', index: true },
|
|
prezzo_ivato: {
|
|
// Con IVA
|
|
type: Number,
|
|
},
|
|
perc_iva: {
|
|
// 4, 10, 22 &
|
|
type: Number,
|
|
},
|
|
price: {
|
|
type: Number,
|
|
required: true,
|
|
},
|
|
arrvariazioni: [
|
|
{
|
|
active: {
|
|
type: Boolean,
|
|
},
|
|
versione: {
|
|
type: Number,
|
|
},
|
|
status: {
|
|
//publish
|
|
type: String,
|
|
},
|
|
price: {
|
|
type: Number,
|
|
},
|
|
sale_price: {
|
|
type: Number,
|
|
},
|
|
quantita: {
|
|
// in magazzino
|
|
type: Number,
|
|
},
|
|
pagine: {
|
|
type: Number,
|
|
},
|
|
misure: {
|
|
type: String,
|
|
},
|
|
edizione: {
|
|
type: String,
|
|
},
|
|
ristampa: {
|
|
type: String,
|
|
},
|
|
formato: {
|
|
type: String,
|
|
},
|
|
tipologia: {
|
|
type: String,
|
|
},
|
|
idTipologia: {
|
|
type: Number,
|
|
},
|
|
idTipoFormato: {
|
|
type: Number,
|
|
},
|
|
edizione: {
|
|
type: String,
|
|
},
|
|
preOrderDate: {
|
|
type: Date,
|
|
},
|
|
addtocart_link: {
|
|
type: String,
|
|
},
|
|
eta: {
|
|
type: String,
|
|
},
|
|
},
|
|
],
|
|
price_acquistato: {
|
|
type: Number,
|
|
},
|
|
after_price: {
|
|
type: String,
|
|
},
|
|
minBuyQty: {
|
|
// quantità minima acquistabile
|
|
type: Number,
|
|
default: 1,
|
|
required: true,
|
|
},
|
|
minStepQty: {
|
|
// step quantità acquistabile
|
|
type: Number,
|
|
default: 1,
|
|
required: true,
|
|
},
|
|
maxBookableSinglePersQty: {
|
|
// quantità massima Pre-ordinabile (singolarmente)
|
|
type: Number,
|
|
},
|
|
stockQty: {
|
|
// in magazzino
|
|
type: Number,
|
|
default: 0,
|
|
index: true,
|
|
},
|
|
stockBloccatiQty: {
|
|
// Prenotati Bloccati
|
|
type: Number,
|
|
},
|
|
bookedQtyOrdered: {
|
|
// Quantità Prenotate ordinate (in Lavorazione)
|
|
type: Number,
|
|
},
|
|
bookedQtyConfirmed: {
|
|
// Quantità Prenotate Confermate Totali
|
|
type: Number,
|
|
},
|
|
|
|
// GAS:
|
|
qtyToReachForGas: {
|
|
// Quantità minima da raggiungere per fare l'ordine GAS
|
|
type: Number,
|
|
},
|
|
maxbookableGASQty: {
|
|
// Quantità massima (ancora disponibile) Ordine GAS prenotabile (Complessivamente tra tutti gli ordini)
|
|
type: Number,
|
|
default: -1,
|
|
},
|
|
bookedGASQtyOrdered: {
|
|
// Quantità Ordine GAS Prenotate Totali
|
|
type: Number,
|
|
},
|
|
bookedGASQtyConfirmed: {
|
|
// Quantità Ordine GAS Confermate Totali
|
|
type: Number,
|
|
},
|
|
bookableGASBloccatiQty: {
|
|
// Quantità Prenotate Bloccate GAS
|
|
type: Number,
|
|
},
|
|
|
|
quantityLow: {
|
|
//Soglia disponibilità bassa
|
|
type: Number,
|
|
},
|
|
visibilityProductOutOfStock: {
|
|
// Visibilità prodotto "esaurito"
|
|
type: Boolean,
|
|
},
|
|
canBeShipped: {
|
|
// è spedibile
|
|
type: Boolean,
|
|
},
|
|
canBeBuyOnline: {
|
|
// è acquistabile online
|
|
type: Boolean,
|
|
},
|
|
stars: {
|
|
type: Number,
|
|
},
|
|
dateAvailableFrom: {
|
|
type: Date,
|
|
},
|
|
note: {
|
|
type: String,
|
|
},
|
|
producer_name: {
|
|
type: String,
|
|
},
|
|
provider_name: {
|
|
type: String,
|
|
},
|
|
magazzino_name: {
|
|
type: String,
|
|
},
|
|
cat_name: {
|
|
type: String,
|
|
},
|
|
subcat_name: {
|
|
type: String,
|
|
},
|
|
sconto1: {
|
|
type: String,
|
|
},
|
|
sconto2: {
|
|
type: String,
|
|
},
|
|
gas_name: {
|
|
type: String,
|
|
},
|
|
|
|
date_created: {
|
|
type: Date,
|
|
default: Date.now,
|
|
},
|
|
date_updated: {
|
|
type: Date,
|
|
},
|
|
scraped: {
|
|
type: Boolean,
|
|
},
|
|
scraped_error: {
|
|
type: Boolean,
|
|
},
|
|
scraped_updated: {
|
|
type: Boolean,
|
|
},
|
|
scraped_date: {
|
|
type: Date,
|
|
},
|
|
validaprod: {
|
|
esito: {
|
|
type: Number,
|
|
},
|
|
data: {
|
|
type: Date,
|
|
},
|
|
username: {
|
|
type: String,
|
|
},
|
|
note: {
|
|
type: String,
|
|
},
|
|
},
|
|
});
|
|
|
|
var Product = (module.exports = mongoose.model('Product', productSchema));
|
|
|
|
productSchema.index({ idapp: 1 });
|
|
|
|
module.exports.getFieldsForSearch = function () {
|
|
return [
|
|
{ field: 'name', type: tools.FieldType.string },
|
|
{ field: 'description', type: tools.FieldType.string },
|
|
];
|
|
};
|
|
|
|
module.exports.executeQueryTable = function (idapp, params) {
|
|
params.fieldsearch = this.getFieldsForSearch();
|
|
return tools.executeQueryTable(this, idapp, params);
|
|
};
|
|
|
|
module.exports.executeQueryPickup = async function (idapp, params) {
|
|
let strfindInput = tools.removeAccents(params.search.trim().toLowerCase());
|
|
|
|
strfindInput = strfindInput.replace(/#/g, ' '); // aggiungi spazio prima di # se vuoi conservarlo
|
|
|
|
// Rimuove le parole "il" e "la" e gli spazi, le @ e i tabulazioni
|
|
// per non farli influire sulla ricerca
|
|
strfindInput = strfindInput
|
|
.replace(/\b(il|la|gli|le|lo|un|una)\b/g, '')
|
|
.replace(/[-@\t]/g, '')
|
|
.trim();
|
|
|
|
if (strfindInput === '' && !params.filter) {
|
|
return [];
|
|
}
|
|
|
|
let filterfindexact = {};
|
|
if (strfindInput) {
|
|
filterfindexact = { comune: strfindInput };
|
|
}
|
|
|
|
let limit = 20;
|
|
let risexact = [];
|
|
|
|
const cleanInput = tools.removeAccents(strfindInput.trim());
|
|
const words = cleanInput.split(/\s+/);
|
|
|
|
const escapeRegex = (w) => {
|
|
// Escape del carattere '#' e altri caratteri speciali
|
|
return w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/#/g, '\\#');
|
|
};
|
|
|
|
// 🔹 Pattern per productInfo.name: tutte le parole devono essere presenti
|
|
const patternAllWords = words.map((w) => `(?=.*\\b${escapeRegex(w)})`).join('') + '.*';
|
|
|
|
let authorConditions = [];
|
|
|
|
if (words.length > 0) {
|
|
authorConditions = words.map((word) => {
|
|
const regex = new RegExp(escapeRegex(word), 'i');
|
|
return {
|
|
'productInfo.authors': {
|
|
$elemMatch: {
|
|
$or: [{ name: regex }, { surname: regex }],
|
|
},
|
|
},
|
|
};
|
|
});
|
|
|
|
authorConditions = [{ $and: authorConditions }];
|
|
}
|
|
|
|
// 🔹 Filtro finale
|
|
const filterfind = {
|
|
idapp,
|
|
$or: [
|
|
{
|
|
'productInfo.name': {
|
|
$regex: patternAllWords,
|
|
$options: 'i',
|
|
},
|
|
},
|
|
{
|
|
'productInfo.code': {
|
|
$regex: `\\b${escapeRegex(cleanInput)}`,
|
|
$options: 'i',
|
|
},
|
|
},
|
|
{
|
|
'productInfo.sku': cleanInput,
|
|
},
|
|
...authorConditions,
|
|
{
|
|
'productInfo.catprods.name': { $in: words.map(escapeRegex) },
|
|
},
|
|
],
|
|
};
|
|
|
|
if (params.filter) {
|
|
filterfind = { ...params.filter, ...filterfind };
|
|
limit = 200;
|
|
}
|
|
|
|
let aggr2 = [
|
|
{
|
|
$lookup: {
|
|
from: 'authors',
|
|
localField: 'productInfo.idAuthors',
|
|
foreignField: '_id',
|
|
as: 'productInfo.authors',
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: '$authors',
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'catprods',
|
|
localField: 'productInfo.idCatProds',
|
|
foreignField: '_id',
|
|
as: 'productInfo.catprods',
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: '$productInfo.catprods',
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$match: filterfind,
|
|
},
|
|
{ $limit: limit },
|
|
{
|
|
$project: {
|
|
name: '$productInfo.name', // Nome del prodotto
|
|
authors: '$productInfo.authors',
|
|
productInfo: {
|
|
name: '$productInfo.name', // Nome dell'autore
|
|
authors: '$productInfo.authors',
|
|
idStatoProdotto: '$productInfo.idStatoProdotto',
|
|
date_pub: '$productInfo.date_pub',
|
|
catprods: '$productInfo.catprods',
|
|
},
|
|
arrvariazioni: '$arrvariazioni',
|
|
},
|
|
},
|
|
{
|
|
$sort: {
|
|
'productInfo.date_pub': -1,
|
|
'productInfo.name': 1, // Ordina per name in ordine crescente
|
|
},
|
|
},
|
|
];
|
|
|
|
let ris = await this.aggregate(aggr2).limit(limit);
|
|
|
|
return [...risexact, ...ris];
|
|
};
|
|
|
|
module.exports.getProductByIsbn = function (idapp, isbn) {
|
|
return Product.findAllIdApp(idapp, null, null, null, isbn);
|
|
};
|
|
module.exports.getProductByCode = function (idapp, code) {
|
|
return Product.findAllIdApp(idapp, code);
|
|
};
|
|
|
|
module.exports.getProductById = async function (id) {
|
|
const arrris = await Product.findAllIdApp('', '', id);
|
|
return arrris && arrris.length > 0 ? arrris[0] : null;
|
|
};
|
|
|
|
module.exports.getInStockById = async function (id) {
|
|
const myprod = await Product.findOne({ _id: id });
|
|
if (myprod) {
|
|
let instock = 0;
|
|
if (myprod.idGasordine) {
|
|
instock = myprod.maxbookableGASQty;
|
|
} else {
|
|
instock = myprod.stockQty;
|
|
}
|
|
return instock;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
module.exports.isLowQuantityInStockById = async function (id) {
|
|
const instock = await Product.getInStockById(id);
|
|
const myprod = await Product.findOne({ _id: id });
|
|
if (instock) {
|
|
return instock <= myprod.quantityLow + 1;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
module.exports.findAllIdApp = async function (idapp, code, id, all, isbn) {
|
|
let myfind = {};
|
|
let myqueryadd = {};
|
|
let query = [];
|
|
|
|
try {
|
|
if (id === 'undefined') {
|
|
return [];
|
|
}
|
|
|
|
if (idapp) {
|
|
myfind = {
|
|
idapp,
|
|
$or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
|
|
};
|
|
}
|
|
|
|
if (!all) {
|
|
myfind = { ...myfind, active: true };
|
|
}
|
|
|
|
if (code) {
|
|
myfind = { ...myfind, code };
|
|
}
|
|
if (isbn) {
|
|
myfind = { ...myfind, isbn };
|
|
}
|
|
if (id) {
|
|
myqueryadd = {
|
|
$addFields: {
|
|
myId1: {
|
|
$toObjectId: id,
|
|
},
|
|
},
|
|
};
|
|
myfind = {
|
|
$expr: {
|
|
$eq: ['$_id', '$myId1'],
|
|
},
|
|
};
|
|
|
|
query.push(myqueryadd);
|
|
}
|
|
|
|
// DA TOGLIEREE
|
|
// myfind = { ...myfind, code: '4012824406094' };
|
|
|
|
// return await Product.find(myfind);
|
|
|
|
query.push(
|
|
// PRIMO FILTRO: riduce subito il numero di documenti
|
|
{ $match: myfind },
|
|
|
|
// UNICO LOOKUP ORDERS CON FACET PER RIDURRE I DOPPIONI
|
|
{
|
|
$lookup: {
|
|
from: 'orders',
|
|
let: { productId: '$_id' },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: {
|
|
$and: [
|
|
{ $eq: ['$idProduct', '$$productId'] },
|
|
{
|
|
$or: [
|
|
{ $eq: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT] },
|
|
{
|
|
$and: [
|
|
{ $lt: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT] },
|
|
{
|
|
$gt: ['$modify_at', { $subtract: [new Date(), 60 * 60 * 1000] }],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
totalQty: { $sum: '$quantity' },
|
|
totalQtyPreordered: { $sum: '$quantitypreordered' },
|
|
},
|
|
},
|
|
],
|
|
as: 'orderSummary',
|
|
},
|
|
},
|
|
|
|
// ESTRAGGO LE QUANTITÀ IN CAMPI AGGIUNTIVI
|
|
{
|
|
$addFields: {
|
|
QuantitaOrdinateInAttesa: {
|
|
$ifNull: [{ $arrayElemAt: ['$orderSummary.totalQty', 0] }, 0],
|
|
},
|
|
QuantitaPrenotateInAttesa: {
|
|
$ifNull: [{ $arrayElemAt: ['$orderSummary.totalQtyPreordered', 0] }, 0],
|
|
},
|
|
},
|
|
},
|
|
|
|
// CALCOLO DELLE DISPONIBILITÀ
|
|
{
|
|
$addFields: {
|
|
quantityAvailable: {
|
|
$subtract: ['$stockQty', '$QuantitaOrdinateInAttesa'],
|
|
},
|
|
bookableAvailableQty: {
|
|
$subtract: ['$maxbookableGASQty', '$QuantitaPrenotateInAttesa'],
|
|
},
|
|
},
|
|
},
|
|
|
|
// ELIMINO IL RISULTATO TEMPORANEO
|
|
{ $unset: 'orderSummary' },
|
|
|
|
// LOOKUP MULTIPLI MA ORGANIZZATI
|
|
{
|
|
$lookup: {
|
|
from: 'producers',
|
|
localField: 'idProducer',
|
|
foreignField: '_id',
|
|
as: 'producer',
|
|
},
|
|
},
|
|
{ $unwind: { path: '$producer', preserveNullAndEmptyArrays: true } },
|
|
|
|
{
|
|
$match: {
|
|
'productInfo.code': { $exists: true, $ne: '' },
|
|
//'productInfo.imagefile': { $ne: 'noimg.jpg' },
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'gasordines',
|
|
localField: 'idGasordine',
|
|
foreignField: '_id',
|
|
as: 'gasordine',
|
|
},
|
|
},
|
|
{ $unwind: { path: '$gasordine', preserveNullAndEmptyArrays: true } },
|
|
|
|
// FILTRO DOPO LOOKUP SU GASORDINE
|
|
{
|
|
$match: {
|
|
$or: [{ 'gasordine.active': true }, { gasordine: { $exists: false } }],
|
|
},
|
|
},
|
|
|
|
// LOOKUP SU AUTHORS
|
|
{
|
|
$lookup: {
|
|
from: 'authors',
|
|
localField: 'productInfo.idAuthors',
|
|
foreignField: '_id',
|
|
as: 'productInfo.authors',
|
|
},
|
|
},
|
|
|
|
// LOOKUP PUBBLICATORI, COLLANE, CATEGORIE, ECC.
|
|
{
|
|
$lookup: {
|
|
from: 'publishers',
|
|
localField: 'productInfo.idPublisher',
|
|
foreignField: '_id',
|
|
as: 'productInfo.publisher',
|
|
},
|
|
},
|
|
{ $unwind: { path: '$productInfo.publisher', preserveNullAndEmptyArrays: true } },
|
|
|
|
{
|
|
$lookup: {
|
|
from: 'collanas',
|
|
localField: 'productInfo.idCollana',
|
|
foreignField: '_id',
|
|
as: 'productInfo.collana',
|
|
},
|
|
},
|
|
{ $unwind: { path: '$productInfo.collana', preserveNullAndEmptyArrays: true } },
|
|
|
|
{
|
|
$lookup: {
|
|
from: 'catprods',
|
|
localField: 'productInfo.idCatProds',
|
|
foreignField: '_id',
|
|
as: 'productInfo.catprods',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'subcatprods',
|
|
localField: 'productInfo.idSubCatProds',
|
|
foreignField: '_id',
|
|
as: 'productInfo.subcatprods',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'scontisticas',
|
|
localField: 'idScontisticas',
|
|
foreignField: '_id',
|
|
as: 'scontisticas',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'storehouses',
|
|
localField: 'idStorehouses',
|
|
foreignField: '_id',
|
|
as: 'storehouses',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'providers',
|
|
localField: 'idProvider',
|
|
foreignField: '_id',
|
|
as: 'provider',
|
|
},
|
|
},
|
|
{ $unwind: { path: '$provider', preserveNullAndEmptyArrays: true } },
|
|
|
|
// ORDINAMENTO FINALE
|
|
{
|
|
$sort: {
|
|
'productInfo.name': 1,
|
|
},
|
|
}
|
|
);
|
|
|
|
// console.log('query=', query);
|
|
|
|
let ris = await Product.aggregate(query);
|
|
|
|
// console.table('ris', ris);
|
|
|
|
return ris;
|
|
} catch (e) {
|
|
console.error('E', e);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
module.exports.getAllProducts = function (query, sort, callback) {
|
|
Product.find(query, null, sort, callback);
|
|
};
|
|
|
|
module.exports.getProductByDepartment = function (query, sort, callback) {
|
|
Product.find(query, null, sort, callback);
|
|
};
|
|
|
|
module.exports.getProductByCatProd = function (query, sort, callback) {
|
|
Product.find(query, null, sort, callback);
|
|
};
|
|
|
|
module.exports.getProductByTitle = function (query, sort, callback) {
|
|
Product.find(query, null, sort, callback);
|
|
};
|
|
|
|
module.exports.filterProductByDepartment = function (department, callback) {
|
|
let regexp = new RegExp(`^${department}$`, 'i');
|
|
var query = { department: { $regex: regexp } };
|
|
Product.find(query, callback);
|
|
};
|
|
|
|
module.exports.filterProductByCatProd = function (catprod, callback) {
|
|
let regexp = new RegExp(`^${catprod}$`, 'i');
|
|
var query = { catprod: { $regex: regexp } };
|
|
Product.find(query, callback);
|
|
};
|
|
|
|
module.exports.filterProductByTitle = function (title, callback) {
|
|
let regexp = new RegExp(`^${title}$`, 'i');
|
|
var query = { title: { $regex: regexp } };
|
|
Product.find(query, callback);
|
|
};
|
|
|
|
module.exports.getProductByID = function (id, callback) {
|
|
Product.findById(id, callback);
|
|
};
|
|
|
|
module.exports.updateProductInOrder = async function (order) {
|
|
if (order.product) order.product = await Product.getProductById(order.product._id);
|
|
|
|
return order;
|
|
};
|
|
|
|
module.exports
|
|
.createIndexes()
|
|
.then(() => {})
|
|
.catch((err) => {
|
|
throw err;
|
|
});
|
|
|
|
module.exports.convertAfterImportALLPROD = async function (idapp, dataObjects) {
|
|
const arrprod = await Product.find({ idapp }).lean();
|
|
for (const prod of arrprod) {
|
|
await this.singlerecconvert_AfterImport_AndSave(prod);
|
|
}
|
|
};
|
|
|
|
module.exports.getArrCatProds = async function (idapp, cosa) {
|
|
try {
|
|
let addquery = [];
|
|
let arr = [];
|
|
if (cosa === shared_consts.PROD.GAS) {
|
|
addquery = [{ $match: { idapp, idGasordine: { $exists: true, $ne: null, $type: 'objectId' } } }];
|
|
|
|
addquery.push({
|
|
$lookup: {
|
|
from: 'gasordines',
|
|
localField: 'idGasordine',
|
|
foreignField: '_id',
|
|
as: 'gasordine',
|
|
},
|
|
});
|
|
|
|
addquery.push({
|
|
$match: {
|
|
'gasordine.active': true,
|
|
},
|
|
});
|
|
} else if (cosa === shared_consts.PROD.BOTTEGA) {
|
|
addquery = [
|
|
{
|
|
$match: {
|
|
idapp,
|
|
$or: [{ idGasordine: { $exists: false } }, { idGasordine: { $exists: true, $eq: null } }],
|
|
},
|
|
},
|
|
];
|
|
} else {
|
|
addquery = [{ $match: { idapp } }];
|
|
}
|
|
|
|
let myquery = [
|
|
...addquery,
|
|
{
|
|
$lookup: {
|
|
from: 'catprods',
|
|
localField: 'productInfo.idCatProds',
|
|
foreignField: '_id',
|
|
as: 'category',
|
|
},
|
|
},
|
|
{ $unwind: '$category' },
|
|
{
|
|
$group: {
|
|
_id: '$category._id',
|
|
name: { $first: '$category.name' },
|
|
idapp: { $first: '$category.idapp' },
|
|
idArgomento: { $first: '$category.idArgomento' },
|
|
},
|
|
},
|
|
{
|
|
$match: {
|
|
$or: [{ idapp: { $ne: tools.MACRO } }, { idapp: tools.MACRO, idArgomento: { $exists: true, $gt: 0 } }],
|
|
},
|
|
},
|
|
|
|
{ $sort: { name: 1 } },
|
|
];
|
|
|
|
try {
|
|
let arr = [];
|
|
arr = await Product.aggregate(myquery);
|
|
|
|
if (cosa !== shared_consts.PROD.GAS) {
|
|
// console.log(JSON.stringify(myquery, null, 2))
|
|
}
|
|
|
|
// arr = result.map(category => category.name);
|
|
return arr;
|
|
} catch (e) {
|
|
console.error('err', e);
|
|
}
|
|
// Ora uniqueCategories contiene l'array delle categorie univoche utilizzate in tutti i prodotti con active = true
|
|
return arr;
|
|
} catch (e) {
|
|
console.error('err', e);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
module.exports.singlerecconvert_AfterImport_AndSave = async function (idapp, prod, isnuovo) {
|
|
let setta = false;
|
|
|
|
try {
|
|
let objtoset = {};
|
|
let rec = null;
|
|
|
|
// Impostazioni Base:
|
|
if (isnuovo) {
|
|
objtoset = {
|
|
idapp,
|
|
// minBuyQty: 1,
|
|
// minStepQty: 1,
|
|
maxBookableSinglePersQty: 0,
|
|
bookedGASQtyOrdered: 0,
|
|
bookableGASBloccatiQty: 0,
|
|
bookedGASQtyConfirmed: 0,
|
|
// qtyToReachForGas: 0,
|
|
// maxbookableGASQty: 0,
|
|
};
|
|
}
|
|
|
|
if (prod.producer_name) {
|
|
// Cerca il produttore
|
|
let recproducer = await Producer.findOne({ idapp, name: prod.producer_name }).lean();
|
|
if (!recproducer) {
|
|
// Non esiste questo produttore, quindi lo creo !
|
|
recproducer = new Producer({ idapp, name: prod.producer_name });
|
|
ris = await recproducer.save();
|
|
recproducer = await Producer.findOne({ idapp, name: prod.producer_name }).lean();
|
|
}
|
|
|
|
if (recproducer) {
|
|
objtoset = {
|
|
...objtoset,
|
|
idProducer: recproducer._id,
|
|
};
|
|
setta = true;
|
|
}
|
|
}
|
|
|
|
if (prod.magazzino_name) {
|
|
// Cerca il produttore
|
|
let recstorehouse = await Storehouse.findOne({ idapp, name: prod.magazzino_name }).lean();
|
|
if (!recstorehouse) {
|
|
// Non esiste questo produttore, quindi lo creo !
|
|
recstorehouse = new Storehouse({ idapp, name: prod.magazzino_name });
|
|
ris = await recstorehouse.save();
|
|
recstorehouse = await Storehouse.findOne({ idapp, name: prod.magazzino_name }).lean();
|
|
}
|
|
|
|
if (recstorehouse) {
|
|
objtoset = {
|
|
...objtoset,
|
|
idStorehouses: [recstorehouse._id],
|
|
};
|
|
setta = true;
|
|
}
|
|
}
|
|
|
|
if (prod.provider_name) {
|
|
// Cerca il produttore
|
|
let recprovider = await Provider.findOne({ idapp, name: prod.provider_name }).lean();
|
|
if (!recprovider) {
|
|
recprovider = new Provider({ idapp, name: prod.provider_name });
|
|
// Non esiste questo produttore, quindi lo creo !
|
|
ris = await recprovider.save();
|
|
recprovider = await Provider.findOne({ idapp, name: prod.provider_name }).lean();
|
|
}
|
|
|
|
if (recprovider) {
|
|
objtoset = {
|
|
...objtoset,
|
|
idProvider: recprovider._id,
|
|
};
|
|
setta = true;
|
|
}
|
|
}
|
|
if (prod.gas_name) {
|
|
// Cerca il GAS
|
|
rec = await Gasordine.findOne({ idapp, name: prod.gas_name }).lean();
|
|
if (!rec) {
|
|
rec = new Gasordine({ idapp, name: prod.gas_name, active: true });
|
|
// Non esiste questo GAS, quindi lo creo !
|
|
ris = await rec.save();
|
|
rec = await Gasordine.findOne({ idapp, name: prod.gas_name }).lean();
|
|
}
|
|
|
|
if (rec) {
|
|
objtoset = {
|
|
...objtoset,
|
|
idGasordine: rec._id,
|
|
};
|
|
setta = true;
|
|
}
|
|
}
|
|
|
|
let arrsconti = [];
|
|
|
|
if (prod.sconto1) {
|
|
// Cerca la scontistica
|
|
let recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto1 }).lean();
|
|
if (!recscontistica) {
|
|
recscontistica = new Scontistica({ idapp, code: prod.sconto1 });
|
|
// Non esiste questa scontistica, quindi lo creo !
|
|
ris = await recscontistica.save();
|
|
recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto1 }).lean();
|
|
}
|
|
|
|
if (recscontistica) {
|
|
arrsconti.push(recscontistica);
|
|
}
|
|
}
|
|
|
|
if (prod.sconto2) {
|
|
// Cerca la scontistica
|
|
let recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto2 }).lean();
|
|
if (!recscontistica) {
|
|
recscontistica = new Scontistica({ idapp, code: prod.sconto2 });
|
|
// Non esiste questa scontistica, quindi lo creo !
|
|
ris = await recscontistica.save();
|
|
recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto2 }).lean();
|
|
}
|
|
|
|
if (recscontistica) {
|
|
arrsconti.push(recscontistica);
|
|
}
|
|
}
|
|
|
|
if (arrsconti.length > 0) {
|
|
objtoset = {
|
|
...objtoset,
|
|
idScontisticas: arrsconti,
|
|
};
|
|
setta = true;
|
|
}
|
|
|
|
// Aggiorna il prezzo ?
|
|
const aggiornaprezzo = false;
|
|
if (aggiornaprezzo) {
|
|
// cerca il prodotto
|
|
const myprodinput = dataObjects.find((rec) => rec._id === prod._id);
|
|
if (myprodinput) {
|
|
objtoset = {
|
|
...objtoset,
|
|
price: myprodinput.price,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!tools.isObjectEmpty(objtoset)) {
|
|
ris = await Product.findOneAndUpdate({ _id: new ObjectId(prod._id) }, { $set: objtoset });
|
|
|
|
const objDelete = {
|
|
cat_name: 1,
|
|
subcat_name: 1,
|
|
producer_name: 1,
|
|
provider_name: 1,
|
|
magazzino_name: 1,
|
|
sconto1: 1,
|
|
sconto2: 1,
|
|
gas_name: 1,
|
|
};
|
|
|
|
ris = await Product.updateOne({ _id: new ObjectId(prod._id) }, { $unset: objDelete });
|
|
|
|
if (ris && ris.modifiedCount > 0) {
|
|
console.log('Modificato: ', objtoset.name);
|
|
}
|
|
|
|
// const campodarimuovere = 'producer_name';
|
|
// await Product.findOneAndUpdate({ _id: prod._id }, { $unset: { [campodarimuovere]: 1 } })
|
|
}
|
|
} catch (e) {
|
|
console.error('Err', e);
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------
|
|
|
|
module.exports.replaceProductImgToImageFile = async function (abilitaserver) {
|
|
const Product = this;
|
|
|
|
if (abilitaserver) {
|
|
// const result = await ProductInfo.updateMany({ "img": { $exists: true } }, { $rename: { 'img': 'imagefile' } });
|
|
|
|
// Trova tutti i documenti con il campo 'img' che esiste
|
|
const documents = await Product.find({ "productInfo.img": { $exists: true } });
|
|
|
|
// Aggiorna ciascun documento
|
|
for (let doc of documents) {
|
|
if (doc.img && doc.img.startsWith('upload/products/')) {
|
|
// Rimuovi il prefisso '/upload/products' dal campo `img`
|
|
doc.imagefile = doc.img.replace(/^\upload\/products\//, '');
|
|
doc.img = undefined; // Può anche rimuovere il campo img corrente se desiderato
|
|
await doc.save(); // Salva il documento aggiornato
|
|
}
|
|
}
|
|
|
|
console.log(`Updated ${documents.length} document(s) with new imagefile paths.`);
|
|
|
|
} else {
|
|
const documents = await Product.find({ "productInfo.imagefile": { $exists: true } });
|
|
|
|
// Aggiorna ciascun documento
|
|
for (let doc of documents) {
|
|
if (doc.imagefile && doc.imagefile.startsWith('upload/products/')) {
|
|
// Rimuovi il prefisso '/upload/products' dal campo `img`
|
|
doc.imagefile = doc.imagefile.replace(/^\upload\/products\//, '');
|
|
await doc.save(); // Salva il documento aggiornato
|
|
}
|
|
}
|
|
|
|
console.log(`Updated ${documents.length} document(s) with new imagefile paths.`);
|
|
|
|
|
|
}
|
|
|
|
|
|
// await Product.updateMany({}, { 'vers_img': 0 });
|
|
|
|
console.log(`Updated ${result.modifiedCount} document(s).`);
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports.correggiProductTypes = async function () {
|
|
const Product = this;
|
|
const bulkOps = [];
|
|
|
|
try {
|
|
// Fase di aggregazione
|
|
const cursor = Product.aggregate([
|
|
{
|
|
$project: {
|
|
_id: 1,
|
|
'productInfo.productType': 1,
|
|
'productInfo.productTypes': [{ $ifNull: ['$productType', 10] }]
|
|
}
|
|
}
|
|
]).cursor({ batchSize: 1000 }).exec();
|
|
|
|
// Itera attraverso ogni documento utilizzando il cursore
|
|
for await (const doc of cursor) {
|
|
bulkOps.push({
|
|
updateOne: {
|
|
filter: { _id: doc._id },
|
|
update: {
|
|
$set: { 'productInfo.productTypes': doc.productTypes },
|
|
$unset: { 'productInfo.productType': "" }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Scrivi i batch di operazioni ogni 1000 documenti
|
|
if (bulkOps.length === 1000) {
|
|
await Product.bulkWrite(bulkOps);
|
|
bulkOps.length = 0;
|
|
}
|
|
}
|
|
|
|
// Scrivi eventuali operazioni rimanenti
|
|
if (bulkOps.length > 0) {
|
|
await Produc.bulkWrite(bulkOps);
|
|
}
|
|
|
|
console.log('ProductInfo.productType converted to productTypes array and saved');
|
|
} catch (err) {
|
|
console.error('Error converting ProductInfo.productType:', err);
|
|
}
|
|
}
|
|
|
|
module.exports.updateProductInfoByStats = async function (idapp) {
|
|
let mylog = '';
|
|
let mylog2 = '';
|
|
let mylogtot = '';
|
|
try {
|
|
const Product = this;
|
|
|
|
const T_WEB_ArticoliFatturati = require('./t_web_articolifatturati');
|
|
const T_WEB_Ordini = require('./t_web_ordini');
|
|
|
|
mylog = "Inizio Aggiornamento Statistiche... \n";
|
|
mylogtot += mylog;
|
|
console.log(mylog);
|
|
|
|
let countUpdate = 0;
|
|
|
|
countUpdate = await T_WEB_ArticoliFatturati.updateStatisticsFatt('', idapp, true);
|
|
|
|
// Itera sui risultati e aggiorna productInfo
|
|
|
|
mylog = `Aggiornati ${countUpdate} record di productInfo`;
|
|
mylogtot += mylog;
|
|
console.log(mylog);
|
|
|
|
mylog2 = "Inizio Aggiornamento Statistiche Ordini ... \n";
|
|
mylogtot += mylog2;
|
|
console.log(mylog2);
|
|
|
|
countUpdate = await T_WEB_Ordini.updateStatisticsOrders('', idapp, true);
|
|
|
|
mylog2 = `Aggiornati ${countUpdate} record di productInfo`;
|
|
mylogtot += mylog2;
|
|
|
|
console.log(mylog2);
|
|
|
|
} catch (error) {
|
|
mylog = "Errore durante l'aggiornamento di productInfo:" + error;
|
|
console.error(mylog);
|
|
}
|
|
|
|
return mylogtot;
|
|
}
|
|
|
|
// crea setImgNotFound
|
|
|
|
module.exports.setImgNotFound = async function (id) {
|
|
// set sul record id il flag image_not_found
|
|
try {
|
|
const Product = this;
|
|
|
|
await Product.updateOne(
|
|
{ _id: id },
|
|
{ $set: { 'productInfo.image_not_found': true } }
|
|
);
|
|
console.log(`Flag image_not_found set for record with id: ${id}`);
|
|
} catch (error) {
|
|
console.error(`Error setting image_not_found flag for id ${id}:`, error);
|
|
}
|
|
|
|
}
|
|
|
|
// imposta tutti i record con image_not_found: false
|
|
module.exports.resetImageNotFound = async function () {
|
|
try {
|
|
const Product = this;
|
|
|
|
await Product.updateMany(
|
|
{ 'productInfo.image_not_found': true },
|
|
{ $set: { image_not_found: false } }
|
|
);
|
|
console.log('Flag image_not_found reset to false for all records');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error resetting image_not_found flag:', error);
|
|
}
|
|
|
|
}
|
|
|
|
// crea una funzione che mi rimuove il record "product" che utilizza productInfo, nel caso in cui date_updated_fromGM non esista
|
|
module.exports.removeProductInfoWithoutDateUpdatedFromGM = async function (idapp) {
|
|
const Product = this;
|
|
|
|
let mylog;
|
|
|
|
try {
|
|
const arrproduct = await Product.find({ idapp, 'productInfo.date_updated_fromGM': { $exists: false } });
|
|
|
|
if (arrproduct.length > 0 && arrproduct.length < 1000) {
|
|
mylog = `Rimuovo ${arrproduct.length} product senza date_updated_fromGM !!`
|
|
console.log(mylog);
|
|
|
|
for (const product of arrproduct) {
|
|
await Product.updateOne(
|
|
{ _id: product._id },
|
|
{ $set: { deleted: true } }
|
|
);
|
|
}
|
|
}
|
|
|
|
return mylog;
|
|
} catch (error) {
|
|
mylog += 'Error removing productInfo without date_updated_fromGM:' + error;
|
|
console.error(mylog);
|
|
}
|
|
|
|
return mylog;
|
|
};
|
|
module.exports.HideProductInfoWithoutDateUpdatedFromGM = async function (idapp) {
|
|
const Product = this;
|
|
|
|
let mylog;
|
|
|
|
try {
|
|
const arrproduct = await Product.find({ idapp, 'productInfo.date_updated_fromGM': { $exists: false } });
|
|
|
|
if (arrproduct.length > 0 && arrproduct.length < 1000) {
|
|
mylog = `Nascondo ${arrproduct.length} product senza date_updated_fromGM !!`
|
|
console.log(mylog);
|
|
|
|
for (const product of arrproduct) {
|
|
await Product.updateOne(
|
|
{ _id: product._id },
|
|
{ $unset: { date_updated_fromGM: true } }
|
|
);
|
|
}
|
|
}
|
|
|
|
return mylog;
|
|
} catch (error) {
|
|
mylog += 'Error Hide productInfo without date_updated_fromGM:' + error;
|
|
console.error(mylog);
|
|
}
|
|
|
|
return mylog;
|
|
};
|