Compare commits

...

5 Commits

18 changed files with 440 additions and 13 deletions

View File

@ -2,6 +2,7 @@ package eu.maiora.db
import eu.maiora.model.* import eu.maiora.model.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.datetime.toKotlinLocalDateTime
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.LongEntity import org.jetbrains.exposed.dao.LongEntity
@ -82,6 +83,7 @@ object SlotPrenotabiliTable : IdTable<Long>("view_slot_prenotabili"){
val giorno = date("giorno") val giorno = date("giorno")
val dataInizio = datetime("data_inizio") val dataInizio = datetime("data_inizio")
val dataFine = datetime("data_fine") val dataFine = datetime("data_fine")
val dataLimiteCancellazione = datetime("data_limite_cancellazione")
val idModalita = long("id_modalita") val idModalita = long("id_modalita")
val modalitaPrenotazione = varchar("modalita_prenotazione", 255) val modalitaPrenotazione = varchar("modalita_prenotazione", 255)
val idPuntoDistribuzione = long("id_punto_distribuzione") val idPuntoDistribuzione = long("id_punto_distribuzione")
@ -117,6 +119,32 @@ object ResidenzeTable : IdTable<Long>("residenze"){
val nome = varchar("nome", 255) val nome = varchar("nome", 255)
} }
object PrenotazioniPastiTable : IdTable<Long>("prenotazioni_pasti"){
override val id = long("id").autoIncrement("seq_prenotazioni_pasti").entityId()
val idTessera = long("id_tessera")
val idSlotPuntoCassa = long("id_slot_punto_cassa")
val idResidenza = long("id_residenza").nullable()
val idVassoio = long("id_vassoio")
val importoPagato = double("importo_pagato")
val codiceRistocloud = varchar("codice_ristocloud", 255).nullable()
}
object PrenotazioniPastiDettaglioTable : IdTable<Long>("prenotazioni_pasti_dettaglio"){
override val id = long("id").autoIncrement("seq_pren_past_dettaglio").entityId()
val idPrenotazione = long("id_prenotazione")
val idProdotto = long("id_prodotto")
val idStato = long("id_stato")
}
object ViewPrenotazioniPastiTable : IdTable<Long>("view_prenotazioni_pasti"){
override val id = long("id").entityId()
val idPrenotazione = long("id_prenotazione")
val idStato = long("id_stato")
val stato = varchar("stato", 255)
val dataLimiteCancellazione = datetime("data_limite_cancellazione")
}
class AccountsDAO(id: EntityID<Int>) :IntEntity(id) { class AccountsDAO(id: EntityID<Int>) :IntEntity(id) {
companion object : IntEntityClass<AccountsDAO>(AccountsTable) companion object : IntEntityClass<AccountsDAO>(AccountsTable)
@ -184,6 +212,7 @@ class SlotPrenotabiliDao(id: EntityID<Long>) :LongEntity(id) {
var giorno by SlotPrenotabiliTable.giorno var giorno by SlotPrenotabiliTable.giorno
var dataInizio by SlotPrenotabiliTable.dataInizio var dataInizio by SlotPrenotabiliTable.dataInizio
var dataFine by SlotPrenotabiliTable.dataFine var dataFine by SlotPrenotabiliTable.dataFine
var dataLimiteCancellazione by SlotPrenotabiliTable.dataLimiteCancellazione
var idModalita by SlotPrenotabiliTable.idModalita var idModalita by SlotPrenotabiliTable.idModalita
var modalitaPrenotazione by SlotPrenotabiliTable.modalitaPrenotazione var modalitaPrenotazione by SlotPrenotabiliTable.modalitaPrenotazione
var idPuntoDistribuzione by SlotPrenotabiliTable.idPuntoDistribuzione var idPuntoDistribuzione by SlotPrenotabiliTable.idPuntoDistribuzione
@ -221,6 +250,34 @@ class ResidenzeDao(id: EntityID<Long>) :LongEntity(id){
val nome by ResidenzeTable.nome val nome by ResidenzeTable.nome
} }
class PrenotazioniPastiDao(id: EntityID<Long>) :LongEntity(id){
companion object : LongEntityClass<PrenotazioniPastiDao>(PrenotazioniPastiTable)
val idTessera by PrenotazioniPastiTable.idTessera
val idSlotPuntoCassa by PrenotazioniPastiTable.idSlotPuntoCassa
val idResidenza by PrenotazioniPastiTable.idResidenza
val idVassoio by PrenotazioniPastiTable.idVassoio
val importoPagato by PrenotazioniPastiTable.importoPagato
val codiceRistocloud by PrenotazioniPastiTable.codiceRistocloud
}
class PrenotazioniPastiDettaglioDao(id: EntityID<Long>) :LongEntity(id){
companion object : LongEntityClass<PrenotazioniPastiDettaglioDao>(PrenotazioniPastiDettaglioTable)
val idPrenotazione by PrenotazioniPastiDettaglioTable.idPrenotazione
val idProdotto by PrenotazioniPastiDettaglioTable.idProdotto
val idStato by PrenotazioniPastiDettaglioTable.idStato
}
class ViewPrenotazioniPastiDao(id: EntityID<Long>) :LongEntity(id){
companion object : LongEntityClass<ViewPrenotazioniPastiDao>(ViewPrenotazioniPastiTable)
val idPrenotazione by ViewPrenotazioniPastiTable.idPrenotazione
val idStato by ViewPrenotazioniPastiTable.idStato
val stato by ViewPrenotazioniPastiTable.stato
val dataLimiteCancellazione by ViewPrenotazioniPastiTable.dataLimiteCancellazione
}
fun accountsDaoToModel(dao: AccountsDAO) = Accounts( fun accountsDaoToModel(dao: AccountsDAO) = Accounts(
dao.id.value, dao.id.value,
dao.username, dao.username,
@ -291,12 +348,14 @@ fun slotOrariDaoToModel(dao: SlotPrenotabiliDao) :SlotOrari{
val formattedGiorno = dao.giorno.format(formatterGiorno) val formattedGiorno = dao.giorno.format(formatterGiorno)
val formattedDataInizio = dao.dataInizio.format(formatterConOra) val formattedDataInizio = dao.dataInizio.format(formatterConOra)
val formattedDataFine = dao.dataFine.format(formatterConOra) val formattedDataFine = dao.dataFine.format(formatterConOra)
val formattedDataLimiteCancellazione = dao.dataLimiteCancellazione.format(formatterConOra)
return SlotOrari( return SlotOrari(
dao.id.value, dao.id.value,
formattedGiorno, formattedGiorno,
formattedDataInizio, formattedDataInizio,
formattedDataFine, formattedDataFine,
formattedDataLimiteCancellazione,
dao.idPuntoDistribuzione, dao.idPuntoDistribuzione,
dao.puntoDistribuzione, dao.puntoDistribuzione,
dao.idPuntoCassa, dao.idPuntoCassa,
@ -340,6 +399,37 @@ fun residenzeDaoToModel(dao: ResidenzeDao) :Residenze{
) )
} }
fun prenotazioniPastiDaoToModel(dao: PrenotazioniPastiDao) : PrenotazioniPasti{
return PrenotazioniPasti(
dao.id.value,
dao.idTessera,
dao.idSlotPuntoCassa,
dao.idResidenza,
dao.idVassoio,
dao.importoPagato,
dao.codiceRistocloud
)
}
fun prenotazioniPastiDettaglioDaoToModel(dao: PrenotazioniPastiDettaglioDao) : PrenotazioniPastiDettaglio{
return PrenotazioniPastiDettaglio(
dao.id.value,
dao.idPrenotazione,
dao.idProdotto,
dao.idStato
)
}
fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenotazioniPasti{
return ViewPrenotazioniPasti(
dao.id.value,
dao.idPrenotazione,
dao.idStato,
dao.stato,
dao.dataLimiteCancellazione.toKotlinLocalDateTime()
)
}
suspend fun <T> suspendTransaction(block: Transaction.() -> T): T = suspend fun <T> suspendTransaction(block: Transaction.() -> T): T =
newSuspendedTransaction(Dispatchers.IO, statement = block) newSuspendedTransaction(Dispatchers.IO, statement = block)

View File

@ -0,0 +1,21 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class Prenotazioni(
val id: Long? = null,
val codiceRistocloud: String? = null,
val idTessera : Long,
val idSlotPuntoCassa : Long,
val giorno : String? = null,
val turno : String? = null,
val dataInizio : String? = null,
val dataFine : String? = null,
val dataLimiteCancellazione : String? = null,
val idVassoio : Long,
val idStato : Long? = null,
val stato : String? = null,
val listaProdotti : List<ProdottiPrenotabili>? = null,
val importoPagato : Double
)

View File

@ -0,0 +1,14 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class PrenotazioniPasti(
val id: Long,
val idTessera : Long,
val idSlotPuntoCassa : Long,
val idResidenza : Long? = null,
val idVassoio : Long,
val importoPagato : Double,
val codiceRistocloud : String? = null
)

View File

@ -0,0 +1,11 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class PrenotazioniPastiDettaglio(
val id: Long? = null,
val idPrenotazione : Long,
val idProdotto : Long,
val idStato : Long
)

View File

@ -0,0 +1,7 @@
package eu.maiora.model
interface PrenotazioniPastiDettaglioRepository {
suspend fun insert(prenotazionePastoDettaglio : PrenotazioniPastiDettaglio): PrenotazioniPastiDettaglio
suspend fun update(prenotazionePastoDettaglio: PrenotazioniPastiDettaglio): PrenotazioniPastiDettaglio
suspend fun listaDettagliByIdPrenotazione(idPrenotazione : Long): List<PrenotazioniPastiDettaglio>
}

View File

@ -0,0 +1,42 @@
package eu.maiora.model
import eu.maiora.db.*
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.update
class PrenotazioniPastiDettaglioRepositoryImpl : PrenotazioniPastiDettaglioRepository {
override suspend fun insert(prenotazionePastoDettaglio : PrenotazioniPastiDettaglio): PrenotazioniPastiDettaglio = suspendTransaction {
// Inserisci un nuovo dettaglio di una prenotazione pasto
// ID non definito: viene gestito dalla sequence
val idPrenPastiDettInserted = PrenotazioniPastiDettaglioTable.insertAndGetId {
it[idPrenotazione] = prenotazionePastoDettaglio.idPrenotazione
it[idProdotto] = prenotazionePastoDettaglio.idProdotto
it[idStato] = prenotazionePastoDettaglio.idStato
}.value
PrenotazioniPastiDettaglio(
idPrenPastiDettInserted,
prenotazionePastoDettaglio.idPrenotazione,
prenotazionePastoDettaglio.idProdotto,
prenotazionePastoDettaglio.idStato
)
}
override suspend fun update (prenotazionePastoDettaglio : PrenotazioniPastiDettaglio): PrenotazioniPastiDettaglio = suspendTransaction {
PrenotazioniPastiDettaglioTable.update({PrenotazioniPastiDettaglioTable.id eq prenotazionePastoDettaglio.id}){
it[idPrenotazione] = prenotazionePastoDettaglio.idPrenotazione
it[idProdotto] = prenotazionePastoDettaglio.idProdotto
it[idStato] = prenotazionePastoDettaglio.idStato
}
prenotazionePastoDettaglio
}
override suspend fun listaDettagliByIdPrenotazione(idPrenotazione : Long): List<PrenotazioniPastiDettaglio> = suspendTransaction {
// Cerca la lista di dettagli della prenotazione (ovvero i prodotti)
PrenotazioniPastiDettaglioDao.find { PrenotazioniPastiDettaglioTable.idPrenotazione eq idPrenotazione }
.toList() // Restituisce la lista dei prodotti
.map { prenotazioniPastiDettaglioDaoToModel(it) } // Converte il DAO in un oggetto PrenotazioniPastiDettaglio
}
}

View File

@ -0,0 +1,5 @@
package eu.maiora.model
interface PrenotazioniPastiRepository {
suspend fun insert(prenotazionePasto : PrenotazioniPasti): PrenotazioniPasti
}

View File

@ -0,0 +1,29 @@
package eu.maiora.model
import eu.maiora.db.*
import org.jetbrains.exposed.sql.insertAndGetId
class PrenotazioniPastiRepositoryImpl : PrenotazioniPastiRepository {
override suspend fun insert(prenotazionePasto : PrenotazioniPasti): PrenotazioniPasti = suspendTransaction {
// Inserisci una nuova prenotazione pasto
// ID non definito: viene gestito dalla sequence
val idPrenPastiInserted = PrenotazioniPastiTable.insertAndGetId {
it[idTessera] = prenotazionePasto.idTessera
it[idSlotPuntoCassa] = prenotazionePasto.idSlotPuntoCassa
it[idResidenza] = prenotazionePasto.idResidenza
it[idVassoio] = prenotazionePasto.idVassoio
it[importoPagato] = prenotazionePasto.importoPagato
it[codiceRistocloud] = prenotazionePasto.codiceRistocloud
}.value
PrenotazioniPasti(
idPrenPastiInserted,
prenotazionePasto.idTessera,
prenotazionePasto.idSlotPuntoCassa,
prenotazionePasto.idResidenza,
prenotazionePasto.idVassoio,
prenotazionePasto.importoPagato,
prenotazionePasto.codiceRistocloud
)
}
}

View File

@ -5,17 +5,17 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ProdottiPrenotabili( data class ProdottiPrenotabili(
val id: Long, val id: Long,
val giorno : String, val giorno : String? = null,
val idPuntoDistribuzione : Long, val idPuntoDistribuzione : Long? = null,
val puntoDistribuzione : String, val puntoDistribuzione : String? = null,
val idTurno : Long, val idTurno : Long? = null,
val turno : String, val turno : String? = null,
val idProdotto : Long, val idProdotto : Long? = null,
val prodotto : String, val prodotto : String? = null,
val codiceProdotto : String, val codiceProdotto : String? = null,
val descrizioneProdotto : String, val descrizioneProdotto : String? = null,
val idCategoria : Long, val idCategoria : Long? = null,
val categoria : String, val categoria : String? = null,
val idModalita : Long, val idModalita : Long? = null,
val modalitaPrenotazione : String val modalitaPrenotazione : String? = null
) )

View File

@ -8,6 +8,7 @@ data class SlotOrari(
val giorno : String, val giorno : String,
val dataInizio : String, val dataInizio : String,
val dataFine : String, val dataFine : String,
val dataLimiteCancellazione : String,
val idPuntoDistribuzione : Long, val idPuntoDistribuzione : Long,
val puntoDistribuzione : String, val puntoDistribuzione : String,
val idPuntoCassa : Long, val idPuntoCassa : Long,

View File

@ -5,4 +5,8 @@ interface SlotOrariRepository {
idTurno : Long, idTurno : Long,
idPuntoDistribuzione : Long, idPuntoDistribuzione : Long,
idModalita : Long): List<SlotOrari>? idModalita : Long): List<SlotOrari>?
suspend fun idResidenzaByIdSlotOrario(idSlot : Long) : Long?
suspend fun slotOrarioById(idSlot : Long) : SlotOrari?
} }

View File

@ -22,4 +22,17 @@ class SlotOrariRepositoryImpl : SlotOrariRepository {
.map { slotOrariDaoToModel(it) } // Converte il DAO in un oggetto SlotOrari .map { slotOrariDaoToModel(it) } // Converte il DAO in un oggetto SlotOrari
} }
override suspend fun idResidenzaByIdSlotOrario(idSlot : Long) : Long? = suspendTransaction {
SlotPrenotabiliDao.find{ SlotPrenotabiliTable.id eq idSlot }
.single() // Restituisce un singolo risultato o null se non trovato
.let { slotOrariDaoToModel(it) } // Converte il DAO in un oggetto SlotOrari
.idResidenza
}
override suspend fun slotOrarioById(idSlot : Long) : SlotOrari? = suspendTransaction {
SlotPrenotabiliDao.find{ SlotPrenotabiliTable.id eq idSlot }
.singleOrNull() // Restituisce un singolo risultato o null se non trovato
?.let { slotOrariDaoToModel(it) } // Converte il DAO in un oggetto SlotOrari
}
} }

