diff --git a/.DS_Store b/.DS_Store
index dce57f2..60709d6 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/.env.development b/.env.development
index 86c5b99..076722d 100644
--- a/.env.development
+++ b/.env.development
@@ -42,5 +42,5 @@ MIAB_HOST=box.lamiaposta.org
MIAB_ADMIN_EMAIL=admin@lamiaposta.org
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
-API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
-SERVER_A_URL="http://51.77.156.69:3000"
\ No newline at end of file
+SERVER_A_URL="http://51.77.156.69:3000"
+API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
\ No newline at end of file
diff --git a/emails/newsletter/it/html.pug b/emails/newsletter/it/html.pug
index d8e525c..b700e41 100755
--- a/emails/newsletter/it/html.pug
+++ b/emails/newsletter/it/html.pug
@@ -18,232 +18,270 @@
- var baseimg = baseurl + '/'
doctype html
html
- - if (dataemail.title)
- head
- title dataemail.subject
+ head
+ meta(charset="utf-8")
+ meta(name="viewport", content="width=device-width, initial-scale=1")
+ title= dataemail.title || "Email"
+ style.
+ /* embedded CSS */
+ body { margin:0; padding:0; background:#E9F2F9; font-family:Tahoma, Geneva, sans-serif; color:#5b656e; }
+ a { color:#09c; text-decoration:none; }
+ table, td { border-collapse:collapse; }
+ h1,h2,h3,p { margin:0; padding:0; }
- //- import css/scss stylesheets
- //- these file names will be replace by gulp with proper css file paths
- link(rel="stylesheet", href="../sass/basic.scss")
- link(rel="stylesheet", href="../sass/one/styles.scss")
+ .logoContainer { text-align:center; padding:20px 0; }
+ .logoContainer img { max-width:200px; }
- //- embdedded css allowed, but not sass
- style.
- .red {
- background-color: #E84C50;
- }
+ .testomail { padding:10px; font-size:0.75rem; line-height:1.4; }
- .full-width {
- width: 100%;
- }
+ .clpromo {
+ background-color:orange;
+ text-align:center;
+ font-size:1rem;
+ padding:10px;
+ color:#fff;
+ font-weight:bold;
+ }
+
+ .emailContainer {
+ background:#fff;
+ border-radius:10px;
+ padding:20px;
+ margin:20px auto;
+ }
+
+ .sectionArticleImage img {
+ max-width:150px;
+ border:1px solid #ccc;
+ display:block;
+ margin-bottom:10px;
+ }
+
+ .teacher {
+ font-style:italic;
+ font-size:0.75rem;
+ color:#555;
+ }
+
+ .contrib {
+ font-size:0.75rem;
+ font-weight:bold;
+ color:#333;
+ }
+
+ .button a {
+ display:inline-block;
+ padding:10px 20px;
+ background:#f75666;
+ color:#fff !important;
+ text-decoration:none;
+ border-radius:10px;
+ font-size:13px;
+ }
+
+ .button2 a {
+ display:block;
+ padding:12px 20px;
+ background:#0000ff;
+ color:#fff !important;
+ text-decoration:none;
+ border-radius:10px;
+ font-size:1.15rem;
+ }
+
+ .center_img img {
+ display:block;
+ margin:0 auto;
+ }
+
+ .discContainer {
+ background:#fff;
+ border-radius:10px;
+ padding:20px;
+ margin:20px auto;
+ }
+
+ .LinkDisc a {
+ text-decoration:none;
+ }
+
+ .pDisc:hover {
+ background:#5c8ef4 !important;
+ color:#fff !important;
+ }
+
+ .pDisc {
+ padding:5px 10px;
+ border-radius:10px;
+ display:inline-block;
+ }
+
+ .socialMedia {
+ background:#8bafcb;
+ text-align:center;
+ padding:10px 0;
+ }
+
+ .socialMedia img {
+ width:29px;
+ height:auto;
+ border:0;
+ }
+
+ .firma-container {
+ background:#ffffff;
+ padding:15px;
+ text-align:center;
+ border-top:1px solid #e0e0e0;
+ font-size:0.85rem;
+ color:#313a42;
+ }
+
+ .disclaimer-container {
+ background:#f9f9f9;
+ padding:15px;
+ text-align:center;
+ font-size:0.75rem;
+ color:#666;
+ border-top:1px solid #eee;
+ border-bottom:1px solid #eee;
+ }
+
+ .bottom-container {
+ background:#e9f2f9;
+ padding:15px;
+ text-align:center;
+ font-size:0.7rem;
+ color:#999;
+ }
+
+ .whitespace {
+ line-height:0;
+ font-size:0;
+ height:20px;
+ }
+
+ @media only screen and (max-width:480px) {
+ .button a, .button2 a {
+ font-size:1rem !important;
+ width:100%;
+ }
+ .sectionArticleImage,
+ .column {
+ width:100% !important;
+ display:block !important;
+ }
+ }
body(yahoofix)
- span(id='body_style', style='display:block')
- table(class="topHeader", cellpadding="0", cellspacing="0", width="100%")
- - if (dataemail.height_logo)
- tr
- td
- table(cellpadding="0", cellspacing="0", align="center", summary="")
- tr
- td.logoContainer
- a(href=baseurl, title='logo')
- img.logo(src=baseurl+"/images/logo.png", height=dataemail.height_logo)
-
+ span#body_style(style='display:block')
+ // Header
+ table(width="100%", cellpadding="0", cellspacing="0", align="center")
tr
- td.testomail
- p!= dataemail.templ.testoheadermail_out
+ td.logoContainer
+ a(href=baseurl)
+ img.logo(src=baseurl+"/public/images/logo.png", alt="Logo")
- - if (dataemail.templ.options.includes('SHOW_PROMO'))
+ if dataemail.templ.testoheadermail_out
+ tr
+ td.testomail
+ p!= dataemail.templ.testoheadermail_out
+
+ if dataemail.templ.options.includes('SHOW_PROMO')
tr
td.clpromo
p!= dataemail.textpromo
- - if (dataemail.templ.content)
- table(cellpadding="0", cellspacing="0", width="95%", align="center")
+ // Main Content
+ if dataemail.templ.content
+ tr
+ td.emailContainer
+ p!=dataemail.templ.content
+ if dataemail.templ.img
+ img(src=baseimg + dataemail.templ.img, class="center_img")
+ if dataemail.templ.content2
+ p!=dataemail.templ.content2
+ if dataemail.templ.img2
+ img(src=baseimg + dataemail.templ.img2, class="center_img")
+
+ // Events
+ if dataemail.templ.options.includes('SHOW_EVENTS')
+ each event in arrevents
tr
- td(class="textIniContainer", valign="top")
- p!=dataemail.templ.content
- - if (dataemail.templ.img)
- img(src=baseimg + dataemail.templ.img, alt="", class="myimg")
- - if (dataemail.templ.content2)
- p!=dataemail.templ.content2
- - if (dataemail.templ.img2)
- img(src=baseimg + dataemail.templ.img2, alt="", class="myimg")
-
- table(cellpadding="0", cellspacing="0", width="640", align="center")
- - if (dataemail.templ.options.includes('SHOW_EVENTS'))
- tr
- td(class="whitespace", height="10")
- p
- tr
- td(class="emailContainer", valign="top")
-
- each event in arrevents
- - var urlevent = baseurl + '/event/' + event.typol + '?eventid=' + event._id
- - var imgev = event.img_small
- - var mydate = prettyDate(event.dateTimeStart)
- unless (imgev)
- - imgev = event.img
- - var teacher1 = ''
- - var teacher2 = ''
- - var teacher3 = ''
- - var teacher4 = ''
- - var contrib = ''
- - var myclteach = 'q-chip'
- - if (event.op1[0] && event.op1[0].username !== 'nessuno')
- - teacher1 = event.op1[0].name + ' ' + event.op1[0].surname
- - if ((event.op2[0] && event.op2[0].username !== 'nessuno'))
- - teacher2 = event.op2[0].name + ' ' + event.op2[0].surname
- - myclteach = 'q-chip2'
- - if (event.op3[0] && event.op3[0].username !== 'nessuno')
- - teacher3 = "
" . event.op3[0].name + ' ' + event.op3[0].surname
- - if (event.op4[0] && event.op4[0].username !== 'nessuno')
- - teacher4 = "
" . event.op4[0].name + ' ' + event.op4[0].surname
-
- - if (event.contrib[0])
- - contrib = event.contrib[0].label
- - if (event.contrib[0].showprice)
- - contrib += ' ' + event.price + ' €'
-
-
- table(cellpadding="0", cellspacing="0", width="100%", summary="", border="0", align="center")
+ td.emailContainer
+ table(width="100%", cellpadding="0", cellspacing="0")
tr
- td(class="column sectionArticleImage", valign="top")
- table(cellpadding="0", cellspacing="0", summary="", border="0")
- - if (event.news)
- tr
- td
- p(class="q-chip row inline no-wrap items-center cltexth5 chipnews shadow-5 glossy text-right bg-red text-white") Novità
+ td.column.sectionArticleImage(width="150")
+ if event.news
+ p.q-chip.bg-red.text-white Novità
+ img(src=baseimg + (event.img_small || event.img), alt=event.title)
+ p.teacher= event.op1[0].name + ' ' + event.op1[0].surname
+ td.column
+ h2.sectionContentTitle= event.title
+ p.sectionContentSubTitle= prettyDate(event.dateTimeStart)
+ p.sectionContent!= event.details
+ if event.contrib.length
+ p.contrib= event.contrib[0].label + (event.contrib[0].showprice ? ' ' + event.price + ' €' : '')
+ table.buttonContainer
tr
- td
- img(src=baseimg + imgev, alt="", width="150")
- p(class="teacher") #{teacher1}
#{teacher2} #{teacher3} #{teacher4}
- td(class="column", valign="top")
+ td.button
+ a(href=baseurl + '/event/' + event.typol + '?eventid=' + event._id, target="_blank") Apri l'Evento
- table(cellpadding="0", cellspacing="0", summary="", border="0")
- tr
- td(class="sectionContentTitle boldhigh", valign="top")
- p #{event.title}
- tr
- td(class="sectionContentSubTitle", valign="top")
- p(class="q-chip row inline no-wrap items-center cltexth5 chipnews shadow-5 glossy text-right bg-blue text-white") #{mydate}
- tr
- td(class="sectionContent", valign="top")
- p!= event.details
- p.contrib= contrib
- tr
- td(class="buttonContainer")
- table(width="50%", cellpadding="0", cellspacing="0", summary="", border="0")
- tr
- td(class="button hoverLink")
- a(href=urlevent, title='Evento', target='_blank') Apri l'Evento
+ tr
+ td.center_img
+ a.button2(href=urlcal, target="_blank") Calendario Eventi
+ // Disciplines
+ if dataemail.templ.options.includes('SHOW_DISC')
+ tr
+ td.center
+ h2.cltitle_disc= dataemail.disc_title
+ each disc in dataemail.arrdiscipline
tr
- table(cellpadding="0", cellspacing="0", summary="", border="0", align="center", class="")
- tr
- td(class="whitespace", height="10")
- p
- tr
- td.center_img(class="button2 hoverLink")
- a(href=urlcal, title='Calendario Eventi', target='_blank') Calendario Eventi
- tr
- td(class="whitespace", height="10")
- p
-
- - if (dataemail.templ.options.includes('SHOW_DISC'))
- tr
- td(class="whitespace bg-white", height="20")
- p(class="bg-white")
- tr
- td(class="center")
- p(class="cltitle_disc") #{dataemail.disc_title}
- tr
- td(class="discContainer", valign="top")
-
- each disc in dataemail.arrdiscipline
- - var urldisc = baseurl + disc.linkpage
- - var imgdisc = disc.img_small
- unless (imgdisc)
- - imgdisc = disc.img
-
- table(cellpadding="0", cellspacing="0", width="100%", summary="", border="0", align="center")
+ td.discContainer
+ table(width="100%", cellpadding="0", cellspacing="0")
tr
- td(class="column sectionArticleImage", valign="top")
- table(cellpadding="0", cellspacing="0", summary="", border="0")
- tr
- td
- img(src=baseimg + imgdisc, alt="", width="150")
- td(class="column", valign="top")
+ td.column.sectionArticleImage(width="150")
+ img(src=baseimg + (disc.img_small || disc.img), alt=disc.label)
+ td.column
+ p.LinkDisc
+ a(href=baseurl + disc.linkpage, target="_blank")
+ span.pDisc(style=`background-color:`+disc.color)= disc.label
+ p.sectionContent!= disc.description
- table(cellpadding="0", cellspacing="0", summary="", border="0")
- tr
- td(class="sectionContentTitle boldhigh center LinkDisc", valign="top")
- a(href=urldisc, title='Disciplina', target='_blank')
- p(class="q-chip row inline no-wrap items-center cltexth4 chipnews shadow-5 glossy text-right text-white pDisc", style=`background-color: `+disc.color) #{disc.label}
- tr
- td(class="sectionContent", valign="top")
- p!= disc.description
-
- tr
- td(class="whitespace", height="20")
- p
-
- - if (dataemail.content_after_events)
+ // Additional Content
+ if dataemail.content_after_events
tr
- table(cellpadding="0", cellspacing="0", summary="", border="0")
- tr
- td.testomail
- p!=dataemail.content_after_events
- tr
- td(class="whitespace", height="20")
- p
+ td.testomail
+ p!=dataemail.content_after_events
// Social Media
- table.socialMedia(cellpadding="0", cellspacing="0", width="100%", summary="", border="0", align="center")
- tr
- td(class="whitespace", height="5")
- p
- tr
- td
- table(width="120", cellpadding="0", cellspacing="0", summary="", border="0", align="center")
- tr
- - if (dataemail.urlinstagram)
- td(width="32", align="center")
- a(href=dataemail.urlinstagram, title='Instagram')
- img(src=imginstagram, alt="Instagram", width="29")
- - if (dataemail.urltwitter)
- td(width="32", align="center")
- a(href=dataemail.urltwitter, title='Twitter')
- img(src=imgtwitter, alt="Twitter", width="29")
- - if (dataemail.urlfb)
- td(width="32", align="center")
- a(href=dataemail.urlfb, title='Facebook')
- img(src=imgfb, alt="Facebook", width="29")
- - if (dataemail.urlyoutube)
- td(width="32", align="center")
- a(href=dataemail.urlyoutube, title='YouTube')
- img(src=imgyoutube, alt="YouTube", width="29")
-
- tr
- td(class="whitespace", height="5")
- p
+ tr
+ td.socialMedia
+ table(width="120", align="center", cellpadding="0", cellspacing="0")
+ if dataemail.urlinstagram
+ td
+ a(href=dataemail.urlinstagram)
+ img(src=imginstagram, alt="Instagram")
+ if dataemail.urltwitter
+ td
+ a(href=dataemail.urltwitter)
+ img(src=imgtwitter, alt="Twitter")
+ if dataemail.urlfb
+ td
+ a(href=dataemail.urlfb)
+ img(src=imgfb, alt="Facebook")
+ if dataemail.urlyoutube
+ td
+ a(href=dataemail.urlyoutube)
+ img(src=imgyoutube, alt="YouTube")
// Footer
- table.footer(cellpadding="0", cellspacing="0", width="100%", summary="", border="0", align="center")
- tr
- td(class="whitespace", height="10")
- p
- tr
- td.firma
- p!= dataemail.firma
-
- tr
- td.disclaimer
- p!= dataemail.disclaimer_out
-
- tr
- td.bottom
- p!= dataemail.disc_bottom_out
-
- tr
- td(class="whitespace", height="10")
- p
+ tr
+ td.firma-container
+ p!= dataemail.firma
+ tr
+ td.disclaimer-container
+ p!= dataemail.disclaimer_out
+ tr
+ td.bottom-container
+ p!= dataemail.disc_bottom_out
\ No newline at end of file
diff --git a/src/server/controllers/articleController.js b/src/server/controllers/articleController.js
index 113b8b0..73d6f05 100644
--- a/src/server/controllers/articleController.js
+++ b/src/server/controllers/articleController.js
@@ -345,7 +345,7 @@ exports.getTableContentBase = async (options) => {
}
if (!records || records.length === 0) {
- console.log(`Nessun record trovato per la tabella ${options.nameTable}.`);
+ console.log(`Nessun record trovato per la tabella ${options.nameTable}.`);
return [];
}
@@ -887,22 +887,34 @@ exports.mssqlmigrateTables = async (req) => {
try {
const options = req.body.mydata.options;
let listaTabelle = [];
-
-
- if (options?.tutte) {
- // const listaTabelle = ['T_WEB_StatiProdotto'];
- listaTabelle = ['T_WEB_TitoliOriginali', 'T_WEB_TestateOrdini', 'T_WEB_Ordini', 'T_WEB_Disponibile', 'T_WOO_TestateOrdini', 'T_WOO_Ordini', 'T_WEB_Articoli',
- 'T_WEB_Argomenti', 'T_WEB_ClientiInternet', 'T_WOO_Clienti', 'T_WEB_Autori', 'T_WEB_Collane', 'T_WEB_MarchiEditoriali', 'T_WEB_StatiProdotto', 'T_WEB_TipiFormato', 'T_WEB_Tipologie', 'T_WEB_ArticoliFatturati', 'T_WEB_IdInternetFatturati',
- 'T_WEB_Edizioni', 'T_WEB_Contratti'];
- } else if (options?.parte1) {
- listaTabelle = ['T_WEB_TitoliOriginali', 'T_WEB_TestateOrdini', 'T_WEB_Ordini', 'T_WOO_TestateOrdini', 'T_WOO_Ordini', 'T_WEB_Articoli'];
- } else if (options?.parte2) {
- listaTabelle = ['T_WEB_Disponibile', 'T_WEB_Argomenti', 'T_WEB_ClientiInternet', 'T_WOO_Clienti', 'T_WEB_Autori'];
- } else if (options?.parte3) {
- listaTabelle = ['T_WEB_Collane', 'T_WEB_MarchiEditoriali', 'T_WEB_StatiProdotto', 'T_WEB_TipiFormato', 'T_WEB_Tipologie', 'T_WEB_ArticoliFatturati', 'T_WEB_IdInternetFatturati',
- 'T_WEB_Edizioni', 'T_WEB_Contratti'];
- } else {
- listaTabelle = ['T_WEB_Articoli'];
+ if (options?.parte1 || options?.tutte) {
+ listaTabelle.push({ table: 'T_WEB_TitoliOriginali', usaDataOra: true, fieldId: 'IdTitoloOriginale' });
+ listaTabelle.push({ table: 'T_WEB_TestateOrdini', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WEB_Ordini', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WOO_TestateOrdini', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WOO_Ordini', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WEB_Articoli', usaDataOra: true, fieldId: 'IdArticolo' });
+ }
+ if (options?.parte2 || options?.tutte) {
+ listaTabelle.push({ table: 'T_WEB_Disponibile', usaDataOra: true, fieldId: 'Codice' });
+ listaTabelle.push({ table: 'T_WEB_Argomenti', usaDataOra: true, fieldId: 'IdArgomento' });
+ listaTabelle.push({ table: 'T_WEB_ClientiInternet', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WOO_Clienti', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WEB_Autori', usaDataOra: true, fieldId: 'IdAutore' });
+ }
+ if (options?.parte3 || options?.tutte) {
+ listaTabelle.push({ table: 'T_WEB_Collane', usaDataOra: true, fieldId: 'IdCollana' });
+ listaTabelle.push({ table: 'T_WEB_MarchiEditoriali', usaDataOra: true, fieldId: 'IdMarchioEditoriale' });
+ listaTabelle.push({ table: 'T_WEB_StatiProdotto', usaDataOra: true, fieldId: 'IdStatoProdotto' });
+ listaTabelle.push({ table: 'T_WEB_TipiFormato', usaDataOra: true, fieldId: 'IdTipoFormato' });
+ listaTabelle.push({ table: 'T_WEB_Tipologie', usaDataOra: true, fieldId: 'IdTipologia' });
+ listaTabelle.push({ table: 'T_WEB_ArticoliFatturati', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WEB_IdInternetFatturati', usaDataOra: false });
+ listaTabelle.push({ table: 'T_WEB_Edizioni', usaDataOra: true, fieldId: 'IdEdizione' });
+ listaTabelle.push({ table: 'T_WEB_Contratti', usaDataOra: true, fieldId: 'IdArticolo' });
+ }
+ if (options?.test) {
+ listaTabelle.push({ table: 'T_WEB_Articoli', usaDataOra: true, fieldId: 'IdArticolo' });
}
const migrator = new MssqlMigrator();
diff --git a/src/server/models/JobsInProgress.js b/src/server/models/JobsInProgress.js
new file mode 100755
index 0000000..01527b8
--- /dev/null
+++ b/src/server/models/JobsInProgress.js
@@ -0,0 +1,128 @@
+const mongoose = require('mongoose').set('debug', false)
+const Schema = mongoose.Schema;
+
+const tools = require('../tools/general');
+
+mongoose.Promise = global.Promise;
+mongoose.level = "F";
+
+const shared_consts = require('../tools/shared_nodejs');
+
+// Resolving error Unknown modifier: $pushAll
+mongoose.plugin(schema => {
+ schema.options.usePushEach = true
+});
+
+
+const JobsInProgressSchema = new Schema({
+ idapp: {
+ type: String,
+ },
+ descr: {
+ type: String,
+ },
+ nomeFunzioneDbOp: {
+ type: String,
+ },
+ status: {
+ type: Number,
+ },
+ terminatedWhy: {
+ type: Number,
+ },
+ comment: {
+ type: String,
+ },
+ date_created: {
+ type: Date,
+ default: Date.now
+ },
+});
+
+JobsInProgressSchema.statics.chechifExistJobWorking = async function (jobData, terminateiftoolong) {
+
+ // controlla se esiste un altro job, non ancora terminato !STATUS_JOB.FINISH
+ // se esiste già allora ritorna false
+ const existingJob = await this.findOne({ idapp: jobData.idapp, nomeFunzioneDbOp: jobData.nomeFunzioneDbOp, status: { $ne: shared_consts.STATUS_JOB.FINISH } });
+ if (existingJob) {
+ // se il Job trovato è passato troppo tempo (oltre 3 ore date_created), allora fai finta che abbia già terminato
+ // (in questo caso, non ritorna false, ma ritorna il job trovato, per permettere di gestire il caso in cui si vuole forzare il job a terminare)
+ if (Math.abs(Date.now() - existingJob.date_created.getTime()) > 180 * 60 * 1000) {
+ if (terminateiftoolong) {
+ existingJob.status = shared_consts.STATUS_JOB.FINISH;
+ existingJob.terminatedWhy = shared_consts.TERMINATED_WHY.TOOLONGTIME;
+ await existingJob.save();
+ return false;
+ }
+ } else {
+ return true; // E' in FUNZIONE il JOB
+ }
+ }
+
+ return false;
+};
+
+JobsInProgressSchema.statics.addNewJob = async function (jobData) {
+ if (!jobData || typeof jobData !== 'object') {
+ console.error('ERRORE: ❌ jobData deve essere un oggetto valido');
+ }
+
+ const esistegia = await this.chechifExistJobWorking(jobData, true);
+
+ if (esistegia) {
+ return null;
+ }
+
+ try {
+ const newJob = await this.create(jobData);
+ return newJob;
+ } catch (err) {
+ console.error('Errore nell\'aggiungere un nuovo record: ', err);
+ throw err;
+ }
+};
+
+JobsInProgressSchema.methods.terminateJob = async function (witherror) {
+ try {
+
+ this.status = shared_consts.STATUS_JOB.FINISH;
+ this.terminatedWhy = witherror ? shared_consts.TERMINATED_WHY.END_WITHERROR : shared_consts.TERMINATED_WHY.END_NORMALLY;
+ await this.save();
+ return true;
+ } catch (err) {
+ console.error('Errore durante la terminazione del job: ', err);
+ return false;
+ }
+};
+
+
+JobsInProgressSchema.statics.getFieldsForSearch = function () {
+ return [{ field: 'descr', type: tools.FieldType.string }]
+};
+
+JobsInProgressSchema.statics.executeQueryTable = function (idapp, params, user) {
+ params.fieldsearch = this.getFieldsForSearch();
+ return tools.executeQueryTable(this, idapp, params, user);
+};
+
+JobsInProgressSchema.statics.findAllIdApp = async function (idapp) {
+ const JobsInProgress = this;
+
+ try {
+ return await JobsInProgress.find({ idapp }).then((arrrec) => {
+ return arrrec;
+ });
+
+ } catch (err) {
+ console.error('Errore: ', err);
+ }
+};
+
+const JobsInProgress = mongoose.model('JobsInProgress', JobsInProgressSchema);
+
+JobsInProgress.createIndexes()
+ .then(() => { })
+ .catch((err) => { throw err; });
+
+
+module.exports = { JobsInProgress };
diff --git a/src/server/models/catalog.js b/src/server/models/catalog.js
index eebc331..2bf38ce 100755
--- a/src/server/models/catalog.js
+++ b/src/server/models/catalog.js
@@ -26,16 +26,17 @@ const CatalogSchema = new Schema({
},
title: {
type: String,
+ index: true,
},
foto_collana: IImg,
-
+
idCollane: [{
type: String,
}],
idTipoFormato: [{
type: Number,
}],
-
+
argomenti: [{
type: String,
}],
@@ -43,9 +44,9 @@ const CatalogSchema = new Schema({
type: Number,
default: 0,
},
-
+
editore: [{ type: String }],
-
+
descr_introduttiva: {
type: String,
},
@@ -93,6 +94,7 @@ const CatalogSchema = new Schema({
type: Schema.Types.ObjectId,
ref: 'Product',
}],
+ isCatalogoGenerale: Boolean,
});
/*
@@ -143,89 +145,102 @@ CatalogSchema.statics.executeQueryTable = function (idapp, params, user) {
CatalogSchema.statics.findAllIdApp = async function (idapp) {
const Catalog = this;
- let arrrec = await Catalog.find({ idapp })
- .sort({ title: 1 }) // Ordina i risultati per titolo
- /*.populate({
- path: "idCollane", // Popola il campo idCollane
- model: "Collana" // Specifica il modello della collezione Collana
- })*/
- .populate({
- path: "lista_prodotti", // Popola il campo lista_prodotti
- populate: {
- path: "idProductInfo",
- model: "ProductInfo",
- populate: [
- {
- path: "idCatProds",
- model: "CatProd"
- },
- {
- path: "idSubCatProds",
- model: "SubCatProd"
- },
- {
- path: "idAuthors",
- model: "Author"
- }
- ],
- },
- })
- .populate({
- path: "lista_prodotti",
- populate: {
- path: "idProducer",
- model: "Producer"
- }
- })
- .populate({
- path: "lista_prodotti",
- populate: {
- path: "idProvider",
- model: "Provider"
- }
- })
- .populate({
- path: "lista_prodotti",
- populate: {
- path: "idStorehouses",
- model: "Storehouse"
- }
- })
- .populate({
- path: "lista_prodotti",
- populate: {
- path: "idScontisticas",
- model: "Scontistica"
- }
- })
- .populate({
- path: "lista_prodotti",
- populate: {
- path: "idGasordine",
- model: "Gasordine"
- }
- })
- ;
+ try {
+ let arrrec = await Catalog.find({ idapp })
+ .sort({ title: 1 }) // Ordina i risultati per titolo
+ /*.populate({
+ path: "idCollane", // Popola il campo idCollane
+ model: "Collana" // Specifica il modello della collezione Collana
+ })*/
+ .populate({
+ path: "lista_prodotti", // Popola il campo lista_prodotti
+ populate: {
+ path: "idProductInfo",
+ model: "ProductInfo",
+ populate: [
+ {
+ path: "idCatProds",
+ model: "CatProd"
+ },
+ {
+ path: "idSubCatProds",
+ model: "SubCatProd"
+ },
+ {
+ path: "idAuthors",
+ model: "Author"
+ }
+ ],
+ },
+ })
+ .populate({
+ path: "lista_prodotti",
+ populate: {
+ path: "idProducer",
+ model: "Producer"
+ }
+ })
+ .populate({
+ path: "lista_prodotti",
+ populate: {
+ path: "idProvider",
+ model: "Provider"
+ }
+ })
+ .populate({
+ path: "lista_prodotti",
+ populate: {
+ path: "idStorehouses",
+ model: "Storehouse"
+ }
+ })
+ .populate({
+ path: "lista_prodotti",
+ populate: {
+ path: "idScontisticas",
+ model: "Scontistica"
+ }
+ })
+ .populate({
+ path: "lista_prodotti",
+ populate: {
+ path: "idGasordine",
+ model: "Gasordine"
+ }
+ })
+ ;
- const transformedArrRec = arrrec.map(catalog => ({
- ...catalog.toObject(), // Converte il documento Mongoose in un oggetto JavaScript puro
- lista_prodotti: catalog.lista_prodotti.map(product => ({
- ...product.toObject(),
- productInfo: {
- ...product.idProductInfo.toObject(), // Copia tutti i campi di idProductInfo
- catprods: product.idProductInfo.idCatProds, // Rinomina idCatProds in catprods
- subcatprods: product.idProductInfo.idSubCatProds,
- collana: product.idProductInfo.idCollana,
- authors: product.idProductInfo.idAuthors,
- },
- producer: product.idProducer,
- storehouse: product.idStorehouses,
- scontisticas: product.idScontisticas,
- gasordine: product.idGasordine,
- })),
- }));
+ // controlla prima se nella lista ci sono dei product che non esistono piu allora li devi rimuovere !
+ for (const catalog of arrrec) {
+ const originalLength = catalog.lista_prodotti.length;
+ catalog.lista_prodotti = catalog.lista_prodotti.filter(product => product.idProductInfo);
+ if (catalog.lista_prodotti.length !== originalLength) {
+ await catalog.save();
+ }
+ }
- return transformedArrRec;
+ const transformedArrRec = arrrec.map(catalog => ({
+ ...catalog.toObject(), // Converte il documento Mongoose in un oggetto JavaScript puro
+ lista_prodotti: catalog.lista_prodotti.map(product => ({
+ ...product.toObject(),
+ productInfo: {
+ ...product.idProductInfo.toObject(), // Copia tutti i campi di idProductInfo
+ catprods: product.idProductInfo.idCatProds, // Rinomina idCatProds in catprods
+ subcatprods: product.idProductInfo.idSubCatProds,
+ collana: product.idProductInfo.idCollana,
+ authors: product.idProductInfo.idAuthors,
+ },
+ producer: product.idProducer,
+ storehouse: product.idStorehouses,
+ scontisticas: product.idScontisticas,
+ gasordine: product.idGasordine,
+ })),
+ }));
+
+ return transformedArrRec;
+ } catch (err) {
+ console.error('Errore: ', err);
+ }
};
const Catalog = mongoose.model('Catalog', CatalogSchema);
diff --git a/src/server/models/catprod.js b/src/server/models/catprod.js
index 9ed3e4e..ce5d06c 100755
--- a/src/server/models/catprod.js
+++ b/src/server/models/catprod.js
@@ -21,6 +21,7 @@ const CatProdSchema = new Schema({
},
name: {
type: String,
+ index: 1,
},
descr_estesa: {
type: String,
@@ -100,7 +101,7 @@ CatProdSchema.statics.updateCatDeleteEmpty = async function (idapp) {
};
-CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp) {
+CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp, updatedata) {
try {
const myquery = [
@@ -151,11 +152,13 @@ CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp) {
const result = await CatProd.aggregate(myquery);
- for (const record of result) {
- await CatProd.updateOne(
- { _id: record._id },
- { $set: { quanti: record.quanti } }
- );
+ if (updatedata) {
+ for (const record of result) {
+ await CatProd.updateOne(
+ { _id: record._id },
+ { $set: { quanti: record.quanti } }
+ );
+ }
}
return result;
diff --git a/src/server/models/collana.js b/src/server/models/collana.js
index 79ee5bc..62a44b2 100755
--- a/src/server/models/collana.js
+++ b/src/server/models/collana.js
@@ -20,6 +20,7 @@ const CollanaSchema = new Schema({
},
title: {
type: String,
+ index: true,
},
dataOra: {
type: Date,
@@ -55,7 +56,7 @@ module.exports.findAllIdApp = async function (idapp) {
return await Collana.find(myfind).sort({title: 1}).lean();
};
-module.exports.getCollaneWithTitleCount = async function (idapp) {
+module.exports.getCollaneWithTitleCount = async function (idapp, updatedata) {
try {
const myquery = [
@@ -104,11 +105,13 @@ module.exports.getCollaneWithTitleCount = async function (idapp) {
const result = await Collana.aggregate(myquery);
- for (const record of result) {
- await Collana.updateOne(
- { _id: record._id },
- { $set: { quanti: record.quanti } }
- );
+ if (updatedata) {
+ for (const record of result) {
+ await Collana.updateOne(
+ { _id: record._id },
+ { $set: { quanti: record.quanti } }
+ );
+ }
}
return result;
diff --git a/src/server/models/cron.js b/src/server/models/cron.js
new file mode 100755
index 0000000..97b6eab
--- /dev/null
+++ b/src/server/models/cron.js
@@ -0,0 +1,77 @@
+const mongoose = require('mongoose').set('debug', false)
+const Schema = mongoose.Schema;
+
+const tools = require('../tools/general');
+
+mongoose.Promise = global.Promise;
+mongoose.level = "F";
+
+
+// Resolving error Unknown modifier: $pushAll
+mongoose.plugin(schema => {
+ schema.options.usePushEach = true
+});
+
+
+const CronSchema = new Schema({
+ idapp: {
+ type: String,
+ },
+ active: {
+ type: Boolean,
+ default: false,
+ },
+ descr: {
+ type: String,
+ },
+ nomeFunzioneDbOp: {
+ type: String,
+ },
+ startTime: { // Orario iniziale (es. "08:30")
+ type: String,
+ default: "00:00"
+ },
+ everyXHours: { // Esegui ogni X ore
+ type: Number,
+ default: 24
+ },
+ date_created: {
+ type: Date,
+ default: Date.now
+ },
+ date_updated: {
+ type: Date,
+ },
+});
+
+
+CronSchema.statics.getFieldsForSearch = function () {
+ return [{ field: 'descr', type: tools.FieldType.string }]
+};
+
+CronSchema.statics.executeQueryTable = function (idapp, params, user) {
+ params.fieldsearch = this.getFieldsForSearch();
+ return tools.executeQueryTable(this, idapp, params, user);
+};
+
+CronSchema.statics.findAllIdApp = async function (idapp) {
+ const Cron = this;
+
+ try {
+ return await Cron.find({ idapp }).then((arrrec) => {
+ return arrrec;
+ });
+
+ } catch (err) {
+ console.error('Errore: ', err);
+ }
+};
+
+const Cron = mongoose.model('Cron', CronSchema);
+
+Cron.createIndexes()
+ .then(() => { })
+ .catch((err) => { throw err; });
+
+
+module.exports = { Cron };
diff --git a/src/server/models/destnewsletter.js b/src/server/models/destnewsletter.js
new file mode 100755
index 0000000..ca3ef26
--- /dev/null
+++ b/src/server/models/destnewsletter.js
@@ -0,0 +1,59 @@
+const mongoose = require('mongoose').set('debug', false)
+const Schema = mongoose.Schema;
+
+const tools = require('../tools/general');
+
+mongoose.Promise = global.Promise;
+mongoose.level = "F";
+
+
+// Resolving error Unknown modifier: $pushAll
+mongoose.plugin(schema => {
+ schema.options.usePushEach = true
+});
+
+const DestNewsletterSchema = new Schema({
+ idapp: {
+ type: String,
+ },
+ descr: {
+ type: String,
+ },
+ tipodest_id: {
+ type: Number,
+ },
+});
+
+DestNewsletterSchema.statics.getFieldsForSearch = function () {
+ return []
+};
+
+DestNewsletterSchema.statics.executeQueryTable = function (idapp, params) {
+ params.fieldsearch = this.getFieldsForSearch();
+ return tools.executeQueryTable(this, idapp, params);
+};
+
+DestNewsletterSchema.statics.DuplicateAllRecords = async function (idapporig, idappdest) {
+
+ return await tools.DuplicateAllRecords(this, idapporig, idappdest);
+
+};
+
+
+DestNewsletterSchema.statics.findAllIdApp = async function (idapp) {
+ const DestNewsletter = this;
+
+ const myfind = { idapp };
+
+ return await DestNewsletter.find(myfind).lean();
+};
+
+
+const DestNewsletter = mongoose.model('DestNewsletter', DestNewsletterSchema);
+
+DestNewsletter.createIndexes()
+ .then(() => { })
+ .catch((err) => { throw err; });
+
+
+module.exports = { DestNewsletter };
diff --git a/src/server/models/mailinglist.js b/src/server/models/mailinglist.js
index 92b5091..3ffa562 100755
--- a/src/server/models/mailinglist.js
+++ b/src/server/models/mailinglist.js
@@ -39,6 +39,41 @@ MailingListSchema.statics.findAllIdAppSubscribed = function (idapp) {
return tools.findAllQueryIdApp(User, myfind);
};
+MailingListSchema.statics.findAllIdAppDiarioSubscr = function (idapp) {
+
+ const myfind = {
+ idapp,
+ diario_on: true,
+ $or: [
+ { deleted: { $exists: false } },
+ { deleted: { $exists: true, $eq: false } }],
+ $or: [
+ { email_errata: { $exists: false } },
+ { email_errata: { $exists: true, $ne: true } }],
+ };
+
+ // Extract only the Teacher where in the users table the field permissions is set 'Teacher' bit.
+
+ return tools.findAllQueryIdApp(User, myfind);
+};
+MailingListSchema.statics.findAllIdAppDiarioSubscr = function (idapp) {
+
+ const myfind = {
+ idapp,
+ test: true,
+ $or: [
+ { deleted: { $exists: false } },
+ { deleted: { $exists: true, $eq: false } }],
+ $or: [
+ { email_errata: { $exists: false } },
+ { email_errata: { $exists: true, $ne: true } }],
+ };
+
+ // Extract only the Teacher where in the users table the field permissions is set 'Teacher' bit.
+
+ return tools.findAllQueryIdApp(User, myfind);
+};
+
MailingListSchema.statics.getnumSent = async function (idapp, idUser) {
const myfind = { idapp, news_on: true, lastid_newstosent: idUser };
diff --git a/src/server/models/newstosent.js b/src/server/models/newstosent.js
index c4eda3e..3ab41ef 100755
--- a/src/server/models/newstosent.js
+++ b/src/server/models/newstosent.js
@@ -22,6 +22,9 @@ const NewstosentSchema = new Schema({
templemail_str: {
type: String,
},
+ destnewsletter_str: {
+ type: String,
+ },
activate: {
type: Boolean,
default: false
@@ -121,7 +124,10 @@ NewstosentSchema.statics.findNewsletterPending_To_Send = function (idapp) {
starting_job: true,
finish_job: false,
processing_job: false,
- lastemailsent_Job: { $gte: tools.IncDateNow(-1000 * 60 * 60 * 15) },
+ $or: [
+ { lastemailsent_Job: { $gte: tools.IncDateNow(-1000 * 60 * 60 * 15) } },
+ { lastemailsent_Job: null }
+ ],
idapp
}).lean();
};
diff --git a/src/server/models/order.js b/src/server/models/order.js
index a4baebb..e84bf3e 100755
--- a/src/server/models/order.js
+++ b/src/server/models/order.js
@@ -22,9 +22,9 @@ const orderSchema = new Schema({
},
userId: { type: Schema.Types.ObjectId, ref: 'User' },
status: {
- type: Number,
+ type: Number, index: true
},
- idProduct: { type: Schema.Types.ObjectId, ref: 'Product' },
+ idProduct: { type: Schema.Types.ObjectId, ref: 'Product', index: true },
idProducer: { type: Schema.Types.ObjectId, ref: 'Producer' },
idStorehouse: { type: Schema.Types.ObjectId, ref: 'StoreHouse' },
idScontisticas: [{ type: Schema.Types.ObjectId, ref: 'Scontistica' }],
@@ -125,7 +125,8 @@ const orderSchema = new Schema({
type: String
},
modify_at: {
- type: Date
+ type: Date,
+ index: true
},
});
diff --git a/src/server/models/product.js b/src/server/models/product.js
index 2f59cae..dc17775 100755
--- a/src/server/models/product.js
+++ b/src/server/models/product.js
@@ -36,16 +36,16 @@ const productSchema = new Schema({
isbn: {
type: String,
},
- idProductInfo: { type: Schema.Types.ObjectId, ref: 'ProductInfo' },
- idProducer: { type: Schema.Types.ObjectId, ref: 'Producer' },
+ idProductInfo: { type: Schema.Types.ObjectId, ref: 'ProductInfo', index: true },
+ idProducer: { type: Schema.Types.ObjectId, ref: 'Producer', index: true },
idStorehouses: [
- { type: Schema.Types.ObjectId, ref: 'Storehouse' }
+ { type: Schema.Types.ObjectId, ref: 'Storehouse', index: true }
],
- idGasordine: { type: Schema.Types.ObjectId, ref: 'Gasordine' },
+ idGasordine: { type: Schema.Types.ObjectId, ref: 'Gasordine', index: true },
idScontisticas: [
- { type: Schema.Types.ObjectId, ref: 'Scontistica' }
+ { type: Schema.Types.ObjectId, ref: 'Scontistica', index: true }
],
- idProvider: { type: Schema.Types.ObjectId, ref: 'Provider' },
+ idProvider: { type: Schema.Types.ObjectId, ref: 'Provider', index: true },
prezzo_ivato: { // Con IVA
type: Number
},
@@ -137,6 +137,7 @@ const productSchema = new Schema({
stockQty: { // in magazzino
type: Number,
default: 0,
+ index: true,
},
stockBloccatiQty: { // Prenotati Bloccati
type: Number,
@@ -220,7 +221,7 @@ const productSchema = new Schema({
},
validaprod: {
esito: {
- type: Number,
+ type: Number,
},
data: {
type: Date,
@@ -468,176 +469,33 @@ module.exports.findAllIdApp = async function (idapp, code, id, all) {
// 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: 'producers',
- localField: 'idProducer',
- foreignField: '_id',
- as: 'producer'
- }
- },
- {
- $unwind: {
- path: '$producer',
- preserveNullAndEmptyArrays: true,
- },
- },
- {
- $lookup: {
- from: 'productinfos',
- localField: 'idProductInfo',
- foreignField: '_id',
- as: 'productInfo'
- }
- },
- {
- $unwind: {
- path: '$productInfo',
- preserveNullAndEmptyArrays: true,
- },
- },
- {
- $lookup: {
- from: 'gasordines',
- localField: 'idGasordine',
- foreignField: '_id',
- as: 'gasordine'
- }
- },
- {
- $unwind: {
- path: '$gasordine',
- preserveNullAndEmptyArrays: true,
- },
- },
- {
- $match: {
- $or: [
- { 'gasordine.active': true }, // Include documents where gasordines.active is true
- { 'gasordine': { $exists: false } } // Include documents where gasordines array doesn't exist
- ]
- }
- },
- {
- $group: {
- _id: '$_id',
- gasordine: { $first: '$gasordine' },
- originalFields: { $first: '$$ROOT' } // Preserve all existing fields
- }
- },
- {
- $replaceRoot: {
- newRoot: {
- $mergeObjects: ['$originalFields', { gasordine: '$gasordine' }]
- }
- }
- },
- {
- $lookup: {
- from: 'providers',
- localField: 'idProvider',
- foreignField: '_id',
- as: 'provider'
- }
- },
- {
- $unwind: {
- path: '$provider',
- preserveNullAndEmptyArrays: true,
- },
- },
- {
- $lookup: {
- from: 'authors',
- localField: 'productInfo.idAuthors',
- foreignField: '_id',
- as: 'productInfo.authors'
- }
- },
- {
- $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: 'orders',
- let: { productId: '$_id' },
+ from: "orders",
+ let: { productId: "$_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
- { $eq: ['$idProduct', '$$productId'] },
+ { $eq: ["$idProduct", "$$productId"] },
{
$or: [
+ { $eq: ["$status", shared_consts.OrderStatus.CHECKOUT_SENT] },
{
- $eq: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT]
- },
- {
- $and: [{ $lt: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT] },
- {
- $gt: [
- '$modify_at',
- { $subtract: [new Date(), 60 * 60 * 1000] } // 1 hour in milliseconds 60 * 60
- ]
- }]
+ $and: [
+ { $lt: ["$status", shared_consts.OrderStatus.CHECKOUT_SENT] },
+ {
+ $gt: [
+ "$modify_at",
+ { $subtract: [new Date(), 60 * 60 * 1000] }
+ ]
+ }
+ ]
}
]
}
@@ -648,102 +506,162 @@ module.exports.findAllIdApp = async function (idapp, code, id, all) {
{
$group: {
_id: null,
- totalQty: { $sum: '$quantity' },
+ totalQty: { $sum: "$quantity" },
+ totalQtyPreordered: { $sum: "$quantitypreordered" }
}
}
],
- as: 'productOrders'
- }
- },
- {
- $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] } // 1 hour in milliseconds 60 * 60
- ]
- }]
- }
- ]
- }
- ]
- }
- }
- },
- {
- $group: {
- _id: null,
- totalQtyPreordered: { $sum: '$quantitypreordered' }
- }
- }
- ],
- as: 'productPreOrders'
+ as: "orderSummary"
}
},
+
+ // ESTRAGGO LE QUANTITÀ IN CAMPI AGGIUNTIVI
{
$addFields: {
QuantitaOrdinateInAttesa: {
- $ifNull: [
- {
- $cond: {
- if: { $isArray: '$productOrders' },
- then: { $arrayElemAt: ['$productOrders.totalQty', 0] },
- else: 0
- }
- },
- 0
- ]
+ $ifNull: [{ $arrayElemAt: ["$orderSummary.totalQty", 0] }, 0]
},
QuantitaPrenotateInAttesa: {
- $ifNull: [
- {
- $cond: {
- if: { $isArray: '$productPreOrders' },
- then: { $arrayElemAt: ['$productPreOrders.totalQtyPreordered', 0] },
- else: 0
- }
- },
- 0
- ]
- },
- },
- },
- {
- $addFields: {
- quantityAvailable: {
- $subtract: ["$stockQty", "$QuantitaOrdinateInAttesa"],
- },
- bookableAvailableQty: {
- $subtract: ["$maxbookableGASQty", "$QuantitaPrenotateInAttesa"],
+ $ifNull: [{ $arrayElemAt: ["$orderSummary.totalQtyPreordered", 0] }, 0]
}
}
},
+
+ // CALCOLO DELLE DISPONIBILITÀ
{
- $unset: 'productOrders'
- },
- {
- $unset: 'productPreOrders'
- },
- {
- $sort: {
- 'productInfo.name': 1 // 1 for ascending order, -1 for descending order
+ $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 } },
+
+ {
+ $lookup: {
+ from: "productinfos",
+ localField: "idProductInfo",
+ foreignField: "_id",
+ as: "productInfo"
+ }
+ },
+ { $unwind: { path: "$productInfo", preserveNullAndEmptyArrays: true } },
+
+ {
+ $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);
diff --git a/src/server/models/productInfo.js b/src/server/models/productInfo.js
index 6b35fb6..a5186f6 100755
--- a/src/server/models/productInfo.js
+++ b/src/server/models/productInfo.js
@@ -41,6 +41,7 @@ const productInfoSchema = new Schema({
},
name: {
type: String,
+ index: true,
},
description: {
type: String,
@@ -48,10 +49,10 @@ const productInfoSchema = new Schema({
short_descr: {
type: String,
},
- idCatProds: [{ type: Schema.Types.ObjectId, ref: 'CatProd' }],
+ idCatProds: [{ type: Schema.Types.ObjectId, ref: 'CatProd', index: true }],
idSubCatProds: [{ type: Schema.Types.ObjectId, ref: 'SubCatProd' }],
idStatoProdotto: {
- type: Number
+ type: Number, index: true
},
color: {
type: String
@@ -86,6 +87,9 @@ const productInfoSchema = new Schema({
imagefile: {
type: String,
},
+ image_not_found: {
+ type: Boolean,
+ },
vers_img: {
type: Number,
},
@@ -122,9 +126,9 @@ const productInfoSchema = new Schema({
note: {
type: String,
},
- idAuthors: [{ type: Schema.Types.ObjectId, ref: 'Author' }],
- idCollana: { type: Schema.Types.ObjectId, ref: 'Collana' },
- idPublisher: { type: Schema.Types.ObjectId, ref: 'Publisher' },
+ 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,
},
@@ -133,6 +137,7 @@ const productInfoSchema = new Schema({
},
date_pub: {
type: Date,
+ index: 1,
},
date_pub_ts: {
type: Number,
@@ -144,7 +149,7 @@ const productInfoSchema = new Schema({
date_updated: {
type: Date,
},
-
+
date_updated_fromGM: {
type: Date,
},
@@ -156,8 +161,13 @@ const productInfoSchema = new Schema({
fatLast6M: Number,
fatLast1Y: Number,
fatLast2Y: Number,
- vLast6M: Number,
- vLast1Y: Number,
+ vLast6M: {
+ type: Number,
+ index: true,
+ },
+ vLast1Y: {
+ type: Number, index: true
+ },
vLast2Y: Number,
dataUltimoOrdine: Date,
rank3M: Number,
@@ -492,6 +502,60 @@ module.exports.updateProductInfoByStats = async function (idapp) {
return mylogtot;
}
+// crea setImgNotFound
+
+module.exports.setImgNotFound = async function (id) {
+ // set sul record id il flag image_not_found
+ try {
+ const ProductInfo = this;
+
+ await ProductInfo.updateOne(
+ { _id: id },
+ { $set: { 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);
+ }
+
+}
+
+// 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 ProductInfo = this;
+
+ const globalTables = require('../tools/globalTables');
+ const Product = globalTables.getTableByTableName('Product');
+
+ let mylog;
+
+ try {
+ const arrproductInfo = await ProductInfo.find({ idapp, date_updated_fromGM: { $exists: false } });
+
+ if (arrproductInfo.length > 0) {
+ mylog = `Rimuovo ${arrproductInfo.length} productInfo senza date_updated_fromGM !!`
+ console.log(mylog);
+
+ for (const productinfo of arrproductInfo) {
+ // cerca nella tabella Product se esiste idProductInfo = _id e cancella tutti i record che hanno questa corrispondenza
+ if (Product) {
+ await Product.deleteMany({ idProductInfo: productinfo._id });
+ }
+
+ // Ora rimuovi anche questo productInfo
+ await ProductInfo.deleteOne({ _id: productinfo._id });
+ }
+ }
+
+ return mylog;
+ } catch (error) {
+ mylog += 'Error removing productInfo without date_updated_fromGM:' + error;
+ console.error(mylog);
+ }
+
+ return mylog;
+};
+
module.exports.createIndexes()
.then(() => { })
.catch((err) => { throw err; });
diff --git a/src/server/models/publisher.js b/src/server/models/publisher.js
index 38b4ed2..f6fafef 100755
--- a/src/server/models/publisher.js
+++ b/src/server/models/publisher.js
@@ -48,7 +48,7 @@ module.exports.findAllIdApp = async function (idapp) {
return await Publisher.find(myfind).sort({ name: 1 }).lean();
};
-module.exports.getEditoriWithTitleCount = async function (idapp) {
+module.exports.getEditoriWithTitleCount = async function (idapp, updatedata) {
try {
const myquery = [
@@ -95,11 +95,13 @@ module.exports.getEditoriWithTitleCount = async function (idapp) {
const result = await Publisher.aggregate(myquery);
- for (const record of result) {
- await Publisher.updateOne(
- { _id: record._id },
- { $set: { quanti: record.quanti } }
- );
+ if (updatedata) {
+ for (const record of result) {
+ await Publisher.updateOne(
+ { _id: record._id },
+ { $set: { quanti: record.quanti } }
+ );
+ }
}
return result;
diff --git a/src/server/models/templemail.js b/src/server/models/templemail.js
index 74316f5..5f4b104 100755
--- a/src/server/models/templemail.js
+++ b/src/server/models/templemail.js
@@ -25,6 +25,15 @@ const TemplEmailSchema = new Schema({
content: {
type: String,
},
+ disclaimer: {
+ type: String,
+ },
+ piedipagina: {
+ type: String,
+ },
+ firma: {
+ type: String,
+ },
img: {
type: String,
},
diff --git a/src/server/models/user.js b/src/server/models/user.js
index 6a51d4b..14b2514 100755
--- a/src/server/models/user.js
+++ b/src/server/models/user.js
@@ -177,6 +177,12 @@ const UserSchema = new mongoose.Schema({
news_on: {
type: Boolean,
},
+ diario_on: {
+ type: Boolean,
+ },
+ test: {
+ type: Boolean,
+ },
email_errata: {
type: Boolean,
},
@@ -640,7 +646,7 @@ UserSchema.statics.canHavePower = function (perm) {
try {
let consentito = false;
if (User.isAdmin(perm) || User.isManager(perm) ||
- User.isEditor(perm) || User.isFacilitatore(perm)) {
+ User.isEditor(perm) || User.isCommerciale(perm) || User.isFacilitatore(perm)) {
consentito = true;
}
@@ -696,6 +702,14 @@ UserSchema.statics.isEditor = function (perm) {
return false;
}
};
+UserSchema.statics.isCommerciale = function (perm) {
+ try {
+ return ((perm & shared_consts.Permissions.Commerciale) ===
+ shared_consts.Permissions.Commerciale);
+ } catch (e) {
+ return false;
+ }
+};
UserSchema.statics.isGrafico = function (perm) {
try {
return ((perm & shared_consts.Permissions.Grafico) ===
@@ -759,7 +773,7 @@ UserSchema.statics.findByToken = async function (token, typeaccess, con_auth, wi
if (!token) {
console.warn('TOKEN VUOTO ! ');
return { user, code };
- }
+ }
try {
@@ -822,7 +836,7 @@ UserSchema.statics.findByToken = async function (token, typeaccess, con_auth, wi
const end_find = process.hrtime.bigint();
// console.log(` User.findOne LEAN impiega ${Math.round(Number(end_find - start_find) / 1e6) / 1000} secondi.`);
}
-
+
if (user) {
const checkExpiry = tools.getEnableTokenExpiredByIdApp(user.idapp);
@@ -1350,6 +1364,16 @@ UserSchema.statics.setaportador_solidario = async function (
return !!myrec;
};
+UserSchema.statics.setNewsletterToAll = async function (
+ idapp) {
+ const User = this;
+
+ return await User.updateMany({
+ idapp,
+ $or: [{ deleted: { $exists: false } }, { deleted: { $exists: true, $eq: false } }],
+ }, { $set: { 'news_on': true } },
+ { new: false });
+};
UserSchema.statics.setNewsletter = async function (
idapp, username, newsletter_on) {
const User = this;
diff --git a/src/server/modules/Macro.js b/src/server/modules/Macro.js
index 2ec8ca5..dbb4ea5 100644
--- a/src/server/modules/Macro.js
+++ b/src/server/modules/Macro.js
@@ -15,6 +15,7 @@ const { getTableContent } = require('../controllers/articleController');
const T_WEB_ArticoliFatturati = require('../models/t_web_articolifatturati');
const T_WEB_Ordini = require('../models/t_web_ordini');
+const { JobsInProgress } = require('../models/JobsInProgress');
class Macro {
constructor(idapp, options) {
@@ -51,6 +52,18 @@ class Macro {
idapp: options.idapp,
}
+ let myjob = null;
+
+ const lavoromassivo = options.caricatutti;
+ if (lavoromassivo) {
+ myjob = await JobsInProgress.addNewJob({ idapp, descr: 'Riaggiorna Articoli', nomeFunzioneDbOp: 'updateAllBook', status: shared_consts.STATUS_JOB.START });
+ if (!myjob) {
+ mylog = 'ATTENZIONE! ❌ STAVO GIA ESEGUENDO QUESTO JOB, quindi ESCO !';
+ console.error(mylog);
+ return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog, idRecUpdated: opt.idRecUpdated, table: opt.table };
+ }
+ }
+
try {
@@ -417,13 +430,21 @@ class Macro {
console.log('numrec', numrec);
}
+ let rimuoviTabellePerIniziare = false;
+
let count = 0;
if (Array.isArray(recproducts)) {
- for (const recproduct of recproducts) {
- // if (!options.caricatutti) {
- await this.elaboraProdotto(recproduct, opt);
+ if (recproducts.length > 10 && lavoromassivo) {
+ // rimuovi dalla tabella productInfo tutti i campi date_updated_fromGM
+ const result = await ProductInfo.updateMany({ idapp: options.idapp }, { $unset: { date_updated_fromGM: null } });
+ let quanti_rimossi = result.modifiedCount;
+ console.log(`Sbianca date_updated_fromGM da ProductInfo: (${quanti_rimossi} su ${result.matchedCount})`);
+ rimuoviTabellePerIniziare = true;
+ }
+ for (const recproduct of recproducts) {
+ await this.elaboraProdotto(recproduct, opt);
const sku = recproduct.IdArticolo;
@@ -439,6 +460,12 @@ class Macro {
}
//}
}
+
+ if (rimuoviTabellePerIniziare) {
+ await ProductInfo.removeProductInfoWithoutDateUpdatedFromGM(options.idapp);
+ }
+ if (myjob)
+ await myjob.terminateJob();
}
if (numrec > 1) {
@@ -455,6 +482,8 @@ class Macro {
} catch (e) {
mylog += 'ERRORE ! *** IMPORTATI: ' + opt?.imported + ' AGGIORNATI = ' + opt?.updated + ' (su ' + numrec + ' RECORD)';
opt.logerror = e.message;
+ if (myjob)
+ await myjob.terminateJob(true);
console.error(e.message);
return { updated: opt.updated, imported: opt.imported, errors: opt.errors, mylog, logerror: opt.logerror };
}
@@ -1075,6 +1104,15 @@ class Macro {
return listaCampi.some((campo) => recordOld[campo] !== recordNew[campo]);
}
+ async getStat() {
+ let mystr = '';
+
+ const ris = await ProductInfo.countDocuments({ $or: [{ date_updated_fromGM: { $exists: false } }, { date_updated_fromGM: null }] });
+ mystr += `${ris} ProductInfo non aggiornati da GM, quindi da cancellare ! \n`;
+
+ return mystr;
+ }
+
}
module.exports = Macro;
\ No newline at end of file
diff --git a/src/server/modules/MssqlMigrator.js b/src/server/modules/MssqlMigrator.js
index e874806..40bbb58 100644
--- a/src/server/modules/MssqlMigrator.js
+++ b/src/server/modules/MssqlMigrator.js
@@ -33,13 +33,27 @@ class MssqlMigrator {
let indtab = 0;
let indtabok = 0;
const logs = [];
- for (const tableName of tableNames) {
+ for (const rectable of tableNames) {
try {
+ const tableName = rectable.table;
+ const usaDataOra = rectable.usaDataOra;
+ const fieldId = rectable.fieldId;
const percentuale = ((indtab / numtables) * 100).toFixed(2);
logs.push(`\n>> Recupero dati da MSSQL per la tabella: ${tableName} - (Completamento: ${percentuale}%)`);
console.log(logs[logs.length - 1]);
- const dataQuery = `SELECT * FROM [${tableName}]`;
+ let dataQuery = `SELECT * FROM ${tableName}`;
+
+ if (usaDataOra) {
+ dataQuery = `SELECT T.* FROM ${tableName} T`;
+ dataQuery += ` JOIN (
+ SELECT ${fieldId}, MAX(DataOra) AS data
+ FROM ${tableName}
+ GROUP BY ${fieldId}
+ ) b ON T.${fieldId} = b.${fieldId} AND T.DataOra = b.data; `;
+ }
+
+ console.log('query', dataQuery);
let dataResponse = null;
@@ -51,11 +65,12 @@ class MssqlMigrator {
null,
{ timeout: 900000 });
} catch (error) {
- console.error('Error: ', error);
+ console.error('Error: ', error.response?.data?.error || error.message || 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 },
@@ -69,6 +84,7 @@ class MssqlMigrator {
}
}
+
const records = dataResponse?.data;
if (!records || records.length === 0) {
diff --git a/src/server/populate/cities_sardegna.js b/src/server/populate/cities_sardegna.js
new file mode 100644
index 0000000..674a423
--- /dev/null
+++ b/src/server/populate/cities_sardegna.js
@@ -0,0 +1,3381 @@
+module.exports = {
+ list: [
+ {
+ "comune": "Abbasanta",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "315",
+ "abitanti": "2579",
+ "country": "IT"
+ },
+ {
+ "comune": "Aggius",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "514",
+ "abitanti": "1409",
+ "country": "IT"
+ },
+ {
+ "comune": "Aglientu",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "420",
+ "abitanti": "1154",
+ "country": "IT"
+ },
+ {
+ "comune": "Aidomaggiore",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "250",
+ "abitanti": "398",
+ "country": "IT"
+ },
+ {
+ "comune": "Alà dei Sardi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "663",
+ "abitanti": "1764",
+ "country": "IT"
+ },
+ {
+ "comune": "Albagiara",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "215",
+ "abitanti": "246",
+ "country": "IT"
+ },
+ {
+ "comune": "Ales",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "194",
+ "abitanti": "1285",
+ "country": "IT"
+ },
+ {
+ "comune": "Alghero",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "079",
+ "cap": "7",
+ "abitanti": "42352",
+ "country": "IT"
+ },
+ {
+ "comune": "Allai",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "60",
+ "abitanti": "355",
+ "country": "IT"
+ },
+ {
+ "comune": "Anela",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "446",
+ "abitanti": "584",
+ "country": "IT"
+ },
+ {
+ "comune": "Arborea",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "7",
+ "abitanti": "3758",
+ "country": "IT"
+ },
+ {
+ "comune": "Arbus",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "311",
+ "abitanti": "5869",
+ "country": "IT"
+ },
+ {
+ "comune": "Ardara",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "296",
+ "abitanti": "736",
+ "country": "IT"
+ },
+ {
+ "comune": "Ardauli",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "421",
+ "abitanti": "782",
+ "country": "IT"
+ },
+ {
+ "comune": "Aritzo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "796",
+ "abitanti": "1223",
+ "country": "IT"
+ },
+ {
+ "comune": "Armungia",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "366",
+ "abitanti": "424",
+ "country": "IT"
+ },
+ {
+ "comune": "Arzachena",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "85",
+ "abitanti": "13331",
+ "country": "IT"
+ },
+ {
+ "comune": "Arzana",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "672",
+ "abitanti": "2255",
+ "country": "IT"
+ },
+ {
+ "comune": "Assemini",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "6",
+ "abitanti": "25944",
+ "country": "IT"
+ },
+ {
+ "comune": "Assolo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "255",
+ "abitanti": "348",
+ "country": "IT"
+ },
+ {
+ "comune": "Asuni",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "233",
+ "abitanti": "311",
+ "country": "IT"
+ },
+ {
+ "comune": "Atzara",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "553",
+ "abitanti": "1018",
+ "country": "IT"
+ },
+ {
+ "comune": "Austis",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "737",
+ "abitanti": "767",
+ "country": "IT"
+ },
+ {
+ "comune": "Badesi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "102",
+ "abitanti": "1825",
+ "country": "IT"
+ },
+ {
+ "comune": "Ballao",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "98",
+ "abitanti": "731",
+ "country": "IT"
+ },
+ {
+ "comune": "Banari",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "419",
+ "abitanti": "533",
+ "country": "IT"
+ },
+ {
+ "comune": "Baradili",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "165",
+ "abitanti": "75",
+ "country": "IT"
+ },
+ {
+ "comune": "Baratili San Pietro",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "11",
+ "abitanti": "1206",
+ "country": "IT"
+ },
+ {
+ "comune": "Baressa",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "165",
+ "abitanti": "566",
+ "country": "IT"
+ },
+ {
+ "comune": "Bari Sardo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "51",
+ "abitanti": "3830",
+ "country": "IT"
+ },
+ {
+ "comune": "Barrali",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "140",
+ "abitanti": "1094",
+ "country": "IT"
+ },
+ {
+ "comune": "Barumini",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "202",
+ "abitanti": "1178",
+ "country": "IT"
+ },
+ {
+ "comune": "Bauladu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "38",
+ "abitanti": "649",
+ "country": "IT"
+ },
+ {
+ "comune": "Baunei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "480",
+ "abitanti": "3442",
+ "country": "IT"
+ },
+ {
+ "comune": "Belvì",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "787",
+ "abitanti": "560",
+ "country": "IT"
+ },
+ {
+ "comune": "Benetutti",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "406",
+ "abitanti": "1704",
+ "country": "IT"
+ },
+ {
+ "comune": "Berchidda",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "290",
+ "abitanti": "2630",
+ "country": "IT"
+ },
+ {
+ "comune": "Bessude",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "447",
+ "abitanti": "391",
+ "country": "IT"
+ },
+ {
+ "comune": "Bidonì",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "250",
+ "abitanti": "127",
+ "country": "IT"
+ },
+ {
+ "comune": "Birori",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "450",
+ "abitanti": "500",
+ "country": "IT"
+ },
+ {
+ "comune": "Bitti",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "549",
+ "abitanti": "2597",
+ "country": "IT"
+ },
+ {
+ "comune": "Bolotana",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "472",
+ "abitanti": "2403",
+ "country": "IT"
+ },
+ {
+ "comune": "Bonarcado",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "284",
+ "abitanti": "1503",
+ "country": "IT"
+ },
+ {
+ "comune": "Bonnanaro",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "405",
+ "abitanti": "930",
+ "country": "IT"
+ },
+ {
+ "comune": "Bono",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "540",
+ "abitanti": "3331",
+ "country": "IT"
+ },
+ {
+ "comune": "Bonorva",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "508",
+ "abitanti": "3211",
+ "country": "IT"
+ },
+ {
+ "comune": "Boroneddu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "206",
+ "abitanti": "154",
+ "country": "IT"
+ },
+ {
+ "comune": "Borore",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "394",
+ "abitanti": "1982",
+ "country": "IT"
+ },
+ {
+ "comune": "Bortigali",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "510",
+ "abitanti": "1244",
+ "country": "IT"
+ },
+ {
+ "comune": "Bortigiadas",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "476",
+ "abitanti": "729",
+ "country": "IT"
+ },
+ {
+ "comune": "Borutta",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "471",
+ "abitanti": "260",
+ "country": "IT"
+ },
+ {
+ "comune": "Bosa",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "2",
+ "abitanti": "7465",
+ "country": "IT"
+ },
+ {
+ "comune": "Bottidda",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "396",
+ "abitanti": "649",
+ "country": "IT"
+ },
+ {
+ "comune": "Buddusò",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "700",
+ "abitanti": "3622",
+ "country": "IT"
+ },
+ {
+ "comune": "Budoni",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "16",
+ "abitanti": "5298",
+ "country": "IT"
+ },
+ {
+ "comune": "Buggerru",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "51",
+ "abitanti": "1050",
+ "country": "IT"
+ },
+ {
+ "comune": "Bultei",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "509",
+ "abitanti": "841",
+ "country": "IT"
+ },
+ {
+ "comune": "Bulzi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "250",
+ "abitanti": "469",
+ "country": "IT"
+ },
+ {
+ "comune": "Burcei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "648",
+ "abitanti": "2646",
+ "country": "IT"
+ },
+ {
+ "comune": "Burgos",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "575",
+ "abitanti": "855",
+ "country": "IT"
+ },
+ {
+ "comune": "Busachi",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "379",
+ "abitanti": "1164",
+ "country": "IT"
+ },
+ {
+ "comune": "Cabras",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "6",
+ "abitanti": "8760",
+ "country": "IT"
+ },
+ {
+ "comune": "Cagliari",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "6",
+ "abitanti": "148881",
+ "country": "IT"
+ },
+ {
+ "comune": "Calangianus",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "518",
+ "abitanti": "3814",
+ "country": "IT"
+ },
+ {
+ "comune": "Calasetta",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "0781",
+ "cap": "9",
+ "abitanti": "2773",
+ "country": "IT"
+ },
+ {
+ "comune": "Capoterra",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "54",
+ "abitanti": "23172",
+ "country": "IT"
+ },
+ {
+ "comune": "Carbonia",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "0781",
+ "cap": "111",
+ "abitanti": "26390",
+ "country": "IT"
+ },
+ {
+ "comune": "Cardedu",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "19",
+ "abitanti": "1923",
+ "country": "IT"
+ },
+ {
+ "comune": "Cargeghe",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "333",
+ "abitanti": "585",
+ "country": "IT"
+ },
+ {
+ "comune": "Carloforte",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "0781",
+ "cap": "10",
+ "abitanti": "5953",
+ "country": "IT"
+ },
+ {
+ "comune": "Castelsardo",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "114",
+ "abitanti": "5651",
+ "country": "IT"
+ },
+ {
+ "comune": "Castiadas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "60",
+ "abitanti": "1651",
+ "country": "IT"
+ },
+ {
+ "comune": "Cheremule",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "540",
+ "abitanti": "402",
+ "country": "IT"
+ },
+ {
+ "comune": "Chiaramonti",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "430",
+ "abitanti": "1511",
+ "country": "IT"
+ },
+ {
+ "comune": "Codrongianos",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "317",
+ "abitanti": "1279",
+ "country": "IT"
+ },
+ {
+ "comune": "Collinas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "249",
+ "abitanti": "774",
+ "country": "IT"
+ },
+ {
+ "comune": "Cossoine",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "529",
+ "abitanti": "762",
+ "country": "IT"
+ },
+ {
+ "comune": "Cuglieri",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "479",
+ "abitanti": "2457",
+ "country": "IT"
+ },
+ {
+ "comune": "Curcuris",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "130",
+ "abitanti": "311",
+ "country": "IT"
+ },
+ {
+ "comune": "Decimomannu",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "10",
+ "abitanti": "8293",
+ "country": "IT"
+ },
+ {
+ "comune": "Decimoputzu",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "17",
+ "abitanti": "4164",
+ "country": "IT"
+ },
+ {
+ "comune": "Desulo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "888",
+ "abitanti": "2137",
+ "country": "IT"
+ },
+ {
+ "comune": "Dolianova",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "212",
+ "abitanti": "9411",
+ "country": "IT"
+ },
+ {
+ "comune": "Domus de Maria",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "66",
+ "abitanti": "1621",
+ "country": "IT"
+ },
+ {
+ "comune": "Domusnovas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "52",
+ "abitanti": "5886",
+ "country": "IT"
+ },
+ {
+ "comune": "Donori",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "141",
+ "abitanti": "1964",
+ "country": "IT"
+ },
+ {
+ "comune": "Dorgali",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "390",
+ "abitanti": "8299",
+ "country": "IT"
+ },
+ {
+ "comune": "Dualchi",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "321",
+ "abitanti": "577",
+ "country": "IT"
+ },
+ {
+ "comune": "Elini",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "472",
+ "abitanti": "559",
+ "country": "IT"
+ },
+ {
+ "comune": "Elmas",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "7",
+ "abitanti": "9358",
+ "country": "IT"
+ },
+ {
+ "comune": "Erula",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "457",
+ "abitanti": "690",
+ "country": "IT"
+ },
+ {
+ "comune": "Escalaplano",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "338",
+ "abitanti": "2079",
+ "country": "IT"
+ },
+ {
+ "comune": "Escolca",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "416",
+ "abitanti": "542",
+ "country": "IT"
+ },
+ {
+ "comune": "Espotlatu",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "473",
+ "abitanti": "381",
+ "country": "IT"
+ },
+ {
+ "comune": "Esterzili",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "731",
+ "abitanti": "565",
+ "country": "IT"
+ },
+ {
+ "comune": "Florinas",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "417",
+ "abitanti": "1442",
+ "country": "IT"
+ },
+ {
+ "comune": "Fluminimaggiore",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "63",
+ "abitanti": "2654",
+ "country": "IT"
+ },
+ {
+ "comune": "Flussio",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "305",
+ "abitanti": "425",
+ "country": "IT"
+ },
+ {
+ "comune": "Fonni",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "1000",
+ "abitanti": "3696",
+ "country": "IT"
+ },
+ {
+ "comune": "Fordongianus",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "35",
+ "abitanti": "852",
+ "country": "IT"
+ },
+ {
+ "comune": "Furtei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "90",
+ "abitanti": "1527",
+ "country": "IT"
+ },
+ {
+ "comune": "Gadoni",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "696",
+ "abitanti": "703",
+ "country": "IT"
+ },
+ {
+ "comune": "Gairo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "670",
+ "abitanti": "1293",
+ "country": "IT"
+ },
+ {
+ "comune": "Galtellì",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "35",
+ "abitanti": "2363",
+ "country": "IT"
+ },
+ {
+ "comune": "Gavoi",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "777",
+ "abitanti": "2492",
+ "country": "IT"
+ },
+ {
+ "comune": "Genoni",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "447",
+ "abitanti": "768",
+ "country": "IT"
+ },
+ {
+ "comune": "Genuri",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "230",
+ "abitanti": "314",
+ "country": "IT"
+ },
+ {
+ "comune": "Gergei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "374",
+ "abitanti": "1129",
+ "country": "IT"
+ },
+ {
+ "comune": "Gesico",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "300",
+ "abitanti": "781",
+ "country": "IT"
+ },
+ {
+ "comune": "Gesturi",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "315",
+ "abitanti": "1153",
+ "country": "IT"
+ },
+ {
+ "comune": "Ghilarza",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "290",
+ "abitanti": "4207",
+ "country": "IT"
+ },
+ {
+ "comune": "Giave",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "595",
+ "abitanti": "493",
+ "country": "IT"
+ },
+ {
+ "comune": "Giba",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "59",
+ "abitanti": "1889",
+ "country": "IT"
+ },
+ {
+ "comune": "Girasole",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0782",
+ "cap": "10",
+ "abitanti": "1326",
+ "country": "IT"
+ },
+ {
+ "comune": "Golfo Aranci",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "19",
+ "abitanti": "2366",
+ "country": "IT"
+ },
+ {
+ "comune": "Goni",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "383",
+ "abitanti": "455",
+ "country": "IT"
+ },
+ {
+ "comune": "Gonnese",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "40",
+ "abitanti": "4674",
+ "country": "IT"
+ },
+ {
+ "comune": "Gonnoscodina",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "112",
+ "abitanti": "435",
+ "country": "IT"
+ },
+ {
+ "comune": "Gonnosfanadiga",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "180",
+ "abitanti": "6185",
+ "country": "IT"
+ },
+ {
+ "comune": "Gonnosnò",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "220",
+ "abitanti": "712",
+ "country": "IT"
+ },
+ {
+ "comune": "Gonnostramatza",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "104",
+ "abitanti": "809",
+ "country": "IT"
+ },
+ {
+ "comune": "Guamaggiore",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "199",
+ "abitanti": "917",
+ "country": "IT"
+ },
+ {
+ "comune": "Guasila",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "210",
+ "abitanti": "2482",
+ "country": "IT"
+ },
+ {
+ "comune": "Guspini",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "130",
+ "abitanti": "11060",
+ "country": "IT"
+ },
+ {
+ "comune": "Iglesias",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "0781",
+ "cap": "200",
+ "abitanti": "25288",
+ "country": "IT"
+ },
+ {
+ "comune": "Ilbono",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "400",
+ "abitanti": "1977",
+ "country": "IT"
+ },
+ {
+ "comune": "Illorai",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "503",
+ "abitanti": "760",
+ "country": "IT"
+ },
+ {
+ "comune": "Irgoli",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "26",
+ "abitanti": "2220",
+ "country": "IT"
+ },
+ {
+ "comune": "Isili",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "523",
+ "abitanti": "2516",
+ "country": "IT"
+ },
+ {
+ "comune": "Ittireddu",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "313",
+ "abitanti": "481",
+ "country": "IT"
+ },
+ {
+ "comune": "Ittiri",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "400",
+ "abitanti": "8069",
+ "country": "IT"
+ },
+ {
+ "comune": "Jerzu",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "427",
+ "abitanti": "3034",
+ "country": "IT"
+ },
+ {
+ "comune": "La Maddalena",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "19",
+ "abitanti": "10617",
+ "country": "IT"
+ },
+ {
+ "comune": "Laconi",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "555",
+ "abitanti": "1673",
+ "country": "IT"
+ },
+ {
+ "comune": "Laerru",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "165",
+ "abitanti": "859",
+ "country": "IT"
+ },
+ {
+ "comune": "Lanusei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "595",
+ "abitanti": "5064",
+ "country": "IT"
+ },
+ {
+ "comune": "Las Plassas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "148",
+ "abitanti": "215",
+ "country": "IT"
+ },
+ {
+ "comune": "Lei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "456",
+ "abitanti": "466",
+ "country": "IT"
+ },
+ {
+ "comune": "Loceri",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "206",
+ "abitanti": "1272",
+ "country": "IT"
+ },
+ {
+ "comune": "Loculi",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "26",
+ "abitanti": "507",
+ "country": "IT"
+ },
+ {
+ "comune": "Lodè",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "345",
+ "abitanti": "1606",
+ "country": "IT"
+ },
+ {
+ "comune": "Lodine",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "884",
+ "abitanti": "306",
+ "country": "IT"
+ },
+ {
+ "comune": "Loiri Porto San Paolo",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "105",
+ "abitanti": "3641",
+ "country": "IT"
+ },
+ {
+ "comune": "Lotzorai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "11",
+ "abitanti": "2093",
+ "country": "IT"
+ },
+ {
+ "comune": "Lula",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "521",
+ "abitanti": "1263",
+ "country": "IT"
+ },
+ {
+ "comune": "Lunamatrona",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "168",
+ "abitanti": "1634",
+ "country": "IT"
+ },
+ {
+ "comune": "Luogosanto",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "321",
+ "abitanti": "1807",
+ "country": "IT"
+ },
+ {
+ "comune": "Luras",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "508",
+ "abitanti": "2429",
+ "country": "IT"
+ },
+ {
+ "comune": "Macomer",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0785",
+ "cap": "563",
+ "abitanti": "9410",
+ "country": "IT"
+ },
+ {
+ "comune": "Magomadas",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "263",
+ "abitanti": "582",
+ "country": "IT"
+ },
+ {
+ "comune": "Mamoiada",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "544",
+ "abitanti": "2404",
+ "country": "IT"
+ },
+ {
+ "comune": "Mandas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "457",
+ "abitanti": "2009",
+ "country": "IT"
+ },
+ {
+ "comune": "Mara",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "257",
+ "abitanti": "525",
+ "country": "IT"
+ },
+ {
+ "comune": "Maracalagonis",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "86",
+ "abitanti": "7873",
+ "country": "IT"
+ },
+ {
+ "comune": "Marrubiu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "7",
+ "abitanti": "4609",
+ "country": "IT"
+ },
+ {
+ "comune": "Martis",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "295",
+ "abitanti": "471",
+ "country": "IT"
+ },
+ {
+ "comune": "Masainas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "57",
+ "abitanti": "1217",
+ "country": "IT"
+ },
+ {
+ "comune": "Masullas",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "129",
+ "abitanti": "1011",
+ "country": "IT"
+ },
+ {
+ "comune": "Meana Sardo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "588",
+ "abitanti": "1602",
+ "country": "IT"
+ },
+ {
+ "comune": "Milis",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "72",
+ "abitanti": "1423",
+ "country": "IT"
+ },
+ {
+ "comune": "Modolo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "134",
+ "abitanti": "156",
+ "country": "IT"
+ },
+ {
+ "comune": "Mogorella",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "265",
+ "abitanti": "410",
+ "country": "IT"
+ },
+ {
+ "comune": "Mogoro",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "136",
+ "abitanti": "3941",
+ "country": "IT"
+ },
+ {
+ "comune": "Monastir",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "81",
+ "abitanti": "4432",
+ "country": "IT"
+ },
+ {
+ "comune": "Monserrato",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "8",
+ "abitanti": "19037",
+ "country": "IT"
+ },
+ {
+ "comune": "Monteleone Rocca Doria",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "368",
+ "abitanti": "109",
+ "country": "IT"
+ },
+ {
+ "comune": "Monti",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "300",
+ "abitanti": "2331",
+ "country": "IT"
+ },
+ {
+ "comune": "Montresta",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "410",
+ "abitanti": "438",
+ "country": "IT"
+ },
+ {
+ "comune": "Mores",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "366",
+ "abitanti": "1747",
+ "country": "IT"
+ },
+ {
+ "comune": "Morgongiori",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "351",
+ "abitanti": "660",
+ "country": "IT"
+ },
+ {
+ "comune": "Muravera",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "9",
+ "abitanti": "5131",
+ "country": "IT"
+ },
+ {
+ "comune": "Muros",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "305",
+ "abitanti": "829",
+ "country": "IT"
+ },
+ {
+ "comune": "Musei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "119",
+ "abitanti": "1491",
+ "country": "IT"
+ },
+ {
+ "comune": "Narbolia",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "57",
+ "abitanti": "1690",
+ "country": "IT"
+ },
+ {
+ "comune": "Narcao",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "125",
+ "abitanti": "3089",
+ "country": "IT"
+ },
+ {
+ "comune": "Neoneli",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "554",
+ "abitanti": "622",
+ "country": "IT"
+ },
+ {
+ "comune": "Noragugume",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "288",
+ "abitanti": "286",
+ "country": "IT"
+ },
+ {
+ "comune": "Norbellu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "350",
+ "abitanti": "1115",
+ "country": "IT"
+ },
+ {
+ "comune": "Nughedu San Nicolò",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "577",
+ "abitanti": "763",
+ "country": "IT"
+ },
+ {
+ "comune": "Nughedu Santa Vittoria",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "496",
+ "abitanti": "441",
+ "country": "IT"
+ },
+ {
+ "comune": "Nule",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "650",
+ "abitanti": "1276",
+ "country": "IT"
+ },
+ {
+ "comune": "Nulvi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "478",
+ "abitanti": "2634",
+ "country": "IT"
+ },
+ {
+ "comune": "Nuoro",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "549",
+ "abitanti": "34105",
+ "country": "IT"
+ },
+ {
+ "comune": "Nurachi",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "7",
+ "abitanti": "1677",
+ "country": "IT"
+ },
+ {
+ "comune": "Nuragus",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "359",
+ "abitanti": "841",
+ "country": "IT"
+ },
+ {
+ "comune": "Nurallao",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "390",
+ "abitanti": "1167",
+ "country": "IT"
+ },
+ {
+ "comune": "Nuraminis",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "91",
+ "abitanti": "2317",
+ "country": "IT"
+ },
+ {
+ "comune": "Nureci",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "335",
+ "abitanti": "319",
+ "country": "IT"
+ },
+ {
+ "comune": "Nurri",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "612",
+ "abitanti": "2025",
+ "country": "IT"
+ },
+ {
+ "comune": "Nuxis",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "196",
+ "abitanti": "1434",
+ "country": "IT"
+ },
+ {
+ "comune": "Olbia",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "15",
+ "abitanti": "60385",
+ "country": "IT"
+ },
+ {
+ "comune": "Oliena",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "379",
+ "abitanti": "6640",
+ "country": "IT"
+ },
+ {
+ "comune": "Ollastra",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "23",
+ "abitanti": "1131",
+ "country": "IT"
+ },
+ {
+ "comune": "Ollolai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "960",
+ "abitanti": "1190",
+ "country": "IT"
+ },
+ {
+ "comune": "Olmedo",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "68",
+ "abitanti": "4113",
+ "country": "IT"
+ },
+ {
+ "comune": "Olzai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "474",
+ "abitanti": "770",
+ "country": "IT"
+ },
+ {
+ "comune": "Onanì",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "482",
+ "abitanti": "371",
+ "country": "IT"
+ },
+ {
+ "comune": "Onifai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "29",
+ "abitanti": "696",
+ "country": "IT"
+ },
+ {
+ "comune": "Oniferi",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "478",
+ "abitanti": "873",
+ "country": "IT"
+ },
+ {
+ "comune": "Orani",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "523",
+ "abitanti": "2706",
+ "country": "IT"
+ },
+ {
+ "comune": "Orgosolo",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "620",
+ "abitanti": "3950",
+ "country": "IT"
+ },
+ {
+ "comune": "Oristano",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "9",
+ "abitanti": "30541",
+ "country": "IT"
+ },
+ {
+ "comune": "Orosei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "19",
+ "abitanti": "6765",
+ "country": "IT"
+ },
+ {
+ "comune": "Orotelli",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "406",
+ "abitanti": "1894",
+ "country": "IT"
+ },
+ {
+ "comune": "Orroli",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "550",
+ "abitanti": "2012",
+ "country": "IT"
+ },
+ {
+ "comune": "Ortacesus",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "161",
+ "abitanti": "881",
+ "country": "IT"
+ },
+ {
+ "comune": "Ortueri",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "584",
+ "abitanti": "1044",
+ "country": "IT"
+ },
+ {
+ "comune": "Orune",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "745",
+ "abitanti": "2150",
+ "country": "IT"
+ },
+ {
+ "comune": "Oschiri",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "202",
+ "abitanti": "3037",
+ "country": "IT"
+ },
+ {
+ "comune": "Osidda",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "650",
+ "abitanti": "219",
+ "country": "IT"
+ },
+ {
+ "comune": "Osilo",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "672",
+ "abitanti": "2826",
+ "country": "IT"
+ },
+ {
+ "comune": "Osini",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "645",
+ "abitanti": "725",
+ "country": "IT"
+ },
+ {
+ "comune": "Ossi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "322",
+ "abitanti": "5462",
+ "country": "IT"
+ },
+ {
+ "comune": "Ottana",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "185",
+ "abitanti": "2195",
+ "country": "IT"
+ },
+ {
+ "comune": "Ovodda",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "751",
+ "abitanti": "1521",
+ "country": "IT"
+ },
+ {
+ "comune": "Ozieri",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "390",
+ "abitanti": "9857",
+ "country": "IT"
+ },
+ {
+ "comune": "Pabillonis",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "42",
+ "abitanti": "2510",
+ "country": "IT"
+ },
+ {
+ "comune": "Padria",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "304",
+ "abitanti": "603",
+ "country": "IT"
+ },
+ {
+ "comune": "Padru",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "160",
+ "abitanti": "2040",
+ "country": "IT"
+ },
+ {
+ "comune": "Palau",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "5",
+ "abitanti": "4034",
+ "country": "IT"
+ },
+ {
+ "comune": "Palmas Arborea",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "4",
+ "abitanti": "1475",
+ "country": "IT"
+ },
+ {
+ "comune": "Pattada",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "794",
+ "abitanti": "2880",
+ "country": "IT"
+ },
+ {
+ "comune": "Pau",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "315",
+ "abitanti": "276",
+ "country": "IT"
+ },
+ {
+ "comune": "Pauli Arbarei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "140",
+ "abitanti": "565",
+ "country": "IT"
+ },
+ {
+ "comune": "Paulilatino",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "280",
+ "abitanti": "2087",
+ "country": "IT"
+ },
+ {
+ "comune": "Perdasdefogu",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "600",
+ "abitanti": "1751",
+ "country": "IT"
+ },
+ {
+ "comune": "Perdaxius",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "98",
+ "abitanti": "1310",
+ "country": "IT"
+ },
+ {
+ "comune": "Perfugas",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "90",
+ "abitanti": "2234",
+ "country": "IT"
+ },
+ {
+ "comune": "Pimentel",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "154",
+ "abitanti": "1126",
+ "country": "IT"
+ },
+ {
+ "comune": "Piscinas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "66",
+ "abitanti": "816",
+ "country": "IT"
+ },
+ {
+ "comune": "Ploaghe",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "427",
+ "abitanti": "4348",
+ "country": "IT"
+ },
+ {
+ "comune": "Pompu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "147",
+ "abitanti": "215",
+ "country": "IT"
+ },
+ {
+ "comune": "Porto Torres",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "5",
+ "abitanti": "21224",
+ "country": "IT"
+ },
+ {
+ "comune": "Portoscuso",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "6",
+ "abitanti": "4859",
+ "country": "IT"
+ },
+ {
+ "comune": "Posada",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "37",
+ "abitanti": "2974",
+ "country": "IT"
+ },
+ {
+ "comune": "Pozzomaggiore",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "438",
+ "abitanti": "2399",
+ "country": "IT"
+ },
+ {
+ "comune": "Pula",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "15",
+ "abitanti": "7054",
+ "country": "IT"
+ },
+ {
+ "comune": "Putifigari",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "267",
+ "abitanti": "694",
+ "country": "IT"
+ },
+ {
+ "comune": "Quartu Sant'Elena",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "6",
+ "abitanti": "68430",
+ "country": "IT"
+ },
+ {
+ "comune": "Quartucciu",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "16",
+ "abitanti": "12756",
+ "country": "IT"
+ },
+ {
+ "comune": "Riola Sardo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "9",
+ "abitanti": "2015",
+ "country": "IT"
+ },
+ {
+ "comune": "Romana",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "267",
+ "abitanti": "496",
+ "country": "IT"
+ },
+ {
+ "comune": "Ruinas",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "359",
+ "abitanti": "619",
+ "country": "IT"
+ },
+ {
+ "comune": "Sadali",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "765",
+ "abitanti": "867",
+ "country": "IT"
+ },
+ {
+ "comune": "Sagama",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "333",
+ "abitanti": "191",
+ "country": "IT"
+ },
+ {
+ "comune": "Samassi",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "56",
+ "abitanti": "4817",
+ "country": "IT"
+ },
+ {
+ "comune": "Samatzai",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "174",
+ "abitanti": "1555",
+ "country": "IT"
+ },
+ {
+ "comune": "Samugheo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "370",
+ "abitanti": "2760",
+ "country": "IT"
+ },
+ {
+ "comune": "San Basilio",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "415",
+ "abitanti": "1132",
+ "country": "IT"
+ },
+ {
+ "comune": "San Gavino Monreale",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "54",
+ "abitanti": "8119",
+ "country": "IT"
+ },
+ {
+ "comune": "San Giovanni Suergiu",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "16",
+ "abitanti": "5673",
+ "country": "IT"
+ },
+ {
+ "comune": "San Nicolò d'Arcidano",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "13",
+ "abitanti": "2516",
+ "country": "IT"
+ },
+ {
+ "comune": "San Nicolò Gerrei",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "365",
+ "abitanti": "717",
+ "country": "IT"
+ },
+ {
+ "comune": "San Sperate",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "41",
+ "abitanti": "8352",
+ "country": "IT"
+ },
+ {
+ "comune": "San Teodoro",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "15",
+ "abitanti": "4941",
+ "country": "IT"
+ },
+ {
+ "comune": "San Vero Milis",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "10",
+ "abitanti": "2416",
+ "country": "IT"
+ },
+ {
+ "comune": "San Vito",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "10",
+ "abitanti": "3436",
+ "country": "IT"
+ },
+ {
+ "comune": "Sanluri",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "135",
+ "abitanti": "8112",
+ "country": "IT"
+ },
+ {
+ "comune": "Santa Giusta",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "10",
+ "abitanti": "4649",
+ "country": "IT"
+ },
+ {
+ "comune": "Santa Maria Coghinas",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "21",
+ "abitanti": "1292",
+ "country": "IT"
+ },
+ {
+ "comune": "Santa Teresa Gallura",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "40",
+ "abitanti": "4969",
+ "country": "IT"
+ },
+ {
+ "comune": "Santadi",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "135",
+ "abitanti": "3156",
+ "country": "IT"
+ },
+ {
+ "comune": "Sant'Andrea Frius",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "280",
+ "abitanti": "1709",
+ "country": "IT"
+ },
+ {
+ "comune": "Sant'Anna Arresi",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "77",
+ "abitanti": "2628",
+ "country": "IT"
+ },
+ {
+ "comune": "Sant'Antioco",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "7",
+ "abitanti": "10670",
+ "country": "IT"
+ },
+ {
+ "comune": "Sant'Antonio di Gallura",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "354",
+ "abitanti": "1438",
+ "country": "IT"
+ },
+ {
+ "comune": "Santu Lussurgiu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "503",
+ "abitanti": "2215",
+ "country": "IT"
+ },
+ {
+ "comune": "Sardara",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "163",
+ "abitanti": "3824",
+ "country": "IT"
+ },
+ {
+ "comune": "Sarroch",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "47",
+ "abitanti": "5033",
+ "country": "IT"
+ },
+ {
+ "comune": "Sarule",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "630",
+ "abitanti": "1568",
+ "country": "IT"
+ },
+ {
+ "comune": "Sassari",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "079",
+ "cap": "100",
+ "abitanti": "121657",
+ "country": "IT"
+ },
+ {
+ "comune": "Scano di Montiferro",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "385",
+ "abitanti": "1407",
+ "country": "IT"
+ },
+ {
+ "comune": "Sedilo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "283",
+ "abitanti": "1975",
+ "country": "IT"
+ },
+ {
+ "comune": "Sedini",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "350",
+ "abitanti": "1245",
+ "country": "IT"
+ },
+ {
+ "comune": "Segariu",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "129",
+ "abitanti": "1099",
+ "country": "IT"
+ },
+ {
+ "comune": "Selargius",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "11",
+ "abitanti": "28501",
+ "country": "IT"
+ },
+ {
+ "comune": "Selegas",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "234",
+ "abitanti": "1292",
+ "country": "IT"
+ },
+ {
+ "comune": "Semestene",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "405",
+ "abitanti": "142",
+ "country": "IT"
+ },
+ {
+ "comune": "Seneghe",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "305",
+ "abitanti": "1676",
+ "country": "IT"
+ },
+ {
+ "comune": "Senis",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "256",
+ "abitanti": "421",
+ "country": "IT"
+ },
+ {
+ "comune": "Sennariolo",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "274",
+ "abitanti": "154",
+ "country": "IT"
+ },
+ {
+ "comune": "Sennori",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "277",
+ "abitanti": "6910",
+ "country": "IT"
+ },
+ {
+ "comune": "Senorbì",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "199",
+ "abitanti": "4729",
+ "country": "IT"
+ },
+ {
+ "comune": "Serdiana",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "171",
+ "abitanti": "2647",
+ "country": "IT"
+ },
+ {
+ "comune": "Serramanna",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "30",
+ "abitanti": "8614",
+ "country": "IT"
+ },
+ {
+ "comune": "Serrenti",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "114",
+ "abitanti": "4557",
+ "country": "IT"
+ },
+ {
+ "comune": "Serri",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "640",
+ "abitanti": "630",
+ "country": "IT"
+ },
+ {
+ "comune": "Sestu",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "44",
+ "abitanti": "20676",
+ "country": "IT"
+ },
+ {
+ "comune": "Settimo San Pietro",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "70",
+ "abitanti": "6839",
+ "country": "IT"
+ },
+ {
+ "comune": "Setzu",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "216",
+ "abitanti": "133",
+ "country": "IT"
+ },
+ {
+ "comune": "Seui",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "820",
+ "abitanti": "1178",
+ "country": "IT"
+ },
+ {
+ "comune": "Seulo",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "797",
+ "abitanti": "795",
+ "country": "IT"
+ },
+ {
+ "comune": "Siamaggiore",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "8",
+ "abitanti": "884",
+ "country": "IT"
+ },
+ {
+ "comune": "Siamanna",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "100",
+ "abitanti": "772",
+ "country": "IT"
+ },
+ {
+ "comune": "Siapiccia",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "64",
+ "abitanti": "344",
+ "country": "IT"
+ },
+ {
+ "comune": "Siddi",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "184",
+ "abitanti": "596",
+ "country": "IT"
+ },
+ {
+ "comune": "Silanus",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "432",
+ "abitanti": "2005",
+ "country": "IT"
+ },
+ {
+ "comune": "Siligo",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "400",
+ "abitanti": "811",
+ "country": "IT"
+ },
+ {
+ "comune": "Siliqua",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "66",
+ "abitanti": "3586",
+ "country": "IT"
+ },
+ {
+ "comune": "Silius",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "585",
+ "abitanti": "1060",
+ "country": "IT"
+ },
+ {
+ "comune": "Simala",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "155",
+ "abitanti": "283",
+ "country": "IT"
+ },
+ {
+ "comune": "Simaxis",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "7",
+ "abitanti": "2124",
+ "country": "IT"
+ },
+ {
+ "comune": "Sindia",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "509",
+ "abitanti": "1605",
+ "country": "IT"
+ },
+ {
+ "comune": "Sini",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "255",
+ "abitanti": "481",
+ "country": "IT"
+ },
+ {
+ "comune": "Siniscola",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "39",
+ "abitanti": "11159",
+ "country": "IT"
+ },
+ {
+ "comune": "Sinnai",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "134",
+ "abitanti": "17172",
+ "country": "IT"
+ },
+ {
+ "comune": "Soddì",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "250",
+ "abitanti": "120",
+ "country": "IT"
+ },
+ {
+ "comune": "Solarussa",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "12",
+ "abitanti": "2288",
+ "country": "IT"
+ },
+ {
+ "comune": "Soleminis",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "200",
+ "abitanti": "1840",
+ "country": "IT"
+ },
+ {
+ "comune": "Sorgono",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "700",
+ "abitanti": "1515",
+ "country": "IT"
+ },
+ {
+ "comune": "Sorradile",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "337",
+ "abitanti": "353",
+ "country": "IT"
+ },
+ {
+ "comune": "Sorso",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "136",
+ "abitanti": "14383",
+ "country": "IT"
+ },
+ {
+ "comune": "Stintino",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "9",
+ "abitanti": "1535",
+ "country": "IT"
+ },
+ {
+ "comune": "Suelli",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "256",
+ "abitanti": "1071",
+ "country": "IT"
+ },
+ {
+ "comune": "Suni",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "340",
+ "abitanti": "975",
+ "country": "IT"
+ },
+ {
+ "comune": "Tadasuni",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "180",
+ "abitanti": "141",
+ "country": "IT"
+ },
+ {
+ "comune": "Talana",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "682",
+ "abitanti": "963",
+ "country": "IT"
+ },
+ {
+ "comune": "Telti",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "326",
+ "abitanti": "2222",
+ "country": "IT"
+ },
+ {
+ "comune": "Tempio Pausania",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "566",
+ "abitanti": "13278",
+ "country": "IT"
+ },
+ {
+ "comune": "Tergu",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "284",
+ "abitanti": "605",
+ "country": "IT"
+ },
+ {
+ "comune": "Terralba",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "12",
+ "abitanti": "9689",
+ "country": "IT"
+ },
+ {
+ "comune": "Tertenia",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "129",
+ "abitanti": "3801",
+ "country": "IT"
+ },
+ {
+ "comune": "Teti",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "714",
+ "abitanti": "612",
+ "country": "IT"
+ },
+ {
+ "comune": "Teulada",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "50",
+ "abitanti": "3293",
+ "country": "IT"
+ },
+ {
+ "comune": "Thiesi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "461",
+ "abitanti": "2796",
+ "country": "IT"
+ },
+ {
+ "comune": "Tiana",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "564",
+ "abitanti": "444",
+ "country": "IT"
+ },
+ {
+ "comune": "Tinnura",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "328",
+ "abitanti": "238",
+ "country": "IT"
+ },
+ {
+ "comune": "Tissi",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "250",
+ "abitanti": "2359",
+ "country": "IT"
+ },
+ {
+ "comune": "Tonara",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "900",
+ "abitanti": "1806",
+ "country": "IT"
+ },
+ {
+ "comune": "Torpè",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "24",
+ "abitanti": "2698",
+ "country": "IT"
+ },
+ {
+ "comune": "Torralba",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "435",
+ "abitanti": "902",
+ "country": "IT"
+ },
+ {
+ "comune": "Tortolì",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "13",
+ "abitanti": "10986",
+ "country": "IT"
+ },
+ {
+ "comune": "Tramatza",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "19",
+ "abitanti": "936",
+ "country": "IT"
+ },
+ {
+ "comune": "Tratalias",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "17",
+ "abitanti": "993",
+ "country": "IT"
+ },
+ {
+ "comune": "Tresnuraghes",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "257",
+ "abitanti": "1111",
+ "country": "IT"
+ },
+ {
+ "comune": "Triei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "140",
+ "abitanti": "1047",
+ "country": "IT"
+ },
+ {
+ "comune": "Trinità d'Agultu e Vignola",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "365",
+ "abitanti": "2210",
+ "country": "IT"
+ },
+ {
+ "comune": "Tuili",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "208",
+ "abitanti": "946",
+ "country": "IT"
+ },
+ {
+ "comune": "Tula",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "275",
+ "abitanti": "1462",
+ "country": "IT"
+ },
+ {
+ "comune": "Turri",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "164",
+ "abitanti": "391",
+ "country": "IT"
+ },
+ {
+ "comune": "Ula Tirso",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "348",
+ "abitanti": "469",
+ "country": "IT"
+ },
+ {
+ "comune": "Ulassai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "775",
+ "abitanti": "1369",
+ "country": "IT"
+ },
+ {
+ "comune": "Uras",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "23",
+ "abitanti": "2682",
+ "country": "IT"
+ },
+ {
+ "comune": "Uri",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "150",
+ "abitanti": "2837",
+ "country": "IT"
+ },
+ {
+ "comune": "Urzulei",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "511",
+ "abitanti": "1114",
+ "country": "IT"
+ },
+ {
+ "comune": "Usellus",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "289",
+ "abitanti": "727",
+ "country": "IT"
+ },
+ {
+ "comune": "Usini",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "200",
+ "abitanti": "4222",
+ "country": "IT"
+ },
+ {
+ "comune": "Ussana",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "97",
+ "abitanti": "4001",
+ "country": "IT"
+ },
+ {
+ "comune": "Ussaramanna",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "157",
+ "abitanti": "493",
+ "country": "IT"
+ },
+ {
+ "comune": "Ussassai",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "710",
+ "abitanti": "472",
+ "country": "IT"
+ },
+ {
+ "comune": "Uta",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "6",
+ "abitanti": "8596",
+ "country": "IT"
+ },
+ {
+ "comune": "Valledoria",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "16",
+ "abitanti": "4218",
+ "country": "IT"
+ },
+ {
+ "comune": "Vallermosa",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "70",
+ "abitanti": "1799",
+ "country": "IT"
+ },
+ {
+ "comune": "Viddalba",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "22",
+ "abitanti": "1611",
+ "country": "IT"
+ },
+ {
+ "comune": "Villa San Pietro",
+ "prov": "CA",
+ "reg": "SAR",
+ "pref": "070",
+ "cap": "37",
+ "abitanti": "2155",
+ "country": "IT"
+ },
+ {
+ "comune": "Villa Sant'Antonio",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "249",
+ "abitanti": "331",
+ "country": "IT"
+ },
+ {
+ "comune": "Villa Verde",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "204",
+ "abitanti": "288",
+ "country": "IT"
+ },
+ {
+ "comune": "Villacidro",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "267",
+ "abitanti": "13216",
+ "country": "IT"
+ },
+ {
+ "comune": "Villagrande Strisaili",
+ "prov": "NU",
+ "reg": "SAR",
+ "pref": "0784",
+ "cap": "700",
+ "abitanti": "2947",
+ "country": "IT"
+ },
+ {
+ "comune": "Villamar",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "108",
+ "abitanti": "2457",
+ "country": "IT"
+ },
+ {
+ "comune": "Villamassargia",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "121",
+ "abitanti": "3411",
+ "country": "IT"
+ },
+ {
+ "comune": "Villanova Monteleone",
+ "prov": "SS",
+ "reg": "SAR",
+ "pref": "0789",
+ "cap": "567",
+ "abitanti": "2133",
+ "country": "IT"
+ },
+ {
+ "comune": "Villanova Truschedu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "56",
+ "abitanti": "293",
+ "country": "IT"
+ },
+ {
+ "comune": "Villanova Tulo",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "600",
+ "abitanti": "1015",
+ "country": "IT"
+ },
+ {
+ "comune": "Villanovaforru",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "324",
+ "abitanti": "611",
+ "country": "IT"
+ },
+ {
+ "comune": "Villanovafranca",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "292",
+ "abitanti": "1194",
+ "country": "IT"
+ },
+ {
+ "comune": "Villaperuccio",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "68",
+ "abitanti": "1007",
+ "country": "IT"
+ },
+ {
+ "comune": "Villaputzu",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "11",
+ "abitanti": "4473",
+ "country": "IT"
+ },
+ [
+ {
+ "comune": "Villasalto",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "502",
+ "abitanti": "987",
+ "country": "IT"
+ },
+ {
+ "comune": "Villasimius",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "49",
+ "abitanti": "3685",
+ "country": "IT"
+ },
+ {
+ "comune": "Villasor",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "25",
+ "abitanti": "6554",
+ "country": "IT"
+ },
+ {
+ "comune": "Villaspeciosa",
+ "prov": "SU",
+ "reg": "SAR",
+ "pref": "07032",
+ "cap": "7",
+ "abitanti": "2519",
+ "country": "IT"
+ },
+ {
+ "comune": "Villaurbana",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "110",
+ "abitanti": "1491",
+ "country": "IT"
+ },
+ {
+ "comune": "Zeddiani",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "10",
+ "abitanti": "1125",
+ "country": "IT"
+ },
+ {
+ "comune": "Zerfaliu",
+ "prov": "OR",
+ "reg": "SAR",
+ "pref": "0783",
+ "cap": "15",
+ "abitanti": "1000",
+ "country": "IT"
+ }
+ ],
+ ]
+}
\ No newline at end of file
diff --git a/src/server/populate/provinces.js b/src/server/populate/provinces.js
index f8b2a52..0c70853 100644
--- a/src/server/populate/provinces.js
+++ b/src/server/populate/provinces.js
@@ -18,10 +18,9 @@ module.exports = {
{ _id: 16, reg: 'TAA', prov: 'BZ', descr: 'Bolzano', link_grp: 'https://t.me/c/1614195634/596?thread=594', link_telegram: '' },
{ _id: 17, reg: 'LOM', prov: 'BS', descr: 'Brescia', link_grp: 'https://t.me/c/1614195634/490?thread=478', link_telegram: '' },
{ _id: 18, reg: 'PUG', prov: 'BR', descr: 'Brindisi', link_grp: 'https://t.me/c/1614195634/536?thread=530', link_telegram: '' },
- { _id: 19, reg: 'SAR', prov: 'CA', descr: 'Cagliari', link_grp: 'https://t.me/c/1614195634/546?thread=541', link_telegram: '' },
+ { _id: 19, reg: 'SAR', prov: 'CA', descr: 'Cagliari', link_grp: 'https://t.me/+9A0xl58CEI8zMWE0', link_telegram: '' },
{ _id: 20, reg: 'SIC', prov: 'CL', descr: 'Caltanissetta', link_grp: 'https://t.me/c/1614195634/563?thread=554', link_telegram: '' },
{ _id: 21, reg: 'MOL', prov: 'CB', descr: 'Campobasso', link_grp: 'https://t.me/c/1614195634/398?thread=396', link_telegram: '' },
- { _id: 22, reg: 'SAR', prov: 'CI', descr: 'Carbonia-Iglesias', link_grp: '', link_telegram: '' },
{ _id: 23, reg: 'CAM', prov: 'CE', descr: 'Caserta', link_grp: 'https://t.me/c/1614195634/414?thread=410', link_telegram: '' },
{ _id: 24, reg: 'SIC', prov: 'CT', descr: 'Catania', link_grp: 'https://t.me/c/1614195634/564?thread=555', link_telegram: '' },
{ _id: 25, reg: 'CAL', prov: 'CZ', descr: 'Catanzaro', link_grp: 'https://t.me/c/1614195634/378?thread=377', link_telegram: '' },
@@ -62,7 +61,6 @@ module.exports = {
{ _id: 60, reg: 'CAM', prov: 'NA', descr: 'Napoli', link_grp: 'https://t.me/c/1614195634/407?thread=406', link_telegram: '' },
{ _id: 61, reg: 'PIE', prov: 'NO', descr: 'Novara', link_grp: 'https://t.me/c/1614195634/517?thread=508', link_telegram: '' },
{ _id: 62, reg: 'SAR', prov: 'NU', descr: 'Nuoro', link_grp: 'https://t.me/c/1614195634/548?thread=542', link_telegram: '' },
- { _id: 63, reg: 'SAR', prov: 'OT', descr: 'Olbia-Tempio', link_grp: '', link_telegram: '' },
{ _id: 64, reg: 'SAR', prov: 'OR', descr: 'Oristano', link_grp: 'https://t.me/c/1614195634/550?thread=543', link_telegram: '' },
{ _id: 65, reg: 'VEN', prov: 'PD', descr: 'Padova', link_grp: 'https://t.me/c/1614195634/610?thread=600', link_telegram: '' },
{ _id: 66, reg: 'SIC', prov: 'PA', descr: 'Palermo', link_grp: 'https://t.me/c/1614195634/568?thread=558', link_telegram: '' },
@@ -86,7 +84,6 @@ module.exports = {
{ _id: 84, reg: 'LAZ', prov: 'RM', descr: 'Roma', link_grp: '', link_telegram: '' },
{ _id: 85, reg: 'VEN', prov: 'RO', descr: 'Rovigo', link_grp: 'https://t.me/c/1614195634/611?thread=601', link_telegram: '' },
{ _id: 86, reg: 'CAM', prov: 'SA', descr: 'Salerno', link_grp: 'https://t.me/c/1614195634/416?thread=412', link_telegram: '' },
- { _id: 87, reg: 'SAR', prov: 'VS', descr: 'Medio Campidano', link_grp: '', link_telegram: '' },
{ _id: 88, reg: 'SAR', prov: 'SS', descr: 'Sassari', link_grp: 'https://t.me/c/1614195634/551?thread=544', link_telegram: '' },
{ _id: 89, reg: 'LIG', prov: 'SV', descr: 'Savona', link_grp: 'https://t.me/c/1614195634/395?thread=391', link_telegram: '' },
{ _id: 90, reg: 'TOS', prov: 'SI', descr: 'Siena', link_grp: 'https://t.me/c/1614195634/592?thread=581', link_telegram: '' },
@@ -96,7 +93,6 @@ module.exports = {
{ _id: 94, reg: 'ABR', prov: 'TE', descr: 'Teramo', link_grp: 'https://t.me/c/1614195634/370?thread=369', link_telegram: '' },
{ _id: 95, reg: 'UMB', prov: 'TR', descr: 'Terni', link_grp: 'https://t.me/c/1614195634/404?thread=402', link_telegram: '' },
{ _id: 96, reg: 'PIE', prov: 'TO', descr: 'Torino', link_grp: 'https://t.me/c/1614195634/518?thread=509', link_telegram: '' },
- { _id: 97, reg: 'SAR', prov: 'OG', descr: 'Ogliastra', link_grp: '', link_telegram: '' },
{ _id: 98, reg: 'SIC', prov: 'TP', descr: 'Trapani', link_grp: 'https://t.me/c/1614195634/571?thread=561', link_telegram: '' },
{ _id: 99, reg: 'TAA', prov: 'TN', descr: 'Trento', link_grp: 'https://t.me/c/1614195634/597?thread=595', link_telegram: '' },
{ _id: 100, reg: 'VEN', prov: 'TV', descr: 'Treviso', link_grp: 'https://t.me/c/1614195634/612?thread=602', link_telegram: '' },
@@ -121,5 +117,6 @@ module.exports = {
{ _id: 119, reg: 'PUG', prov: 'VAL', descr: 'Valle D\'Itria', link_grp: 'https://t.me/progettoriso/7016?thread=7015', link_telegram: '' },
{ _id: 121, reg: 'ITA', prov: 'ITA', descr: 'Italia', link_grp: '', link_telegram: '' },
{ _id: 122, reg: 'LOM', prov: 'MI', descr: 'Milano Est', card: 'EST', link_grp: '', link_telegram: '' },
+ { _id: 123, reg: 'SAR', prov: 'SU', descr: 'Sud Sardegna', link_grp: '', link_telegram: '' },
],
};
diff --git a/src/server/router/index_router.js b/src/server/router/index_router.js
index 29aeefc..d17bebb 100755
--- a/src/server/router/index_router.js
+++ b/src/server/router/index_router.js
@@ -354,13 +354,14 @@ router.post('/settable', authenticate, async (req, res) => {
try {
if (User.isAdmin(req.user.perm) || User.isManager(req.user.perm) ||
- User.isEditor(req.user.perm) || User.isFacilitatore(req.user.perm)) {
+ User.isEditor(req.user.perm) || User.isCommerciale(req.user.perm) || User.isFacilitatore(req.user.perm)) {
consentito = true;
}
if ((!User.isAdmin(req.user.perm)
&& !User.isManager(req.user.perm)
&& !User.isEditor(req.user.perm)
+ && !User.isCommerciale(req.user.perm)
&& !User.isGrafico(req.user.perm)
&& !User.isFacilitatore(req.user.perm))
&&
@@ -1065,7 +1066,7 @@ async function importPage(req, idapp, jsonString) {
const table = globalTables.getTableByTableName(tableName);
if (tableName === 'mypages') {
- if (User.isEditor(req.user.perm)) {
+ if (User.isEditor(req.user.perm) || User.isCommerciale(req.user.perm)) {
for (const page of myexp.mypages) {
const { ImportedRecords, newId } = await upsertRecord(table, page, idapp);
if (!newIdPage && newId) {
@@ -1095,7 +1096,7 @@ async function importPage(req, idapp, jsonString) {
const table = globalTables.getTableByTableName(tableName);
if (tableName === 'myelems') {
- if (User.isEditor(req.user.perm)) {
+ if (User.isEditor(req.user.perm) || User.isCommerciale(req.user.perm)) {
for (const elem of myexp.myelems) {
const { ImportedRecords, newId } = await upsertRecord(table, elem, idapp, newIdPage);
ImportedRecordstemp += ImportedRecords ? 1 : 0;
@@ -1258,6 +1259,7 @@ router.patch('/chval', authenticate, async (req, res) => {
(!User.isAdmin(req.user.perm)
&& !User.isManager(req.user.perm)
&& !User.isEditor(req.user.perm)
+ && !User.isCommerciale(req.user.perm)
&& !User.isFacilitatore(req.user.perm))
&& (await !tools.ModificheConsentite(req, mydata.table, fieldsvalue, id)))
&& !((mydata.table === 'accounts')
@@ -1620,7 +1622,7 @@ router.get('/copyfromapptoapp/:idapporig/:idappdest', async (req, res) => {
if (!idapporig || !idappdest || (idcode !== 'ASD3429Kjgà#@cvX'))
res.status(400).send();
- const mytablesstr = ['settings', 'users', 'templemail'];
+ const mytablesstr = ['settings', 'users', 'templemail', 'destnewsletter'];
try {
let numrectot = 0;
@@ -1908,7 +1910,7 @@ async function measurePromises(promises) {
// Ordina le chiamate per tempo decrescente e prende le 10 più lente
const slowCalls = Object.entries(timings)
.sort(([, timeA], [, timeB]) => timeB - timeA)
- .slice(0, 5)
+ .slice(0, 10)
.map(([key, time]) => ({ key, time }));
return { data, totalTime, slowCalls };
@@ -2007,10 +2009,12 @@ async function load(req, res, version = '0') {
providers: version >= 91 ? Provider.findAllIdApp(idapp) : Promise.resolve([]),
scontisticas: version >= 91 ? Scontistica.findAllIdApp(idapp) : Promise.resolve([]),
gasordines: version >= 91 ? Gasordine.findAllIdApp(idapp) : Promise.resolve([]),
- products: version >= 91
+ /*products: version >= 91
? Product.findAllIdApp(idapp, undefined, undefined, req.user ? User.isManager(req.user.perm) : false)
- : Promise.resolve([]),
- productInfos: version >= 91 ? ProductInfo.findAllIdApp(idapp) : Promise.resolve([]),
+ : Promise.resolve([]),*/
+ products: Promise.resolve([]),
+ // productInfos: version >= 91 ? ProductInfo.findAllIdApp(idapp) : Promise.resolve([]),
+ productInfos: Promise.resolve([]),
catprods: version >= 91 ? Product.getArrCatProds(idapp, shared_consts.PROD.BOTTEGA) : Promise.resolve([]),
subcatprods: version >= 91 ? SubCatProd.findAllIdApp(idapp) : Promise.resolve([]),
catprods_gas: version >= 91 ? Product.getArrCatProds(idapp, shared_consts.PROD.GAS) : Promise.resolve([]),
@@ -2040,8 +2044,8 @@ async function load(req, res, version = '0') {
const { data, totalTime, slowCalls } = await measurePromises(promises);
// console.log('Risultati delle promise:', data);
- // console.log('Tempo totale di esecuzione:', totalTime, 'secondi');
- // console.log('Le 5 chiamate più lente:', slowCalls);
+ console.log('Tempo di esecuzione:', totalTime, 'secondi');
+ //console.log('Le 10 chiamate più lente:', slowCalls);
// Aggiornamento delle informazioni dell'utente, se presente
let myuser = req.user;
diff --git a/src/server/router/newsletter_router.js b/src/server/router/newsletter_router.js
index f2c5a42..cf40070 100755
--- a/src/server/router/newsletter_router.js
+++ b/src/server/router/newsletter_router.js
@@ -10,6 +10,7 @@ const { User } = require('../models/user');
const { MailingList } = require('../models/mailinglist');
const { Newstosent } = require('../models/newstosent');
const { TemplEmail } = require('../models/templemail');
+const { DestNewsletter } = require('../models/destnewsletter');
const { OpzEmail } = require('../models/opzemail');
const { Settings } = require('../models/settings');
@@ -254,12 +255,21 @@ router.post('/load', authenticate, async (req, res) => {
idapp = req.body.idapp;
locale = req.body.locale;
- const ris = {
- newsstate: await getDataNewsletter(locale, idapp),
- serv_settings: await Settings.findAllIdApp(idapp, true, false),
- templemail: await TemplEmail.findAllIdApp(idapp, true),
- opzemail: await OpzEmail.findAllIdApp(idapp)
- };
+ let ris;
+
+ try {
+ ris = {
+ newsstate: await getDataNewsletter(locale, idapp),
+ serv_settings: await Settings.findAllIdApp(idapp, true, false),
+ templemail: await TemplEmail.findAllIdApp(idapp, true),
+ destnewsletter: await DestNewsletter.findAllIdApp(idapp, true),
+ opzemail: await OpzEmail.findAllIdApp(idapp)
+ };
+
+ } catch (e) {
+ console.error('Errore load newsletter: ', e);
+ ris = { code: server_constants.RIS_CODE_ERR, msg: e.message };
+ }
return res.send(ris);
});
@@ -275,12 +285,19 @@ router.post('/setactivate', authenticate, async (req, res) => {
activate,
};
- return await Newstosent.findOneAndUpdate({ _id: id }, { $set: rec }, { new: false }).then((item) => {
- const ris = getDataNewsletter(locale, idapp);
+ try {
+ const item = await Newstosent.findOneAndUpdate({ _id: id }, { $set: rec }, { new: false });
- return res.send(ris);
+ if (item) {
+ const ris = await getDataNewsletter(locale, idapp);
+ return res.send(ris);
+ } else {
+ return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: 'Record not found' });
+ }
- });
+ } catch (e) {
+ return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: e.message });
+ }
});
@@ -296,7 +313,7 @@ router.post('/unsubscribe_user', async (req, res) => {
if (myuser) {
const hashcalc = tools.getHash(myuser.email + myuser.username);
- if (hashcalc === hashemail) {
+ if (hashcalc === hashemail) {
await User.setNewsletter(idapp, myuser.username, false);
return res.send({
@@ -329,28 +346,37 @@ router.post('/unsubscribe', (req, res) => {
console.log('Remove_from_MailingList -> ', ris);
if (!!ris.myperson && mailchimpactive) {
- const subscriber_md5_email = tools.getmd5(ris.myperson.email);
- request
- .put('https://' + newsletter[idapp].mailchimpInstance + '.api.mailchimp.com/3.0/lists/' + newsletter[idapp].listUniqueId + '/members/' + subscriber_md5_email)
- .set('Content-Type', 'application/json;charset=utf-8')
- .set('Authorization', 'Basic ' + new Buffer('any:' + newsletter[idapp].mailchimpApiKey).toString('base64'))
- .send({
- 'email_address': ris.myperson.email,
- 'status': server_constants.RIS_UNSUBSCRIBED_STR
- })
- .end(function (err, response) {
- console.log("STAT", response.status);
+ try {
+ const subscriber_md5_email = tools.getmd5(ris.myperson.email);
+ request
+ .put('https://' + newsletter[idapp].mailchimpInstance + '.api.mailchimp.com/3.0/lists/' + newsletter[idapp].listUniqueId + '/members/' + subscriber_md5_email)
+ .set('Content-Type', 'application/json;charset=utf-8')
+ .set('Authorization', 'Basic ' + new Buffer('any:' + newsletter[idapp].mailchimpApiKey).toString('base64'))
+ .send({
+ 'email_address': ris.myperson.email,
+ 'status': server_constants.RIS_UNSUBSCRIBED_STR
+ })
+ .end(function (err, response) {
+ console.log("STAT", response.status);
- if (response.status < 300 || (response.status === 400 && response.body.title === "Member Exists")) {
- res.send({
- code: server_constants.RIS_UNSUBSCRIBED_OK
- });
- } else {
- res.send({
- code: server_constants.RIS_SUBSCRIBED_ERR
- });
- }
+ if (response.status < 300 || (response.status === 400 && response.body.title === "Member Exists")) {
+ res.send({
+ code: server_constants.RIS_UNSUBSCRIBED_OK
+ });
+ } else {
+ res.send({
+ code: server_constants.RIS_SUBSCRIBED_ERR
+ });
+ }
+ });
+
+ } catch (e) {
+ console.error('Errore unsubscribe -> ', e);
+ res.send({
+ code: server_constants.RIS_SUBSCRIBED_ERR,
+ msg: e.message
});
+ }
} else {
res.send({ code: ris.code, msg: ris.msg });
diff --git a/src/server/router/users_router.js b/src/server/router/users_router.js
index 36094a5..31b5f94 100755
--- a/src/server/router/users_router.js
+++ b/src/server/router/users_router.js
@@ -54,6 +54,7 @@ const { Account } = require('../models/account');
const mongoose = require('mongoose').set('debug', false);
const Subscription = require('../models/subscribers');
+const Macro = require('../modules/Macro');
async function existSubScribe(userId, access, browser) {
try {
@@ -1074,6 +1075,17 @@ async function eseguiDbOp(idapp, mydata, locale, req, res) {
await CatProd.deleteMany({ idapp });
await SubCatProd.deleteMany({ idapp });
+ } else if (mydata.dbop === 'removeProductInfoWithoutDateUpdatedFromGM') {
+
+ mystr = await ProductInfo.removeProductInfoWithoutDateUpdatedFromGM(idapp);
+ ris = { mystr };
+
+ } else if (mydata.dbop === 'StatMacro') {
+
+ const macro = new Macro(idapp, {});
+ mystr = await macro.getStat();
+ ris = { mystr };
+
} else if (mydata.dbop === 'updateAllBook') {
// chiama updateAllBook
const { updateAllBook } = require("../controllers/articleController");
@@ -1203,6 +1215,7 @@ async function eseguiDbOp(idapp, mydata, locale, req, res) {
'settings',
'users',
'templemail',
+ 'destnewsletter',
'contribtypes',
'bots',
'cfgservers'];
@@ -1255,6 +1268,9 @@ async function eseguiDbOp(idapp, mydata, locale, req, res) {
} else if (mydata.dbop === 'listCollectionsBySize') {
mystr = await tools.listCollectionsBySize();
ris = { mystr };
+ } else if (mydata.dbop === 'EnableNewsOn_ToAll') {
+ mystr = await User.setNewsletterToAll(idapp);
+
} else if (mydata.dbop === 'MyElemSetIdPageInsteadThePah') {
mystr = await MyElem.SetIdPageInsteadThePah(idapp);
ris = { mystr };
diff --git a/src/server/sendemail.js b/src/server/sendemail.js
index f71d8eb..b3af33c 100755
--- a/src/server/sendemail.js
+++ b/src/server/sendemail.js
@@ -12,6 +12,7 @@ const i18n = require('i18n');
const { ObjectId } = require('mongodb');
const { Settings } = require('./models/settings');
const { TemplEmail } = require('./models/templemail');
+const { DestNewsletter } = require('./models/destnewsletter');
const { Discipline } = require('./models/discipline');
const previewEmail = require('preview-email');
@@ -57,7 +58,7 @@ module.exports = {
if (to === '')
return false;
-
+
// console.log('mylocalsconf', mylocalsconf);
// console.log("check EMAIL :" + checkifSendEmail());
@@ -166,7 +167,7 @@ module.exports = {
else
transport_preview.sendMail(mailOptions).then(console.log).catch(console.error);
}
-
+
} catch (e) {
console.error('Errore Sendmail', e);
}
@@ -679,12 +680,20 @@ module.exports = {
replacefields: function (mylocalsconf) {
try {
- mylocalsconf.dataemail.disclaimer_out = !!mylocalsconf.dataemail.disclaimer ? this.fieldsloop(mylocalsconf,
- mylocalsconf.dataemail.disclaimer) : '';
- mylocalsconf.dataemail.disc_bottom_out = !!mylocalsconf.dataemail.disc_bottom ? this.fieldsloop(mylocalsconf,
- mylocalsconf.dataemail.disc_bottom) : '';
+ mylocalsconf.dataemail.disclaimer_out = !!mylocalsconf.dataemail.disclaimer ? this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.disclaimer) : '';
+ mylocalsconf.dataemail.disc_bottom_out = !!mylocalsconf.dataemail.disc_bottom ? this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.disc_bottom) : '';
+ mylocalsconf.dataemail.firma = !!mylocalsconf.dataemail.firma ? this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.firma) : '';
if (mylocalsconf.dataemail.templ) {
+ if (mylocalsconf.dataemail.templ.disclaimer)
+ mylocalsconf.dataemail.disclaimer_out = this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.templ.disclaimer);
+
+ if (mylocalsconf.dataemail.templ.piedipagina)
+ mylocalsconf.dataemail.disc_bottom_out = this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.templ.piedipagina);
+
+ if (mylocalsconf.dataemail.templ.firma)
+ mylocalsconf.dataemail.firma = this.fieldsloop(mylocalsconf, mylocalsconf.dataemail.templ.firma);
+
mylocalsconf.dataemail.templ.testoheadermail_out = !!mylocalsconf.dataemail.templ.testoheadermail ? this.fieldsloop(mylocalsconf,
mylocalsconf.dataemail.templ.testoheadermail) : '';
@@ -701,37 +710,47 @@ module.exports = {
getdataemail: async (idapp, templemail_id) => {
- const pwd_from = await Settings.getValDbSettings(idapp, 'PWD_FROM');
+ try {
- // console.log('getdataemail');
- const mydata = {
- content_after_events: await Settings.getValDbSettings(idapp, 'TEXT_AFTER_EV'),
- mailchimpactive: tools.BoolToInt(await Settings.getValDbSettings(idapp, 'MAILCHIMP_ON')),
- urltwitter: await Settings.getValDbSettings(idapp, 'URL_TWITTER'),
- urlfb: await Settings.getValDbSettings(idapp, 'URL_FACEBOOK'),
- urlyoutube: await Settings.getValDbSettings(idapp, 'URL_YOUTUBE'),
- urlinstagram: await Settings.getValDbSettings(idapp, 'URL_INSTAGRAM'),
- textpromo: await Settings.getValDbSettings(idapp, 'TEXT_PROMO'),
- disclaimer: await Settings.getValDbSettings(idapp, 'TEXT_DISCLAIMER'),
- disc_bottom: await Settings.getValDbSettings(idapp, 'TEXT_DISC_BOTTOM'),
- firma: await Settings.getValDbSettings(idapp, 'TEXT_SIGN'),
+ const pwd_from = await Settings.getValDbSettings(idapp, 'PWD_FROM');
- arrdiscipline: await Discipline.getDisciplineforNewsletter(idapp),
- disc_title: await Settings.getValDbSettings(idapp, 'DISC_TITLE'),
- height_logo: await Settings.getValDbSettings(idapp, 'HEIGHT_LOGO'),
- from: await Settings.getValDbSettings(idapp, 'EMAIL_FROM'),
- email_reply: await Settings.getValDbSettings(idapp, 'EMAIL_REPLY', ''),
- pwd_from: pwd_from,
- email_service: await Settings.getValDbSettings(idapp, 'EMAIL_SERVICE_SEND'),
- email_port: await Settings.getValDbSettings(idapp, 'EMAIL_PORT'),
- templemail_id: templemail_id ? templemail_id : await Settings.getValDbSettings(idapp, 'TEMPLEMAIL_ID'),
- };
+ let mydata = {
+ templemail_id: templemail_id ? templemail_id : await Settings.getValDbSettings(idapp, 'TEMPLEMAIL_ID'),
+ destnewsletter_id: await Settings.getValDbSettings(idapp, 'TEMPLEMAIL_DEST'),
+ };
- // console.log(mydata.templemail_id);
- mydata.templ = await TemplEmail.findOne({ _id: mydata.templemail_id });
- // console.log(mydata.templ);
+ mydata.templ = await TemplEmail.findOne({ _id: mydata.templemail_id }).lean();
+ mydata.destnewsl = await DestNewsletter.findOne({ _id: mydata.destnewsletter_id }).lean();
+
+ // console.log('getdataemail');
+ mydata = {
+ ...mydata,
+ content_after_events: await Settings.getValDbSettings(idapp, 'TEXT_AFTER_EV'),
+ mailchimpactive: tools.BoolToInt(await Settings.getValDbSettings(idapp, 'MAILCHIMP_ON')),
+ urltwitter: await Settings.getValDbSettings(idapp, 'URL_TWITTER'),
+ urlfb: await Settings.getValDbSettings(idapp, 'URL_FACEBOOK'),
+ urlyoutube: await Settings.getValDbSettings(idapp, 'URL_YOUTUBE'),
+ urlinstagram: await Settings.getValDbSettings(idapp, 'URL_INSTAGRAM'),
+ textpromo: await Settings.getValDbSettings(idapp, 'TEXT_PROMO'),
+ disc_bottom: await Settings.getValDbSettings(idapp, 'TEXT_DISC_BOTTOM'),
+
+ arrdiscipline: await Discipline.getDisciplineforNewsletter(idapp),
+ disc_title: await Settings.getValDbSettings(idapp, 'DISC_TITLE'),
+ height_logo: await Settings.getValDbSettings(idapp, 'HEIGHT_LOGO'),
+ from: await Settings.getValDbSettings(idapp, 'EMAIL_FROM'),
+ email_reply: await Settings.getValDbSettings(idapp, 'EMAIL_REPLY', ''),
+ pwd_from: pwd_from,
+ email_service: await Settings.getValDbSettings(idapp, 'EMAIL_SERVICE_SEND'),
+ email_port: await Settings.getValDbSettings(idapp, 'EMAIL_PORT'),
+ };
+
+ return mydata;
+
+ } catch (e) {
+ console.error('Error getdataemail: ' + e);
+ return null;
+ }
- return mydata;
},
getTransport: (mylocalsconf) => {
@@ -888,9 +907,6 @@ module.exports = {
await telegrambot.sendMsgTelegramToTheManagers(idapp, msginizio);
- //++Todo Extract List Email to send
- const userstosend = await MailingList.findAllIdAppSubscribed(idapp);
-
const myarrevents = await MyEvent.getLastEvents(idapp);
let mylocalsconf = {
@@ -908,8 +924,24 @@ module.exports = {
const mynewsrec = await Newstosent.findOne({ _id: id_newstosent });
try {
- mynewsrec.numemail_tot = userstosend.length;
mynewsrec.templemail_str = mylocalsconf.dataemail.templ.subject;
+ mynewsrec.destnewsletter_str = mylocalsconf.dataemail.destnewsl?.descr;
+
+ let userstosend = [];
+
+ if (mylocalsconf.dataemail.destnewsl?.tipodest_id === shared_consts.DESTNEWSLETTER.UTENTI) {
+ userstosend = await MailingList.findAllIdAppSubscribed(idapp);
+ } else if (mylocalsconf.dataemail.destnewsl?.tipodest_id === shared_consts.DESTNEWSLETTER.DIARIO) {
+ userstosend = await MailingList.findAllIdAppDiarioSubscr(idapp);
+ } else if (mylocalsconf.dataemail.destnewsl?.tipodest_id === shared_consts.DESTNEWSLETTER.TEST) {
+ userstosend = await MailingList.findAllIdAppTestSubscr(idapp);
+ } else {
+ userstosend = await MailingList.findAllIdAppSubscribed(idapp);
+ }
+
+
+ mynewsrec.numemail_tot = userstosend.length;
+
mynewsrec.numemail_sent = await MailingList.getnumSent(idapp, id_newstosent);
const smtpTransport = this.getTransport(mylocalsconf);
@@ -990,6 +1022,8 @@ module.exports = {
} catch (e) {
+ console.error('*** Errore su sendEmail_Newsletter: ! ', e.message);
+
const activate = await Newstosent.isActivated(id_newstosent);
if (!activate) {
diff --git a/src/server/tools/general.js b/src/server/tools/general.js
index 9f8c2f1..fb58990 100755
--- a/src/server/tools/general.js
+++ b/src/server/tools/general.js
@@ -537,6 +537,11 @@ class ImageDownloader {
fs.unlinkSync(filepath);
}
+ // se in error.message c'è '404' allora esci e ritorna code: 404
+ if (error.message.includes('404')) {
+ return { ris: false, code: 404 };
+ }
+
if (attempt === maxRetries) {
console.error(`Download fallito dopo ${maxRetries} tentativi: ${error.message}`);
return { ris: false };
@@ -6109,28 +6114,30 @@ module.exports = {
async downloadImgIfMissing(productInfo) {
+ const ProductInfo = require('../models/productInfo');
+
try {
if (this.sulServer()) {
dirmain = '';
} else {
dirmain = server_constants.DIR_PUBLIC_LOCALE;
}
-
+
const vecchiomodo = false;
-
+
if (productInfo.image_link && vecchiomodo) {
-
+
const relativeimg = productInfo.image_link.split('/').pop();
const img = this.getdirByIdApp(productInfo.idapp) + dirmain +
server_constants.DIR_UPLOAD + '/products/' + productInfo.image_link.split('/').pop();
const savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine
-
+
let scaricaimg = !productInfo.imagefile || !fs.existsSync(savePath);
-
+
if (!productInfo.imagefile && fs.existsSync(savePath)) {
// esiste il file, ma sul DB non è corretto
const stats = fs.statSync(savePath); // Ottieni informazioni sul file
-
+
if (stats.size > 0) { // Controlla se la dimensione del file è maggiore di zero
// Esiste il file ed è non vuoto, ma sul DB non è corretto
productInfo.imagefile = relativeimg;
@@ -6139,23 +6146,24 @@ module.exports = {
scaricaimg = true;
}
}
-
+
+
if (productInfo.imagefile && fs.existsSync(savePath)) {
// esiste il file, ma sul DB non è corretto
const stats = fs.statSync(savePath); // Ottieni informazioni sul file
-
+
if (stats.size <= 0) { // Controlla se la dimensione del file è maggiore di zero
scaricaimg = true;
}
}
-
-
+
+
if (scaricaimg && vecchiomodo) {
// Download image from the URL productInfo.image_link
productInfo.imagefile = relativeimg;
-
+
const downloader = new ImageDownloader();
-
+
const aggiornatoimg = await downloader.downloadImage(productInfo.image_link, savePath,
{
maxRetries: 1,
@@ -6167,80 +6175,82 @@ module.exports = {
} else {
console.log('Download non riuscito.');
}
-
+
return result;
-
+
});
return { prodInfo: productInfo, aggiornatoimg: aggiornatoimg.ris };
}
}
-
+
let fileesistente = false;
if (productInfo.imagefile) {
// controlla se esiste il file
const img = this.getdirByIdApp(productInfo.idapp) + dirmain +
server_constants.DIR_UPLOAD + '/products/' + productInfo.imagefile.split('/').pop();
const filecompleto = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine
-
+
// Se non esiste lo scarico !
fileesistente = fs.existsSync(filecompleto);
}
-
+
if (!vecchiomodo && (!productInfo.image_link || !fileesistente)) {
-
+
let scarica_da_sito = !productInfo.imagefile;
-
+
if (!scarica_da_sito && productInfo.imagefile) {
scarica_da_sito = !fileesistente; // Se non esiste lo scarico !
}
-
- if (scarica_da_sito) {
+
+ if (scarica_da_sito && !productInfo.image_not_found) {
// date and time
productInfo.imagefile = 'img_' + new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
const img = this.getdirByIdApp(productInfo.idapp) + dirmain +
server_constants.DIR_UPLOAD + '/products/' + productInfo.imagefile.split('/').pop();
let savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine
-
-
+
+
let link = 'https://www.gruppomacro.com/copertine.php?id_gm=' + productInfo.sku
-
+
const downloader = new ImageDownloader();
-
-
- const aggiornatoimg = await downloader.downloadImage(link, savePath,
- {
- maxRetries: 1,
- initialDelay: 300,
- timeout: 15000,
- nomefileoriginale: true,
- }).then(result => {
- if (result) {
- // console.log('Download completato con successo!');
- } else {
- console.log('Download non riuscito.');
- }
-
- return result;
-
- });
- if (aggiornatoimg.filepath) {
+
+
+ let aggiornatoimg;
+ try {
+ aggiornatoimg = await downloader.downloadImage(link, savePath,
+ {
+ maxRetries: 1,
+ initialDelay: 300,
+ timeout: 15000,
+ nomefileoriginale: true,
+ });
+ } catch (e) {
+ aggiornatoimg = { ris: false };
+ }
+ if (aggiornatoimg?.code === 404) {
+ // non trovato quindi la prossima volta non richiederlo
+ await ProductInfo.setImgNotFound(productInfo._id);
+ }
+
+
+ if (aggiornatoimg?.filepath) {
const filenamebase = path.basename(aggiornatoimg.filepath);
// const img = '/upload/products/' + filenamebase;
productInfo.imagefile = filenamebase;
}
-
+
return { prodInfo: productInfo, aggiornatoimg: aggiornatoimg.ris };
} else {
return { prodInfo: null, aggiornatoimg: false };
}
}
-
+
} catch (e) {
console.error('downloadImgIfMissing', e.message);
}
-
+
return { prodInfo: null, aggiornatoimg: false };
-
+
},
removeAccents(mystr) {
@@ -6257,7 +6267,7 @@ module.exports = {
return Array.from(mystr).map(char => accentsMap.get(char) || char).join('');
},
-
+
diff --git a/src/server/tools/globalTables.js b/src/server/tools/globalTables.js
index 9686806..56814e8 100755
--- a/src/server/tools/globalTables.js
+++ b/src/server/tools/globalTables.js
@@ -53,6 +53,7 @@ const { CfgServer } = require('../models/cfgserver');
const { CalZoom } = require('../models/calzoom');
const { Gallery } = require('../models/gallery');
const { TemplEmail } = require('../models/templemail');
+const { DestNewsletter } = require('../models/destnewsletter');
const { OpzEmail } = require('../models/opzemail');
const { MailingList } = require('../models/mailinglist');
const { Settings } = require('../models/settings');
@@ -207,6 +208,8 @@ module.exports = {
mytable = CalZoom;
else if (tablename === 'templemail')
mytable = TemplEmail;
+ else if (tablename === 'destnewsletter')
+ mytable = DestNewsletter;
else if (tablename === 'opzemail')
mytable = OpzEmail;
else if (tablename === 'settings')
diff --git a/src/server/tools/shared_nodejs.js b/src/server/tools/shared_nodejs.js
index e80f9aa..61f0a4a 100755
--- a/src/server/tools/shared_nodejs.js
+++ b/src/server/tools/shared_nodejs.js
@@ -359,6 +359,7 @@ module.exports = {
Zoomeri: 32,
Department: 64,
Grafico: 128,
+ Commerciale: 256,
},
MessageOptions: {
@@ -730,6 +731,8 @@ module.exports = {
'profile',
'calcstat',
'news_on',
+ 'diario_on',
+ 'test',
'aportador_solidario',
'made_gift',
'ind_order',
@@ -1195,6 +1198,26 @@ module.exports = {
EXCEED_QTAMAX: 20,
},
+ DESTNEWSLETTER: {
+ LISTA_NEWSLETTER: 0,
+ UTENTI: 1,
+ DIARIO: 2,
+ TEST: 10,
+ },
+
+ STATUS_JOB: {
+ NONE: 0,
+ START: 1,
+ FINISH: 10,
+ PAUSE: 2,
+ },
+
+ TERMINATED_WHY: {
+ END_NORMALLY: 1,
+ END_WITHERROR: -50,
+ TOOLONGTIME: -10,
+ }
+
// Download, DVD, Epub, Mobi, Nuovo, PDF, Streaming, Usato
};
diff --git a/src/server/version.txt b/src/server/version.txt
index 2aa6561..f480e79 100644
--- a/src/server/version.txt
+++ b/src/server/version.txt
@@ -1 +1 @@
-1.2.35
\ No newline at end of file
+1.2.37
\ No newline at end of file