Está en la página 1de 12

Universidad Tecnológica Nacional – FRC/FRVM

Cátedra de Diseño de Sistemas

Consigna:
1. Patrones de Diseño
Realice la vista de estructura (diagrama de clases) y la vista dinámica (diagrama de secuencia) de los dos patrones de diseño
que resuelvan las siguientes consideraciones. Especificar el comportamiento de cada uno de los métodos utilizados en la
dinámica, mediante una descripción textual o pseudocódigo:
b) Resolver la forma en que el sistema notificará a través de los TV de la sala y los parlantes, el número de ticket e
identificador del puesto donde será atendido el cliente. Modele la dinámica al momento de notificar tanto por parlante
como por los TV´s de la sala
c) Rediseñar el software de forma tal que pueda eficientizarse el manejo del comportamiento variable del puesto de
trabajo en todas las situaciones en las que se pueda encontrar. Modelar la dinámica al momento de iniciar la atención
a un cliente

Nota: En este caso se ha simplificado la realidad para adecuar la complejidad del ejercicio a las necesidades planteadas en la
evaluación.

Gestión de Afluencias
Una empresa de servicios de la Ciudad de Córdoba ha decidido mudar sus oficinas comerciales y quiere aprovechar el cambio de
locación para darle una nueva imagen. Como parte de esta renovación se ha decidido desarrollar un sistema que permita gestionar
la afluencia de los clientes, pudiendo además determinar la cantidad de clientes atendidos al final del día y el motivo del contacto.
El sistema a construir deberá permitir a los clientes que se presenten en las oficinas de la empresa, emitir un ticket de atención
mediante el cual serán llamados para ser atendidos. Este ticket se imprimirá desde una terminal con pantalla táctil, en donde el
cliente deberá seleccionar previamente el tipo de trámite a realizar y, en función de la opción seleccionada, el sistema imprimirá
el ticket con una letra y un número.
La letra se asignará de acuerdo al tipo/subtipo de trámite a realizar, ya que en función de esto se administrarán distintas colas de
atención. El número es el orden de atención en la cola de atención para esa letra. Por ejemplo: Para el tipo de trámite “Cajas” se
asignará la letra C. Para el tipo de trámite “Atención al Cliente” subtipo “Reclamos” se le asignará la letra R. Cada día se inicia el
contador de atención en cero, con lo cual al primer cliente del día en Cajas se le entrega un ticket con el orden C1, al segundo que
se presente en el día se le entregará el ticket C2 y así sucesivamente.
Para emitir los tickets se incorporarán 3 terminales de atención en el ingreso de la sucursal. Estas terminales permitirán al cliente
seleccionar entre las distintas opciones de tipo y subtipo de trámite, tal como se detalla a continuación (se muestra una
configuración de menú a modo de ejemplo)

Logo de la Empresa

Bienvenido

Seleccione una opción y retire su


ticket
MENÚ Subtipo de trámite

Atención al Cliente

Reclamos

Solicitudes

Cajas

Grandes Clientes Tipo de trámite

Dispenser de Ticket
Lunes 08/06/2015 10:05:00

www.xxxx.com.ar

Hoja: 1 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Se debe poder asignar a las colas el tipo y/o subtipo de trámite que se necesite. Si el tipo de trámite tiene subtipos, la asignación
se hace a nivel de subtipo; de no existir subtipos de trámites, la asignación se hace por Tipo de Trámite.

Para el ejemplo, se formarían cuatro colas de espera:


