Merge pull request '0002593-endpoint-prenotazioni-adeguamento-strutture-dati' (#23) from 0002593-endpoint-prenotazioni-adeguamento-strutture-dati into dev

Reviewed-on: #23
This commit is contained in:
Francesco Di Sciascio 2025-08-20 14:35:44 +00:00
commit 07f4fab26e
9 changed files with 171 additions and 114 deletions

View File

@ -3,12 +3,7 @@ package eu.maiora.db
import eu.maiora.model.*
import kotlinx.coroutines.Dispatchers
import kotlinx.datetime.toKotlinLocalDateTime
import kotlinx.datetime.toKotlinLocalDate
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.LongEntityClass
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.Transaction
@ -149,10 +144,17 @@ object ViewPrenotazioniPastiTable : IdTable<Long>("view_prenotazioni_pasti"){
val giorno = date("giorno")
val turno = varchar("turno", 255)
val dataInizio = datetime("data_inizio")
val dataFine = datetime("data_fine")
val dataLimiteCancellazione = datetime("data_limite_cancellazione")
val idVassoio = long("id_vassoio").nullable()
val codiceRistocloud = varchar("codice_ristocloud", 255).nullable()
val importoPagato = double("importo_pagato")
val puntoDistribuzione = varchar("punto_distribuzione", 255)
val puntoCassa = varchar("punto_cassa", 255)
val modalita = varchar("modalita", 255)
val idProdotto = long("id_prodotto")
val prodotto = varchar("prodotto", 255)
}
class AccountsDAO(id: EntityID<Int>) :IntEntity(id) {
@ -290,10 +292,17 @@ class ViewPrenotazioniPastiDao(id: EntityID<Long>) :LongEntity(id){
val giorno by ViewPrenotazioniPastiTable.giorno
val turno by ViewPrenotazioniPastiTable.turno
val dataInizio by ViewPrenotazioniPastiTable.dataInizio
val dataFine by ViewPrenotazioniPastiTable.dataFine
val dataLimiteCancellazione by ViewPrenotazioniPastiTable.dataLimiteCancellazione
val idVassoio by ViewPrenotazioniPastiTable.idVassoio
val codiceRistocloud by ViewPrenotazioniPastiTable.codiceRistocloud
val importoPagato by ViewPrenotazioniPastiTable.importoPagato
val puntoDistribuzione by ViewPrenotazioniPastiTable.puntoDistribuzione
val puntoCassa by ViewPrenotazioniPastiTable.puntoCassa
val modalita by ViewPrenotazioniPastiTable.modalita
val idProdotto by ViewPrenotazioniPastiTable.idProdotto
val prodotto by ViewPrenotazioniPastiTable.prodotto
}
fun accountsDaoToModel(dao: AccountsDAO) = Accounts(
@ -438,12 +447,14 @@ fun prenotazioniPastiDettaglioDaoToModel(dao: PrenotazioniPastiDettaglioDao) : P
)
}
fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenotazioniPasti{
/*fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenotazioniPasti{
val formatterGiorno = DateTimeFormatter.ofPattern("ddMMyyyy")
val formatterDataInizio = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formatterDataFine = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formatterDataLimiteCancellazione = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formattedGiorno = dao.giorno.format(formatterGiorno)
val formattedDataInizio = dao.dataInizio.format(formatterDataInizio)
val formattedDataFine = dao.dataFine.format(formatterDataFine)
val formattedDataLimiteCanc = dao.dataLimiteCancellazione.format(formatterDataLimiteCancellazione)
@ -456,12 +467,48 @@ fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenota
formattedGiorno,
dao.turno,
formattedDataInizio,
formattedDataFine,
formattedDataLimiteCanc,
dao.idVassoio,
dao.codiceRistocloud,
dao.importoPagato,
dao.puntoDistribuzione,
dao.puntoCassa,
dao.modalita,
null
)
}*/
fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao, listaProdotti: List<ListaProdottiElement>? = null) : ViewPrenotazioniPasti{
val formatterGiorno = DateTimeFormatter.ofPattern("ddMMyyyy")
val formatterDataInizio = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formatterDataFine = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formatterDataLimiteCancellazione = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formattedGiorno = dao.giorno.format(formatterGiorno)
val formattedDataInizio = dao.dataInizio.format(formatterDataInizio)
val formattedDataFine = dao.dataFine.format(formatterDataFine)
val formattedDataLimiteCanc = dao.dataLimiteCancellazione.format(formatterDataLimiteCancellazione)
return ViewPrenotazioniPasti(
dao.idPrenotazione,
dao.idStato,
dao.stato,
dao.idTessera,
dao.idSlotPuntoCassa,
formattedGiorno,
dao.turno,
formattedDataInizio,
formattedDataFine,
formattedDataLimiteCanc,
dao.idVassoio,
dao.codiceRistocloud,
dao.importoPagato,
dao.puntoDistribuzione,
dao.puntoCassa,
dao.modalita,
listaProdotti
)
}

View File

@ -0,0 +1,6 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class ListaProdottiElement (val id: Long, val nome: String? = null, val quantita: Int)

View File

@ -16,6 +16,7 @@ data class Prenotazioni(
val idVassoio : Long,
val idStato : Long? = null,
val stato : String? = null,
val listaProdotti : List<ProdottiPrenotabili>? = null,
//val listaProdotti : List<ProdottiPrenotabili>? = null,
val listaProdotti: List<ListaProdottiElement>? = null,
val importoPagato : Double
)

View File

@ -7,5 +7,7 @@ data class PrenotazioniPastiDettaglio(
val id: Long? = null,
val idPrenotazione : Long,
val idProdotto : Long,
val idStato : Long
val idStato : Long,
val quantita : Int? = null,
val prodotto : String? = null
)

View File

@ -1,7 +1,5 @@
package eu.maiora.model
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.Serializable
@Serializable
@ -14,10 +12,14 @@ data class ViewPrenotazioniPasti(
val giorno: String,
val turno: String,
val dataInizio: String,
val dataFine: String,
val dataLimiteCancellazione: String,
val idVassoio: Long? = null,
val codiceRistocloud: String? = null,
val importoPagato: Double,
var listaProdotti: List<PrenotazioniPastiDettaglio>? = null
val puntoDistribuzione: String,
val puntoCassa: String,
val modalita: String,
var listaProdotti: List<ListaProdottiElement>? = null
)

View File

@ -2,15 +2,19 @@ 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
val prenotazione = ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idPrenotazione eq id }
.firstOrNull() //prendo solo il primo record, dato che ci sono tanti record quanti prodotti prenotati
if (prenotazione == null) {
throw IllegalArgumentException("Prenotazione con ID $id non trovata")
}
viewPrenotazioniPastiDaoToModel(prenotazione, null)
}
override suspend fun prenotazioniPastiByIdTessera(idTessera : Long, limit : Long, offset : Long, order : String): List<ViewPrenotazioniPasti> = suspendTransaction {
@ -20,20 +24,41 @@ class ViewPrenotazioniPastiRepositoryImpl : ViewPrenotazioniPastiRepository {
else -> SortOrder.DESC // Default a DESC in caso di valore invalido
}
// Cerca la lista di prenotazioni
ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idTessera eq idTessera }
val prenotazioniRaw = ViewPrenotazioniPastiDao
.find { ViewPrenotazioniPastiTable.idTessera eq idTessera }
.orderBy(ViewPrenotazioniPastiTable.giorno to sortOrder, ViewPrenotazioniPastiTable.id to sortOrder)
.limit(limit.toInt())
.offset(offset)
.toList() // Restituisce la lista delle prenotazioni
.map { viewPrenotazioniPastiDaoToModel(it) } // Converte il DAO in un oggetto Movimenti
.toList()
//raggruppo i record per idPrenotazione e, successivamente, per idProdotto per poter calcolare le quantita
val listaPrenotazioniPasti = prenotazioniRaw
.groupBy { it.idPrenotazione }
.map { (_, dettagliPrenotazione) ->
val primaRiga = dettagliPrenotazione.first() //la prima riga la uso per leggere i dati della prenotazione
val prodottiRaggruppati = dettagliPrenotazione
.groupBy { it.idProdotto }
.map { (idProdotto, prodotti) ->
ListaProdottiElement(
id = idProdotto,
nome = prodotti.first().prodotto,
quantita = prodotti.size
)
}
viewPrenotazioniPastiDaoToModel(primaRiga, prodottiRaggruppati)
}
listaPrenotazioniPasti
}
override suspend fun countPrenotazioniPastiByIdTessera(idTessera : Long): Int = suspendTransaction {
// Conta il numero totale di prenotazioni della tessera
ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idTessera eq idTessera }
.toList() // Restituisce la lista delle prenotazioni
.count()
ViewPrenotazioniPastiDao
.find { ViewPrenotazioniPastiTable.idTessera eq idTessera }
.toList()
.map { it.idPrenotazione } // Estrai solo gli ID delle prenotazioni
.distinct() // Rimuovi duplicati
.count() // Conta le prenotazioni uniche
}
}

