Está en la página 1de 203

1

Desarrollo de un programa para ventas de galletas Guille

Carlos Enrique Guillent Carruyo

Martin Camilo Chaverra Toledo

Sebastián Hernández Bustamante

Facultad de ingeniería, Tecnológico de Antioquia

Proyecto de Aula: G002

Jhon Hernandez alias el profe

7 de octubre de 2023

Analisis del proyecto ............................................................................................................................ 4


2

Descripción del problema ................................................................................................................ 4


Planteamiento del problema ........................................................................................................... 4
Funcionalidades requeridas para usuarios tipo administrador ....................................................... 5
Formulación problema ..................................................................................................................... 5
Objetivo General .............................................................................................................................. 5
Objetivos Específicos: ....................................................................................................................... 5
Análisis de requisitos ........................................................................................................................ 6
¿Qué necesita el cliente? ................................................................................................................. 6
Requerimientos ................................................................................................................................ 6
Requisitos ............................................................................................................................................. 7
Requisitos funcionales: .................................................................................................................... 9
Requisitos no funcionales .............................................................................................................. 24
Analisis de Riesgos: ............................................................................................................................ 37
Casos de Uso HU ................................................................................................................................ 41
Vistas (mockups) ................................................................................................................................ 46
Plan de Pruebas de software ............................................................................................................. 67
Documento Soporte y Mantenimiento .............................................................................................. 78
Documentación Código ...................................................................................................................... 83
Página de inicio: ............................................................................................................................. 83
Register........................................................................................................................................... 86
Login ............................................................................................................................................... 90
Pagina Principal Cliente .................................................................................................................. 95
Producto compra .......................................................................................................................... 100
Carrito de compras ....................................................................................................................... 105
Generar pedido ............................................................................................................................ 110
Pagar pedido ................................................................................................................................ 114
Error de pago................................................................................................................................ 117
Ver pedido .................................................................................................................................... 120
Modificar pedido .......................................................................................................................... 125
Lista de productos ........................................................................................................................ 130
Vista menu empleado .................................................................................................................. 134
Empleado Gestion Producto ........................................................................................................ 138
Gestion Puntos de venta .............................................................................................................. 142
3

Gestion Catalogo .......................................................................................................................... 147


Gestion de pedidos ...................................................................................................................... 152
Crear Producto ............................................................................................................................. 156
Crear catalogo .............................................................................................................................. 160
Crear Punto de venta ................................................................................................................... 164
Categorizar Producto.................................................................................................................... 169
Modificar Punto de Venta ............................................................................................................ 173
Eliminar punto de venta ............................................................................................................... 179
Modificar Producto ...................................................................................................................... 183
Eliminar Producto ......................................................................................................................... 189
Modificar Catalogo ....................................................................................................................... 192
Eliminar Catalogo ......................................................................................................................... 198
Presupuestos ................................................................................................................................ 202
4

Análisis del proyecto


Descripción del problema
Debido a la pandemia del COVID-19, muchas empresas que vivían de la

clientela en puntos de ventas presenciales quedaron en quiebra. Posterior a la

pandemia, los emprendimientos como Galletas Guille surgieron como una

alternativa de venta que se dio a conocer por medio de las redes sociales.

Galletas Guille fue fundada en el año 2020 por Carlos Guillent, y surgió

como una necesidad de tener ingresos adicionales mientras la pandemia y los

nuevos casos de infección estaban en su punto más alto. Gracias al buen manejo

de redes sociales y publicidad por parte de su fundador, así como

recomendaciones de amigos, familia y conocidos, Carlos ahora hace pedidos para

más de 50 clientes al día, sin contar los pedidos que realizan para eventos, en los

que la demanda del producto es mayor.

Planteamiento del problema


Debido a al crecimiento de la empresa, no se cuenta con las capacidades

para atender todos los nuevos clientes por medio de Instagram y WhatsApp, por lo

que Galletas Guille requiere de una nueva estrategia y herramientas para suplir el

aumento de la demanda. Para expandir la empresa e incursionar en el mercado

digital para automatizar la compra y venta de pedidos, realizar envíos locales y

nacionales, Galletas Guille requiere una página web para que sus clientes usen

las siguientes funcionalidades.

• Realizar compras.

• Agendar pedidos.
5

• Permitir cancelar pedidos.

• Permitir realizar pagos por plataformas en línea y bancos.

• Permitir reagendar pedidos.

• Permitir enlace a las redes sociales de la empresa.

• Permitir ver puntos de venta físicos.

Funcionalidades requeridas para usuarios tipo administrador

• Permitir agregar y guardar productos en el catálogo.

• Eliminar productos descontinuados del catálogo.

• Permitir modificar precios y características de los productos.

• Ver información de los productos desde la perspectiva del cliente.

Formulación problema
¿Puede una aplicación web permitir realizar la correcta gestión de

productos, pedidos, usuarios, facturación y sucursales físicas, con una experiencia

de usuario de alta calidad para la empresa Galletas Guille?

Objetivo General
Implementar de un aplicativo web que permita realizar la correcta gestión

de productos, pedidos, usuarios, facturación y sucursales físicas, con una

experiencia de usuario de alta calidad satisfaciendo las necesidades del vendedor,

proveedor y maximizando ganancias y tiempo de producción para la empresa

Galletas Guille.

Objetivos Específicos:
6

1. Realizar levantamiento de requisitos para la implementación del aplicativo

web.

2. Diseñar los diagramas requeridos para la implementación del aplicativo

web.

3. Desarrollar del aplicativo web que permita realizar la correcta gestión de

productos, pedidos, usuarios, facturación y sucursales físicas.

4. Probar en el sistema.

Análisis de requisitos
1. Identificar necesidades

2. Definir requerimientos

3. Rastrear los requisitos.

¿Qué necesita el cliente?

• El cliente necesita manejar un catálogo de productos que ofrece la empresa

• El cliente necesita una página web en la que pueda realizar la venta de sus

productos

• El cliente necesita que los usuarios puedan agregar productos a un carrito

de compra

• El cliente necesita que los usuarios puedan cancelar todos sus pedidos al

finalizar sus compras

• El cliente necesita ver los pedidos realizados

• El cliente necesita ver los pagos realizados

Requerimientos
7

• Gestión pago

• Gestión de Usuarios

• Gestión de catálogos

• Gestión de puntos físicos

Requisitos
Funcionales:

1. Registrar Usuario

2. Log in

3. Olvidaste Contraseña

4. Crear productos

5. Crear catalogo

6. Añadir productos al catalogo

7. Eliminar productos

8. Modificar productos

9. Ver catálogo(s)

10. Eliminar productos del catalogo

11. Modificar catálogo

12. Eliminar catálogo

13. Guardar producto en carrito de compras

14. Eliminar producto en carrito de compras

15. Generar pedido

16. Modificar pedido

17. Ver pedido


8

18. Cancelar pedido

19. Pagar pedido

20. Visualizar errores de pago

21. Generar nuevo pago

22. Crear puntos físicos en un mapa

23. Ver puntos físicos en un mapa

24. Modificar puntos físicos en un mapa

25. Eliminar puntos físicos en un mapa

26. Ver enlaces de las redes sociales

27.

No funcionales

1. Configuración Base de datos

2. Configurar API Google Maps

3. Configurar API Aplicación Front

4. Configurar API Aplicación BackEnd

5. Configurar BD velocidad (escalabilidad y rendimiento)

6. Configurar Firewall

6.1 Configurar Firewall capacidad de procesamiento

6.2 Configurar Firewall Informes

7. Crear protección para las SQL inyección

8. Validar productos antes de guardarlos en la BD

9. Validar catálogos antes de guardarlos en la BD


9

10. Validar pedidos antes de guardarlos en la BD

11. Validar y gestionar errores de pago

12. Validar información para crear punto físico

13. Tiempo límite de pagos

14. Logs de errores dentro de la BD informes

15. Disponibilidad de la aplicación

16. Aplicación Multiplataforma

17. Interfaz Amígale

18. Flujo de usuarios

19. Generar referencia de pedido.

20. API Correo

21. Enviar contraseña al correo

Requisitos funcionales:
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Requisitos no funcionales
25
26
27
28
29
30
31
32
33
34
35
36
37

Análisis de Riesgos:
https://tecnologicodeantioquia-

my.sharepoint.com/:x:/r/personal/carlos_guillent_correo_tdea_edu_co/_layouts/15/doc2.

aspx?sourcedoc=%7BCDDDB777-BBA4-45E2-92C5-

2312A72C2240%7D&file=Libro.xlsx&action=default&mobileredirect=true&DefaultItemOp

en=1&ct=1699041474198&wdOrigin=OFFICECOM-

WEB.START.EDGEWORTH&cid=d7459f64-673f-43a6-bab9-

