296 lines
6.5 KiB
JavaScript
296 lines
6.5 KiB
JavaScript
const mongoose = require('mongoose');
|
|
const Schema = mongoose.Schema;
|
|
|
|
// Schema per le coordinate
|
|
const CoordinatesSchema = new Schema({
|
|
lat: {
|
|
type: Number,
|
|
required: true
|
|
},
|
|
lng: {
|
|
type: Number,
|
|
required: true
|
|
}
|
|
}, { _id: false });
|
|
|
|
// Schema per località
|
|
const LocationSchema = new Schema({
|
|
city: {
|
|
type: String,
|
|
required: true,
|
|
trim: true
|
|
},
|
|
address: {
|
|
type: String,
|
|
trim: true
|
|
},
|
|
province: {
|
|
type: String,
|
|
trim: true
|
|
},
|
|
coordinates: {
|
|
type: CoordinatesSchema,
|
|
required: true
|
|
}
|
|
}, { _id: false });
|
|
|
|
const RideRequestSchema = new Schema({
|
|
idapp: {
|
|
type: String,
|
|
required: true,
|
|
index: true
|
|
},
|
|
rideId: {
|
|
type: Schema.Types.ObjectId,
|
|
ref: 'Ride',
|
|
required: true,
|
|
index: true
|
|
},
|
|
passengerId: {
|
|
type: Schema.Types.ObjectId,
|
|
ref: 'User',
|
|
required: true,
|
|
index: true
|
|
},
|
|
driverId: {
|
|
type: Schema.Types.ObjectId,
|
|
ref: 'User',
|
|
required: true,
|
|
index: true
|
|
},
|
|
message: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 500
|
|
},
|
|
pickupPoint: {
|
|
type: LocationSchema
|
|
},
|
|
dropoffPoint: {
|
|
type: LocationSchema
|
|
},
|
|
useOriginalRoute: {
|
|
type: Boolean,
|
|
default: true
|
|
// true = usa partenza/destinazione originali del ride
|
|
},
|
|
seatsRequested: {
|
|
type: Number,
|
|
required: true,
|
|
min: 1,
|
|
default: 1
|
|
},
|
|
hasLuggage: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
luggageSize: {
|
|
type: String,
|
|
enum: ['small', 'medium', 'large'],
|
|
default: 'small'
|
|
},
|
|
hasPackages: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
packageDescription: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 200
|
|
},
|
|
hasPets: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
petType: {
|
|
type: String,
|
|
trim: true
|
|
},
|
|
petSize: {
|
|
type: String,
|
|
enum: ['small', 'medium', 'large']
|
|
},
|
|
specialNeeds: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 300
|
|
},
|
|
status: {
|
|
type: String,
|
|
enum: ['pending', 'accepted', 'rejected', 'cancelled', 'expired', 'completed'],
|
|
default: 'pending',
|
|
index: true
|
|
},
|
|
responseMessage: {
|
|
type: String,
|
|
trim: true,
|
|
maxlength: 500
|
|
},
|
|
respondedAt: {
|
|
type: Date
|
|
},
|
|
contribution: {
|
|
agreed: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
contribTypeId: {
|
|
type: Schema.Types.ObjectId,
|
|
ref: 'Contribtype'
|
|
},
|
|
amount: {
|
|
type: Number,
|
|
min: 0
|
|
},
|
|
notes: {
|
|
type: String,
|
|
trim: true
|
|
}
|
|
},
|
|
cancelledBy: {
|
|
type: String,
|
|
enum: ['passenger', 'driver']
|
|
},
|
|
cancellationReason: {
|
|
type: String,
|
|
trim: true
|
|
},
|
|
cancelledAt: {
|
|
type: Date
|
|
},
|
|
completedAt: {
|
|
type: Date
|
|
},
|
|
feedbackGiven: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
}, {
|
|
timestamps: true,
|
|
toJSON: { virtuals: true },
|
|
toObject: { virtuals: true }
|
|
});
|
|
|
|
// Indici composti per ricerche ottimizzate
|
|
RideRequestSchema.index({ rideId: 1, status: 1 });
|
|
RideRequestSchema.index({ passengerId: 1, status: 1 });
|
|
RideRequestSchema.index({ driverId: 1, status: 1 });
|
|
RideRequestSchema.index({ idapp: 1, createdAt: -1 });
|
|
|
|
// Virtual per verificare se la richiesta può essere cancellata
|
|
RideRequestSchema.virtual('canCancel').get(function() {
|
|
return ['pending', 'accepted'].includes(this.status);
|
|
});
|
|
|
|
// Virtual per verificare se è in attesa
|
|
RideRequestSchema.virtual('isPending').get(function() {
|
|
return this.status === 'pending';
|
|
});
|
|
|
|
// Metodo per accettare la richiesta
|
|
RideRequestSchema.methods.accept = async function(responseMessage = '') {
|
|
this.status = 'accepted';
|
|
this.responseMessage = responseMessage;
|
|
this.respondedAt = new Date();
|
|
|
|
// Aggiorna il ride con il passeggero confermato
|
|
const Ride = mongoose.model('Ride');
|
|
const ride = await Ride.findById(this.rideId);
|
|
|
|
if (ride) {
|
|
ride.confirmedPassengers.push({
|
|
userId: this.passengerId,
|
|
seats: this.seatsRequested,
|
|
pickupPoint: this.pickupPoint || ride.departure,
|
|
dropoffPoint: this.dropoffPoint || ride.destination,
|
|
confirmedAt: new Date()
|
|
});
|
|
await ride.updateAvailableSeats();
|
|
}
|
|
|
|
return this.save();
|
|
};
|
|
|
|
// Metodo per rifiutare la richiesta
|
|
RideRequestSchema.methods.reject = function(responseMessage = '') {
|
|
this.status = 'rejected';
|
|
this.responseMessage = responseMessage;
|
|
this.respondedAt = new Date();
|
|
return this.save();
|
|
};
|
|
|
|
// Metodo per cancellare la richiesta
|
|
RideRequestSchema.methods.cancel = async function(cancelledBy, reason = '') {
|
|
this.status = 'cancelled';
|
|
this.cancelledBy = cancelledBy;
|
|
this.cancellationReason = reason;
|
|
this.cancelledAt = new Date();
|
|
|
|
// Se era accettata, rimuovi il passeggero dal ride
|
|
if (this.status === 'accepted') {
|
|
const Ride = mongoose.model('Ride');
|
|
const ride = await Ride.findById(this.rideId);
|
|
|
|
if (ride) {
|
|
ride.confirmedPassengers = ride.confirmedPassengers.filter(
|
|
p => p.userId.toString() !== this.passengerId.toString()
|
|
);
|
|
await ride.updateAvailableSeats();
|
|
}
|
|
}
|
|
|
|
return this.save();
|
|
};
|
|
|
|
// Metodo statico per ottenere richieste pendenti di un conducente
|
|
RideRequestSchema.statics.getPendingForDriver = function(idapp, driverId) {
|
|
return this.find({
|
|
idapp,
|
|
driverId,
|
|
status: 'pending'
|
|
})
|
|
.populate('passengerId', 'username name surname email')
|
|
.populate('rideId', 'departure destination dateTime')
|
|
.sort({ createdAt: -1 });
|
|
};
|
|
|
|
// Metodo statico per ottenere richieste di un passeggero
|
|
RideRequestSchema.statics.getByPassenger = function(idapp, passengerId, status = null) {
|
|
const query = { idapp, passengerId };
|
|
if (status) {
|
|
query.status = status;
|
|
}
|
|
return this.find(query)
|
|
.populate('rideId')
|
|
.populate('driverId', 'username name surname')
|
|
.sort({ createdAt: -1 });
|
|
};
|
|
|
|
// Pre-save hook per validazioni
|
|
RideRequestSchema.pre('save', async function(next) {
|
|
if (this.isNew) {
|
|
// Verifica che il ride esista e abbia posti disponibili
|
|
const Ride = mongoose.model('Ride');
|
|
const ride = await Ride.findById(this.rideId);
|
|
|
|
if (!ride) {
|
|
throw new Error('Viaggio non trovato');
|
|
}
|
|
|
|
if (ride.type === 'offer' && ride.passengers.available < this.seatsRequested) {
|
|
throw new Error('Posti non sufficienti per questo viaggio');
|
|
}
|
|
|
|
if (ride.userId.toString() === this.passengerId.toString()) {
|
|
throw new Error('Non puoi richiedere un passaggio per il tuo stesso viaggio');
|
|
}
|
|
|
|
// Imposta il driverId dal ride
|
|
this.driverId = ride.userId;
|
|
}
|
|
next();
|
|
});
|
|
|
|
const RideRequest = mongoose.model('RideRequest', RideRequestSchema);
|
|
|
|
module.exports = RideRequest; |