gestione struttura lista prodotti

aggiunta gestione quantita negli endpoint legati alle prenotazioni
gestione eccezione se id prenotazione da annullare non esiste
This commit is contained in:
Francesco Di Sciascio 2025-08-20 16:31:48 +02:00
parent 52b8936bfe
commit a5a154c7bf
9 changed files with 150 additions and 108 deletions

View File

@ -3,8 +3,6 @@ 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 kotlinx.datetime.toKotlinLocalDate
import org.jetbrains.exposed.dao.* import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IdTable
@ -154,6 +152,8 @@ object ViewPrenotazioniPastiTable : IdTable<Long>("view_prenotazioni_pasti"){
val puntoDistribuzione = varchar("punto_distribuzione", 255) val puntoDistribuzione = varchar("punto_distribuzione", 255)
val puntoCassa = varchar("punto_cassa", 255) val puntoCassa = varchar("punto_cassa", 255)
val modalita = varchar("modalita", 255) val modalita = varchar("modalita", 255)
val idProdotto = long("id_prodotto")
val prodotto = varchar("prodotto", 255)
} }
@ -300,6 +300,8 @@ class ViewPrenotazioniPastiDao(id: EntityID<Long>) :LongEntity(id){
val puntoDistribuzione by ViewPrenotazioniPastiTable.puntoDistribuzione val puntoDistribuzione by ViewPrenotazioniPastiTable.puntoDistribuzione
val puntoCassa by ViewPrenotazioniPastiTable.puntoCassa val puntoCassa by ViewPrenotazioniPastiTable.puntoCassa
val modalita by ViewPrenotazioniPastiTable.modalita val modalita by ViewPrenotazioniPastiTable.modalita
val idProdotto by ViewPrenotazioniPastiTable.idProdotto
val prodotto by ViewPrenotazioniPastiTable.prodotto
} }
@ -445,7 +447,7 @@ fun prenotazioniPastiDettaglioDaoToModel(dao: PrenotazioniPastiDettaglioDao) : P
) )
} }
fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenotazioniPasti{ /*fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenotazioniPasti{
val formatterGiorno = DateTimeFormatter.ofPattern("ddMMyyyy") val formatterGiorno = DateTimeFormatter.ofPattern("ddMMyyyy")
val formatterDataInizio = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm") val formatterDataInizio = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formatterDataFine = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm") val formatterDataFine = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
@ -475,6 +477,38 @@ fun viewPrenotazioniPastiDaoToModel(dao: ViewPrenotazioniPastiDao) : ViewPrenota
dao.modalita, dao.modalita,
null 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 idVassoio : Long,
val idStato : Long? = null, val idStato : Long? = null,
val stato : String? = null, val stato : String? = null,
val listaProdotti : List<ProdottiPrenotabili>? = null, //val listaProdotti : List<ProdottiPrenotabili>? = null,
val listaProdotti: List<ListaProdottiElement>? = null,
val importoPagato : Double val importoPagato : Double
) )

View File

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

View File

@ -20,6 +20,6 @@ data class ViewPrenotazioniPasti(
val puntoDistribuzione: String, val puntoDistribuzione: String,
val puntoCassa: String, val puntoCassa: String,
val modalita: String, val modalita: String,
var listaProdotti: List<PrenotazioniPastiDettaglio>? = null var listaProdotti: List<ListaProdottiElement>? = null
) )

View File

@ -2,15 +2,19 @@ package eu.maiora.model
import eu.maiora.db.* import eu.maiora.db.*
import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.insertAndGetId
class ViewPrenotazioniPastiRepositoryImpl : ViewPrenotazioniPastiRepository { class ViewPrenotazioniPastiRepositoryImpl : ViewPrenotazioniPastiRepository {
override suspend fun prenotazioniPastiById(id : Long): ViewPrenotazioniPasti = suspendTransaction { override suspend fun prenotazioniPastiById(id : Long): ViewPrenotazioniPasti = suspendTransaction {
// Cerca la prenotazione // Cerca la prenotazione
ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idPrenotazione eq id } val prenotazione = ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idPrenotazione eq id }
.first() //prendo solo il primo record, dato che ci sono tanti record quanti prodotti prenotati .firstOrNull() //prendo solo il primo record, dato che ci sono tanti record quanti prodotti prenotati
.let { viewPrenotazioniPastiDaoToModel(it) } // Converte il DAO in un oggetto ViewPrenotazioniPasti
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 { 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 else -> SortOrder.DESC // Default a DESC in caso di valore invalido
} }
// Cerca la lista di prenotazioni // 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) .orderBy(ViewPrenotazioniPastiTable.giorno to sortOrder, ViewPrenotazioniPastiTable.id to sortOrder)
.limit(limit.toInt()) .limit(limit.toInt())
.offset(offset) .offset(offset)
.toList() // Restituisce la lista delle prenotazioni .toList()
.map { viewPrenotazioniPastiDaoToModel(it) } // Converte il DAO in un oggetto Movimenti
//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 { override suspend fun countPrenotazioniPastiByIdTessera(idTessera : Long): Int = suspendTransaction {
// Conta il numero totale di prenotazioni della tessera // Conta il numero totale di prenotazioni della tessera
ViewPrenotazioniPastiDao.find { ViewPrenotazioniPastiTable.idTessera eq idTessera } ViewPrenotazioniPastiDao
.toList() // Restituisce la lista delle prenotazioni .find { ViewPrenotazioniPastiTable.idTessera eq idTessera }
.count() .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 return@patch
} }
val prenotazione = viewPrenotazioniPastiRepository.prenotazioniPastiById(idPrenotazione.toLong()) try{
val statoPrenotazione = prenotazione.stato val prenotazione = viewPrenotazioniPastiRepository.prenotazioniPastiById(idPrenotazione.toLong())
val statoPrenotazione = prenotazione.stato
//prenotazione annullabile se lo stato non è SERVITA 2L //prenotazione annullabile se lo stato non è SERVITA 2L
if (prenotazione.idStato == 2L) { if (prenotazione.idStato == 2L) {
call.respond( call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
"Prenotazione non annullata. Stato corrente della prenotazione: $statoPrenotazione" "Prenotazione non annullata. Stato corrente della prenotazione: $statoPrenotazione"
) )
return@patch 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(), offset.toLong(),
order) order)
listaPrenotazioniPasti.forEach{ el ->
el.listaProdotti =
prenotazioniPastiDettaglioRepository.listaDettagliByIdPrenotazione(el.id)
}
call.respond(ListaPrenotazioni(totalRecords, listaPrenotazioniPasti)) call.respond(ListaPrenotazioni(totalRecords, listaPrenotazioniPasti))
} }
@ -68,6 +63,15 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
return@post 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( val prenotazionePasto = PrenotazioniPasti(
-1, -1,
@ -83,15 +87,19 @@ fun Route.prenotazioni(prenotazioniPastiRepository: PrenotazioniPastiRepository,
val prenotazioneInserita = prenotazioniPastiRepository.insert(prenotazionePasto) val prenotazioneInserita = prenotazioniPastiRepository.insert(prenotazionePasto)
//per ogni prodotto, inserisci il dettaglio della prenotazione //per ogni prodotto, inserisci il dettaglio della prenotazione
prenotazioneRequest.listaProdotti?.forEach { el -> prenotazioneRequest.listaProdotti.forEach { prodotto ->
val dettaglioPrenotazione = PrenotazioniPastiDettaglio(
-1,
prenotazioneInserita.id,
el.id,
0L
)
listaDettagliInseriti.add(prenotazioniPastiDettaglioRepository.insert(dettaglioPrenotazione))
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){ catch (e: Exception){
call.respond( call.respond(
HttpStatusCode.BadRequest, 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 { try {
val dati = call.receive<DatiVassoio>() val dati = call.receive<DatiVassoio>()
//val idsConcatenati = dati.listaProdotti.joinToString(separator = "|") { it.id.toString() }
val idsConcatenati = dati.listaProdotti val idsConcatenati = dati.listaProdotti
.flatMap { prodotto -> List(prodotto.quantita) { prodotto.id } } .flatMap { prodotto -> List(prodotto.quantita) { prodotto.id } }
.joinToString(separator = "|") { it.toString() } .joinToString(separator = "|") { it.toString() }
@ -35,43 +34,6 @@ fun Route.verificaVassoio(selezionaComposizioneRepository: SelezionaComposizione
call.respond(HttpStatusCode.BadRequest, "Body non valido") 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 @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)