183dd4082648&wdPreviousSessionSrc=HarmonyWeb&wdPreviousSession=674a5e36-

4995-4430-84e6-2bf7f81585be
38
39
40
41

Casos de Uso HU
Diagrama de clase (módulos combinados).

Descripción: Permite a los clientes hacer compras en un catálogo, agregar

productos, generar un pedido y pagar o cancelarla.

Permite a los usuarios administrativos, crear nuevos productos, guardarlos en

catálogos, actualizarlos y publicarlos; y validar pagos.


42
43

Diagrama de Gestión de usuarios.

Descripcion: Permite al usuario empleado loguearse Y/O generar nuevas

credenciales para un nuevo usuario empleado.

Diagrama de Gestión de pagos.

Descripcion: Permite al usuario cliente generar un pedido con una serie de

productos ya generados con anterioridad, generar el pago y realizarlo.

Permite al usuario administrativo validar el pago.


44

Diagrama de gestión de productos/catálogos.

Descripcion: Permite al usuario cliente ver un catálogo de productos, agregar

productos de ese catalogo generar un pedido y pagarlo.

Permite al usuario administrativo crear productos, agregarlos al catálogo y

publicarlos.
45

Diagrama de gestión de puntos de venta.

Administrador

Permite al usuario administrador gestionar los puntos físicos de venta en los que

se pueden comprar, retirar pedidos realizados de manera online.

El administrador puede crear un nuevo punto físico de ventas, eliminar uno de

venta antes creado, actualizar/editar uno de venta, lo que al generarse,

actualizarse y eliminarse se reflejará en la base de datos de todos los registros de

dicho aplicativo.

Cliente

El usuario cliente puede ver en el mapa al consumir la API (Backend/Frontend) los

puntos físico más cercanos a su ubicación actual como también acceder a todos

los puntos físicos que desee.


46

Vistas (mockups)
Vista principal para administradores y empleados. En esta primera vista

encontramos el logo de la tienda y un botón de inicio de sesión. Al presionar este

botón seremos dirigidos a la vista de inicio de sesión.


47

Vista de inicio de sesión. En esta vista encontraremos los campos de texto para

ingresar el correo y la contraseña asignada para la cuenta de administrador /

empleado. Si los datos son correctos, al pulsar el botón de ‘Iniciar Sesión’ seremos

dirigidos a vista principal de productos.


48

Vista principal de productos. En esta vista encontraremos una barra superior con 4
iconos.
-Icono de barra desplegable, Icono para búsqueda de productos, Icono de
notificaciones e Icono de carrito de compras.
También podemos ver un módulo que podemos deslizar hacia la izquierda para
ver los productos más destacados con su nombre y respectivo precio. Al tocar
cualquiera de las imágenes de estos productos seremos dirigidos al detalle del
producto seleccionado. En la parte inferior encontramos un texto ‘VER TODOS
LOS PRODUCTOS’ que, al ser tocado, nos dirigirá a la lista de productos.
49

Vista de lista de productos. En esta vista encontramos una barra superior con un

botón para regresar a la vista principal, un botón para buscar y un botón de carrito

de compras. También podemos ver los productos ordenados en cuadrículas con

su foto, nombre y precio respectivos. Al tocar la imagen de cualquiera de los

productos, seremos dirigidos al detalle del producto al que corresponda la imagen.


50

Vista de detalle de producto. En esta vista podemos encontrar la misma barra de

la vista anterior. Pero con el nombre detallado del producto, la foto y precio

correspondiente. En la parte inferior del detalle podemos encontrar dos botones.

Uno de comprar y otro de agregar al carrito.


51
52

Vista de “empleados”. En esta vista se accede luego de iniciar sesión y podemos

encontrar 4 botones con el siguiente orden “productos”, “puntos de venta”,

“pedidos”, “catálogos” y una flecha que retorna a la página anterior


53

Vista de “botón productos” En esta vista podemos encontrar 3 botones con el

siguiente orden “crear producto, “actualizar producto”, “Eliminar producto” y una

flecha que retorna a la página anterior


54

Vista de crear producto. En esta vista podemos encontrar el mismo logo con un

label “Crear producto” seguido de 3 Textfield en los que se coloca de manera

ordenada el nombre, precio y la foto del producto, seguido de el botón que registra

los datos en la BDD.


55

Vista de “Modificar producto”. En esta vista podemos encontrar el mismo logo con

un label “Modificar producto” seguido de 4 Textfield en los que se coloca de

manera ordenada el nombre del producto a seleccionar, nuevo nombre del

producto a cambiar, imagen nueva (de ser necesario) y el precio, seguido de el

botón que registra los datos en la BDD.


56

Vista de "Eliminar producto”. En esta vista podemos encontrar el mismo logo con

un label “Eliminar producto” seguido de 1 Textfield en los que se coloca el nombre

del producto a eliminar, luego el botón que “Eliminar producto” que elimina

producto de la BDD.
57

Vista de “botón Catálogos” En esta vista podemos encontrar 3 botones con el

siguiente orden “crear catálogo, “actualizar catalogo”, “Eliminar catalogo” y una

flecha que retorna a la página anterior.


58

Vista de crear catálogo. En esta vista podemos encontrar el mismo logo con un

label “Crear catalogo” seguido de 2 Textfield en los que se coloca de manera

ordenada el nombre, descripcion del catálogo, seguido de el botón que “crear

catalogo” que ingresa los datos en la BDD.


59

Vista de “Modificar catálogo. En esta vista podemos encontrar el mismo logo con

un label “Modificar catalogo” seguido de 2 Textfield en los que se selecciona el

nombre del catálogo, nuevo nombre del catálogo a cambiar, seguido de el botón

“Actualizar catalogo” que registra los datos en la BDD.


60

Vista de "Eliminar catalogo”. En esta vista podemos encontrar el mismo logo con

un label “Eliminar catalogo” seguido de 1 Textfield en los que se coloca el nombre

del catálogo a eliminar, luego el botón que “Eliminar catalogo” que elimina el

catálogo de la BDD.
61

Vista de “botón Puntos de venta” En esta vista podemos encontrar 3 botones con

el siguiente orden “crear Puntos de venta, “actualizar Puntos de venta”, “Eliminar

Puntos de venta” y una flecha que retorna a la página anterior.


62

Vista de crear punto de venta. En esta vista podemos encontrar el mismo logo con

un label “Crear punto de venta” seguido de 2 Textfield en los que se coloca de

manera ordenada el nombre, coordenadas(opcional), seguido del API de google

maps y luego el botón que “crear punto de venta” que ingresa los datos en la BDD.

Vista de “crear punto de venta”. En esta vista podemos encontrar el mismo logo

con un label “modificar punto de venta” seguido de 3 Textfield en los que se coloca
63

de manera ordenada el nombre del punto de venta a cambiar,

coordenadas(opcional), seguido del API de google maps, nombre del nuevo punto

luego el botón que “Actualizar punto de venta” que ingresa los datos en la BDD.
64

Vista de "Eliminar punto de venta”. En esta vista podemos encontrar el mismo logo

con un label “Eliminar punto de venta” seguido de 1 Textfield en los que se coloca

el nombre del punto de venta a eliminar, luego el botón que “Eliminar punto de

venta” que elimina dicho punto de venta de la BDD.


65

Vista de categorizar producto. En esta vista podemos encontrar el mismo logo con

un label “Categorizar producto” seguido de 1 drop Down donde se elige el catálogo

y un Checklist en los que se selecciona el catálogo a elegir, “crear punto de venta”

luego el botón “guardar en catalogo” que actualiza los datos/productos ingresados

al catalogo en la BDD.
66
67

Plan de Pruebas de software


Proyecto Galletas Guillent
Líder Técnico Martín Camilo Chaverra Toledo

Analista Desarrollo Carlos Enrique Guillent Carruyo


Analista Calidad Sebastián Hernández Bustamante
Cliente Alejandro Torres Jaraba
Fecha 11/07/2023

Introducción

La realización de pruebas es una parte esencial del proceso de desarrollo de


software, ya que permite identificar y corregir errores antes de que el proceso final
se entregue a los usuarios. En este trabajo, se presentarán los resultados de las
pruebas realizadas en el software Galletas Guillent, con el objetivo de evaluar su
calidad y funcionalidad. Se analizarán los resultados de las pruebas realizadas en
un periodo de tiempo. Se describirá el enfoque utilizado para las pruebas,
incluyendo los casos de pruebas diseñados y los métodos utilizados. Además, se
discutirán los hallazgos más significativos y las recomendaciones para la calidad del
software.

Propósito del documento

El plan de pruebas que se tendrá con la aplicación de Galletas Guillent, es


establecer un marco de trabajo para la realización de las actividades, incluyendo la
definición de los casos de prueba y criterios de aceptación, la ejecución de las
pruebas y el informe de los resultados. El plan de pruebas está diseñado para
asegurar que se cumplan todos los objetivos de calidad y se alcancen los resultados
deseados