View File

@ -0,0 +1,13 @@
package eu.maiora.model
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.Serializable
@Serializable
data class ViewPrenotazioniPasti(
val id: Long,
val idPrenotazione: Long,
val idStato: Long,
val stato: String,
val dataLimiteCancellazione: LocalDateTime
)

View File

@ -0,0 +1,5 @@
package eu.maiora.model
interface ViewPrenotazioniPastiRepository {
suspend fun prenotazioniPastiById(id : Long): ViewPrenotazioniPasti
}

View File

@ -0,0 +1,15 @@
package eu.maiora.model
import eu.maiora.db.*
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.insertAndGetId
class ViewPrenotazioniPastiRepositoryImpl : ViewPrenotazioniPastiRepository {
override suspend fun prenotazioniPastiById(id : Long): ViewPrenotazioniPasti = suspendTransaction {
// Cerca la prenotazione
ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idPrenotazione eq id }
.first() //prendo solo il primo record, dato che ci sono tanti record quanti prodotti prenotati
.let { viewPrenotazioniPastiDaoToModel(it) } // Converte il DAO in un oggetto ViewPrenotazioniPasti
}
}

View File

@ -22,5 +22,7 @@ fun Application.configureRouting() {
slotOrari(SlotOrariRepositoryImpl()) slotOrari(SlotOrariRepositoryImpl())
prodottiPrenotabili(ProdottiPrenotabiliRepositoryImpl()) prodottiPrenotabili(ProdottiPrenotabiliRepositoryImpl())
residenze(ResidenzeRepositoryImpl()) residenze(ResidenzeRepositoryImpl())
prenotazioni(PrenotazioniPastiRepositoryImpl(), SlotOrariRepositoryImpl(), PrenotazioniPastiDettaglioRepositoryImpl())
annullaPrenotazioni(ViewPrenotazioniPastiRepositoryImpl(), PrenotazioniPastiDettaglioRepositoryImpl())
} }
} }

