- aggiornamento di tante cose...

- generazione Volantini
- pagina RIS
This commit is contained in:
Surya Paolo
2025-12-17 10:07:51 +01:00
parent 037ff6f7f9
commit 3d87c336de
43 changed files with 7123 additions and 518 deletions

137
src/models/Asset.js Normal file
View File

@@ -0,0 +1,137 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Sub-schema: File Info
const FileInfoSchema = new Schema({
path: { type: String, required: true },
url: { type: String },
thumbnailPath: { type: String },
thumbnailUrl: { type: String },
originalName: { type: String },
mimeType: { type: String, required: true },
size: { type: Number }, // bytes
dimensions: {
width: { type: Number },
height: { type: Number }
}
}, { _id: false });
// Sub-schema: AI Generation Params
const AiGenerationSchema = new Schema({
prompt: { type: String, required: true },
negativePrompt: { type: String },
provider: {
type: String,
required: true,
enum: ['hf', 'fal', 'ideogram', 'openai', 'stability', 'midjourney']
},
model: { type: String },
seed: { type: Number },
steps: { type: Number },
cfg: { type: Number },
requestedSize: { type: String },
actualSize: { type: String },
aspectRatio: { type: String },
styleType: { type: String },
generationTime: { type: Number }, // ms
cost: { type: Number, default: 0 },
rawResponse: { type: Schema.Types.Mixed }
}, { _id: false });
// Sub-schema: Usage Tracking
const UsageTrackingSchema = new Schema({
usedInPosters: [{ type: Schema.Types.ObjectId, ref: 'Poster' }],
usedInTemplates: [{ type: Schema.Types.ObjectId, ref: 'Template' }],
usageCount: { type: Number, default: 0 }
}, { _id: false });
// Sub-schema: Asset Metadata
const AssetMetadataSchema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: 'User', index: true },
projectId: { type: Schema.Types.ObjectId, ref: 'Project' },
tags: [{ type: String }],
description: { type: String },
isReusable: { type: Boolean, default: true },
isPublic: { type: Boolean, default: false }
}, { _id: false });
// MAIN SCHEMA: Asset
const AssetSchema = new Schema({
type: {
type: String,
required: true,
enum: ['image', 'logo', 'icon', 'font']
},
category: {
type: String,
required: true,
enum: ['background', 'main', 'logo', 'decoration', 'overlay', 'other'],
index: true
},
sourceType: {
type: String,
required: true,
enum: ['upload', 'ai', 'library', 'url'],
index: true
},
file: { type: FileInfoSchema, required: true },
aiGeneration: { type: AiGenerationSchema },
usage: { type: UsageTrackingSchema, default: () => ({}) },
metadata: { type: AssetMetadataSchema, default: () => ({}) },
status: {
type: String,
enum: ['processing', 'ready', 'error', 'deleted'],
default: 'ready'
},
errorMessage: { type: String }
}, {
timestamps: true,
toJSON: { virtuals: true }
});
// Indexes
AssetSchema.index({ 'metadata.userId': 1, category: 1 });
AssetSchema.index({ 'metadata.tags': 1 });
AssetSchema.index({ sourceType: 1, status: 1 });
// Virtual: isAiGenerated
AssetSchema.virtual('isAiGenerated').get(function() {
return this.sourceType === 'ai';
});
// Methods
AssetSchema.methods.addUsage = async function(posterId, type = 'poster') {
if (type === 'poster' && !this.usage.usedInPosters.includes(posterId)) {
this.usage.usedInPosters.push(posterId);
} else if (type === 'template' && !this.usage.usedInTemplates.includes(posterId)) {
this.usage.usedInTemplates.push(posterId);
}
this.usage.usageCount = this.usage.usedInPosters.length + this.usage.usedInTemplates.length;
return this.save();
};
AssetSchema.methods.getPublicUrl = function() {
return this.file.url || `/api/assets/${this._id}/file`;
};
// Statics
AssetSchema.statics.findByUser = function(userId, category = null) {
const query = { 'metadata.userId': userId, status: 'ready' };
if (category) query.category = category;
return this.find(query).sort({ createdAt: -1 });
};
AssetSchema.statics.findReusable = function(userId, category = null) {
const query = {
'metadata.userId': userId,
'metadata.isReusable': true,
status: 'ready'
};
if (category) query.category = category;
return this.find(query).sort({ 'usage.usageCount': -1 });
};
module.exports = mongoose.model('Asset', AssetSchema);

