Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Proyecto de Aula1
Proyecto de Aula1
7 de octubre de 2023
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ó
nuevos casos de infección estaban en su punto más alto. Gracias al buen manejo
más de 50 clientes al día, sin contar los pedidos que realizan para eventos, en los
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
nacionales, Galletas Guille requiere una página web para que sus clientes usen
• Realizar compras.
• Agendar pedidos.
5
Formulación problema
¿Puede una aplicación web permitir realizar la correcta gestión de
Objetivo General
Implementar de un aplicativo web que permita realizar la correcta gestión
Galletas Guille.
Objetivos Específicos:
6
web.
web.
4. Probar en el sistema.
Análisis de requisitos
1. Identificar necesidades
2. Definir requerimientos
• El cliente necesita una página web en la que pueda realizar la venta de sus
productos
de compra
• El cliente necesita que los usuarios puedan cancelar todos sus pedidos al
Requerimientos
7
• Gestión pago
• Gestión de Usuarios
• Gestión de catálogos
Requisitos
Funcionales:
1. Registrar Usuario
2. Log in
3. Olvidaste Contraseña
4. Crear productos
5. Crear catalogo
7. Eliminar productos
8. Modificar productos
9. Ver catálogo(s)
27.
No funcionales
6. Configurar Firewall
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).
publicarlos.
45
Administrador
Permite al usuario administrador gestionar los puntos físicos de venta en los que
dicho aplicativo.
Cliente
puntos físico más cercanos a su ubicación actual como también acceder a todos
Vistas (mockups)
Vista principal para administradores y empleados. En esta primera vista
Vista de inicio de sesión. En esta vista encontraremos los campos de texto para
empleado. Si los datos son correctos, al pulsar el botón de ‘Iniciar Sesión’ seremos
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
la vista anterior. Pero con el nombre detallado del producto, la foto y precio
Vista de crear producto. En esta vista podemos encontrar el mismo logo con un
ordenada el nombre, precio y la foto del producto, seguido de el botón que registra
Vista de “Modificar producto”. En esta vista podemos encontrar el mismo logo con
Vista de "Eliminar producto”. En esta vista podemos encontrar el mismo logo con
del producto a eliminar, luego el botón que “Eliminar producto” que elimina
producto de la BDD.
57
Vista de crear catálogo. En esta vista podemos encontrar el mismo logo con un
Vista de “Modificar catálogo. En esta vista podemos encontrar el mismo logo con
nombre del catálogo, nuevo nombre del catálogo a cambiar, seguido de el botón
Vista de "Eliminar catalogo”. En esta vista podemos encontrar el mismo logo con
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
Vista de crear punto de venta. En esta vista podemos encontrar el mismo logo con
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
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
Vista de categorizar producto. En esta vista podemos encontrar el mismo logo con
al catalogo en la BDD.
66
67
Introducción
Justificación
Objetivos
68
• Objetivo General.
• Objetivos específicos.
Alcance
Historias de Usuario:
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.
Estrategia de Pruebas
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
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
Repositorio de Código:
Desarrollo de Aplicación:
Pruebas Automatizadas:
Control de Versión
Introducción
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.
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
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.
Documentación Código
Página de inicio:
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
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';
class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
});
Register
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
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';
class Galleta {
final int id;
final String nombre;
final String descripcion;
final String imagen;
Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
});
}
}
90
Login
// ----------------------
// Página de Inicio de Sesión
// ----------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Texto de encabezado
Text (
'Galletas Guille',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
// Texto de subtitulo
Text (
'(Solo empleados)',
style: TextStyle(
fontSize: 16,
),
),
92
),
obscureText: true,
),
class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';
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,
});
// ------------------------------
// Página Principal del Cliente
// ------------------------------
/*front*/
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
void main() {
runApp(MyApp());
}
@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,
),
),
}
}
/*backend*/
import 'package:http/http.dart' as http;
class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';
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,
});
Producto compra
// ------------------------------
// Página de Producto para Compra
// ------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
),
],
),
),
),
);
}
}
/*backend*/
import 'package:http/http.dart' as http;
class GalletasService {
static const baseUrl = 'https://galletasguillent.com/api';
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,
});
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());
}
),
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';
class Galleta {
final int id;
109
Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});
Generar pedido
// -----------------------------
// Página de Generar un Pedido
// -----------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// 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',
),
),
],
),
),
),
/*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));
Pagar pedido
// ---------------------------
// Página de Pagar un Pedido
// ---------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Texto de error
Text(
'Ha ocurrido un error. Por favor, inténtelo de
nuevo.',
style: TextStyle(
fontSize: 16,
),
),
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));
Error de pago
// ------------------------------
// Página de Error de Pago
// ------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Texto de error
Text(
'Tu pedido no se pudo procesar. Por favor, inténtelo
de nuevo.',
style: TextStyle(
fontSize: 16,
),
),
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));
Ver pedido
// ---------------------------
// Página de Ver un Pedido
// ---------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Método de pago
Text(
'Método de pago: 0455',
style: TextStyle(
fontSize: 16,
),
122
),
// Texto de productos
Text(
'Productos:',
style: TextStyle(
fontSize: 16,
),
),
});
}
125
Modificar pedido
// -------------------------
// Página de Modificar Pedido
// -------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Método de pago
Text(
'Método de pago: 0455',
style: TextStyle(
fontSize: 16,
),
127
),
// Texto de productos
Text(
'Productos:',
style: TextStyle(
fontSize: 16,
),
),
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));
Lista de productos
// -------------------------
// Página de Lista de Productos
// -------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// 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'),
),
],
),
// -----------------------------------
// Página de Vista del Menú del Empleado
// -----------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// Hora actual
Text(
'Hora actual: 9:27 AM',
style: TextStyle(
fontSize: 16,
),
),
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));
// -----------------------------------------
// Página de Gestión de Productos del Empleado
// -----------------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// 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
// -----------------------------------
// Página de Gestión de Puntos de Venta
// -----------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
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
}
});
}
147
Gestión Catalogo
// ----------------------------------
// Página de Gestión de Catálogo
// ----------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// 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
}
});
}
152
Gestión de pedidos
// --------------------------------------
// Página de Gestión de Pedidos
// --------------------------------------
/*frontend*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
// 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));
Crear Producto
// --------------------------------
// Página de Creación de Producto
// --------------------------------
/*backend*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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));
Crear catalogo
// --------------------------------
// Página de Creación de Catálogo
// --------------------------------
/*frontend*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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));
// --------------------------------------
// Página de Creación de Punto de Venta
// --------------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
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
Galleta ({
this.id,
this.nombre,
this.descripcion,
this.imagen,
this.cantidad,
});
@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
169
Categorizar Producto
// -------------------------------------------
// -------------------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
@override
@override
return MaterialApp(
home: Scaffold(
appBar: AppBar(
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
decoration: InputDecoration(
labelText: 'Producto',
),
validator: (value) {
if (value.isEmpty) {
return null;
},
171
onChanged: (value) {
_producto = value;
},
),
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;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
},
),
],
),
),
),
172
);
/*backend*/
class GalletasService {
if (response.statusCode == 200) {
} else {
class Galleta {
// -------------------------------------
// Página de Modificación de Punto de Venta
// -------------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
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');
}
}
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
});
@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
179
// ---------------------------------
// Eliminar punto de venta
// ---------------------------------
/*frontend*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
return json['galletas']
182
Modificar Producto
// ---------------------------------
// Modificar Producto
// ---------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
if (response.statusCode == 200) {
187
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,
});
@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
}
}
189
Eliminar Producto
// ---------------------------------
// Eliminar Producto
// ---------------------------------
/*frontend*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
Modificar Catalogo
// ---------------------------------
// Modificar Catálogo
// ---------------------------------
/*front*/
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@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';
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');
}
}
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,
});
@override
String toString() {
return 'Galleta ${nombre} (${cantidad})';
197
}
}
198
Eliminar Catalogo
// ---------------------------------
// Eliminar Catálogo
// ---------------------------------
/*front*/
@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';
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');
}
}
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,
});
@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