View File

@ -26,45 +26,51 @@ fun Route.annullaPrenotazioni(
return@patch
}
val prenotazione = viewPrenotazioniPastiRepository.prenotazioniPastiById(idPrenotazione.toLong())
val statoPrenotazione = prenotazione.stato
try{
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 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 è stata superata
val formatter = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formattedDate = LocalDateTime.parse(prenotazione.dataLimiteCancellazione, formatter)
if (LocalDateTime.now().isAfter(formattedDate)) {
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")
}
catch (e: IllegalArgumentException) {
call.respond(HttpStatusCode.NotFound, "Prenotazione non trovata: ${e.message}")
}
//prenotazione annullabile se la data limite per la cancellazione non è stata superata
val formatter = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formattedDate = LocalDateTime.parse(prenotazione.dataLimiteCancellazione, formatter)
if (LocalDateTime.now().isAfter(formattedDate)) {
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

@ -44,11 +44,6 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
offset.toLong(),
order)
listaPrenotazioniPasti.forEach{ el ->
el.listaProdotti =
prenotazioniPastiDettaglioRepository.listaDettagliByIdPrenotazione(el.id)
}
call.respond(ListaPrenotazioni(totalRecords, listaPrenotazioniPasti))
}
@ -68,6 +63,15 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
return@post
}
prenotazioneRequest.listaProdotti.forEach { prodotto ->
prodotto.quantita
?: throw IllegalArgumentException("Quantita non specificata per il prodotto con ID ${prodotto.id}")
if (prodotto.quantita < 1) {
throw IllegalArgumentException("Quantita non valida (${prodotto.quantita}) per il prodotto con ID ${prodotto.id}")
}
}
val prenotazionePasto = PrenotazioniPasti(
-1,
@ -83,15 +87,19 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
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))
prenotazioneRequest.listaProdotti.forEach { prodotto ->
repeat(prodotto.quantita!!) {
val dettaglioPrenotazione = PrenotazioniPastiDettaglio(
-1,
prenotazioneInserita.id,
prodotto.id,
0L
)
listaDettagliInseriti.add(
prenotazioniPastiDettaglioRepository.insert(dettaglioPrenotazione)
)
}
}
@ -117,7 +125,7 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
catch (e: Exception){
call.respond(
HttpStatusCode.BadRequest,
"Errore nel processare la richiesta: ${e.cause}"
"Errore nel processare la richiesta: ${e.message}"
)
}
}

