sábado, 20 de abril de 2024

Entrada 8 - 20/04/24

Hora Inicio: 8 p.m

Hora Fin: 10 p.m

Horas Trabajadas: 2 horas

Modalidad: Virtual


Nos reunimos para terminar los últimos detalles del proyecto y poder terminar el documento escrito.

Teníamos una duda sobre que Id guardar en la bitácora cuando se hacía una inserción no exitosa por lo que decidimos preguntar en el grupo:


Una vez hecha la consulta se realizaron las modificaciones y el codigo quedo de la sigiente manera:
@inUserName VARCHAR(64) 
    , @inPassword VARCHAR(64)
, @inIP VARCHAR(64)
, @OutResulTCode INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
-- ejemplo de ejecucion del SP

ALTER PROCEDURE [dbo].[VerificarUsuario]

-- se hacen declaraciones
DECLARE @IdPostByUser INT;
-- se hacen inicializacion
    SET @OutResulTCode = 0;
SET @IdPostByUser = 0;
-- Buscamos si el usuario existe en la base de datos
    IF NOT EXISTS (
            SELECT 1
            FROM dbo.Usuario AS U
            WHERE U.Username = @inUserName COLLATE SQL_Latin1_General_CP1_CS_AS
        )
    BEGIN
SET @IdPostByUser = 1;
        SET @OutResulTCode = 50001; -- Código de error para usuario no encontrado
INSERT INTO dbo.BitacoraEvento(
                [IdTipoEvento]
, [Descripcion]
                , [IdPostByUser]
                , [PostInIP]
                , [PostTime]
            )
            VALUES (
                2
, ' Numero de intento:'
+' Codigo de error:' + CAST(@OutResulTCode AS VARCHAR)
, @IdPostByUser
, @inIP
                , GETDATE()
            );
RETURN;
    END;
    
ELSE IF NOT EXISTS (
        SELECT 1
        FROM dbo.Usuario AS U
        WHERE U.[Password] = @inPassword COLLATE SQL_Latin1_General_CP1_CS_AS
    )
BEGIN
SET @IdPostByUser = 1;
SET @OutResulTCode = 50002; -- Código de error para contraseña incorrecta
INSERT INTO dbo.BitacoraEvento(
                [IdTipoEvento]
, [Descripcion]
                , [IdPostByUser]
                , [PostInIP]
                , [PostTime]
            )
            VALUES (
                2
, ' Numero de intento:'
+' Codigo de error:' + CAST(@OutResulTCode AS VARCHAR)
, @IdPostByUser
, @inIP
                , GETDATE()
            );
RETURN;
END;
ELSE IF EXISTS (
SELECT 1
        FROM dbo.Usuario AS U
        WHERE U.Username = @inUserName COLLATE SQL_Latin1_General_CP1_CS_AS
AND U.[Password] = @inPassword COLLATE SQL_Latin1_General_CP1_CS_AS)
BEGIN
SELECT @IdPostByUser = U.id
FROM dbo.Usuario AS U 
WHERE U.[Username] = @inUserName 
-- Login exitoso: Registro en la bitácora
INSERT INTO dbo.BitacoraEvento(
[IdTipoEvento]
, [Descripcion]
, [IdPostByUser]
, [PostInIP]
, [PostTime]
)
VALUES (
1
,''
, @IdPostByUser
, @inIP
, GETDATE()
);
RETURN;
    END;
    END TRY
    BEGIN CATCH
        INSERT INTO dbo.DBError VALUES (
USER_NAME()
, ERROR_NUMBER()
, ERROR_STATE()
, ERROR_SEVERITY()
, ERROR_LINE()
, ERROR_PROCEDURE()
, ERROR_MESSAGE()
,GETDATE()
);
        -- Retorna un mensaje de error para el usuario
        SET @OutResulTCode=50008;  -- Codigo de error standar del profe para informar de un error capturado en el catch
    END CATCH
SET NOCOUNT OFF;
END


Finalmente, se eliminaron los datos del XML ya que teniamos cargados unos datos viejos y empezamos a realizar pruebas para asegurarnos que todo el codigo funcionara correctamente.

miércoles, 17 de abril de 2024

Entrada 7 - 17/04/24

Hora Inicio: 2 p.m

Hora Fin: 8 p.m

Horas Trabajadas: 6 horas

Modalidad: Virtual


Se realizó el SP de LogOut, el cual se encarga de insertar en la tabla Bitácora de Eventos de la Bases de Datos, este recibe el nombre del usuario que estaba loggeado así como su ip.

ALTER PROCEDURE [dbo].[LogOut]

@inUser VARCHAR(64)

, @inIP VARCHAR(64)

, @OutResulTCode INT OUTPUT

AS

BEGIN

    SET NOCOUNT ON;

    BEGIN TRY

-- se hacen declaraciones

DECLARE @IdUser INT;

-- se hacen inicializacion

    SET @OutResulTCode = 0;

SELECT

@IdUser = U.id

FROM dbo.Usuario AS U

WHERE U.Username = @inUser