View File

@ -0,0 +1,71 @@
package eu.maiora.routes
import eu.maiora.model.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.datetime.toJavaLocalDateTime
import java.time.LocalDateTime
fun Route.annullaPrenotazioni(
viewPrenotazioniPastiRepository: ViewPrenotazioniPastiRepositoryImpl,
prenotazioniPastiDettaglioRepository: PrenotazioniPastiDettaglioRepositoryImpl
) {
route("/api/annullaPrenotazioni") {
authenticate("auth-jwt") {
patch("{idPrenotazione}") {
//ottieni l'id della prenotazione dal percorso
val idPrenotazione = call.parameters["idPrenotazione"]
if (idPrenotazione == null) {
call.respondText("ID prenotazione non valido", status = HttpStatusCode.BadRequest)
return@patch
}
val prenotazione = viewPrenotazioniPastiRepository.prenotazioniPastiById(idPrenotazione.toLong())
val statoPrenotazione = prenotazione.stato
//prenotazione annullabile se lo stato non è SERVITA 2L
if (prenotazione.idStato == 2L) {
call.respond(
HttpStatusCode.BadRequest,
"Prenotazione non annullata. Stato corrente della prenotazione: $statoPrenotazione"
)
return@patch
}
//prenotazione annullabile se la data limite per la cancellazione non è stato superato
if (LocalDateTime.now().isAfter(prenotazione.dataLimiteCancellazione.toJavaLocalDateTime())) {
call.respond(
HttpStatusCode.BadRequest,
"Prenotazione non annullata: data limite cancellazione superata"
)
return@patch
}
val listaProdottiPrenotati =
prenotazioniPastiDettaglioRepository.listaDettagliByIdPrenotazione(idPrenotazione.toLong())
//aggiorna lo stato di ogni prodotto per annullare la prenotazione
listaProdottiPrenotati.forEach { el ->
val prodottoDaAggiornare = PrenotazioniPastiDettaglio(
el.id,
el.idPrenotazione,
el.idProdotto,
3L //ANNULLATA
)
prenotazioniPastiDettaglioRepository.update(prodottoDaAggiornare)
}
call.respond(HttpStatusCode.OK, "Prenotazione $idPrenotazione annullata")
}
}
}
}