View File

@ -19,7 +19,6 @@ fun Route.verificaVassoio(selezionaComposizioneRepository: SelezionaComposizione
try {
val dati = call.receive<DatiVassoio>()
//val idsConcatenati = dati.listaProdotti.joinToString(separator = "|") { it.id.toString() }
val idsConcatenati = dati.listaProdotti
.flatMap { prodotto -> List(prodotto.quantita) { prodotto.id } }
.joinToString(separator = "|") { it.toString() }
@ -35,43 +34,6 @@ fun Route.verificaVassoio(selezionaComposizioneRepository: SelezionaComposizione
call.respond(HttpStatusCode.BadRequest, "Body non valido")
}
/*val idTessera = call.parameters["idTessera"]
val idVassoio = call.parameters["idVassoio"]
val data = call.parameters["data"]
if (idTessera == null) {
call.respondText("ID tessera non valido", status = HttpStatusCode.BadRequest)
return@get
}
if (idVassoio == null) {
call.respondText("ID vassoio non valido", status = HttpStatusCode.BadRequest)
return@get
}
if (data == null) {
call.respondText("data non valida", status = HttpStatusCode.BadRequest)
return@get
}
// Cerca la tessera per codice fiscale
val valorePasto = valorePastiRepository.valorePastoByTessVassData(idTessera, idVassoio, data)
val prezzo = valorePasto.split("#").get(0)
val punti = valorePasto.split("#").get(1)
if(prezzo == "-1" && punti == "-1")
call.respondText("Prezzo e punti non trovati", status = HttpStatusCode.NotFound)
call.respond( ValorePasti(idTessera.toLong(),
idVassoio.toLong(), data,
prezzo.replace(',', '.').toDouble(),
punti.toInt()))*/
}
}
@ -79,7 +41,5 @@ fun Route.verificaVassoio(selezionaComposizioneRepository: SelezionaComposizione
}
@Serializable
data class DatiVassoio (val idUtente : Long, val listaProdotti: List<Prodotti>)
data class DatiVassoio (val idUtente : Long, val listaProdotti: List<ListaProdottiElement>)
@Serializable
data class Prodotti (val id: Long, val quantita: Int)