INSERT INTO dbo.BitacoraEvento(

            [IdTipoEvento]

, [Descripcion]

            , [IdPostByUser]

            , [PostInIP]

            , [PostTime]

        )

        VALUES (

            4

, ''

, @IdUser

, @inIP

            , GETDATE()

        );

    END TRY

    BEGIN CATCH

        INSERT INTO dbo.DBError VALUES (

USER_NAME()

, ERROR_NUMBER()

, ERROR_STATE()

, ERROR_SEVERITY()

, ERROR_LINE()

, ERROR_PROCEDURE()

, ERROR_MESSAGE()

,GETDATE()

);

        -- Retorna un mensaje de error para el usuario

        SET @OutResulTCode=50008;  -- Codigo de error standar del profe para informar de un error capturado en el catch

    END CATCH

SET NOCOUNT OFF;

END


Por ello, se debió de modificar una vez más la ventana que lista los empleados, para que esta reciba como parámetros una nueva función desde el controlador, la cual se encarga de capturar el usuario y ejecutar por medio de una solicitud a base de datos el stored procedure correspondiente. 

async function logOutStoredProcedure(usuario, ip) { //Función asincrónica para llamar a un procedimiento almacenado de inserción

    try {

        await sql.connect(config); //Conecta con la base de datos SQL Server

        const request = new sql.Request(); //Crea una nueva solicitud SQL

        request.input('inIP', sql.VarChar(64), ip);

        request.input('inUser', sql.VarChar(64), usuario);

        request.output('OutResulTCode', sql.Int); //Define un parámetro de salida para el código de resultado

        const result = await request.execute('dbo.LogOut'); //Ejecuta el procedimiento almacenado de inserción

        const outputResultCode = result.output.OutResulTCode; //Obtiene el código de resultado de la salida

        if (outputResultCode === 0) { //Si el código de resultado es 0 (éxito)

            return result.recordset; //Devuelve el conjunto de registros resultante

        } else { //Si hay un error en la operación

            console.error(`Error in operation. Result Code: ${outputResultCode}`);

            throw new Error(`Error in operation. Result Code: ${outputResultCode}`);

        }

    } catch (error) {

        console.error('Error executing stored procedure:', error.message);

        throw error; //Captura y lanza cualquier error que ocurra

    } finally {

        await sql.close(); //Cierra la conexión a la base de datos

    }

}

app.post('/api/logout', async (req, res) => {

    const { usuario } = req.body;

    const ip = '25.3.206.243';

    try {

        console.log('Received data:', req.body);

        const result = await logOutStoredProcedure(usuario, ip);

        res.json(result); //Devuelve los resultados como una respuesta JSON

    } catch (error) {

        console.error('Error calling mov insertion stored procedure:', error);

        res.status(500).json({ error: 'Internal Server Error' }); //Devuelve un error 500 si hay un problema

    }

});

Por otro lado, se realizo dentro del API los métodos correspondientes al control de errores, los cuales obtienen la descripción (que se indican el documento xml) correspondiente según el código de error devuelto por el SP, para esto se creó swhitch que contiene un caso según cada código de error. Dichas descripciones son enviadas para ser visualizadas por el usuario.

async function verificarUsuarioStoredProcedure(username, password, ip) {

    try {

        await sql.connect(config);

        const request = new sql.Request();

        request.input('inUserName', sql.VarChar(64), username);

        request.input('inPassword', sql.VarChar(64), password);

        request.input('inIP', sql.VarChar(64), ip);

        request.output('OutResulTCode', sql.Int);

        const result = await request.execute('dbo.VerificarUsuario');

        const outputResultCode = result.output.OutResulTCode;

        if (outputResultCode === 0) {

            return result.recordset;

        } else {

            let errorMessage = ''; // Initialize an empty error message

            switch (outputResultCode) {

                case 50001:

                    errorMessage = 'Username no existe';

                    break;

                case 50002:

                    errorMessage = 'Password no existe';

                    break;

                default:

                    errorMessage = `Error in operation. Result Code: ${outputResultCode}`;

                    break;

            }

            throw new Error(errorMessage);

        }

    } catch (error) {

        console.error('Error executing stored procedure:', error.message);

        throw error;

    } finally {

        await sql.close();

    }

}

Se nos presentaron múltiples errores dentro de los sp de verificarUsuario, borrar, insertar y modificar, causados por un mal manejo de la tabla de bitácora, ya que la capa lógica enviaba valores que se convertían a nulos en el transcurso API-BD, para ello se realizaron ciertas modificaciones dentro de la API para solucionarlos. Un ejemplo de estos fue

Received data: { username: 'UsuarioScripts', password: 'UsuarioScripts' }

ip ::1

Error in operation. Result Code: 50008

Error executing stored procedure: Error in operation. Result Code: 50008

Error calling user verification stored procedure: Error: Error in operation. Result Code: 50008

    at verificarUsuarioStoredProcedure


Por otra parte, debido a estos errores tambien decidimos realizar cambio en los SP, a continuacion vamos a mosrtrar un ejemplo del cambio que se realizo en el SP modificar.

ALTER PROCEDURE [dbo].[EditarEmpleado]