• Cola 1: “Cajas”
• Cola 2: “Atención al Cliente-Reclamos”
• Cola 3: “Atención al Cliente-Solicitudes”
• Cola 4: “Grandes Clientes”
Para cada tipo/subtipo de trámite deberá definirse en qué piso será atendido, para poder derivarlo correctamente a una sala de
espera.
Cada uno de los puestos de trabajo (ocupados por un Representante Comercial o RECO) tendrá previamente asignada las colas
que pueden atender por un período de tiempo. Esta asignación es temporal y la realiza el Supervisor del área en función de las
estadísticas de afluencia por tipo de trámite. Cuando se crea un puesto de trabajo estará no disponible hasta que sea asignado a
una o más colas. Una vez asignado, ya se encuentra en condiciones para que en la apertura de la oficina comercial quede disponible
para que un RECO pueda utilizarlo.
Cuando el RECO inicie sesión, se le asignará el puesto de trabajo en el que se encuentre, y se actualizará el estado a Libre.
El proceso de asignación del próximo cliente a ser atendido depende de quien tenga mayor tiempo de espera en el local por tipo
o subtipo de trámite, según corresponda. El sistema asignará los tickets a los puestos de trabajo asignados a cada cola a medida
que éstos se desocupen, cuando los mismos se encuentren en estado Libre, siempre priorizando el cliente que hace más tiempo
que espera. Para esto, cuando el RECO seleccione la opción de llamar al próximo cliente, el sistema buscará las colas de atención
asignadas al puesto en el que se encuentre logueado el RECO y buscará el ticket de atención que registre el mayor tiempo de
espera en esa cola, el cual será llamado para ser atendido.
En las salas de espera se instalarán Televisores donde se informará el número a ser atendido y el puesto al cual el cliente debe
dirigirse. Cuando el cliente sea llamado, el estado del puesto que lo atiende pasará a Ocupado. Si el cliente ingresó su
identificación, el RECO podrá visualizar, además del número, los datos del cliente. Una vez que el cliente se presente ante el RECO,
comienza a correr su tiempo de atención, y el puesto para a estar Atendiendo. Al finalizar la atención, cambiará el estado a Libre.
Cuando el Representante Comercial se retira para almorzar, desayunar o necesita ir al baño deberá dejar su puesto en estado
Fuera de línea. Si el RECO regresa dentro de los próximos 40 minutos, el puesto quedará libre para seguir atendiendo. Si permanece
más de 40 minutos Fuera de Línea, el puesto estará disponible para que sea utilizado por otro RECO.
Si por alguna razón el RECO necesita resolver trámites pendientes de los clientes (sin que se encuentren presentes), el puesto se
encontrará en Back Office, mientras no haya un cliente en atención y el RECO no se encuentre fuera de línea. Una vez finalizadas
estas tareas, el puesto estará nuevamente libre para seguir atendiendo o, si el RECO cierra la sesión, quedará disponible para que
otro continúe ateniendo.
Mientras se estén atendiendo clientes o haya algún RECO con sesión abierta en los puestos de trabajo no se podrá cerrar la oficina.
Cuando la oficina está cerrada, los puestos de trabajo quedan asignados a cola, por lo tanto, no podrán ser utilizados hasta tanto
no se realice la apertura de la oficina, momento a partir del cual volverán a estar disponible.
Los puestos de trabajo podrán ser utilizados hasta que se den de baja, situación que puede producirse por diversas causas, siempre
y cuando la oficina esté cerrada.

Hoja: 2 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Descripción CU
Nombre del Caso de uso: Registrar inicio de atención de cliente Nro. de Orden: 22
Prioridad: Alta Media Baja
Complejidad: Simple Mediano Complejo Muy Complejo Extremadamente Complejo
Actor Principal: RECO (Representante Comercial) Actor Secundario: No Aplica.
Tipo de Caso de uso: Concreto Abstracto
Objetivo: Registrar la llamada y el inicio de la atención de un cliente que se encuentra en cola de espera con el ticket
correspondiente.
Flujo Básico
1. RECO: Selecciona la opción “Llamar próximo cliente”.
2. Sistema: Busca las colas de atención asociadas actualmente (Observación 2) al puesto de trabajo en el que se encuentra
el usuario que inició sesión, y encuentra al menos una.
3. Sistema: Para las colas de atención asociadas al puesto de trabajo, busca el ticket pendiente de atención que registre el
mayor tiempo de espera. (Observación 3)
4. Sistema: muestra los datos del ticket de atención: número de ticket, nombre del cliente, fecha y hora de emisión, tipo y
subtipo de trámite.
5. Sistema: actualiza el estado del puesto de trabajo a Ocupado.
6. Sistema: Muestra en los TV de la Sala el número del ticket y el identificador del puesto en el que será atendido, e
informa por el parlante de la sala los mismos datos.
7. Sistema: solicita indicar el inicio de la atención al cliente.
8. RECO: confirma el inicio de la atención.
9. Sistema: realiza la asignación del ticket al puesto de trabajo y actualiza la fecha y hora de inicio de atención del ticket, y
actualiza el estado del puesto de trabajo a Atendiendo. Fin del caso de uso.
Flujos Alternativos
A1: No hay colas de atención asociadas al puesto de trabajo asignado al RECO.
A2: No hay tickets en espera en las colas de atención asociadas al puesto de trabajo asignado al RECO.
A3: Si el cliente no se presenta para ser atendido, se cancela la atención marcando el ticket correspondiente.
Observaciones:
1. El RECO puede cancelar el caso de uso en cualquier momento
2. Un puesto de trabajo puede estar asociado a una o más colas vigentes durante un período de tiempo.
3. Un ticket está pendiente de atención cuando no posee fecha y hora de inicio de atención. El mayor tiempo de espera se
determinará por aquel que tenga menor fecha y hora de emisión.