Justificación

Hoy el uso y la cantidad de sistemas de software existentes se involucran


cada vez más en las actividades cotidianas del ser humano. Sin embargo, estos
sistemas son responsables de varios problemas y desastres en los ámbitos donde
son ocupados, ejemplo de ello son la perdida de información, filtración de datos
personales, robo de identidades, entre otros que generan una desconfianza en el
software. Las pruebas nos ayudan a detectar errores para evitar todo tipo de
desastres que puedan ocasionar.

Objetivos
68

• Objetivo General.

Garantizar la calidad de la aplicación de Galletas Guillent a través de la


realización de pruebas exhaustivas y la identificación y corrección de cualquier
problema o defecto. tiene como finalidad asegurar que la aplicación de Galletas
Guillent cumpla con los estándares de calidad establecidos y proporcione una
experiencia satisfactoria para los usuarios. Esto puede mejorar la satisfacción del
usuario, la confiabilidad del sitio, la reducción de errores y problemas, y la
optimización del rendimiento general de la plataforma.

• Objetivos específicos.

1. Desarrollar un Plan Integral de Pruebas: Definir y documentar un plan


detallado para la ejecución de pruebas exhaustivas en todos los aspectos de
la aplicación.

2. Identificar y Documentar Requisitos de Calidad: Establecer criterios


específicos de calidad que la aplicación debe cumplir, documentando
claramente los requisitos de rendimiento, seguridad, usabilidad y
funcionalidad.

3. Diseñar Casos de Prueba Exhaustivos: Crear casos de prueba detallados


que cubran todos los escenarios posibles de uso de la aplicación,
asegurando una cobertura completa.

4. Implementar Pruebas Automatizadas: Desarrollar y ejecutar pruebas


automatizadas para garantizar una detección rápida y eficiente de posibles
problemas a medida que se realizan actualizaciones en la aplicación.

5. Establecer Métricas de Desempeño: Definir métricas específicas para


evaluar el rendimiento de la aplicación, como tiempos de carga, capacidad
de respuesta y escalabilidad.

6. Realizar Pruebas de Seguridad: Llevar a cabo pruebas de seguridad para


identificar y corregir posibles vulnerabilidades que puedan comprometer la
integridad y la privacidad de los usuarios.

7. Implementar un Proceso de Retroalimentación del Usuario: Establecer


un sistema para recopilar comentarios de los usuarios con respecto a la
calidad y la usabilidad de la aplicación, y utilizar esa retroalimentación para
realizar mejoras continuas.
69

8. Monitorear y Analizar Incidentes: Establecer un sistema de monitoreo para


detectar y analizar incidentes, y desarrollar planes de acción para abordar y
corregir cualquier problema identificado.

9. Proporcionar Formación Continua al Personal: Capacitar al personal


responsable de la aplicación en las mejores prácticas de calidad y pruebas,
asegurando un conocimiento actualizado y la adopción de estándares de
calidad.

10. Evaluar y Mejorar Procesos Continuamente: Establecer un ciclo de mejora


continua, analizando regularmente los procesos de prueba, identificando
áreas de mejora y aplicando ajustes para optimizar la eficiencia y la eficacia
del aseguramiento de la calidad.

Alcance

Dentro del Sprint número (1) se realizarán diferentes tipos de historias de


usuarios basadas en la realización de diversos tipos de pruebas (Funciones,
Interfaces Grafica, Documentación, smoke test, regresión, integración, exploración,
performance, seguridad) fundamentado en la aplicación Galletas Guillent.

Historias de Usuario:

• HU-01: Registro de Usuario


• HU-02: Inicio de Sesión
• HU-03: Visualización de Productos
• HU-04: Creación de Catálogo
• HU-05: Añadir Producto al Catálogo
• HU-06: Eliminar Producto
• HU-07: Modificar Producto
• HU-08: Ver Catálogo(s)
• HU-09: Eliminar Producto del Catálogo
• HU-10: Modificar Catálogo
• HU-11: Agregar Productos al Carrito de Compra
• HU-12: Eliminar Producto del Carrito de Compra
• HU-13: Generación de Pedidos
• HU-14: Modificar Pedido
• HU-15: Ver Pedido
• HU-16: Cancelar Pedido
• HU-17: Pagar Pedido
• HU-18: Visualizar Errores de Pago
• HU-19: Generar Nuevo Pago
70

• HU-20: Crear Puntos Físicos en un Mapa


• HU-21: Ver Punto Físico en Mapa
• HU-22: Modificar Punto Físico
• HU-23: Eliminar Punto Físico en un Mapa
• HU-24: Ver Enlaces de Redes Sociales
• HU-25: Navegación por Menú
• HU-26: Vista de Empleados

Supuestos

Se realizarán aspectos que no son objeto de prueba pero que son necesarios
para el desarrollo de la misma y su validación para ser entregado al cliente:
1.
2. Usar un celular bien optimizado, como Samsung A22, Redmi note 10, Huawei
para realizar las pruebas.
3. Infraestructura incluya un servidor y la red, este funcionando correctamente
y es capaz de soportar el tráfico esperado de usuarios y pruebas.
4. Base de datos consistente que pueda guardar los datos del usuario para el
plan de pruebas.
5. Los recursos externos utilizados en la aplicación, como imagines, videos o
archivos widgets, estén disponibles y accesibles para los usuarios y la
realización de las pruebas.
6. Abrir otras aplicaciones mientras se navega por la aplicación para realizar las
pruebas.
7. La aplicación este desarrollada bajo los acuerdos de los requisitos
especiados en la documentación para realización de pruebas.
8. La aplicación esté libre de cualquier malware o virus que pueda afectar su
comportamiento de las pruebas.
9. La conexión a internet sea estable y rápida para evitar errores y retrasos en
la carga de “páginas” para la realización de pruebas
10. La aplicación se ha desarrollada de acuerdo con los estándares y las buenas
prácticas de codificación para garantizar su estabilidad y seguridad.

Tipos de Pruebas
Teniendo en cuenta la base de la página y la estructura de esta se realizarán
los siguientes tipos de prueba, para la validación y verificación de error que se
encuentre en esta:

• Funcionales.
• Interfaz Gráfica.
• Smoke Test.
71

• Regresión.
• Carga.
• Exploratorias.
• Performance .
• Seguridad.
• Automatización.

6. Documentación Recibida Para la Ejecución de las Pruebas.

Documento Nombre Responsable Observaciones


Historias de Carlos Guillent Analista N/A
usuario Desarrollo

Manuales Carlos Guillent Analista N/A


Desarrollo

Cpl (Plan de Carlos Guillent Analista N/A


Control de Desarrollo
Cambios)
Prototipos Carlos Enrique Analista N/A
Guillent Desarrollo

Estándar diseño Carlos Guillent Analista N/A


Desarrollo

Modelo datos Carlos Guillent Analista N/A


Desarrollo

Diagramas casos Carlos Guillent Analista N/A


de uso Desarrollo

Estrategia de Pruebas

Escenario de Pruebas Método de Pruebas Encargado


HU-02: Inicio de Sesión Verificar la accesibilidad Carlos Enrique Guillent
del formulario de registro.
Ingresar datos válidos y
completar el registro.
72

Validar que el usuario


registrado pueda iniciar
sesión.
HU-02: Inicio de Sesión Carlos Enrique Guillent
Verificar que la página de
inicio de sesión esté
accesible.
Ingresar credenciales
válidas y confirmar el
inicio de sesión.
Probar el manejo
adecuado de
credenciales inválidas.
HU-03: Visualización de Confirmar que la lista de Carlos Enrique Guillent
Productos productos se carga
correctamente.
Verificar que se muestren
detalles precisos para
cada producto.
HU-04: Creación de Carlos Enrique Guillent
Catálogo Verificar la accesibilidad
del formulario de
creación de catálogo.
Completar el formulario y
confirmar que el catálogo
se crea correctamente.
HU-05: Añadir Producto Confirmar que la interfaz Carlos Enrique Guillent
al Catálogo para agregar productos a
un catálogo esté
disponible.
Agregar productos y
validar que se reflejen
correctamente en el
catálogo.
HU-06: Eliminar Producto Verificar que la opción de Carlos Enrique Guillent
eliminar esté disponible
para productos
existentes.
Eliminar un producto y
confirmar que
desaparezca
correctamente del
sistema.
HU-07: Modificar Confirmar que la opción Carlos Enrique Guillent
Producto de modificar esté
disponible.
73