@inEmpleado VARCHAR(64)

, @inNuevoValorDocumentoIdentidad INT

    , @inNuevoNombre VARCHAR(64)

    , @inNuevoPuesto VARCHAR(64)

, @inUser VARCHAR(64)

, @inIP VARCHAR(64)

    , @OutResulTCode INT OUTPUT

AS

BEGIN

    SET NOCOUNT ON;

    BEGIN TRY

-- se hacen declaraciones

DECLARE @ValorDocumentoIdentidad INT

, @IdUser INT

, @SaldoVacaciones SMALLINT

, @IdPuesto INT

, @Puesto VARCHAR(64)

, @DescripcionError VARCHAR(2000);

        -- se hacen inicializacion

        SET @OutResulTCode = 0;

SELECT

@IdUser = U.id

FROM dbo.Usuario AS U

WHERE U.Username = @inUser

SELECT 

@ValorDocumentoIdentidad = E.ValorDocumentoIdentidad

, @Puesto = P.Nombre

, @SaldoVacaciones = E.SaldoVacaciones

FROM dbo.Empleado AS E

INNER JOIN dbo.Puesto AS P ON E.idPuesto = P.id

WHERE E.Nombre = @inEmpleado

        -- se hacen validaciones

        IF EXISTS (SELECT 1 FROM dbo.Empleado AS E

WHERE E.ValorDocumentoIdentidad = @inNuevoValorDocumentoIdentidad)

        BEGIN

            SET @OutResulTCode = 50004; 

SELECT @DescripcionError = Err.Descripcion

FROM dbo.Error AS Err

WHERE Err.Codigo = @OutResulTCode;

INSERT dbo.BitacoraEvento(

[IdTipoEvento]

, [Descripcion]

, [IdPostByUser]

, [PostInIP]

, [PostTime]

)

VALUES (

7

, ' Descripcion del error: '+ @DescripcionError

+ ' Valor documento identidad antes de editarse:' + CAST(@ValorDocumentoIdentidad AS VARCHAR)

+' Nombre antes de editarse:' + @inEmpleado

+ ' Nombre del Puesto antes de editarse:'+ @Puesto

+ ' Valor documento identidad despues de editarse:' + CAST(@inNuevoValorDocumentoIdentidad AS VARCHAR)

+' Nombre despues de editarse:' + @inNuevoNombre

+ ' Nombre del Puesto despues de editarse:'+ @inNuevoPuesto

, @IdUser

, @inIP

, GETDATE()

);

        END;

ELSE IF EXISTS (SELECT 1 FROM dbo.Empleado AS E

WHERE E.Nombre = @inNuevoNombre)

        BEGIN

            SET @OutResulTCode = 50005; 

SELECT @DescripcionError = Err.Descripcion

FROM dbo.Error AS Err

WHERE Err.Codigo = @OutResulTCode;

INSERT dbo.BitacoraEvento(

[IdTipoEvento]

, [Descripcion]

, [IdPostByUser]

, [PostInIP]

, [PostTime]

)

VALUES (

7

, ' Descripcion del error:'+ @DescripcionError

+ ' Valor documento identidad antes de editarse:' + CAST(@ValorDocumentoIdentidad AS VARCHAR)

+' Nombre antes de editarse:' + @inEmpleado

+ ' Nombre del Puesto antes de editarse:'+ @Puesto

+ ' Valor documento identidad despues de editarse:' + CAST(@inNuevoValorDocumentoIdentidad AS VARCHAR)

+' Nombre despues de editarse:' + @inNuevoNombre

+ ' Nombre del Puesto despues de editarse:'+ @inNuevoPuesto

, @IdUser

, @inIP

, GETDATE()

);

        END;

ELSE

BEGIN

-- Obtener el Id del puesto utilizando el nombre del puesto

SELECT @IdPuesto =  P.id

FROM dbo.Puesto AS P 

WHERE P.Nombre = @inNuevoPuesto;

-- Realizar la edición del empleado

UPDATE E

SET 

E.ValorDocumentoIdentidad = @inNuevoValorDocumentoIdentidad

, E.Nombre = @inNuevoNombre

, E.IdPuesto = @IdPuesto

FROM dbo.Empleado AS E

WHERE e.Nombre = @inEmpleado;

INSERT dbo.BitacoraEvento(

[IdTipoEvento]

, [Descripcion]

, [IdPostByUser]

, [PostInIP]

, [PostTime]

)

VALUES (

8

, ' Valor documento identidad antes de editarse:' + CAST(@ValorDocumentoIdentidad AS VARCHAR)

+' Nombre antes de editarse:' + @inEmpleado

+ ' Nombre del Puesto antes de editarse:'+ @Puesto

+ ' Valor documento identidad despues de editarse:' + CAST(@inNuevoValorDocumentoIdentidad AS VARCHAR)

+' Nombre despues de editarse:' + @inNuevoNombre

+ ' Nombre del Puesto despues de editarse:'+ @inNuevoPuesto

+ 'Saldo Vacaciones:' + CAST(@SaldoVacaciones AS VARCHAR)

, @IdUser

, @inIP

, GETDATE()

);