Hoja: 3 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

a) Realización de Caso de Uso de Análisis Vista Dinámica con Diagrama de Comunicación- Escenario del Flujo Básico del CU Registrar
Inicio de Atención del Cliente
sd RegistrarAtenciónACliente
44: finalizarCU()
38: obtenerFechaHoraActual()
37: asignarTicketAPuesto()
31: llamarACliente()
5: getPuestoDeTrabajo() :
27: ocuparPuesto() SesionUsuario
15: obtenerDatosTicket()
7: buscarTicketConMayorEspera()
2: habilitarVentana() 4: buscarPuestoDelUsuarioLogueado()
28: *esOcupado()
35: seleccionarIniciarAtencion() 39: *esAtendiendo()
36: tomarInicioAtencion()
1: seleccionarOpcionProximoCliente() 3: nuevaAtencion() :Estado
: 26: visualizarDatosTicket()
Representante : 34: solicitarInicioAtencion() :
Comercial PantallaRegistrarAtencion GestorRegistrarAtencion
6: getIdentificacion()
32: *publicar() 8: buscarTicketConMayorEspera()
43: setFechaHoraInicio() 16: obtenerDatosTicket()
33: anunciar() 29: ocupar()
22: conocerTipoYSubtipo()
20: conocerCliente() 40: asignarTicketAAtender()
:InterfazTV 41: setEstado()
30: setEstado()
9: buscarAsignacionColaActual()
:InterfazParlante
11: *buscarTicketConMayorEspera()
42: iniciarAtencion() 10: *estaVigente()
21: getNombreCompleto()
elegido: :
:Cliente actual:
TicketAtencion AsignacionColaAtencion
PuestoTrabajo
12: buscarTicketPendConMayorEspera()
23: getNombre() 19: getDatosTicket()
24: conocerTipo()

17: obtenerDatosTicket()
25: getNombre() :ColaAtencion

:
:TipoTramite 13: *estaPendiente()
SubtipoTramite
14: *calcularTiempoEspera()

18: obtenerDatosTicket()
elegida:
asigDelTicket:
ColaAtencion :TicketAtencion
AsignacionColaAtencion