View File

@ -0,0 +1,84 @@
package eu.maiora.routes
import eu.maiora.model.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
slotOrariRepository: SlotOrariRepository,
prenotazioniPastiDettaglioRepository : PrenotazioniPastiDettaglioRepository){
route("/api/prenotazioni"){
authenticate("auth-jwt") {
post(){
try{
val listaDettagliInseriti = mutableListOf<PrenotazioniPastiDettaglio>()
val prenotazioneRequest = call.receive<Prenotazioni>()
//recupera lo slot orario selezionato
val idSlotOrario = slotOrariRepository.slotOrarioById(prenotazioneRequest.idSlotPuntoCassa)
if(idSlotOrario == null){
call.respond(HttpStatusCode.BadRequest,"Slot orario non esistente")
return@post
}
val prenotazionePasto = PrenotazioniPasti(
-1,
prenotazioneRequest.idTessera,
prenotazioneRequest.idSlotPuntoCassa,
idSlotOrario.idResidenza,
prenotazioneRequest.idVassoio,
prenotazioneRequest.importoPagato,
prenotazioneRequest.codiceRistocloud
)
//Inserisci la prenotazione nel database
val prenotazioneInserita = prenotazioniPastiRepository.insert(prenotazionePasto)
//per ogni prodotto, inserisci il dettaglio della prenotazione
prenotazioneRequest.listaProdotti?.forEach { el ->
val dettaglioPrenotazione = PrenotazioniPastiDettaglio(
-1,
prenotazioneInserita.id,
el.id,
0L
)
listaDettagliInseriti.add(prenotazioniPastiDettaglioRepository.insert(dettaglioPrenotazione))
}
//restituisci la prenotazione
val prenotazione = Prenotazioni(
prenotazioneInserita.id,
prenotazioneRequest.codiceRistocloud,
prenotazioneInserita.idTessera,
prenotazioneInserita.idSlotPuntoCassa,
idSlotOrario.giorno,
idSlotOrario.turno,
idSlotOrario.dataInizio,
idSlotOrario.dataFine,
idSlotOrario.dataLimiteCancellazione,
prenotazioneRequest.idVassoio,
prenotazioneRequest.idStato,
prenotazioneRequest.stato,
prenotazioneRequest.listaProdotti,
prenotazioneRequest.importoPagato
)
call.respond(HttpStatusCode.Created, prenotazione)
}
catch (e: Exception){
call.respond(
HttpStatusCode.BadRequest,
"Errore nel processare la richiesta: ${e.cause}"
)
}
}
}
}
}