END

    END TRY

    BEGIN CATCH

        -- Registro de error en la tabla de errores

        INSERT INTO dbo.DBError VALUES (

            SUSER_SNAME(),

            ERROR_NUMBER(),

            ERROR_STATE(),

            ERROR_SEVERITY(),

            ERROR_LINE(),

            ERROR_PROCEDURE(),

            ERROR_MESSAGE(),

            GETDATE()

        );

        SET @OutResulTCode = 50008; -- Código de error para error capturado en el bloque TRY-CATCH

    END CATCH;

    SET NOCOUNT OFF;

END;


martes, 16 de abril de 2024

Entrada 6 - 16/04/24

Hora Inicio: 6 p.m

Hora Fin: 10 p.m

Horas Trabajadas: 4 horas

Modalidad: Virtual


Se realizó el SP de ListaArticulosQueCumplenPatron, el cual se encarga de filtrar tanto por nombre como por cedula los empleados que se encuentran activos y cuyos atributos coinciden con los parámetros de entrada.

ALTER PROCEDURE [dbo].[ListaArticulosQueCumplenPatron] 

@inPatron VARCHAR(64)

, @inIP VARCHAR(64)

, @inUser VARCHAR(64)

, @outResultCode INT OUTPUT

AS

BEGIN

SET NOCOUNT ON;

BEGIN TRY

-- se hacen declaraciones

DECLARE @IdTipoEvento INT

, @IdUser INT

, @Filtro VARCHAR(2000);


-- se hacen inicializacion

SET @outResultCode = 0;  -- no error code


SELECT

@IdUser = U.id

FROM dbo.Usuario AS U

WHERE U.Username = @inUser

-- se hacen validaciones

IF (@inPatron IS NULL)

BEGIN

SET @outResultCode = 50008;  -- parametro de entrada es nulo

RETURN;

END;

-- Determinar el tipo de filtro y asignar el IdTipoEvento correspondiente

IF (@inPatron LIKE '%[0-9]%') -- Si el patrón contiene números, probablemente se filtra por cedula

BEGIN

SET @IdTipoEvento = 12 -- Si el filtro es por cedula

END

ELSE

BEGIN

SET @IdTipoEvento = 11; -- Si el filtro es por nombre

END;

IF (@OutResulTCode=0)

BEGIN

IF (@inPatron ='')

BEGIN

SELECT E.[Nombre]

FROM dbo.Empleado E 

WHERE E.EsActivo = 1

ORDER BY E.Nombre;

END

ELSE

BEGIN

SELECT 

E.Nombre

FROM dbo.Empleado E

WHERE (E.Nombre like '%'+@inPatron+'%' 

OR CAST(E.ValorDocumentoIdentidad AS VARCHAR(64)) like '%'+@inPatron+'%')

AND E.EsActivo = 1;

INSERT dbo.BitacoraEvento(

[IdTipoEvento]

, [Descripcion]

, [IdPostByUser]

, [PostInIP]

, [PostTime]

)

VALUES (

@IdTipoEvento

, ' Filtro:' + @inPatron

, @IdUser

, @inIP

, GETDATE()

);

END

END

END TRY

BEGIN CATCH

INSERT INTO dbo.DBError VALUES (

SUSER_SNAME(),

ERROR_NUMBER(),

ERROR_STATE(),

ERROR_SEVERITY(),

ERROR_LINE(),

ERROR_PROCEDURE(),

ERROR_MESSAGE(),

GETDATE()

);

Set @outResultCode=50008;

END CATCH

SET NOCOUNT OFF;

END;


Siendo así se debió de realizar un ajuste en la ventana de listar empleados para agregar la caja de texto y el botón de búsqueda, de aquí surgió un problema pues no conocíamos como actualizar la información de la ventana de manera dinámica, por lo que se tuvo que crear un nuevo contenedor que almacena y mapeara de forma ordenada los datos devueltos por el sp, para posteriormente desplegar la nueva información mediante la renderización completa de la ventana.

Además, se crearon tanto la solicitud como al función dentro del API que invocan el stored procedure correspondiente.

app.get('/api/list-similar', async (req, res) => {

    try {

        const patron = req.query.patron; // Access the employee parameter from the query string

        const user = req.query.user; // Access the employee parameter from the query string

        const ip = '25.3.206.243';


        const result = await listaArticulosQueCumplenPatronStoredProcedure(patron, ip, user);

        res.json(result);

    } catch (error) {

        console.error('Error calling listingPatron stored procedure:', error);

        res.status(500).json({ error: 'Internal Server Error' });

    }

});