Hoja: 4 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Realización de Casos de Uso de Análisis – Vista de Estructura del CU Registrar Inicio de Atención del
Cliente
class RegistrarAtencionACliente
«boundary» «control»
PantallaRegistrarAtencion GestorRegistrarAtencion
btnFinalizar actual
gestor elegido
lblUsuario estado
txtCliente fechaHoraInicioAtencion
txtNroCliente pantalla
txtTipoTramite parlante
«entity»
habilitarVentana() sesion
Estado
mostrarUsuario() tv
ambito
seleccionarIniciarAtencion() actualizarEstadoPuesto()
seleccionarOpcionProximoCliente() actualizarInicioAtencion() nombre
solicitarInicioAtencion() anunciarEnParlante() esAtendiendo()
visualizarDatosTicket() asignarTicketAPuesto() esOcupado()
buscarColasDelPuesto()
buscarEstadoTicket()
«boundary» buscarPuestoDelUsuarioLogueado() «entity»
buscarTicketConMayorEspera()
1
InterfazTV PuestoTrabajo
buscarUsuario() estado
grillaNroTicketPuestoAsignado finalizarAtencionTicket() descripcion
lblfechaHoraActual finalizarCU() identificador
publicar() iniciarAtencion() abrirOficina()
llamarACliente() asignarCola()
nuevaAtencion() asignarTicketAAtender()
obtenerDatosTicket() buscarAsignacionColaActual()
obtenerFechaHoraActual() buscarColasConTicketsSinAtender()
ocuparPuesto() buscarTicketConMayorEspera()
«boundary» publicarEnTV() cerrarOficina()
InterfazParlante tomarFinAtencion() cerrarSesion()
tomarInicioAtencion() «entity» controlarCaducidad()
proximoLlamado verificarColasConTicketsPendientes() SesionUsuario 0..1 darDeBaja()
anunciar() finalizarBackOffice()
fechaHoraInicio finalizarFueraDeLínea()
conocerNombreUsuario() getIdentificacion()
getPuestoDeTrabajo() iniciarBackOffice()
getUsuario() iniciarFueraDeLínea()
liberar()
«entity» new()
TicketAtencion obtenerDatosTicket()
fechaHoraFinAtencion ocupar()
fechaHoraInicioAtencion ponerLibre()
fehaHoraIngreso ticketAtención setEstado()
0..*
numero
calcularTiempoEspera() ticketAtención
conocerCliente()
conocerTipoTramite() 0..*
conocerTipoYSubtipo() «entity»
estaPendiente() subTipoTrámite SubtipoTramite
finalizarAtencion() asignaciónCola
getDatosTicket() letra
0..1
iniciarAtencion() [m.e.] nombre
setEstado() conocerTipo()
setFechaHoraInicio() tipoTrámite getNombre()
0..*
0..* «entity»
colaAtención
cliente AsignacionColaAtencion
0..1 fechaDesde
0..1 0..1 «entity» fechaHasta
«entity» ColaAtencion buscarTicketConMayorEspera()
«entity» colaAtención
Cliente TipoTramite numero conocerClienteConMayorEspera()
letra colaAtención estaVigente()
apellido buscarTicketPendConMayorEspera() 1 getCola()
nombre
nombre 0..1 conocerClienteConMayorEspera() obtenerDatosTicket()
numero getNombre() getNumero() tieneCliente()
obtenerDatosTicket()
getNombreCompleto()
getNumero()

Hoja: 5 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Máquina de Estado de PuestoTrabajo

stm PuestoTrabajo
9. Registrar puesto de 12. Eliminar puesto de
No Disponible DeBaja
trabajo /new() trabajo
/darDeBaja()

17. Asignar colas a


puesto de trabajo 12. Eliminar puesto de
/asignarCola() trabajo
/darDeBaja()

AsignadoACola

20. Registrar
apertura de oficina
/abrirOficina()
25. Registrar cierre de
oficina
/cerrarOficina() Disponible
19. Cerrar sesión
/cerrarSesion()

24. Controlar caducidad de estado Fuera EnBackOffice


de Línea [tiempo > 40min] 18. Iniciar sesión
/controlarCaducidad() 19. Cerrar sesión /ponerLibre()
/cerrarSesion()
30. Registrar fin de
tareas en BackOffice
FueraDeLinea 26. Registrar fin de fuera de línea /finalizarBackOffice()
/finalizarFueraDeLínea() 29. Registrar inicio de
tareas en BackOffice
Libre /iniciarBackOffice()
23. Registrar fuera de línea
/iniciarFueraDeLínea()
24. Controlar caducidad de estado
Fuera de Línea [tiempo <= 40min]
/controlarCaducidad() 28. Registrar fin de atención
de cliente /liberar()
22. Registrar inicio de atención de
cliente /ocupar()

Ocupado 22. Registrar inicio de Atendiendo


atención
/asignarTicketAAtender()

Hoja: 6 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Solución Propuesta
Patrones de Diseño
State Estructura

Hoja: 7 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Dinámica