Realizar cambios en un
producto y validar que
los cambios se reflejen
correctamente.
HU-08: Ver Catálogo(s) Verificar que la lista de Carlos Enrique Guillent
catálogos se muestre
correctamente.
Validar que se puedan
seleccionar y ver los
productos en cada
catálogo.
Carlos Enrique Guillent
HU-09: Eliminar Producto Confirmar que la opción
del Catálogo de eliminar esté
disponible para
productos en un
catálogo.
Eliminar un producto y
validar que se refleje
correctamente en el
catálogo.
Carlos Enrique Guillent
HU-10: Modificar Verificar que la opción de
Catálogo modificar esté disponible
para catálogos
existentes.
Realizar cambios en un
catálogo y validar que los
cambios se reflejen
correctamente.
Confirmar que la opción Carlos Enrique Guillent
HU-11: Agregar de agregar al carrito esté
Productos al Carrito de disponible para
Compra productos.
Agregar productos al
carrito y verificar que se
reflejen correctamente.
Confirmar que la opción Carlos Enrique Guillent
HU-12: Eliminar Producto de eliminar del carrito
del Carrito de Compra esté disponible.
Eliminar productos del
carrito y validar que se
actualice correctamente.
Carlos Enrique Guillent
HU-13: Generación de
Pedidos
74

Verificar que la opción de


generar pedido esté
disponible.
Generar un pedido y
confirmar que se registre
correctamente en el
sistema.
Confirmar que la opción Carlos Enrique Guillent
HU-14: Modificar Pedido de modificar pedido esté
disponible.
Realizar cambios en un
pedido y validar que se
actualice correctamente.
HU-15: Ver Pedido Verificar que la opción de Carlos Enrique Guillent
ver pedido esté
disponible.
Seleccionar un pedido y
confirmar que los
detalles se muestren
correctamente.
HU-16: Cancelar Pedido Confirmar que la opción Carlos Enrique Guillent
de cancelar pedido esté
disponible.
Cancelar un pedido y
validar que se refleje
correctamente en el
sistema.
HU-17: Pagar Pedido Verificar que la opción de Carlos Enrique Guillent
pagar pedido esté
disponible.
Simular un proceso de
pago y confirmar que se
complete correctamente.
HU-18: Visualizar Errores Confirmar que la opción Carlos Enrique Guillent
de Pago de visualizar errores de
pago esté disponible.
Simular un error de pago
y validar que se
HU-19: Generar Nuevo Verificar que la opción de Carlos Enrique Guillent
Pago generar nuevo pago esté
disponible.
Ingresar los detalles de
la transacción y
confirmar que el pago se
genere correctamente.
75

Validar que la
información del pago se
refleje en el sistema.
HU-20: Crear Puntos Verificar la accesibilidad Carlos Enrique Guillent
Físicos en un Mapa de la funcionalidad para
crear puntos físicos.
Agregar un nuevo punto
físico al mapa y
confirmar que se refleje
correctamente.
HU-21: Ver Punto Físico Confirmar que la opción Carlos Enrique Guillent
en Mapa de ver punto físico en el
mapa esté disponible.
Seleccionar un punto
físico y validar que se
muestren los detalles
correctamente.
HU-22: Modificar Punto Verificar que la opción de Carlos Enrique Guillent
Físico modificar punto físico
esté disponible.
Realizar cambios en un
punto físico y validar que
se actualice
correctamente en el
mapa
HU-23: Eliminar Punto Confirmar que la opción Carlos Enrique Guillent
Físico en un Mapa de eliminar punto físico
esté disponible.
Eliminar un punto físico y
validar que desaparezca
correctamente del mapa.
HU-24: Ver Enlaces de Verificar que la opción de Carlos Enrique Guillent
Redes Sociales ver enlaces de redes
sociales esté disponible.
Confirmar que se
muestren correctamente
todos los enlaces
asociados.
HU-25: Navegación por Verificar que todas las Carlos Enrique Guillent
Menú opciones de menú sean
accesibles.
Navegar a través del
menú y validar que cada
sección se cargue
correctamente.
76

HU-26: Vista de Confirmar que la opción Carlos Enrique Guillent


Empleados de ver la vista de
empleados esté
disponible.
Validar que se muestren
los detalles de cada
empleado correctamente.
77

Ambiente de Pruebas Y Base de Datos

Repositorio de Código:

GitHub Enterprise: GitHub como el repositorio central para el código fuente.

Integración Continua / Despliegue Continuo (CI/CD): Azure DevOps para la


automatización de CI/CD, integrando GitHub y Azure Cloud Services.

Desarrollo de Aplicación:

Android Studio: Para el desarrollo de aplicaciones Android.

Pruebas Automatizadas:

Postman: Automatizar pruebas de API.

Herramientas de prueba de Android Studio para pruebas de aplicaciones móviles.

Virtualización y Contenedores: Máquinas virtuales en Azure Cloud Services para


la creación de entornos de prueba replicables.

Monitoreo y Analítica: PowerBI para el monitoreo y análisis de datos de prueba.

Seguridad: Ethical Hacker para pruebas de seguridad y análisis de


vulnerabilidades.

Licencias y Certificados: Licencias de Windows para las máquinas virtuales de


prueba.

Utilizar SSL Certificates para asegurar la comunicación segura.

Firewall: Configurar un firewall para controlar el tráfico en el entorno de pruebas.

Base de Datos de Pruebas:

Azure Cosmos DB: Para la base de datos en el entorno de pruebas.

Administración de Base de Datos: Administrador de BD para gestionar y


optimizar el rendimiento de la base de datos.

Herramientas de Desarrollo de Bases de Datos: Herramientas proporcionadas


por Azure Cosmos DB y Android Studio para el desarrollo y gestión de la base de
datos.
78

Copias de Seguridad y Restauración: Procesos automáticos de copia de


seguridad y restauración para mantener la integridad de los datos de prueba.

Control de Versión

Documento Soporte y Mantenimiento


Galletas Guille - Documentación de Mantenimiento de Software
79

Introducción

Con un mantenimiento periódico y con buen enfoque podemos abordar problemas


que pueden no suponer gran importancia, pero en el futuro se podrían traducir en
costos para la empresa o equipo de trabajo o fallos que afecten las operaciones
que dependan de nuestro producto.

Objetivo
Dar una guía detallada que asegure la eficiencia, la consistencia y la calidad en
todas las actividades de mantenimiento del software, además, garantizar el
estándar de calidad que manejamos y que se presentó al cliente, para mantener la
calidad en nuestro producto y la satisfacción de nuestros usuarios finales.

Alcance del Mantenimiento


Estos elementos ayudan a delimitar de manera precisa el alcance del
mantenimiento, lo que nos proporciona una base sólida para llevar a cabo la
planificación y posterior ejecución de las actividades relacionadas al
mantenimiento.
Descripción General del Software
Galletas Guille App es una aplicación móvil diseñada para facilitar a los usuarios la
compra de productos y creación de pedidos de la tienda Galletas Guille. Esta
aplicación proporciona una interfaz amigable y de fácil uso que permita a los
usuarios realizar compras desde su celular sin salir de casa, realizar seguimiento
de sus pedidos y conocer puntos físicos de venta.
Módulos o Componentes
a. Registro de Cuenta
b. Inicio de sesión
c. Vista Empleado
d. Vista Productos
e. Crear Producto
f. Modificar producto
g. Eliminar producto
h. Vista Catálogo
i. Crear Catálogo
j. Modificar Catálogo
k. Eliminar Catálogo
l. Vista Punto de Venta
m. Crear Punto de Venta
80

n. Modificar Punto de Venta


o. Eliminar Punto de Venta
p. Categorizar Producto
q. Ver Pedidos de Clientes (Vista Trabajador)
r. Página Principal
s. Menú Scroll (productos destacados)
t. Lista General de Productos
u. Vista de detalles de productos (de manera individual)
v. Carrito de Compras
w. Generar Pedido
x. Pagar Pedido
y. Ver Pedido (Vista Cliente)
z. Modificar Pedido (Cambiar día de recogida y agregar productos al pedido)

Tipo de Mantenimiento
Se llevarán a cabo los siguientes tipos de mantenimiento:
• Correctivo: Este tipo de mantenimiento se realizará cuando se presenten
fallos y errores en el back y front de la aplicación.
• Preventivo: El mantenimiento preventivo en el software conlleva la
realización de cambios pertinentes, actualización de versiones,
adaptaciones, corrección de errores, etc.
• Evolutivo: Se realizará cuando se requiera potenciar las funcionalidades
existentes del sistema, y también si se requieren funcionalidades nuevas.
Versiones del Software
Se manejará un repositorio de tipo empresarial en la plataforma GitHub, para un
correcto control de versiones, gestión de cambios y monitoreo general del código
fuente.
Plataformas o Entornos Compatibles
La aplicación de software se desarrolló para sistemas Android v.11.0 y fue
testeada en los siguientes dispositivos:
• Samsung A22 5G
• Redmi Note 10
• Huawei Mate 50 Pro
1. Exclusiones del Alcance
• Compatibilidad con dispositivos Android de versiones anteriores a la 11.0
• Mantenimiento de infraestructura (Hardware, Server Side, Hosting) de la
aplicación
• Actualización de productos en la tienda
2. Ciclo de Vida del Software
Las tareas de mantenimiento se llevarán a cabo en las siguientes fases del ciclo
de vida del software:
81