async function listaArticulosQueCumplenPatronStoredProcedure(patron, ip, user) { //Función asincrónica para llamar a un procedimiento almacenado de inserción

    try {

        await sql.connect(config); //Conecta con la base de datos SQL Server


        const request = new sql.Request(); //Crea una nueva solicitud SQL


        request.input('inPatron', sql.VarChar(64), patron);

        request.input('inIP', sql.VarChar(64), ip);

        request.input('inUser', sql.VarChar(64), user);

        request.output('outResultCode', sql.Int); //Define un parámetro de salida para el código de resultado


        const result = await request.execute('dbo.ListaArticulosQueCumplenPatron'); //Ejecuta el procedimiento almacenado de inserción


        const outputResultCode = result.output.outResultCode; //Obtiene el código de resultado de la salida


        if (outputResultCode === 0) { //Si el código de resultado es 1 (éxito)

            return result.recordset; //Devuelve el conjunto de registros resultante

        } else { //Si hay un error en la operación

            console.error(`Error in operation. Result Code: ${outputResultCode}`);

            throw new Error(`Error in operation. Result Code: ${outputResultCode}`);

        }

    } catch (error) {

        console.error('Error executing stored procedure:', error.message);

        throw error; //Captura y lanza cualquier error que ocurra

    } finally {

        await sql.close(); //Cierra la conexión a la base de datos

    }

}


Finalmente, se creó un .css a para cada ventanas y así el usuario pueda ver el proyecto más estético y con más agrado. Algunas cosas que se agregaron fueron las siguiente:

/* InsertEmployee.css */

.container {

    max-width: 600px;

    margin: 0 auto;

    padding: 2rem;

    text-align: center;

}

button{

    margin-left: 10px;

    margin-top: 15px;

}

.list-container {

    margin-bottom: 20px;

    margin-top: 20px;

}

lunes, 15 de abril de 2024

Entrada 5 - 15/04/24

Hora Inicio: 9 p.m

Hora Fin: 1 a.m

Horas Trabajadas: 4 horas

Modalidad: Virtual


Se crearon varios SP complementarios los cuales son los siguientes:

  • InfoBorrar: este SP lo que realiza es mostrar la información del empleado que se desea eliminar, como el nombre y documento de identidad.
  • InfoInsertarMovimiento: realiza lo mismo que el anterior, sin embargo, este también muestra el saldo de vacaciones. 
  • ListarPuesto: este SP nos ayuda a poder obtener los nombres de todos los puestos para poder mostrarlos en una lista despegable donde el usuario debe elegir uno de todos los puestos.
  • ListarTipoMovimiento: realiza lo mismo del SP anterior, pero esta muestra todos los tipos de movimientos que se pueden realizar.
  • LogOut: este SP nos ayuda para que, a la hora de presionar el botón de salir en capa lógica, esto se guarde en la bitacota de evento.