Pseudocódigo
• Al momento de asignar el ticket elegido (aquel que de las colas asignadas al puesto que tiene mayor tiempo de espera),
el GestorRegistrarAtención obtiene la fecha y hora actual, y se la envía por parámetro al PuestoDeTrabajo.
• El PuestoDeTrabajo que antes enviaba la fecha y hora actual al TicketAtención, ahora llama mediante un mensaje
asignarTicketAAtender a su estado, para delegarle el comportamiento.
• El método polimórfico asignarTicketAtender, del estado (en este caso del estado Ocupado) tiene definido el
comportamiento correspondiente: informar al TicketAtención la fecha y hora actual. Para esto recibe por parámetro el
puntero correspondiente, y envía el mensaje IniciarAtención, con la fecha y hora actual, también por parámetro.
• El TicketAtención setea su fecha y hora actual.
• Luego el estado Ocupado continúa la ejecución del método asignarTicketAAtender, creando el siguiente estado, en este
caso, el estado Atendido.
• Al haber recibido por parámetro el puntero al PuestoDeTrabajo actual, el estado Ocupado puede setear su estado,
haciendo que el PuestoDeTrabajo deje de apuntar al estado Ocupado, y pase a apuntar al estado Atendiendo.
• La búsqueda del estado Atendido no se debe realizar más, ya que esto se resuelve a partir de la utilización del
polimorfismo, asegurando que no importa el estado en el que esté el PuestoDeTrabajo, se comportará de la manera
adecuada.
• Se modifica el orden de los métodos realizando al final el seteo del estado, ya que el comportamiento delegado debe
ejecutarse antes del cambio de estado.

Hoja: 8 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Observer – Sujeto Gestor


Estructura

class Observer
«interface»
ISujetoLlamadoPróximo También está correcta la solución si el
- observador: IObservadorLlamadaPróximoCliente[] estudiante mantiene los métodos de
análisis publicar() y anunciar(), siempre y
+ notificar()
+ quitar(observadores: IObservadorLlamadaPróximoCliente[]) cuando no estén declarados como
+ suscribir(observadores: IObservadorLlamadaPróximoCliente[]) polimórficos en la interfaz y deberían ser
métodos privados.

«interface»
«control» IObservadorLlamadaPróximoCliente
GestorRegistrarAtencion
+ actualizar(identificadorPuesto: String, nroTicket: String)
- actual : PuestoTrabajo + llamarPróximoCliente()
- elegido: TicketAtencion
- parlante: InterfazParlante
- tv: InterfazTV
+ llamarACliente(): void
+ notificar()
+ obtenerObservadores(): void
+ quitar(observadores: IObservadorLlamadaPróximoCliente[])
+ suscribir(observadores: IObservadorLlamadaPróximoCliente[])

«boundary»
InterfazTV

«boundary» - grillaNroTicketPuestoAsignado
InterfazParlante - lblfechaHoraActual

- proximoLlamado + actualizar(identificadorPuesto: String, nroTicket: String)


+ llamarPróximoCliente(): void
+ actualizar(identificadorPuesto: String, nroTicket: String) + new(): InterfazTV
+ llamarPróximoCliente(): void
+ new(): InterfazParlante

Hoja: 9 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Dinámica
sd Observer

GestorRegistrarAtencion
llamarACliente()
obtenerObservadores()
new():
InterfazTV
InterfazTV
new():
InterfazParlante
InterfazParlante
suscribir(observadores:
IObservadorLlamadaPróximoCliente[])
notificar()

loop interfazTV
[mientras existan tv en la sala]
actualizar(identificadorPuesto: String,
nroTicket: String) llamarPróximoCliente()

actualizar(identificadorPuesto:
String, nroTicket: String)
llamarPróximoCliente
()