• Implementación: Mejoras en la documentación para mantenimientos


futuros, mejoras de rendimiento por medio de la simplificación de tareas
(lógica sencilla).
• Pruebas: Es probable que algún bug no haya sido detectado en la fase de
pruebas, por lo que es importante realizar pruebas en el mantenimiento de
la aplicación para comprobar que las funcionalidades trabajen
correctamente.
• Uso y Mantenimiento: Eliminar los defectos detectados durante la vida útil
del software y añadir nuevas funcionalidades.
Responsabilidades del Equipo de Mantenimiento
Definir las responsabilidades ayuda al equipo a garantizar la calidad de la
aplicación y la capacidad de evolucionar. Los siguientes ítems describen las
responsabilidades del equipo de mantenimiento:
• Corrección de Errores: Identificar y corregir errores reportados por usuarios
o detectados internamente. Priorizar la corrección de errores críticos que
afecten la funcionalidad principal de la aplicación.
• Actualizaciones de Seguridad: Monitorear las posibles vulnerabilidades de
seguridad. Implementar parches de seguridad.
• Optimización del Rendimiento: Evaluar y en lo posible mejorar el
rendimiento de la aplicación. Optimizar el uso de recursos.
• Compatibilidad con nuevas versiones: Mantener la compatibilidad con las
nuevas versiones de Android. Adaptar la aplicación a posibles cambios en
las políticas.
• Gestión de versiones: Administrar versiones anteriores de la aplicación en
caso de ser necesitado. Dar de baja versiones obsoletas y mantener en
constante actualización las versiones recientes.
• Actualizaciones de Funcionalidad: Implementar características y mejoras
según los nuevos requisitos del cliente.
• Pruebas y Validaciones Continuas: Realizar pruebas de manera periódica
para garantizar estabilidad en las funcionalidades de la aplicación.
• Gestión de Configuración: Gestionar y versionar el código fuente de manera
efectiva.
• Soporte Técnico: Proporcionar soporte técnico a usuarios, respondiendo
preguntas y resolviendo problemas técnicos. Recopilar comentarios y
sugerencias para realizar futuras mejoras.
• Documentación del Código: Mantener una documentación clara y
actualizada del código fuente y los procedimientos de mantenimiento.

Frecuencia de las Actividades de Mantenimiento


• Corrección de Errores: Frecuencia: Inmediata
• Actualizaciones de Seguridad: Frecuencia: Mensualmente
• Optimización del Rendimiento: Frecuencia: Trimestralmente
82

• Compatibilidad con nuevas versiones: Frecuencia: Máximo un mes después


del lanzamiento de una nueva versión de Android
• Gestión de versiones: Frecuencia: Cada vez que salga una nueva versión
de la aplicación
• Actualizaciones de Funcionalidad: Frecuencia: 3-4 meses
• Pruebas y Validaciones Continuas: Frecuencia: Antes del lanzamiento de
una nueva versión
• Soporte Técnico: Frecuencia: Diariamente

Descripción del Software


Arquitectura de Software para Aplicación Móvil en Dart y Flutter
Frontend:
• Framework: Flutter
• Lenguaje de Programación: Dart
• Widgets / IU: Material Design
• Gestión de Estados: Provider
• Navegación: Navigator Flutter

Backend:
• API: Uso del protocolo http para realizar solicitudes a nuestra API de base
de datos.
• API Response:json.encode para enviar datos en formato JSON y
json.decode para convertir datos JSON en cadenas de caracteres, por
ejemplo.
Despliegue:
• Plataforma de Despliegue: Google Play Store.

Herramientas y Tecnologías Utilizadas jaja


• Flutter
• Dart
• Visual Studio Code
• Android Studio
• Git
• GitHub
• FireBase
• Provider
83

Documentación Código
Página de inicio:

/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Imagen de la página de inicio
84

Image(
image: AssetImage('assets/images/inicio.png'),
width: 200,
height: 200,
),
// Título de la aplicación
Text (
'Galletas Guille',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
// Texto con imagen del autor
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text (
'Carlos Guillent',
style: TextStyle(
fontSize: 16,
),
),
Image(
image: AssetImage('assets/images/perfil.png'),
width: 50,
height: 50,
),
],
),
// Botón para iniciar sesión
ElevatedButton(
onPressed: () {},
child: Text ('Iniciar sesión'),
),
],
),
),
),
);
}
}

/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


85

final response = await http.get(Uri.parse(baseUrl + '/galletas'));


if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
);
}
}
86

Register

/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la página de registro
87

Text (
'Crear cuenta',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
// Texto de subtitulo (para empleados)
Text (
'(Solo empleados)',
style: TextStyle(
fontSize: 16,
),
),
// Campo de entrada de texto para el correo electrónico
TextField(
decoration: InputDecoration(
labelText: 'Correo electrónico',
labelStyle: TextStyle(color: Colors.black),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.blue),
),
),
),
// Campo de entrada de texto para el documento del empleado
TextField(
decoration: InputDecoration(
labelText: 'Documento del empleado',
labelStyle: TextStyle(color: Colors.black),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.blue),
),
),
),
// Botón para crear la cuenta
ElevatedButton(
onPressed: () {},
child: Text('Crear'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
88

onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
);
89

}
}
90

Login

// ----------------------
// Página de Inicio de Sesión
// ----------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
91

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Imagen de la página de inicio de sesión
Image(
image: AssetImage('assets/images/inicio.png'),
width: 200,
height: 200,
),

// Texto de encabezado
Text (
'Galletas Guille',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Texto de subtitulo
Text (
'(Solo empleados)',
style: TextStyle(
fontSize: 16,
),
),
92

// Campo de entrada de texto para el correo electrónico


TextField(
decoration: InputDecoration(
labelText: 'Correo electrónico',
labelStyle: TextStyle(color: Colors.black),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.blue),
),
),
),

// Campo de entrada de texto para la contraseña


TextField(
decoration: InputDecoration(
labelText: 'Contraseña',
labelStyle: TextStyle(color: Colors.black),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.blue),
),
suffixIcon: Icon(Icons.visibility_off),
93

),
obscureText: true,
),

// Botón para iniciar sesión


ElevatedButton(
onPressed: () {},
child: Text ('Iniciar sesión'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}
94

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}
}
95

Página Principal Cliente

// ------------------------------
// Página Principal del Cliente
// ------------------------------
/*front*/
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


96

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Imagen de la página principal del cliente
Image(
image: AssetImage('assets/images/inicio.png'),
width: 200,
height: 200,
),

// Texto de encabezado
Text (
'Galletas Guille',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Texto de subtitulo
Text (
'(Solo empleados)',
style: TextStyle(
fontSize: 16,
),
97

),

// Carrusel de imágenes
CarouselSlider(
images: [
Image.network('https://i.imgur.com/k5952i2.jpg'),
Image.network('https://i.imgur.com/y3o551M.jpg'),
Image.network('https://i.imgur.com/20o8o7D.jpg'),
],
// Opciones del carrusel
options: CarouselOptions(
autoplay: true,
autoplayInterval: Duration(seconds: 3),
height: 200,
),
),

// Botón para iniciar sesión


ElevatedButton(
onPressed: () {},
child: Text ('Iniciar sesión'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
),
),
);
98

}
}

/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}
}
99
100

Producto compra

// ------------------------------
// Página de Producto para Compra
// ------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
101

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Imagen del producto
Image(
image: AssetImage('assets/images/producto.png'),
width: 200,
height: 200,
),

// Texto de encabezado del producto


Text (
'Galletas Chips Chocolate',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Texto de subtitulo del producto


Text (
'Caja x 10 unidades',
style: TextStyle(
fontSize: 16,
),
),
102

// Texto de precio del producto


Text (
'\$20.000',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),

// Botones de comprar y agregar al carrito


Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {},
child: Text('Comprar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
ElevatedButton(
onPressed: () {},
child: Text ('Agregar al carrito'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.green,
onPrimary: Colors.white,
),
),
],
103

),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
104

nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}
}
105

Carrito de compras