Estos fueron conectados con sus respectivas solicitudes a la base de datos, en las cuales se debió de investigar una nueva forma de paso de parámetros mediante concatenación de url. Un ejemplo de esto es el siguiente:
const fetchData = async () => {
    try {
        const response = await fetch(`http://localhost:3006/api/borrarInfo?employee=${employee}`, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        });
        if (!response.ok) {
            throw new Error(`Network response was not ok: ${response.statusText}`);
        }
        const result = await response.json();
        setData(result);
    } catch (error) {
        console.error('Error fetching data:', error);
        setError('Error fetching data. Please try again.');
    }
};
useEffect(() => {
    fetchData(); //Llama a la función fetchData cuando el componente se monta
}, []);
Además, el API debe de hacer uso de una query para la recepción de dichos parametros
app.get('/api/borrarInfo', async (req, res) => {

    try {
        const employee = req.query.employee; // Access the employee parameter from the query string
        const result = await infoBorrarStoredProcedure(employee);
        res.json(result);
    } catch (error) {
        console.error('Error calling infoBorrar stored procedure:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

Se logró conectar todas las ventanas de la capa lógica con sus respectivos sp mediantes solicitudes a la base de datos y sus respectivas funciones dentro de la API. Además se completo una vista estructurada de cada una de ellas mediante componentes pertinentes, como las listas desplegables para los tipos puestos y tipos de movimientos.

Los SP quedaron de la siguiente manera:



Todos los SP siguen la siguiente estructura:
ALTER PROCEDURE [dbo].[InsertarEmpleado]
@OutResulTCode INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY

-- ejemplo de ejecucion del SP

-- se hacen declaraciones

-- se hacen inicializacion
SET @OutResulTCode=0;

-- se hacen validaciones

        -- inicia el código

        INSERT dbo.BitacoraEvento(
[IdTipoEvento]
, [Descripcion]
, [IdPostByUser]
, [PostInIP]
, [PostTime]
)
VALUES (
                        -- se ingresan los valores dependiendo de lo que se solicite
                        );

END TRY
BEGIN CATCH

INSERT INTO dbo.DBError VALUES (
SUSER_SNAME(),
ERROR_NUMBER(),
ERROR_STATE(),
ERROR_SEVERITY(),
ERROR_LINE(),
ERROR_PROCEDURE(),
ERROR_MESSAGE(),
GETDATE()
);

SET @OutResulTCode=50008  ;  
END CATCH;

SET NOCOUNT OFF;
END;

miércoles, 10 de abril de 2024

Entrada 4 - 10/04/24

Hora Inicio: 5 p.m

Hora Fin: 8 p.m

Horas Trabajadas: 3 horas

Modalidad: Virtual


Se comenzó con la creación de las múltiples ventanas referentes a la capa lógica del proyecto. Primeramente se utiliza una ventana main.jsx (componente raiz) que renderiza la ventana App.jsx que funciona como controlador gráfico que interconecta las diversas secciones de la página web. Además, se inició el diseño de las ventanas de Inserción, Listado de Empleados, Modificación, Borrado, entre otras.

ReactDOM.createRoot(document.getElementById('root')).render(

  <React.StrictMode>

    <App />

  </React.StrictMode>,

)

Se desarrollaron varias funciones de validación de entradas, utilizando las siguientes funciones que hacen uso de expresiones regex.

function isValidDocIdentidad(docIdentidad) { //Valida el docIdentidad

    const isValid = /^[0-9]+$/.test(docIdentidad); //Verifica si el salario es un número o un número con un punto decimal opcional

    console.log(`Resultado Validacion Cedula: ${isValid}`);

    return isValid; //Devuelve true si el salario es válido, de lo contrario, false

}

function isValidName(name) { //Valida el nombre

    const isValid = /^[A-Za-z\s]+$/.test(name); //Verifica si el nombre contiene solo letras y guiones

    console.log(`Name Validation Result: ${isValid}`);

    return isValid; //Devuelve true si el nombre es válido, de lo contrario, false

}


El código referente a la capa lógica aun no funciona correctamente debido a que se necesita interrelacionar las variables y componentes entre ventanas, sin embargo, la lógica y estructuración principal ya se encuentran creadas. 

Ahora, en cuanto a la base de datos, se crearon los siguientes SP:

        - BorrarEmpleado

        - EditarEmpleado

        - InsertarEmpleado

        - InsertarMovimiento

        - ListaArticulosQueCumplenPatron

        - ListarEmpledo

        - VerificarUsuario

Los SP mencionados anteriormente, no se encuentran totalmente finalizados ya que se deben probar en capa lógica y modificar algunas cosas para que estén al 100%.

Finalmente, teníamos una consulta sobre la pantalla de iniciar sesión ya que no estábamos seguros de cómo realizar los 5 intentos y si la página debía seguir sin funcionar si la página se refrescaba o debía funcionar si se refrescaba, por lo que le realizamos al profesor la siguiente pregunta:


Debido a esta respuesta tomamos la decisión de mejor no realizar lo de los 5 intentos ya que es muy complicado, únicamente validaremos en el código que exista el usuario y la contraseña en la base de datos.


domingo, 7 de abril de 2024

Entrada 3 - 07/04/24

Hora Inicio: 10:00 a.m

Hora Fin: 3:00 p.m

Horas Trabajadas: 5 horas

Modalidad: Virtual


Se intento correr el SP en la base de datos, sin embargo, estaba tirando el siguiente error:

Cannot bulk load because the file "direccion\datos.xml" could not be opened. Operating system error code 5(Acceso denegado.).

Por lo que se decidió preguntar en el grupo si alguien más estaba teniendo el problema para poder saber si alguien había tenido el mismo error y sabia como solucionarlo.


Después de recibir varias respuestas y intentar todo lo que nos decían, aun salía el mismo error por lo que decidimos buscar alguna forma de solucionarlo.

Después de estar 3 horas analizando el bug sin poder solucionarlo y leer muchas formas, encontramos la solución al problema.

https://stackoverflow.com/questions/14555262/cannot-bulk-load-operating-system-error-code-5-access-is-denied#:~:text=This%20error%20appears%20when%20you,%3EAdvanced%20%2D%3E%20Find%20Now

Se logro solucionar, el error era que el servidor estaba en express por lo que SQL Express debía tener permisos para poder leer el XML. Una vez se pudo correr el SP había varios errores y al corregirlo el código quedo así:

ALTER PROCEDURE [dbo].[CargarXML]

AS

BEGIN

    DECLARE @Datos XML

    -- Aquí se asigna la ruta del archivo XML directamente en el código

    DECLARE @RutaXML NVARCHAR(500) = 'C:\Users\vales\OneDrive\Escritorio\datos.xml'

    -- Para cargar el archivo con una variable, CHAR(39) son comillas simples

    DECLARE @Comando NVARCHAR(500) = 'SELECT @Datos = D FROM OPENROWSET (BULK '  + CHAR(39) + @RutaXML + CHAR(39) + ', SINGLE_BLOB) AS Datos(D)' -- comando que va a ejecutar el sql dinámico

    DECLARE @Parametros NVARCHAR(500)

    SET @Parametros = N'@Datos XML OUTPUT' -- parámetros del sql dinámico

    EXECUTE sp_executesql @Comando, @Parametros, @Datos OUTPUT -- ejecutamos el comando que hicimos dinámicamente

    DECLARE @hdoc INT /*Creamos hdoc que va a ser un identificador*/

    EXEC sp_xml_preparedocument @hdoc OUTPUT, @Datos /*Toma el identificador y a la variable con el documento y las asocia*/

    -- Inserta en la tabla Puesto

    INSERT INTO [dbo].[Puesto] 

([Nombre]

, [SalarioxHora])

    SELECT 

        P.Nombre

        , SalarioxHora

    FROM OPENXML (@hdoc, '/Datos/Puestos/Puesto' , 1)

    WITH (

        Nombre VARCHAR(64) '@Nombre'

        ,SalarioxHora MONEY '@SalarioxHora'

    ) AS P

    -- Inserta en la tabla TipoEvento

    INSERT INTO [dbo].[TipoEvento] 

([id]

, [Nombre])

    SELECT 

        TE.Id

        , TE.Nombre

    FROM OPENXML (@hdoc, '/Datos/TiposEvento/TipoEvento' , 1)

    WITH (

        Id int '@Id'

        , Nombre VARCHAR(64) '@Nombre'

    ) AS TE

    -- Inserta en la tabla TipoMovimiento

    INSERT INTO [dbo].[TipoMovimiento] 

([Id]

, [Nombre]

, [TipoAccion])

    SELECT 

        TM.Id

        , TM.Nombre

        , CASE WHEN TipoAccion = 'Credito' THEN 'CR' 

           WHEN TipoAccion = 'Debito' THEN 'CD' 

           ELSE NULL

        END

    FROM OPENXML (@hdoc, '/Datos/TiposMovimientos/TipoMovimiento' , 1)

    WITH (

        Id INT '@Id'

        , Nombre VARCHAR(64) '@Nombre'

        , TipoAccion VARCHAR(64) '@TipoAccion'

    ) AS TM

    /*-- Limpia la tabla empleados

    DELETE FROM [dbo].[Empleado]

    -- Reinicia el identify

    DBCC CHECKIDENT ('Empleado', RESEED, 0)*/

    -- Inserta en la tabla Empleados

    INSERT INTO [dbo].[Empleado] 

([IdPuesto]

, [ValorDocumentoIdentidad]

, [Nombre]

, [FechaContratacion]

, [SaldoVacaciones]

, [EsActivo])

    SELECT 

        P.id

        , ValorDocumentoIdentidad

        , Emp.Nombre

        , FechaContratacion

        , 0

        , 1

    FROM OPENXML (@hdoc, '/Datos/Empleados/empleado' , 1)

    WITH (

        Puesto VARCHAR(64) '@Puesto'

        , ValorDocumentoIdentidad INT '@ValorDocumentoIdentidad'

        , Nombre VARCHAR(64) '@Nombre'

        , FechaContratacion DATE '@FechaContratacion'

    ) AS Emp

    INNER JOIN [dbo].[Puesto] AS P ON Emp.Puesto = P.Nombre;

    -- Inserta en la tabla Usuario

    INSERT INTO [dbo].[Usuario] 

([id]

, [Username]

, [Password])

    SELECT 

        Us.Id,

        Us.Nombre,

        Pass

    FROM OPENXML (@hdoc, '/Datos/Usuarios/usuario' , 1)

    WITH (

        Id INT '@Id'

        , Nombre VARCHAR(64) '@Nombre'

        , Pass NVARCHAR(16) '@Pass'

    ) AS Us

    -- Inserta en la tabla Movimiento

    INSERT INTO [dbo].[Movimiento] 

([IdEmpleado]

, [IdTipoMovimiento]

, [Fecha]

, [Monto]

, [NuevoSaldo]

, [IdPostByUser]

, [PostInIP]

, [PostTime])

    SELECT 

        E.id

        , TM.id

        , Mov.Fecha

        , Mov.Monto

, 0.0

        , U.id

        , Mov.PostInIP

        , Mov.PostTime

    FROM OPENXML (@hdoc, '/Datos/Movimientos/movimiento' , 1)

    WITH (

        ValorDocId INT '@ValorDocId'

        , IdTipoMovimiento VARCHAR(64) '@IdTipoMovimiento'

        , Fecha DATE '@Fecha'

        , Monto INT '@Monto'

        , PostByUser VARCHAR(64) '@PostByUser'

        , PostInIP VARCHAR(64) '@PostInIP'

        , PostTime DATETIME '@PostTime'

    ) AS Mov

    INNER JOIN [dbo].[Empleado] AS E ON Mov.ValorDocId = E.ValorDocumentoIdentidad

    INNER JOIN [dbo].[TipoMovimiento] AS TM ON Mov.IdTipoMovimiento = TM.Nombre

    INNER JOIN [dbo].[Usuario] AS U ON Mov.PostByUser = U.Username;

    -- Inserta en la tabla Error

    INSERT INTO [dbo].[Error] 

([Codigo]

, [Descripcion])

    SELECT 

        Codigo

        , Descripcion

    FROM OPENXML (@hdoc, '/Datos/Error/error' , 1)

    WITH (

        Codigo INT '@Codigo',

        Descripcion VARCHAR(2000) '@Descripcion'

    )

    EXEC sp_xml_removedocument @hdoc /*Remueve el documento XML de la memoria*/

END






viernes, 5 de abril de 2024

Entrada 2 - 05/04/24

Hora Inicio: 1:00 p.m

Hora Fin: 3:00 p.m

Horas Trabajadas: 3 horas

Modalidad: Virtual


En el proyecto de Visual Studio se creó una prueba para poder ver el tamaño de las imágenes en los botones de Update, Delete y Consulta.





Una vez hecha esta prueba se decidió cambiar las imágenes a color blanco para que se vieran más e iniciar con la creación del SP para cargar los datos del XML. Teníamos una duda al respecto por lo cual se le realizo una consulta al profesor sobre el SP para cargar el XML

Una vez resuelta esta duda se creó un SP básico el cual es el siguiente:


CREATE PROCEDURE [dbo].[CargarXML]

    -- Parametro de entrada

    @inRutaXML NVARCHAR(500)

AS

DECLARE @Datos xml/*Declaramos la variable Datos como un tipo XML*/

 -- Para cargar el archivo con una variable, CHAR(39) son comillas simples

DECLARE @Comando NVARCHAR(500)= 'SELECT @Datos = D FROM OPENROWSET (BULK '  + CHAR(39) + @inRutaXML + CHAR(39) + ', SINGLE_BLOB) AS Datos(D)' -- comando que va a ejecutar el sql dinamico

DECLARE @Parametros NVARCHAR(500)

SET @Parametros = N'@Datos xml OUTPUT' --parametros del sql dinamico

EXECUTE sp_executesql @Comando, @Parametros, @Datos OUTPUT -- ejecutamos el comando que hicimos dinamicamente

DECLARE @hdoc int /*Creamos hdoc que va a ser un identificador*/

EXEC sp_xml_preparedocument @hdoc OUTPUT, @Datos/*Toma el identificador y a la variable con el documento y las asocia*/

INSERT INTO [dbo].[Puesto]

           ([Nombre] /*Inserta en la tabla Puestos*/

   , [SalarioxHora])

SELECT *

FROM OPENXML (@hdoc, '/Datos/Puestos/Puesto' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el 

PATH del nodo y el 1 que sirve para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

Nombre VARCHAR(64)

, SalarioXHora MONEY

    )

INSERT INTO [dbo].[TipoEvento]

([Id]

, [Nombre])/*Inserta en la tabla Tipo Evento*/

SELECT *

FROM OPENXML (@hdoc, '/Datos/TipoEventos/TipoEvento' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 1 que sirve 

para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

Nombre VARCHAR(64)

    )

INSERT INTO [dbo].[TipoMovimiento]

           ([Id]

   , [Nombre]

   , [TipoAccion])

SELECT *

FROM OPENXML (@hdoc, '/Datos/TiposMovimientos/TiposMovimiento' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 

1 que sirve para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

Nombre VARCHAR(64)

, TipoAccion bit

    )

DELETE FROM [dbo].[Empleado]/*Limpia la tabla empleados*/

DBCC CHECKIDENT ('id', RESEED, 1)/*Reinicia el identify*/

INSERT INTO [dbo].[Empleado]

           ([IdPuesto]

           , [ValorDocumentoIdentidad]

           , [Nombre]

           , [FechaContratacion]

           , [SaldoVacaciones]

           , [EsActivo])/*Inserta en la tabla Empleados*/

SELECT *

FROM OPENXML (@hdoc, '/Datos/Empleados/Empleado' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 1 que sirve

para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

    Puesto INT

, ValorDocumentoIdentidad INT

, Nombre VARCHAR(64)

, FechaContratacion DATE

    )

INSERT INTO [dbo].[Usuario]

           ([id]

           , [Username]

           , [Password])

SELECT *

FROM OPENXML (@hdoc, '/Datos/Usuarios/usuario' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 1 que sirve

para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

    Id INT

, Nombre VARCHAR(64)

, Pass NVARCHAR(16)

    )

INSERT INTO [dbo].[Movimiento]

           ([IdEmpleado]

   , [IdTipoMovimiento]

           , [Fecha]

   , [Monto]

   , [IdPostByUser]

   , [PostInIP]

   , [PostTime])

SELECT *

FROM OPENXML (@hdoc, '/Datos/Movimientos/movimiento' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 1 que sirve

para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

    ValorDocId INT

, IdTipoMovimiento INT

, Fecha DATE

, Monto INT

, PostByUser INT

, PostInIP VARCHAR(64)

, PostTime DATETIME

    )

INSERT INTO [dbo].[Error]

           ([Codigo]

   , [Descripcion])

SELECT *

FROM OPENXML (@hdoc, '/Datos/Error/error' , 1)/*Lee los contenidos del XML y para eso necesita un identificador,el PATH del nodo y el 1 que sirve

para retornar solo atributos*/

WITH(/*Dentro del WITH se pone el nombre y el tipo de los atributos a retornar*/

    Codigo INT

, Descripcion VARCHAR(2000)

    )

EXEC sp_xml_removedocument @hdoc/*Remueve el documento XML de la memoria*/



Entrada 8 - 20/04/24

Hora Inicio: 8 p.m Hora Fin: 10 p.m Horas Trabajadas: 2 horas Modalidad: Virtual Nos reunimos para terminar los últimos detalles del proyect...