Pseudocódigo
• El patrón se aplica a partir del método 31 del análisis- llamarCliente() cuando se seleccionó el cliente que será atendido y
se asignó el estado “ocupado” al puesto.
• El GestorRegistrarAtención es el sujeto concreto que puede suscribir y quitar observadores.
• Al momento de llamar al cliente, el gestor crea las interfaces con la TV y el parlante, y los suscribe.
• Cada interfaz es un observador concreto que se suscribe para actualizarse con cada notificación.
• Cuando el Gestor notifica, el método actualizar() le pasa por parámetro el número de ticket que se está llamando con el
identificador del puesto de trabajo.
• Pueden existir varios TV en una sala y un parlante, por lo que se realiza un loop mientras existan TVs en la sala.
• Cada interfaz resuelve cómo mostrar estos datos recibidos.
• La solución es de tipo “push”.

Hoja: 10 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Observer – Sujeto – Puesto de trabajo


Estructura
class Observer - Puesto Sujeto
«interface»
ISujetoLlamadoPróximo
- observador: IObservadorLlamadaPróximoCliente[] «interface»
+ notificar() IObservadorLlamadaPróximoCliente
+ quitar(observadores: IObservadorLlamadaPróximoCliente[]) + actualizar(identificadorPuesto: String, nroTicket: String)
+ suscribir(observadores: IObservadorLlamadaPróximoCliente[]) + llamarPróximoCliente()

«entity»
PuestoTrabajo
- identificador
- ticketAtencion: TicketAtencion
+ asignarTicketAAtender(fechaActual: Date, ticket: TicketAtencion): void
+ notificar()
+ quitar(observadores: IObservadorLlamadaPróximoCliente[])
+ suscribir(observadores: IObservadorLlamadaPróximoCliente[])

«boundary»
InterfazParlante
- proximoLlamado
+ actualizar(identificadorPuesto: String, nroTicket: String)
+ llamarPróximoCliente(): void
«control» + new(): InterfazParlante
GestorRegistrarAtencion «boundary»
InterfazTV
- actual : PuestoTrabajo
+ obtenerObservadores(): void - grillaNroTicketPuestoAsignado
- lblfechaHoraActual
+ ocuparPuesto(): void
+ actualizar(identificadorPuesto: String, nroTicket: String)
+ llamarPróximoCliente(): void
+ new(): InterfazTV

Hoja: 11 de 12
Universidad Tecnológica Nacional – FRC/FRVM
Cátedra de Diseño de Sistemas

Dinámica
sd Observer - Puesto Sujeto

GestorRegistrarAtencion PuestoTrabajo
ocuparPuesto
()
obtenerObservadores()
new(): InterfazParlante
new(): InterfazTV InterfazParlante
suscribir(observadores:
IObservadorLlamadaPróximoCliente[]) InterfazTV

...
Búsqueda del estado. Tener en cuenta que si lo resuelven junto con el
patrón State no realizararán la búsqueda y la solución es correcta
también.
ocupar(estado: Estado,
nroTicket: int) setEstado(object: Estado)
notificar()

actualizar(identificadorPuesto: String, nroTicket:


String)
llamarPróximoCliente()

loop interfaz TV
[mientras existan TV en la sala ]
actualizar(identificadorPuesto:
String, nroTicket: String)
llamarPróximoCliente()

Pseudocódigo
• Una vez que se obtiene el ticket con mayor espera de las colas que tiene asignado el puesto de trabajo actual, se obtienen
y suscriben los observadores al sujeto concreto [actual:PuestoTrabajo].
• La operación suscribir(observador:IObservador[1..*]):Void de la clase PuestoTrabajo implementa la operación definida
en la interfaz ISujeto. A través de dicha operación se asignan los observadores a notificar; de esta manera se actualiza el
atributo definido en la interfaz ISujeto, lo cual permite conocer los observadores a notificar ante el cambio en el estado
de la clase PuestoTrabajo.
• El objeto de control envía al sujeto concreto el mensaje ocupar() con el número del ticket que debe ser anunciado para
su atención. Una vez que el PuestoTrabajo actualiza su estado, notifica a los observadores concretos correspondientes
para lo que primero debe obtener el identificador del puesto, invocando al método publicar(nroTicket:String,
identificadorPuesto:String):void, el cual implementa la definición realizada en la interfaz IObservador.
• Pueden existir varios TV en una sala y un parlante, por lo que se realiza un loop mientras existan TVs en la sala.

Hoja: 12 de 12

También podría gustarte