// ------------------------------
// Página de Carrito de Compras
// ------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
106

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado del carrito de compras
Text (
'Carrito de compras',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Lista de productos en el carrito


ListView(
shrinkWrap: true,
children: [
ListTile(
leading:
Image.asset('assets/images/producto.png'),
title: Text('Galletas Chips Chocolate'),
subtitle: Text('Caja x 10 unidades'),
trailing: Text('\$20.000'),
),
ListTile(
leading:
Image.asset('assets/images/producto.png'),
title: Text('Galletas Vainilla'),
107

subtitle: Text('Caja x 10 unidades'),


trailing: Text('\$20.000'),
),
],
),

// Total del carrito


Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text (
'Total: \$40.000',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),

// Botones de continuar y vaciar carrito


Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {},
child: Text('Continuar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
108

),
ElevatedButton(
onPressed: () {},
child: Text('Vaciar carrito'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.red,
onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
109

final String nombre;


final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}
}
110

Generar pedido

// -----------------------------
// Página de Generar un Pedido
// -----------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
111

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para el proceso de pago
Text(
'Pago',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Tarjeta de crédito
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text('Número de tarjeta'),
TextField(
decoration: InputDecoration(
hintText: '1234-5678-9012-3456',
),
),
Text('Fecha de vencimiento'),
TextField(
112

decoration: InputDecoration(
hintText: 'MM/AA',
),
),
Text('Código de seguridad'),
TextField(
decoration: InputDecoration(
hintText: 'CVV',
),
),
],
),
),
),

// Botón para procesar el pago