262
src/models/Poster.js Normal file
View File

@@ -0,0 +1,262 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Sub-schema: Content
const PosterContentSchema = new Schema({
title: { type: String, maxlength: 500 },
subtitle: { type: String, maxlength: 500 },
eventDate: { type: String, maxlength: 200 },
eventTime: { type: String, maxlength: 100 },
location: { type: String, maxlength: 500 },
contacts: { type: String, maxlength: 1000 },
extraText: [{ type: String }],
customFields: { type: Map, of: String }
}, { _id: false });
// Sub-schema: Asset AI Params (embedded)
const EmbeddedAiParamsSchema = new Schema({
prompt: { type: String },
negativePrompt: { type: String },
provider: { type: String },
model: { type: String },
seed: { type: Number },
steps: { type: Number },
cfg: { type: Number },
size: { type: String },
generatedAt: { type: Date }
}, { _id: false });
// Sub-schema: Poster Asset Reference
const PosterAssetSchema = new Schema({
id: { type: String },
assetId: { type: Schema.Types.ObjectId, ref: 'Asset' },
slotId: { type: String }, // per loghi
sourceType: { type: String, enum: ['upload', 'ai', 'library', 'url'] },
url: { type: String },
thumbnailUrl: { type: String },
originalName: { type: String },
mimeType: { type: String },
size: { type: Number },
dimensions: {
width: { type: Number },
height: { type: Number }
},
aiParams: EmbeddedAiParamsSchema
}, { _id: false });
// Sub-schema: Assets Container
const PosterAssetsSchema = new Schema({
backgroundImage: PosterAssetSchema,
mainImage: PosterAssetSchema,
logos: [PosterAssetSchema]
}, { _id: false });
// Sub-schema: Layer Override Style
const LayerOverrideStyleSchema = new Schema({
fontSize: { type: Number },
color: { type: String },
fontWeight: { type: Number },
opacity: { type: Number },
// altri override possibili
}, { _id: false });
// Sub-schema: Layer Override
const LayerOverrideSchema = new Schema({
position: {
x: { type: Number },
y: { type: Number },
w: { type: Number },
h: { type: Number }
},
visible: { type: Boolean },
style: LayerOverrideStyleSchema
}, { _id: false });
// Sub-schema: Render Output File
const RenderOutputFileSchema = new Schema({
path: { type: String, required: true },
url: { type: String },
size: { type: Number },
quality: { type: Number }
}, { _id: false });
// Sub-schema: Render Output
const RenderOutputSchema = new Schema({
png: RenderOutputFileSchema,
jpg: RenderOutputFileSchema,
webp: RenderOutputFileSchema,
pdf: RenderOutputFileSchema,
dimensions: {
width: { type: Number },
height: { type: Number }
},
renderedAt: { type: Date }
}, { _id: false });
// Sub-schema: History Entry
const HistoryEntrySchema = new Schema({
action: {
type: String,
required: true,
enum: ['created', 'updated', 'ai_background_generated', 'ai_main_generated', 'rendered', 'downloaded', 'shared', 'deleted']
},
timestamp: { type: Date, default: Date.now },
userId: { type: Schema.Types.ObjectId, ref: 'User' },
details: { type: Schema.Types.Mixed }
}, { _id: false });
// Sub-schema: Poster Metadata
const PosterMetadataSchema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true, index: true },
projectId: { type: Schema.Types.ObjectId, ref: 'Project' },
tags: [{ type: String }],
isPublic: { type: Boolean, default: false },
isFavorite: { type: Boolean, default: false },
viewCount: { type: Number, default: 0 },
downloadCount: { type: Number, default: 0 }
}, { _id: false });
// MAIN SCHEMA: Poster
const PosterSchema = new Schema({
templateId: {
type: Schema.Types.ObjectId,
ref: 'Template',
required: true,
index: true
},
templateSnapshot: { type: Schema.Types.Mixed }, // copia del template al momento della creazione
name: { type: String, required: true, trim: true, maxlength: 300 },
description: { type: String, maxlength: 1000 },
status: {
type: String,
enum: ['draft', 'processing', 'completed', 'error'],
default: 'draft',
index: true
},
content: { type: PosterContentSchema, required: true },
assets: { type: PosterAssetsSchema, default: () => ({}) },
layerOverrides: { type: Map, of: LayerOverrideSchema, default: () => new Map() },
renderOutput: RenderOutputSchema,
renderEngineVersion: { type: String, default: '1.0.0' },
history: [HistoryEntrySchema],
metadata: { type: PosterMetadataSchema, required: true },
errorMessage: { type: String },
// Campi dalla tua bozza originale
originalPrompt: { type: String }, // prompt completo usato
styleUsed: { type: String },
aspectRatio: { type: String },
provider: { type: String }
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Indexes
PosterSchema.index({ 'metadata.userId': 1, status: 1 });
PosterSchema.index({ 'metadata.tags': 1 });
PosterSchema.index({ 'metadata.isFavorite': 1, 'metadata.userId': 1 });
PosterSchema.index({ createdAt: -1 });
PosterSchema.index({ name: 'text', description: 'text' });
// Virtual: isCompleted
PosterSchema.virtual('isCompleted').get(function() {
return this.status === 'completed' && this.renderOutput?.png?.path;
});
// Virtual: downloadUrl
PosterSchema.virtual('downloadUrl').get(function() {
if (this.renderOutput?.png?.path) {
return `/api/posters/${this._id}/download/png`;
}
return null;
});
// Pre-save: aggiorna history
PosterSchema.pre('save', function(next) {
if (this.isNew) {
this.history = this.history || [];
this.history.push({
action: 'created',
timestamp: new Date(),
userId: this.metadata.userId
});
}
next();
});
// Methods
PosterSchema.methods.addHistory = function(action, details = {}) {
this.history.push({
action,
timestamp: new Date(),
userId: this.metadata.userId,
details
});
return this;
};
PosterSchema.methods.setRenderOutput = function(outputData) {
this.renderOutput = {
...outputData,
renderedAt: new Date()
};
this.status = 'completed';
this.addHistory('rendered', { duration: outputData.duration });
return this;
};
PosterSchema.methods.setError = function(errorMessage) {
this.status = 'error';
this.errorMessage = errorMessage;
return this;
};
PosterSchema.methods.incrementDownload = async function() {
this.metadata.downloadCount = (this.metadata.downloadCount || 0) + 1;
this.addHistory('downloaded');
return this.save();
};
PosterSchema.methods.toggleFavorite = async function() {
this.metadata.isFavorite = !this.metadata.isFavorite;
return this.save();
};
// Statics
PosterSchema.statics.findByUser = function(userId, options = {}) {
const query = { 'metadata.userId': userId };
if (options.status) query.status = options.status;
if (options.isFavorite) query['metadata.isFavorite'] = true;
return this.find(query)
.populate('templateId', 'name templateType thumbnailUrl')
.sort({ createdAt: -1 })
.limit(options.limit || 50);
};
PosterSchema.statics.findFavorites = function(userId) {
return this.find({
'metadata.userId': userId,
'metadata.isFavorite': true
}).sort({ updatedAt: -1 });
};
PosterSchema.statics.findRecent = function(userId, limit = 10) {
return this.find({
'metadata.userId': userId,
status: 'completed'
})
.sort({ createdAt: -1 })
.limit(limit)
.select('name renderOutput.png.url thumbnailUrl createdAt');
};
module.exports = mongoose.model('Poster', PosterSchema);

253
src/models/Template.js Normal file
View File

@@ -0,0 +1,253 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Sub-schema: Posizione layer
const PositionSchema = new Schema({
x: { type: Number, required: true, min: 0, max: 1 },
y: { type: Number, required: true, min: 0, max: 1 },
w: { type: Number, required: true, min: 0, max: 1 },
h: { type: Number, required: true, min: 0, max: 1 }
}, { _id: false });
// Sub-schema: Ombra
const ShadowSchema = new Schema({
enabled: { type: Boolean, default: false },
blur: { type: Number, default: 10 },
spread: { type: Number, default: 0 },
offsetX: { type: Number, default: 0 },
offsetY: { type: Number, default: 4 },
color: { type: String, default: 'rgba(0,0,0,0.5)' }
}, { _id: false });
// Sub-schema: Stroke
const StrokeSchema = new Schema({
enabled: { type: Boolean, default: false },
width: { type: Number, default: 2 },
color: { type: String, default: '#000000' }
}, { _id: false });
// Sub-schema: Border
const BorderSchema = new Schema({
enabled: { type: Boolean, default: false },
width: { type: Number, default: 2 },
color: { type: String, default: '#ffffff' },
style: { type: String, enum: ['solid', 'dashed', 'dotted'], default: 'solid' }
}, { _id: false });
// Sub-schema: Gradient Stop
const GradientStopSchema = new Schema({
position: { type: Number, required: true, min: 0, max: 1 },
color: { type: String, required: true }
}, { _id: false });
// Sub-schema: Overlay
const OverlaySchema = new Schema({
enabled: { type: Boolean, default: false },
type: { type: String, enum: ['solid', 'gradient'], default: 'gradient' },
color: { type: String },
direction: { type: String, default: 'to-bottom' },
stops: [GradientStopSchema]
}, { _id: false });
// Sub-schema: Fallback
const FallbackSchema = new Schema({
type: { type: String, enum: ['solid', 'gradient'], default: 'solid' },
color: { type: String },
direction: { type: String },
colors: [{ type: String }]
}, { _id: false });
// Sub-schema: Icon
const IconSchema = new Schema({
enabled: { type: Boolean, default: false },
name: { type: String },
size: { type: Number, default: 24 },
color: { type: String, default: '#ffffff' }
}, { _id: false });
// Sub-schema: Stile Layer (unificato per immagini e testi)
const LayerStyleSchema = new Schema({
// Comuni
opacity: { type: Number, default: 1, min: 0, max: 1 },
// Per immagini
objectFit: { type: String, enum: ['cover', 'contain', 'fill', 'none'], default: 'cover' },
blur: { type: Number, default: 0 },
borderRadius: { type: Number, default: 0 },
overlay: OverlaySchema,
border: BorderSchema,
// Per testi
fontFamily: { type: String },
fontWeight: { type: Number, default: 400 },
fontSize: { type: Number },
fontSizeMin: { type: Number },
fontSizeMax: { type: Number },
autoFit: { type: Boolean, default: false },
fontStyle: { type: String, enum: ['normal', 'italic'], default: 'normal' },
color: { type: String },
textAlign: { type: String, enum: ['left', 'center', 'right'], default: 'center' },
textTransform: { type: String, enum: ['none', 'uppercase', 'lowercase', 'capitalize'], default: 'none' },
letterSpacing: { type: Number, default: 0 },
lineHeight: { type: Number, default: 1.2 },
// Effetti
shadow: ShadowSchema,
stroke: StrokeSchema
}, { _id: false });
// Sub-schema: Layer
const LayerSchema = new Schema({
id: { type: String, required: true },
type: {
type: String,
required: true,
enum: ['backgroundImage', 'mainImage', 'logo', 'title', 'subtitle', 'eventDate', 'eventTime', 'location', 'contacts', 'extraText', 'customText', 'customImage', 'shape', 'divider']
},
zIndex: { type: Number, default: 0 },
position: { type: PositionSchema, required: true },
anchor: {
type: String,
enum: ['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'],
default: 'center'
},
required: { type: Boolean, default: false },
visible: { type: Boolean, default: true },
locked: { type: Boolean, default: false },
maxLines: { type: Number },
fallback: FallbackSchema,
icon: IconSchema,
style: { type: LayerStyleSchema, default: () => ({}) }
}, { _id: false });
// Sub-schema: Logo Slot
const LogoSlotSchema = new Schema({
id: { type: String, required: true },
position: { type: PositionSchema, required: true },
anchor: { type: String, default: 'center' },
style: { type: LayerStyleSchema, default: () => ({}) }
}, { _id: false });
// Sub-schema: Logo Slots Config
const LogoSlotsConfigSchema = new Schema({
enabled: { type: Boolean, default: true },
maxCount: { type: Number, default: 3, min: 1, max: 10 },
collapseIfEmpty: { type: Boolean, default: true },
slots: [LogoSlotSchema]
}, { _id: false });
// Sub-schema: Format
const FormatSchema = new Schema({
preset: { type: String, default: 'custom' }, // A4, A3, Instagram, Facebook, custom
width: { type: Number, required: true },
height: { type: Number, required: true },
unit: { type: String, enum: ['px', 'mm', 'in'], default: 'px' },
dpi: { type: Number, default: 300 }
}, { _id: false });
// Sub-schema: Safe Area
const SafeAreaSchema = new Schema({
top: { type: Number, default: 0, min: 0, max: 0.5 },
right: { type: Number, default: 0, min: 0, max: 0.5 },
bottom: { type: Number, default: 0, min: 0, max: 0.5 },
left: { type: Number, default: 0, min: 0, max: 0.5 }
}, { _id: false });
// Sub-schema: Palette
const PaletteSchema = new Schema({
primary: { type: String, default: '#e94560' },
secondary: { type: String, default: '#0f3460' },
accent: { type: String, default: '#ffd700' },
background: { type: String, default: '#1a1a2e' },
text: { type: String, default: '#ffffff' },
textSecondary: { type: String, default: '#cccccc' },
textMuted: { type: String, default: '#888888' }
}, { _id: false });
// Sub-schema: Typography
const TypographySchema = new Schema({
titleFont: { type: String, default: 'Montserrat' },
headingFont: { type: String, default: 'Bebas Neue' },
bodyFont: { type: String, default: 'Open Sans' },
accentFont: { type: String, default: 'Playfair Display' }
}, { _id: false });
// Sub-schema: AI Prompt Hints
const AiPromptHintsSchema = new Schema({
backgroundImage: { type: String },
mainImage: { type: String }
}, { _id: false });
// Sub-schema: Metadata
const TemplateMetadataSchema = new Schema({
author: { type: String, default: 'System' },
version: { type: String, default: '1.0.0' },
tags: [{ type: String }],
isPublic: { type: Boolean, default: false },
usageCount: { type: Number, default: 0 }
}, { _id: false });
// MAIN SCHEMA: Template
const TemplateSchema = new Schema({
name: { type: String, required: true, trim: true, maxlength: 200 },
templateType: { type: String, required: true, trim: true, index: true },
description: { type: String, maxlength: 1000 },
format: { type: FormatSchema, required: true },
safeArea: { type: SafeAreaSchema, default: () => ({}) },
backgroundColor: { type: String, default: '#1a1a2e' },
layers: { type: [LayerSchema], required: true, validate: [arr => arr.length > 0, 'Almeno un layer richiesto'] },
logoSlots: { type: LogoSlotsConfigSchema, default: () => ({}) },
palette: { type: PaletteSchema, default: () => ({}) },
typography: { type: TypographySchema, default: () => ({}) },
defaultAiPromptHints: { type: AiPromptHintsSchema, default: () => ({}) },
previewUrl: { type: String },
thumbnailUrl: { type: String },
metadata: { type: TemplateMetadataSchema, default: () => ({}) },
isActive: { type: Boolean, default: true },
userId: { type: Schema.Types.ObjectId, ref: 'User', index: true }
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Indexes
TemplateSchema.index({ templateType: 1, isActive: 1 });
TemplateSchema.index({ 'metadata.tags': 1 });
TemplateSchema.index({ name: 'text', description: 'text', templateType: 'text' });
// Virtual: layer count
TemplateSchema.virtual('layerCount').get(function() {
return this.layers ? this.layers.length : 0;
});
// Methods
TemplateSchema.methods.getLayerById = function(layerId) {
return this.layers.find(l => l.id === layerId);
};
TemplateSchema.methods.getLayersByType = function(type) {
return this.layers.filter(l => l.type === type);
};
TemplateSchema.methods.incrementUsage = async function() {
this.metadata.usageCount = (this.metadata.usageCount || 0) + 1;
return this.save();
};
// Statics
TemplateSchema.statics.findByType = function(templateType) {
return this.find({ templateType, isActive: true }).sort({ 'metadata.usageCount': -1 });
};
TemplateSchema.statics.findPublic = function() {
return this.find({ 'metadata.isPublic': true, isActive: true });
};
module.exports = mongoose.model('Template', TemplateSchema);

View File

@@ -32,6 +32,12 @@ const AccountSchema = new Schema({
numtransactions: {
type: Number,
},
sent: {
type: Number,
},
received: {
type: Number,
},
username: {
type: String,
},
@@ -242,6 +248,12 @@ AccountSchema.statics.addtoSaldo = async function (myaccount, amount, mitt) {
myaccountupdate.saldo = myaccount.saldo;
myaccountupdate.totTransato = myaccount.totTransato;
myaccountupdate.numtransactions = myaccount.numtransactions;
if (amount > 0) {
myaccountupdate.received += 1;
} else {
myaccountupdate.sent += 1;
}
myaccountupdate.date_updated = myaccount.date_updated;
const ris = await Account.updateOne(
@@ -324,6 +336,8 @@ AccountSchema.statics.getAccountByUsernameAndCircuitId = async function (
username_admin_abilitante: '',
qta_maxConcessa: 0,
totTransato: 0,
sent: 0,
received: 0,
numtransactions: 0,
totTransato_pend: 0,
});

View File

@@ -87,6 +87,9 @@ const CircuitSchema = new Schema({
totTransato: {
type: Number,
},
numTransazioni: {
type: Number,
},
nome_valuta: {
type: String,
maxlength: 20,
@@ -327,6 +330,7 @@ CircuitSchema.statics.getWhatToShow = function (idapp, username) {
numMembers: 1,
totCircolante: 1,
totTransato: 1,
numTransazioni: 1,
systemUserId: 1,
createdBy: 1,
date_created: 1,
@@ -412,6 +416,7 @@ CircuitSchema.statics.getWhatToShow_Unknown = function (idapp, username) {
nome_valuta: 1,
totCircolante: 1,
totTransato: 1,
numTransazioni: 1,
fido_scoperto_default: 1,
fido_scoperto_default_grp: 1,
qta_max_default_grp: 1,
@@ -825,6 +830,7 @@ CircuitSchema.statics.sendCoins = async function (onlycheck, idapp, usernameOrig
const circolanteAtt = this.getCircolanteSingolaTransaz(accountorigTable, accountdestTable);
// Somma di tutte le transazioni
circuittable.numTransazioni += 1;
circuittable.totTransato += myqty;
// circuittable.totCircolante = circuittable.totCircolante + (circolanteAtt - circolantePrec);
circuittable.totCircolante = await Account.calcTotCircolante(idapp, circuittable._id);
@@ -901,7 +907,14 @@ CircuitSchema.statics.sendCoins = async function (onlycheck, idapp, usernameOrig
let myuserDest = await User.getUserByUsername(idapp, extrarec.dest);
// Invia una email al destinatario !
await sendemail.sendEmail_RisRicevuti(myuserDest.lang, myuserDest, myuserDest.email, idapp, paramsrec, extrarec);
await sendemail.sendEmail_RisRicevuti(
myuserDest.lang,
myuserDest,
myuserDest.email,
idapp,
paramsrec,
extrarec
);
} else if (extrarec.groupdest || extrarec.contoComDest) {
const groupDestoContoCom = extrarec.groupdest
? extrarec.groupdest
@@ -1047,16 +1060,16 @@ CircuitSchema.statics.getListAdminsByCircuitPath = async function (idapp, circui
let adminObjects = circuit && circuit.admins ? circuit.admins : [];
// Aggiungi USER_ADMIN_CIRCUITS come oggetti
let systemAdmins = shared_consts.USER_ADMIN_CIRCUITS.map(username => ({
let systemAdmins = shared_consts.USER_ADMIN_CIRCUITS.map((username) => ({
username,
date: null,
_id: null
_id: null,
}));
// Unisci e rimuovi duplicati per username
let allAdmins = [...adminObjects, ...systemAdmins];
let uniqueAdmins = allAdmins.filter((admin, index, self) =>
index === self.findIndex(a => a.username === admin.username)
let uniqueAdmins = allAdmins.filter(
(admin, index, self) => index === self.findIndex((a) => a.username === admin.username)
);
return uniqueAdmins;
@@ -1190,6 +1203,7 @@ CircuitSchema.statics.createCircuitIfNotExist = async function (req, idapp, prov
qta_max_default_grp: shared_consts.CIRCUIT_PARAMS.SCOPERTO_MAX_GRP,
valuta_per_euro: 1,
totTransato: 0,
numTransazioni: 0,
totCircolante: 0,
date_created: new Date(),
admins: admins.map((username) => ({ username })),
@@ -1388,7 +1402,12 @@ CircuitSchema.statics.setFido = async function (idapp, username, circuitName, gr
const ris = await Account.updateFido(idapp, username, groupname, circuitId, fido, username_action);
if (ris) {
return { qta_maxConcessa: qtamax, fidoConcesso: fido, username_admin_abilitante: username_action, changed: variato || (ris && ris.modifiedCount > 0) };
return {
qta_maxConcessa: qtamax,
fidoConcesso: fido,
username_admin_abilitante: username_action,
changed: variato || (ris && ris.modifiedCount > 0),
};
}
}
}
@@ -1441,7 +1460,7 @@ CircuitSchema.statics.getFido = async function (idapp, username, circuitName, gr
return null;
};
CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi, options) {
const { User } = require('../models/user');
const { MyGroup } = require('../models/mygroup');
const { SendNotif } = require('../models/sendnotif');
@@ -1540,7 +1559,7 @@ CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
let numtransazionitot = 0;
const arrcircuits = await Circuit.find({ idapp }).lean();
const arrcircuits = await Circuit.find({ idapp });
for (const circuit of arrcircuits) {
let strusersnotinaCircuit = '';
let strusersnotExist = '';
@@ -1620,6 +1639,16 @@ CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
_id: null,
numtransactions: { $sum: 1 },
totTransato: { $sum: { $abs: '$amount' } },
sentCount: {
$sum: {
$cond: [{ $eq: ['$accountFromId', account._id] }, 1, 0],
},
},
receivedCount: {
$sum: {
$cond: [{ $eq: ['$accountToId', account._id] }, 1, 0],
},
},
saldo: {
$sum: {
$cond: [
@@ -1636,6 +1665,8 @@ CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
]);
let numtransactions = result && result.length > 0 ? result[0].numtransactions : 0;
let sentCount = result && result.length > 0 ? result[0].sentCount : 0;
let receivedCount = result && result.length > 0 ? result[0].receivedCount : 0;
let totTransato = result && result.length > 0 ? result[0].totTransato : 0;
let saldo = result && result.length > 0 ? result[0].saldo : 0;
@@ -1679,6 +1710,8 @@ CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
if (correggi) await Account.findOneAndUpdate({ _id: account._id }, { $set: { totTransato } });
}
await Account.findOneAndUpdate({ _id: account._id }, { $set: { sent: sentCount, received: receivedCount } });
saldotot += account.saldo;
// if (account.totTransato === NaN || account.totTransato === undefined)
@@ -1693,6 +1726,11 @@ CircuitSchema.statics.CheckTransazioniCircuiti = async function (correggi) {
// await account.calcPending();
ind++;
} // FINE ACCOUNT
if (options?.setnumtransaction) {
circuit.numTransazioni = numtransazionitot;
await circuit.save(); // salva su db
}
let numaccounts = accounts.length;
@@ -1876,6 +1914,11 @@ CircuitSchema.statics.getCircuitiExtraProvinciali = async function (idapp) {
return circuits;
};
CircuitSchema.statics.ricalcolaNumTransazioni = async function (circuitId) {
const Circuit = this;
// +TODO: Ricalcola il numero delle transazioni avvenute
};
CircuitSchema.statics.getCircuitoItalia = async function (idapp) {
const Circuit = this;
@@ -1884,6 +1927,13 @@ CircuitSchema.statics.getCircuitoItalia = async function (idapp) {
return circuit;
};
CircuitSchema.statics.getSymbolByCircuitId = async function (circuitId) {
const Circuit = this;
const circuit = await Circuit.findOne({ _id: circuitId }, { symbol: 1});
return circuit?.symbol || '';
};
CircuitSchema.statics.isEnableToReceiveEmailByExtraRec = async function (idapp, recnotif) {
let ricevo = true;
if (recnotif.tag === 'setfido') {

View File

@@ -106,6 +106,8 @@ MovementSchema.statics.addMov = async function (
idOrdersCart
) {
try {
const { Circuit } = require('./circuit');
// Only positive values
amount = Math.abs(amount);

View File

@@ -174,6 +174,7 @@ const SiteSchema = new Schema({
bookingEvents: { type: Boolean, default: false },
enableEcommerce: { type: Boolean, default: false },
enableAI: { type: Boolean, default: false },
enablePoster: { type: Boolean, default: false },
enableGroups: { type: Boolean, default: false },
enableCircuits: { type: Boolean, default: false },
enableGoods: { type: Boolean, default: false },