ElevatedButton(
onPressed: () {},
child: Text('Pagar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
113

/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
114

Pagar pedido

// ---------------------------
// Página de Pagar un Pedido
// ---------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
115

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la página de error
Text(
'Error',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Texto de error
Text(
'Ha ocurrido un error. Por favor, inténtelo de
nuevo.',
style: TextStyle(
fontSize: 16,
),
),

// Botón para volver a intentarlo


ElevatedButton(
onPressed: () {},
child: Text('Volver a intentarlo'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
116

onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
117

Error de pago

// ------------------------------
// Página de Error de Pago
// ------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
118

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para el mensaje de error de pago
Text(
'Pedido fallido',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Texto de error
Text(
'Tu pedido no se pudo procesar. Por favor, inténtelo
de nuevo.',
style: TextStyle(
fontSize: 16,
),
),

// Botón para volver a intentarlo


ElevatedButton(
onPressed: () {},
child: Text('Volver a intentarlo'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
119

onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
120

Ver pedido

// ---------------------------
// Página de Ver un Pedido
// ---------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
121

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la visualización del pedido
Text(
'Ver Pedido',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Fecha y hora del pedido


Text(
'Fecha y hora del pedido: 9:27 AM',
style: TextStyle(
fontSize: 16,
),
),

// Método de pago
Text(
'Método de pago: 0455',
style: TextStyle(
fontSize: 16,
),
122

),

// Texto de productos
Text(
'Productos:',
style: TextStyle(
fontSize: 16,
),
),

// Lista de productos en el pedido


ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text('Galletas Vainilla Caja (10
Galletas)'),
subtitle: Text('$20.000'),
),
],
),

// Botón para modificar el pedido


ElevatedButton(
onPressed: () {},
child: Text('Modificar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
123

// Botón para cancelar el pedido


ElevatedButton(
onPressed: () {},
child: Text('Cancelar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.red,
onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
124

});
}
125

Modificar pedido

// -------------------------
// Página de Modificar Pedido
// -------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
126

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la página de modificación de
pedido
Text(
'Modificar Pedido',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Fecha y hora del pedido


Text(
'Fecha y hora del pedido: 9:27 AM',
style: TextStyle(
fontSize: 16,
),
),

// Método de pago
Text(
'Método de pago: 0455',
style: TextStyle(
fontSize: 16,
),
127

),

// Texto de productos
Text(
'Productos:',
style: TextStyle(
fontSize: 16,
),
),

// Lista de productos en el pedido


ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text('Galletas Vainilla Caja (10
Galletas)'),
subtitle: Text('$20.000'),

// Botón de edición del producto


trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () {},
),
),
],
),

// Botón para modificar el pedido


ElevatedButton(
onPressed: () {},
128

child: Text('Modificar'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
129
130

Lista de productos

// -------------------------
// Página de Lista de Productos
// -------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
131

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la página de lista de
productos
Text(
'Ver Productos',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Lista de productos
ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text('Galletas Chips Chocolate'),
subtitle: Text('$20.000'),
),
ListTile(
title: Text('Galletas Vainilla'),
subtitle: Text('$20.000'),
),
ListTile(
title: Text('Galletas Red Velvet'),
132

subtitle: Text('$20.000'),
),
ListTile(
title: Text('Galletas Doble Chocolate'),
subtitle: Text('$20.000'),
),
],
),

// Botón para agregar productos al carrito


ElevatedButton(
onPressed: () {},
child: Text('Agregar al carrito'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));
133

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
134

Vista menú empleado

// -----------------------------------
// Página de Vista del Menú del Empleado
// -----------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
135

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la vista del menú del
empleado
Text(
'Vista empleado',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Hora actual
Text(
'Hora actual: 9:27 AM',
style: TextStyle(
fontSize: 16,
),
),

// Botones del menú


Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Botón de "Productos"
ElevatedButton(
136

onPressed: () {},
child: Text('Productos'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),

// Botón de "Pedidos"
ElevatedButton(
onPressed: () {},
child: Text('Pedidos'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
137

expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
138

Empleado Gestión Producto

// -----------------------------------------
// Página de Gestión de Productos del Empleado
// -----------------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
139

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la gestión de productos
Text(
'Gestión de productos',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight bold,
),
),

// Botones de acciones
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Botón de "Crear producto"
ElevatedButton(
onPressed: () {},
child: Text('Crear producto'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
140

// Botón de "Actualizar producto"


ElevatedButton(
onPressed: () {},
child: Text('Actualizar producto'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),

// Botón de "Eliminar producto"


ElevatedButton(
onPressed: () {},
child: Text('Eliminar producto'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test('El método getGalletas() devuelve una lista de galletas', ()
async {
141

final galletas = await GalletasService.getGalletas();


expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
142

Gestión Puntos de venta

// -----------------------------------
// Página de Gestión de Puntos de Venta
// -----------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
143

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la gestión de puntos de
venta
Text(
'Gestión de puntos de venta',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Lista de puntos de venta


ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text('Punto de venta 1'),
subtitle: Text('Activo'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () {},
),
),
ListTile(
title: Text('Punto de venta 2'),
144

subtitle: Text('Inactivo'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () {},
),
),
],
),

// Botones de acciones
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Botón de "Crear punto de venta"
ElevatedButton(
onPressed: () {},
child: Text('Crear punto de venta'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
// Botón de "Activar punto de venta"
ElevatedButton(
onPressed: () {},
child: Text('Activar punto de venta'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
145

// Botón de "Desactivar punto de venta"


ElevatedButton(
onPressed: () {},
child: Text('Desactivar punto de venta'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test ('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
146

}
});
}
147

Gestión Catalogo

// ----------------------------------
// Página de Gestión de Catálogo
// ----------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
148

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la gestión de catálogo
Text(
'Gestión de catálogo',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Lista de categorías
ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text('Categoría 1'),
subtitle: Text('Descripción'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () {},
),
),
ListTile(
title: Text('Categoría 2'),
149

subtitle: Text('Descripción'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () {},
),
),
],
),

// Botones de acciones
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Botón de "Crear categoría"
ElevatedButton(
onPressed: () {},
child: Text('Crear categoría'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
// Botón de "Actualizar categoría"
ElevatedButton(
onPressed: () {},
child: Text('Actualizar categoría'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
150

// Botón de "Eliminar categoría"


ElevatedButton(
onPressed: () {},
child: Text('Eliminar categoría'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test ('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
151

}
});
}
152

Gestión de pedidos

// --------------------------------------
// Página de Gestión de Pedidos
// --------------------------------------
/*frontend*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
153

Widget build(BuildContext context) {


return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Texto de encabezado para la gestión de pedidos
Text(
'Gestión de pedidos',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),

// Lista de pedidos
ListView(
shrinkWrap: true,
children: [
ListTile(
title: Text ('Pedido 1'),
subtitle: Text ('Fecha: 2023-07-20'),
trailing: Text ('$20.000'),
),
ListTile(
title: Text('Pedido 2'),
subtitle: Text ('Fecha: 2023-07-21'),
trailing: Text ('$20.000'),
),
154

],
),

// Botones de acciones
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Botón de "Ver pedido"
ElevatedButton(
onPressed: () {},
child: Text('Ver pedido'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
// Botón de "Actualizar pedido"
ElevatedButton(
onPressed: () {},
child: Text ('Actualizar pedido'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.blue,
onPrimary: Colors.white,
),
),
// Botón de "Cancelar pedido"
ElevatedButton(
onPressed: () {},
child: Text ('Cancelar pedido'),
style: ElevatedButton.styleFrom(
primaryColor: Colors.red,
155

onPrimary: Colors.white,
),
),
],
),
],
),
),
),
);
}
}
/*backend*/
void main() {
test ('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
156

Crear Producto

// --------------------------------
// Página de Creación de Producto
// --------------------------------
/*backend*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
157

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _formKey = GlobalKey<FormState> ();

String _nombre = '';


String _precio = '';

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Crear producto'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el nombre del producto
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del producto es obligatorio';
}
158

return null;
},
onChanged: (value) {
_nombre = value;
},
),
// Campo de texto para el precio del producto
TextFormField(
decoration: InputDecoration(
labelText: 'Precio',
),
validator: (value) {
if (value.isEmpty) {
return 'El precio del producto es obligatorio';
}
return null;
},
onChanged: (value) {
_precio = value;
},
),
// Botón para crear el producto
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se crearía el producto
}
},
child: Text ('Crear producto'),
),
],
159

),
),
),
);
}
}
/*backend*/
void main() {
test ('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
160

Crear catalogo

// --------------------------------
// Página de Creación de Catálogo
// --------------------------------
/*frontend*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
161

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _formKey = GlobalKey<FormState> ();

String _nombre = '';


String _descripcion = '';

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Crear catálogo'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el nombre del catálogo
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del catálogo es obligatorio';
}
162

return null;
},
onChanged: (value) {
_nombre = value;
},
),
// Campo de texto para la descripción del catálogo
TextFormField(
decoration: InputDecoration(
labelText: 'Descripción',
),
maxLines: 3,
validator: (value) {
if (value.isEmpty) {
return 'La descripción del catálogo es
obligatoria';
}
return null;
},
onChanged: (value) {
_descripcion = value;
},
),
// Botón para crear el catálogo
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se crearía el catálogo
}
},
child: Text ('Crear catálogo'),
),
163

],
),
),
),
);
}
}
/*backend*/
void main() {
test ('El método getGalletas() devuelve una lista de galletas', ()
async {
final galletas = await GalletasService.getGalletas();
expect(galletas, isList);
expect(galletas.length, greaterThanOrEqualTo(0));

for (final galleta in galletas) {


expect(galleta is Galleta);
expect(galleta.nombre, isNotNull);
expect(galleta.descripcion, isNotNull);
expect(galleta.imagen, isNotNull);
expect(galleta.cantidad, isNotNull);
}
});
}
164

Crear Punto de venta

// --------------------------------------
// Página de Creación de Punto de Venta
// --------------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
165

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _formKey = GlobalKey<FormState> ();

String _nombre = '';


String _ubicacion = '';

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Crear punto de venta'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el nombre del punto de venta
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del punto de venta es
obligatorio';
166

}
return null;
},
onChanged: (value) {
_nombre = value;
},
),
// Campo de texto para la ubicación del punto de venta
TextFormField(
decoration: InputDecoration(
labelText: 'Ubicación',
),
validator: (value) {
if (value.isEmpty) {
return 'La ubicación del punto de venta es
obligatoria';
}
return null;
},
onChanged: (value) {
_ubicacion = value;
},
),
// Botón para crear el punto de venta
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se crearía el punto de venta
}
},
child: Text ('Crear punto de venta'),
),
167

],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
} else {
throw Exception('Error al obtener las galletas');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
168

final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}

@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
169

Categorizar Producto

// -------------------------------------------

// Página de Categorización de Productos

// -------------------------------------------

/*front*/

import 'package:flutter/material.dart';

void main() {

runApp(MyApp());

class MyApp extends StatefulWidget {

@override

_MyAppState createState() => _MyAppState();


170

class _MyAppState extends State<MyApp> {

final _formKey = GlobalKey<FormState> ();

String _producto = '';

List<String> _categorias = ['Categoría 1', 'Categoría 2', 'Categoría 3'];

String _categoriaSeleccionada = '';

@override

Widget build(BuildContext context) {

return MaterialApp(

title: 'Flutter Demo',

home: Scaffold(

appBar: AppBar(

title: Text ('Categorizar producto'),

),

body: Form(

key: _formKey,

child: Column(

mainAxisAlignment: MainAxisAlignment.center,

children: [

// Campo de texto para el nombre del producto

TextFormField(

decoration: InputDecoration(

labelText: 'Producto',

),

validator: (value) {

if (value.isEmpty) {

return 'El nombre del producto es obligatorio';

return null;

},
171

onChanged: (value) {

_producto = value;

},

),

// Campo de selección para la categoría del producto

DropdownButtonFormField<String> (

value: _categoriaSeleccionada,

decoration: InputDecoration(

labelText: 'Categoría',

),

items: _categorias.map((categoria) {

return DropdownMenuItem(

value: categoria,

child: Text(categoria),

);

}).toList(),

onChanged: (categoria) {

_categoriaSeleccionada = categoria;

},

),

// Botón para categorizar el producto

ElevatedButton(

onPressed: () {

if (_formKey.currentState.validate()) {

// Aquí se categorizaría el producto

},

child: Text ('Categorizar producto'),

),

],

),

),

),
172

);

/*backend*/

import 'package:http/http.dart' as http;

class GalletasService {

static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {

final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {

final json = jsonDecode(response.body);

return json['galletas'].map((galleta) =>


Galleta.fromJson(galleta)).toList();

} else {

throw Exception('Error al obtener las galletas');

class Galleta {

final int id;

final String nombre;

final String descripcion;


173

Modificar Punto de Venta

// -------------------------------------
// Página de Modificación de Punto de Venta
// -------------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
174

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _formKey = GlobalKey<FormState> ();

String _id = '';


String _nombre = '';
String _ubicacion = '';

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Modificar punto de venta'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el ID del punto de venta
TextFormField(
decoration: InputDecoration(
labelText: 'ID',
),
readOnly: true,
onChanged: (value) {
_id = value;
175

},
),
// Campo de texto para el nombre del punto de venta
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del punto de venta es
obligatorio';
}
return null;
},
onChanged: (value) {
_nombre = value;
},
),
// Campo de texto para la ubicación del punto de venta
TextFormField(
decoration: InputDecoration(
labelText: 'Ubicación',
),
validator: (value) {
if (value.isEmpty) {
return 'La ubicación del punto de venta es
obligatoria';
}
return null;
},
onChanged: (value) {
_ubicacion = value;
176

},
),
// Botón para modificar el punto de venta
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se modificaría el punto de venta
}
},
child: Text ('Modificar punto de venta'),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas'].map((galleta) =>
Galleta.fromJson(galleta)).toList();
177

} else {
throw Exception('Error al obtener las galletas');
}
}

Future<Galleta> getGalleta(int id) async {


final response = await http.get(Uri.parse(baseUrl +
'/galletas/$id'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return Galleta.fromJson(json);
} else {
throw Exception('Error al obtener la galleta');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
178

});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}

@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
179

Eliminar punto de venta

// ---------------------------------
// Eliminar punto de venta
// ---------------------------------
/*frontend*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
180

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _id = ''; // Variable para almacenar el ID del punto de venta
a eliminar.

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Eliminar punto de venta'),
),
body: Center (
child: Text (
'¿Estás seguro de que quieres eliminar el punto de venta
con ID ${_id}?',
style: TextStyle(fontSize: 20),
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
// Botón para cancelar la eliminación
TextButton(
onPressed: () {
// Aquí se maneja la acción de cancelar la
eliminación del punto de venta.
// Puedes agregar código para cerrar la página o
realizar otras acciones necesarias.
181

},
child: Text('Cancelar'),
),
// Botón para eliminar el punto de venta
ElevatedButton(
onPressed: () {
// Aquí se maneja la acción de eliminar el punto de
venta.
// Debes agregar código para confirmar y ejecutar la
eliminación.
},
child: Text('Eliminar'),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas']
182

.map((galleta) => Galleta.fromJson(galleta))


.toList();
} else {
throw Exception('Error al obtener las galletas');
}
}

Future<Galleta> getGalleta(int id) async {


final response =
183

Modificar Producto

// ---------------------------------
// Modificar Producto
// ---------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
184

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _id = ''; // Variable para el ID del producto a modificar.
String _nombre = ''; // Variable para almacenar el nombre del
producto.
int _precio = 0; // Variable para almacenar el precio del producto.

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Modificar producto'),
),
body: Form(
key: _formKey, // Clave global para validar el formulario.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el ID del producto
TextFormField(
decoration: InputDecoration(
labelText: 'ID',
),
readOnly: true, // El campo es de solo lectura.
onChanged: (value) {
_id = value; // Actualiza la variable _id cuando se
cambia el valor del campo.
},
185

),
// Campo de texto para el nombre del producto
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del producto es obligatorio';
}
return null;
},
onChanged: (value) {
_nombre = value; // Almacena el nombre del
producto.
},
),
// Campo de texto para el precio del producto
TextFormField(
decoration: InputDecoration(
labelText: 'Precio',
),
validator: (value) {
if (value.isEmpty) {
return 'El precio del producto es obligatorio';
}
if (int.parse(value) <= 0) {
return 'El precio del producto debe ser mayor que
0';
}
return null;
},
186

onChanged: (value) {
_precio = int.parse(value); // Almacena el precio
como un entero.
},
),
// Botón para modificar el producto
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se ejecutaría la acción para modificar el
producto.
}
},
child: Text ('Modificar producto'),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
187

final json = jsonDecode(response.body);


return json['galletas']
.map((galleta) => Galleta.fromJson(galleta))
.toList();
} else {
throw Exception('Error al obtener las galletas');
}
}

Future<Galleta> getGalleta(int id) async {


final response = await http.get(Uri.parse(baseUrl +
'/galletas/$id'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return Galleta.fromJson(json);
} else {
throw Exception('Error al obtener la galleta');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
188

this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}

@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
189

Eliminar Producto

// ---------------------------------
// Eliminar Producto
// ---------------------------------
/*frontend*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
190

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _id = ''; // Variable para almacenar el ID del producto a
eliminar.

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Eliminar producto'),
),
body: Center (
child: Text (
'¿Estás seguro de que quieres eliminar el producto con ID
${_id}?',
style: TextStyle(fontSize: 20),
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
// Botón para cancelar la eliminación
TextButton(
onPressed: () {
// Aquí se maneja la acción de cancelar la
eliminación del producto.
// Puedes agregar código para cerrar la página o
realizar otras acciones necesarias.
191

},
child: Text('Cancelar'),
),
// Botón para eliminar el producto
ElevatedButton(
onPressed: () {
// Aquí se maneja la acción de eliminar el producto.
// Debes agregar código para confirmar y ejecutar la
eliminación.
},
child: Text('Eliminar'),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));
192

Modificar Catalogo

// ---------------------------------
// Modificar Catálogo
// ---------------------------------
/*front*/
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {


@override
193

_MyAppState createState() => _MyAppState();


}

class _MyAppState extends State<MyApp> {


final _id = ''; // Variable para almacenar el ID del catálogo a
modificar.
String _nombre = ''; // Variable para almacenar el nombre del
catálogo.

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text ('Modificar catálogo'),
),
body: Form(
key: _formKey, // Clave global para validar el formulario.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Campo de texto para el ID del catálogo
TextFormField(
decoration: InputDecoration(
labelText: 'ID',
),
readOnly: true, // El campo es de solo lectura.
onChanged: (value) {
_id = value; // Actualiza la variable _id cuando se
cambia el valor del campo.
},
194

),
// Campo de texto para el nombre del catálogo
TextFormField(
decoration: InputDecoration(
labelText: 'Nombre',
),
validator: (value) {
if (value.isEmpty) {
return 'El nombre del catálogo es obligatorio';
}
return null;
},
onChanged: (value) {
_nombre = value; // Almacena el nombre del
catálogo.
},
),
// Botón para modificar el catálogo
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Aquí se ejecutaría la acción para modificar el
catálogo.
}
},
child: Text ('Modificar catálogo'),
),
],
),
),
),
);
195

}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas']
.map((galleta) => Galleta.fromJson(galleta))
.toList();
} else {
throw Exception('Error al obtener las galletas');
}
}

Future<Galleta> getGalleta(int id) async {


final response = await http.get(Uri.parse(baseUrl +
'/galletas/$id'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return Galleta.fromJson(json);
} else {
throw Exception('Error al obtener la galleta');
}
196

}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}

@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
197

}
}
198

Eliminar Catalogo

// ---------------------------------
// Eliminar Catálogo
// ---------------------------------
/*front*/

// Importa el paquete Flutter necesario para la aplicación.


import 'package:flutter/material.dart';

// La función principal que se ejecuta al iniciar la aplicación.


void main() {
// Inicia la aplicación llamando a la función MyApp.
runApp(MyApp());
}

// La clase MyApp que extiende StatefulWidget, es el componente


principal de la aplicación.
class MyApp extends StatefulWidget {
@override
199

// Crea una instancia de la clase _MyAppState cuando se construye


MyApp.
_MyAppState createState() => _MyAppState();
}

// La clase _MyAppState que extiende State<MyApp> es la que define la


interfaz de usuario y la lógica del componente.
class _MyAppState extends State<MyApp> {
final _id = ''; // Variable para almacenar el ID del catálogo a
eliminar.

@override
// El método build es donde se construye la interfaz de usuario.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
// Define la estructura básica de la página con un título y
contenido centralizado.
home: Scaffold(
appBar: AppBar(
title: Text ('Eliminar catálogo'), // Título de la página.
),
body: Center (
child: Text (
'¿Estás seguro de que quieres eliminar el catálogo con ID
${_id}?',
style: TextStyle(fontSize: 20), // Texto de confirmación.
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
// Botón para cancelar la eliminación.
TextButton(
onPressed: () {
// Aquí se maneja la acción de cancelar la
eliminación del catálogo.
// Puedes agregar código para cerrar la página o
realizar otras acciones necesarias.
},
child: Text('Cancelar'),
),
// Botón para eliminar el catálogo.
ElevatedButton(
onPressed: () {
// Aquí se maneja la acción de eliminar el catálogo.
// Debes agregar código para confirmar y ejecutar la
eliminación.
},
200

child: Text('Eliminar'),
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;

class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';

Future<List<Galleta>> getGalletas() async {


final response = await http.get(Uri.parse(baseUrl + '/galletas'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas']
.map((galleta) => Galleta.fromJson(galleta))
.toList();
} else {
throw Exception('Error al obtener las galletas');
}
}

Future<Galleta> getGalleta(int id) async {


final response = await http.get(Uri.parse(baseUrl +
'/galletas/$id'));

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return Galleta.fromJson(json);
} else {
throw Exception('Error al obtener la galleta');
}
}
}

class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
final int cantidad;

Galleta ({
201

this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});

factory Galleta.fromJson(Map<String, dynamic> json) {


return Galleta (
id: json['id'],
nombre: json['nombre'],
descripcion: json['descripcion'],
imagen: json['imagen'],
cantidad: json['cantidad'],
);
}

@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
202

Presupuestos

Galletas
CLIENTE: Guille €20,783. Días
working 180
TOTAL
12/10/20
FECHA: 23
63
DESCRIP PRUEBA SUBTOT
Costos CION MESES PRECIO TIPO COSTO GESTION S AL
Funcione
s (Azure
Function
s), Redes,
Almacen
amiento,
Bases de
Azure Datos
cloud Administ
services radas 6 €12.00 Administrative €132.00 €13.20 €0.00 €145.20
Control
de
versiones
GitHub y
Enterpris despliegu
e e 6 €21.00 Coding €231.00 €23.10 €46.20 300.3
Platafor
ma de Registro
lanzamie google
nto play 1 €25.00 Administrative €45.00 €4.50 €0.00 49.5
Android desarroll
studio o 6 €0.00 Coding €210.00 €21.00 €42.00 273
Azure
cosmo base de
db datos 6 €5.50 Coding €215.50 €21.55 €43.10 280.15
Prototipa
do y
maqueta
cion
Figma grafica 6 €10.00 Mockup €220.00 €22.00 €0.00 242
Excel
Word
PowerPoi
nt
Office documen
365 tación 6 €8.25 Administrative €128.25 €12.83 €0.00 141.075
203

Api de
platafor
ma de
pago 1
usd por
Pago trans 6 €20.00 Coding €230.00 €23.00 €46.00 299
Desarroll Senior €2,718.
ador fullstack 6 00 Coding €2,928.00 €292.80 €585.60 3806.4
Administ
rador de €2,153.
BD Mid 6 00 Coding €2,363.00 €236.30 €472.60 3071.9
Desarroll Junior €2,000.
ador fullstack 6 00 Coding €2,210.00 €221.00 €442.00 2873
5
portátiles
para el
desarroll €2,000.
Portátiles o 3 00 Administrative €2,060.00 €206.00 €0.00 2266
Prueba
Postman de Apis 6 €19.00 Administrative €139.00 €13.90 €0.00 152.9
Ethical €2,500.
Hacker mid 6 00 Coding €2,710.00 €271.00 €542.00 3523
€1,400.
Tester Senior 6 00 Coding €1,610.00 €161.00 €322.00 2093
5
Licencias licencias €140.0
Windows retail 1 0 Administrative €160.00 €16.00 €0.00 176
Certificac
ines SSL
SSL para las
certificat peticione
es s 6 €12.00 Administrative €132.00 €13.20 €0.00 145.2
€700.0
Firewall Fortinet 1 0 Administrative €720.00 €72.00 €0.00 792
Analítica
y
presenta
ción de
PowerBI datos 6 €20.00 Administrative €140.00 €14.00 €0.00 154

También podría gustarte