diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..cea4d3f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..eb0d3fb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "c:/Users/eduar/OneDrive/Escritorio/repositorios/Forks/DataFire/server/services", + "program": "c:/Users/eduar/OneDrive/Escritorio/repositorios/Forks/DataFire/server/services/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c9e66f1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index c124f4d..57d2687 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -176,6 +176,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -204,26 +228,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mutex: dependency: transitive description: @@ -268,10 +292,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_provider: dependency: transitive description: @@ -437,14 +461,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "13.0.0" win32: dependency: transitive description: @@ -462,5 +486,5 @@ packages: source: hosted version: "1.0.0" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" diff --git a/server/config/config.js b/server/config/config.js index 426fce9..7d9a98c 100644 --- a/server/config/config.js +++ b/server/config/config.js @@ -1,8 +1,8 @@ -require("dotenv").config() +require('dotenv').config(); const config = { env: process.env.NODE_ENV || 'dev', - isProd: process.env.NODE_ENV === "production", + isProd: process.env.NODE_ENV === 'production', port: process.env.PORT || 3001, dbUser: process.env.DB_USER, dbPassword: process.env.DB_PASSWORD, @@ -10,6 +10,8 @@ const config = { dbName: process.env.DB_NAME, dbPort: process.env.DB_PORT, dbUrl: process.env.DATABASE_URL, + apikey: process.env.API_KEY, + jwtsecret: process.env.JWT_SECRET, }; module.exports = { config }; diff --git a/server/db/migrations/20231215215840-add-customerId.js b/server/db/migrations/20231215215840-add-customerId.js deleted file mode 100644 index c2e1121..0000000 --- a/server/db/migrations/20231215215840-add-customerId.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const { PROJECT_TABLE, ProjectSchema } = require('../models/proyectos.model'); - -module.exports = { - up: async (queryInterface) => { - await queryInterface.addColumn(PROJECT_TABLE, "customer_id", ProjectSchema.customerId); - }, - - down: async (queryInterface) => { - await queryInterface.removeColumn(PROJECT_TABLE, "customer_id") - } -}; diff --git a/server/db/migrations/20231219210347-remove-abono-incorrect.js b/server/db/migrations/20231219210347-remove-abono-incorrect.js deleted file mode 100644 index 3e4128c..0000000 --- a/server/db/migrations/20231219210347-remove-abono-incorrect.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const { PROJECT_CUSTOMER_TABLE, ProjectCustomerSchema } = require('../models/proyecto-cliente.model'); - -module.exports = { - up: async (queryInterface) => { - await queryInterface.removeColumn(PROJECT_CUSTOMER_TABLE, "abono"); - }, - - down: async (queryInterface) => { - await queryInterface.addColumn(PROJECT_CUSTOMER_TABLE, "abono", ProjectCustomerSchema.abono) - } -}; diff --git a/server/db/migrations/20231221183127-add_customername_projectname.js b/server/db/migrations/20231221183127-add_customername_projectname.js deleted file mode 100644 index 5282a6c..0000000 --- a/server/db/migrations/20231221183127-add_customername_projectname.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -const { - PROJECT_CUSTOMER_TABLE, - ProjectCustomerSchema, -} = require('../models/proyecto-cliente.model'); - -module.exports = { - up: async (queryInterface) => { - await queryInterface.addColumn( - PROJECT_CUSTOMER_TABLE, - 'project_name', - ProjectCustomerSchema.project_name, - ); - await queryInterface.addColumn( - PROJECT_CUSTOMER_TABLE, - 'customer_name', - ProjectCustomerSchema.customer_name, - ); - }, - - down: async (queryInterface) => { - await queryInterface.removeColumn(PROJECT_CUSTOMER_TABLE, 'project_name'); - await queryInterface.removeColumn(PROJECT_CUSTOMER_TABLE, 'customer_name'); - }, -}; diff --git a/server/db/migrations/20240111225052-update-abonos-total.js b/server/db/migrations/20240111225052-update-abonos-total.js new file mode 100644 index 0000000..a705ad6 --- /dev/null +++ b/server/db/migrations/20240111225052-update-abonos-total.js @@ -0,0 +1,34 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION actualizar_monto_abonado(nuevo_monto INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET abonado = abonado + nuevo_monto + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION restar_monto_abonado(monto_a_restar INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET abonado = abonado - monto_a_restar + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS actualizar_monto_abonado(INT, INT); + `); + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS restar_monto_abonado(INT, INT); + `); + }, +}; diff --git a/server/db/migrations/20240112081704-services_total.js b/server/db/migrations/20240112081704-services_total.js new file mode 100644 index 0000000..e9ba181 --- /dev/null +++ b/server/db/migrations/20240112081704-services_total.js @@ -0,0 +1,38 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + // Agrega una función para actualizar el 'costo' en la tabla de proyectos al insertar un nuevo servicio + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION actualizar_costo_proyecto_al_insertar_servicio(nuevo_costo INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET costo = costo + nuevo_costo + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + + // Agrega una función para actualizar el 'costo' en la tabla de proyectos al eliminar un servicio + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION actualizar_costo_proyecto_al_eliminar_servicio(costo_eliminar INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET costo = costo - costo_eliminar + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + }, + + down: async (queryInterface, Sequelize) => { + // Elimina las funciones + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS actualizar_costo_proyecto_al_insertar_servicio(INT, INT); + `); + + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS actualizar_costo_proyecto_al_eliminar_servicio(INT, INT); + `); + }, +}; diff --git a/server/db/migrations/20240112222459-calculate_remaining.js b/server/db/migrations/20240112222459-calculate_remaining.js new file mode 100644 index 0000000..de20f5a --- /dev/null +++ b/server/db/migrations/20240112222459-calculate_remaining.js @@ -0,0 +1,32 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION update_remaining() + RETURNS TRIGGER AS $$ + BEGIN + NEW.remaining := NEW.costo - NEW.abonado; + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + `); + + await queryInterface.sequelize.query(` + CREATE TRIGGER before_update_proyectos + BEFORE UPDATE ON proyectos + FOR EACH ROW + EXECUTE FUNCTION update_remaining(); + `); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.query(` + DROP TRIGGER IF EXISTS before_update_proyectos ON proyectos; + `); + + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS update_remaining(); + `); + }, +}; diff --git a/server/db/migrations/20240124175259-users_table.js b/server/db/migrations/20240124175259-users_table.js new file mode 100644 index 0000000..a9a7a33 --- /dev/null +++ b/server/db/migrations/20240124175259-users_table.js @@ -0,0 +1,13 @@ +'use strict'; + +const { USER_TABLE, UserSchema } = require('../models/users.model'); + +module.exports = { + up: async (queryInterface) => { + await queryInterface.createTable(USER_TABLE, UserSchema); + }, + + down: async (queryInterface) => { + await queryInterface.dropTable(USER_TABLE); + }, +}; diff --git a/server/db/migrations/20240213192733-add-weeks-worked-column.js b/server/db/migrations/20240213192733-add-weeks-worked-column.js new file mode 100644 index 0000000..cab0622 --- /dev/null +++ b/server/db/migrations/20240213192733-add-weeks-worked-column.js @@ -0,0 +1,42 @@ +'use strict'; + +const { DataTypes } = require('sequelize'); + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('proyectos', 'duracion', { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }); + + // Recuperar todos los proyectos existentes y actualizar la duracion + const projects = await queryInterface.sequelize.query( + 'SELECT * FROM proyectos', + { type: Sequelize.QueryTypes.SELECT }, + ); + + // Actualizar la duracion para cada proyecto + await Promise.all( + projects.map(async (project) => { + const start = new Date(project.fecha_inicio); + const end = new Date(project.fecha_fin); + + const durationInWeeks = Math.ceil( + (end - start) / (7 * 24 * 60 * 60 * 1000), + ); + await queryInterface.sequelize.query( + `UPDATE proyectos SET duracion = :duration WHERE id = :projectId`, + { + replacements: { duration: durationInWeeks, projectId: project.id }, + type: Sequelize.QueryTypes.UPDATE, + }, + ); + }), + ); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('proyectos', 'duracion'); + }, +}; diff --git a/server/db/migrations/20240214200959-add-nominas-column.js b/server/db/migrations/20240214200959-add-nominas-column.js new file mode 100644 index 0000000..4f06a0b --- /dev/null +++ b/server/db/migrations/20240214200959-add-nominas-column.js @@ -0,0 +1,13 @@ +'use strict'; + +const { NOMINA_TABLE, NominaSchema } = require('../models/nominas.model'); + +module.exports = { + up: async (queryInterface) => { + await queryInterface.createTable(NOMINA_TABLE, NominaSchema); + }, + + down: async (queryInterface) => { + await queryInterface.dropTable(NOMINA_TABLE); + }, +}; diff --git a/server/db/migrations/20240215200347-add_fecha_costo.js b/server/db/migrations/20240215200347-add_fecha_costo.js new file mode 100644 index 0000000..1832c20 --- /dev/null +++ b/server/db/migrations/20240215200347-add_fecha_costo.js @@ -0,0 +1,17 @@ +'use strict'; + +const { SERVICES_TABLE, ServiceSchema } = require('../models/servicios.model'); + +module.exports = { + up: async (queryInterface) => { + await queryInterface.addColumn( + SERVICES_TABLE, + 'fecha_costo', + ServiceSchema.fecha_costo, + ); + }, + + down: async (queryInterface) => { + await queryInterface.removeColumn(SERVICES_TABLE, 'fecha_costo'); + }, +}; diff --git a/server/db/migrations/20231215182706-add-costo.js b/server/db/migrations/20240228222307-finishedProjects.js similarity index 52% rename from server/db/migrations/20231215182706-add-costo.js rename to server/db/migrations/20240228222307-finishedProjects.js index 9ec8f0f..db610c0 100644 --- a/server/db/migrations/20231215182706-add-costo.js +++ b/server/db/migrations/20240228222307-finishedProjects.js @@ -4,12 +4,14 @@ const { PROJECT_TABLE, ProjectSchema } = require('../models/proyectos.model'); module.exports = { up: async (queryInterface) => { - await queryInterface.addColumn(PROJECT_TABLE, "costo", ProjectSchema.costo); + await queryInterface.addColumn( + PROJECT_TABLE, + 'status', + ProjectSchema.status, + ); }, down: async (queryInterface) => { - await queryInterface.removeColumn(PROJECT_TABLE, "costo") - } + await queryInterface.removeColumn(PROJECT_TABLE, 'status'); + }, }; - - diff --git a/server/db/migrations/20240301082035-prestamos.js b/server/db/migrations/20240301082035-prestamos.js new file mode 100644 index 0000000..289735e --- /dev/null +++ b/server/db/migrations/20240301082035-prestamos.js @@ -0,0 +1,13 @@ +'use strict'; + +const { PRESTAMO_TABLE, PrestamoSchema } = require('../models/prestamos.model'); + +module.exports = { + up: async (queryInterface) => { + await queryInterface.createTable(PRESTAMO_TABLE, PrestamoSchema); + }, + + down: async (queryInterface) => { + await queryInterface.dropTable(PRESTAMO_TABLE); + }, +}; diff --git a/server/db/migrations/20240312162758-update_workers_schema.js b/server/db/migrations/20240312162758-update_workers_schema.js new file mode 100644 index 0000000..78c3f06 --- /dev/null +++ b/server/db/migrations/20240312162758-update_workers_schema.js @@ -0,0 +1,30 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.changeColumn('trabajadores', 'salary', { + type: Sequelize.FLOAT, + allowNull: false, + defaultValue: 0, + }); + await queryInterface.addColumn('trabajadores', 'salary_hour', { + type: Sequelize.FLOAT, + allowNull: false, + after: 'position', + defaultValue: 0, // Especifica dónde quieres que se añada la columna + }); + await queryInterface.addColumn('trabajadores', 'semanal_hours', { + type: Sequelize.INTEGER, + allowNull: false, + after: 'salary_hour', + defaultValue: 0, + }); + // Asegura que la columna salary exista y tenga la configuración deseada + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('trabajadores', 'salary_hour'); + await queryInterface.removeColumn('trabajadores', 'semanal_hours'); + // Opcionalmente, revierte los cambios en la columna salary si es necesario + }, +}; diff --git a/server/db/migrations/20240312183604-TablenominasSemanales.js b/server/db/migrations/20240312183604-TablenominasSemanales.js new file mode 100644 index 0000000..69758b6 --- /dev/null +++ b/server/db/migrations/20240312183604-TablenominasSemanales.js @@ -0,0 +1,19 @@ +'use strict'; + +const { + NOMINAS_SEMANALES_TABLE, + NominasSemanalesSchema, +} = require('../models/nominasSemanales'); + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable( + NOMINAS_SEMANALES_TABLE, + NominasSemanalesSchema, + ); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable(NOMINAS_SEMANALES_TABLE); + }, +}; diff --git a/server/db/migrations/20240323202534-columnWorkerCosts.js b/server/db/migrations/20240323202534-columnWorkerCosts.js new file mode 100644 index 0000000..511be1e --- /dev/null +++ b/server/db/migrations/20240323202534-columnWorkerCosts.js @@ -0,0 +1,17 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('trabajadores', 'WorkerCost', { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + }); + + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('trabajadores', 'WorkerCost'); + + }, +}; diff --git a/server/db/migrations/20240323203722-TableWorkerCost.js b/server/db/migrations/20240323203722-TableWorkerCost.js new file mode 100644 index 0000000..d44dab3 --- /dev/null +++ b/server/db/migrations/20240323203722-TableWorkerCost.js @@ -0,0 +1,19 @@ +'use strict'; + +const { + WORKERCOSTS_TABLE, + WorkerCostsSchema, +} = require('../models/WorkerCosts.model'); + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable( + WORKERCOSTS_TABLE, + WorkerCostsSchema, + ); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable(WORKERCOSTS_TABLE); + }, +}; diff --git a/server/db/migrations/20240411162200-updateNominasDate.js b/server/db/migrations/20240411162200-updateNominasDate.js new file mode 100644 index 0000000..6002f9b --- /dev/null +++ b/server/db/migrations/20240411162200-updateNominasDate.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.changeColumn( + 'nominas_semanales', + 'fecha_inicio_semana', + { + type: Sequelize.DATE, + }, + ); + await queryInterface.changeColumn('nominas_semanales', 'fecha_fin_semana', { + type: Sequelize.DATE, + }); + }, + + async down(queryInterface, Sequelize) {}, +}; diff --git a/server/db/migrations/20240412181629-addAnticipoColumn.js b/server/db/migrations/20240412181629-addAnticipoColumn.js new file mode 100644 index 0000000..267eaef --- /dev/null +++ b/server/db/migrations/20240412181629-addAnticipoColumn.js @@ -0,0 +1,30 @@ +'use strict'; + +const { PROJECT_TABLE, ProjectSchema } = require('../models/proyectos.model'); + +module.exports = { + up: async (queryInterface, Sequelize) => { + // Step 1: Add column as nullable + await queryInterface.addColumn(PROJECT_TABLE, 'ganancia', { + ...ProjectSchema.ganancia, + allowNull: true, + }); + + // Step 2: Update existing records + await queryInterface.sequelize.query( + `UPDATE ${PROJECT_TABLE} SET ganancia = abonado - costo`, + ); + + // Step 3: Alter column to non-nullable + await queryInterface.changeColumn(PROJECT_TABLE, 'ganancia', { + ...ProjectSchema.ganancia, + allowNull: false, + }); + }, + + down: async (queryInterface) => { + await queryInterface.removeColumn(PROJECT_TABLE, 'anticipo'); + await queryInterface.removeColumn(PROJECT_TABLE, 'presupuesto'); + await queryInterface.removeColumn(PROJECT_TABLE, 'ganancia'); + }, +}; diff --git a/server/db/migrations/20240415064624-adjustmentsTable.js b/server/db/migrations/20240415064624-adjustmentsTable.js new file mode 100644 index 0000000..55f265f --- /dev/null +++ b/server/db/migrations/20240415064624-adjustmentsTable.js @@ -0,0 +1,16 @@ +'use strict'; + +const { + ADJUSTMENTS_TABLE, + AdjustmentSchema, +} = require('../models/adjustmentsProject.models'); + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable(ADJUSTMENTS_TABLE, AdjustmentSchema); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable(ADJUSTMENTS_TABLE); + }, +}; diff --git a/server/db/migrations/20240415170708-addToolsTable.js b/server/db/migrations/20240415170708-addToolsTable.js new file mode 100644 index 0000000..bd8ca16 --- /dev/null +++ b/server/db/migrations/20240415170708-addToolsTable.js @@ -0,0 +1,13 @@ +'use strict'; + +const { TOOLS_TABLE, ToolsSchema } = require('../models/tools.model'); + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable(TOOLS_TABLE, ToolsSchema); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable(TOOLS_TABLE); + }, +}; diff --git a/server/db/migrations/20240424070531-changeDefaultGanancia.js b/server/db/migrations/20240424070531-changeDefaultGanancia.js new file mode 100644 index 0000000..9b6b6c8 --- /dev/null +++ b/server/db/migrations/20240424070531-changeDefaultGanancia.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.changeColumn('proyectos', 'ganancia', { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + }); + + }, + + async down(queryInterface, Sequelize) { + + }, +}; diff --git a/server/db/migrations/20240426220212-columnYearsWorked.js b/server/db/migrations/20240426220212-columnYearsWorked.js new file mode 100644 index 0000000..89c70af --- /dev/null +++ b/server/db/migrations/20240426220212-columnYearsWorked.js @@ -0,0 +1,30 @@ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up (queryInterface, Sequelize) { + /** + * Add altering commands here. + * + * Example: + * await queryInterface.createTable('users', { id: Sequelize.INTEGER }); + */ + + await queryInterface.addColumn('trabajadores', 'years_worked', { + type: Sequelize.INTEGER, + allowNull: true, + after: 'semanal_hours', + defaultValue: 0, // Especifica dónde quieres que se añada la columna + }); + }, + + async down (queryInterface,) { + /** + * Add reverting commands here. + * + * Example: + * await queryInterface.dropTable('users'); + */ + await queryInterface.removeColumn('trabajadores', 'years_worked'); + } +}; diff --git a/server/db/models/WorkerCosts.model.js b/server/db/models/WorkerCosts.model.js new file mode 100644 index 0000000..e62db11 --- /dev/null +++ b/server/db/models/WorkerCosts.model.js @@ -0,0 +1,95 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); + +const { WORKER_TABLE } = require('./trabajadores.model'); + +const WORKERCOSTS_TABLE = 'WorkerCosts'; + +const WorkerCostsSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + worker_id: { + field: 'worker_id', + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: WORKER_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + }, + service: { + allowNull: false, + type: DataTypes.STRING, + }, + cost: { + allowNull: false, + type: DataTypes.INTEGER, + }, + fecha_costo: { + allowNull: true, + type: DataTypes.DATE, + default: '2019-01-12T08:28:24.762Z', + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class WorkerCost extends Model { + static associate(models) { + this.belongsTo(models.Worker, { + as: 'Worker', + foreignKey: 'worker_id', + }); + } + + static config(sequelize) { + return { + sequelize, + tableName: WORKERCOSTS_TABLE, + modelName: 'WorkerCost', + timestamps: false, + hooks: { + afterCreate: async (workerCost) => { + const worker = await this.sequelize.models.Worker.findByPk(workerCost.worker_id); + if (worker) { + worker.WorkerCost += workerCost.cost; + await worker.save(); + } + }, + beforeDestroy: async (workerCost ) => { + const worker = await this.sequelize.models.Worker.findByPk(workerCost.worker_id); + if (worker) { + worker.WorkerCost -= workerCost.cost; + await worker.save(); + } + }, + beforeUpdate: async (workerCost ) => { + const worker = await this.sequelize.models.Worker.findByPk(workerCost.worker_id); + if (worker) { + // Restar el coste antiguo y sumar el nuevo + if (workerCost._previousDataValues.cost !== workerCost.cost) { + worker.WorkerCost = worker.WorkerCost - workerCost._previousDataValues.cost + workerCost.cost; + await worker.save(); + } + } + }, + } + }; + } +} + + +module.exports = { + WORKERCOSTS_TABLE, + WorkerCostsSchema, + WorkerCost, +}; diff --git a/server/db/models/abonos.model.js b/server/db/models/abonos.model.js index cc44fa8..fcbd9fc 100644 --- a/server/db/models/abonos.model.js +++ b/server/db/models/abonos.model.js @@ -1,65 +1,65 @@ -const { Model, DataTypes, Sequelize } = require("sequelize"); -const { PROJECT_TABLE } = require("./proyectos.model"); -const { CUSTOMER_TABLE } = require("./cliente.model"); +const { Model, DataTypes, Sequelize } = require('sequelize'); +const { PROJECT_TABLE } = require('./proyectos.model'); +const { CUSTOMER_TABLE } = require('./cliente.model'); -const ABONOS_TABLE = "Abonos"; +const ABONOS_TABLE = 'Abonos'; const AbonosSchema = { id: { allowNull: false, autoIncrement: true, primaryKey: true, - type: DataTypes.INTEGER + type: DataTypes.INTEGER, }, monto: { allowNull: false, - type: DataTypes.INTEGER + type: DataTypes.INTEGER, }, fecha_abono: { allowNull: false, - type: DataTypes.DATE + type: DataTypes.DATE, }, projectId: { - field: "proyecto_id", + field: 'proyecto_id', allowNull: false, type: DataTypes.INTEGER, references: { model: PROJECT_TABLE, - key: "id" + key: 'id', }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - defaultValue: 0 + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + defaultValue: 0, }, customerId: { - field: "customer_id", + field: 'customer_id', allowNull: false, type: DataTypes.INTEGER, references: { model: CUSTOMER_TABLE, - key: "id" + key: 'id', }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - defaultValue: 0 + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + defaultValue: 0, }, createdAt: { allowNull: false, type: DataTypes.DATE, - field: "create_at", - defaultValue: Sequelize.NOW - } + field: 'create_at', + defaultValue: Sequelize.NOW, + }, }; class Abonos extends Model { static associate(models) { this.belongsTo(models.Project, { - as: "project", - foreignKey: "projectId" + as: 'project', + foreignKey: 'projectId', }); this.belongsTo(models.Customer, { - as: "customer", - foreignKey: "customerId" + as: 'customer', + foreignKey: 'customerId', }); } @@ -67,8 +67,23 @@ class Abonos extends Model { return { sequelize, tableName: ABONOS_TABLE, - modelName: "Abonos", - timestamps: false + modelName: 'Abonos', + timestamps: false, + hooks: { + afterCreate: async (abono,) => { + setTimeout(async () => { + console.log(abono.monto); + const proyecto = await abono.getProject(); + if (proyecto.remaining === 0) { + proyecto.status = true; + await proyecto.save(); // Guarda los cambios en la base de datos + } else { + proyecto.status = false; + await proyecto.save(); + } + }, 2000); // 2000 milisegundos = 2 segundos + }, + }, }; } } diff --git a/server/db/models/adjustmentsProject.models.js b/server/db/models/adjustmentsProject.models.js new file mode 100644 index 0000000..4a04123 --- /dev/null +++ b/server/db/models/adjustmentsProject.models.js @@ -0,0 +1,89 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); +const { PROJECT_TABLE } = require('./proyectos.model'); + + +const ADJUSTMENTS_TABLE = 'Adjustments'; + +const AdjustmentSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + monto: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + fecha_ajuste: { + allowNull: false, + type: DataTypes.DATE, + }, + projectId: { + field: 'proyecto_id', + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: PROJECT_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + defaultValue: 0, + }, + motive: { + allowNull: true, + type: DataTypes.STRING, + }, + operation: { + allowNull: false, + type: DataTypes.BOOLEAN, + default: false, + defaultValue: 0, + //true sera suma al presupuesto + //false sera resta al presupuesto + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class Adjustments extends Model { + static associate(models) { + this.belongsTo(models.Project, { + as: 'project', + foreignKey: 'projectId', + }); + } + + static config(sequelize) { + return { + sequelize, + tableName: ADJUSTMENTS_TABLE, + modelName: 'Adjustments', + timestamps: false, + hooks: { + afterCreate: async (adjustment) => { + const project = await sequelize.models.Project.findByPk( + adjustment.projectId, + ); + if (project) { + let newBudget = adjustment.operation + ? project.presupuesto + adjustment.monto + : project.presupuesto - adjustment.monto; + await project.update({ presupuesto: newBudget }); + }}, + }, + }; + } +} + +module.exports = { + ADJUSTMENTS_TABLE, + AdjustmentSchema, + Adjustments, +}; diff --git a/server/db/models/calculos.models.js b/server/db/models/calculos.models.js new file mode 100644 index 0000000..bd2ca21 --- /dev/null +++ b/server/db/models/calculos.models.js @@ -0,0 +1,58 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); + +const { PROJECT_TABLE } = require('./proyectos.model'); + +const CalculosHugo_TABLE = 'CalculosHugo'; + +const CalculosHugoSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + amount: { + allowNull: false, + type: DataTypes.INTEGER, + }, + service: { + allowNull: false, + type: DataTypes.STRING, + }, + cost: { + allowNull: false, + type: DataTypes.INTEGER, + }, + fecha_costo: { + allowNull: true, + type: DataTypes.DATE, + default: '2019-01-12T08:28:24.762Z', + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class CalculosHugo extends Model { + static associate(models) { + + } + + static config(sequelize) { + return { + sequelize, + tableName: CalculosHugo_TABLE, + modelName: 'Calculo', + timestamps: false, + }; + } +} + +module.exports = { + CalculosHugo_TABLE, + CalculosHugoSchema, + CalculosHugo, +}; diff --git a/server/db/models/index.js b/server/db/models/index.js index 2484626..a382030 100644 --- a/server/db/models/index.js +++ b/server/db/models/index.js @@ -2,6 +2,10 @@ const { Customer, CustomerSchema } = require('./cliente.model'); const { Project, ProjectSchema } = require('./proyectos.model'); const { Worker, WorkerSchema } = require('./trabajadores.model'); const { Service, ServiceSchema } = require('./servicios.model'); +const { User, UserSchema } = require('./users.model'); +const { Prestamo, PrestamoSchema } = require('./prestamos.model'); +const { WorkerCost, WorkerCostsSchema } = require('./WorkerCosts.model'); + const { ProjectCustomer, ProjectCustomerSchema, @@ -12,19 +16,52 @@ const { } = require('./proyecto-trabajador.model'); const { Abonos, AbonosSchema } = require('./abonos.model'); +//finances +const { Nomina, NominaSchema } = require('./nominas.model'); + +const { + NominasSemanales, + NominasSemanalesSchema, +} = require('./nominasSemanales'); + +const { CalculosHugo, CalculosHugoSchema } = require('./calculos.models'); + +const { + Adjustments, + AdjustmentSchema, +} = require('./adjustmentsProject.models'); + +const { Tools, ToolsSchema } = require('./tools.model'); + function setupModels(sequelize) { - Customer.init(CustomerSchema, Customer.config(sequelize)), - Project.init(ProjectSchema, Project.config(sequelize)), - Worker.init(WorkerSchema, Worker.config(sequelize)), - Service.init(ServiceSchema, Service.config(sequelize)); - ProjectWorker.init(ProjectWorkerSchema, ProjectWorker.config(sequelize)), - ProjectCustomer.init( - ProjectCustomerSchema, - ProjectCustomer.config(sequelize), - ), - Abonos.init(AbonosSchema, Abonos.config(sequelize)), - Project.associate(sequelize.models); + Customer.init(CustomerSchema, Customer.config(sequelize)); + Project.init(ProjectSchema, Project.config(sequelize)); + Worker.init(WorkerSchema, Worker.config(sequelize)); + WorkerCost.init(WorkerCostsSchema, WorkerCost.config(sequelize)); + NominasSemanales.init( + NominasSemanalesSchema, + NominasSemanales.config(sequelize), + ); + Service.init(ServiceSchema, Service.config(sequelize)); + Nomina.init(NominaSchema, Nomina.config(sequelize)); + ProjectWorker.init(ProjectWorkerSchema, ProjectWorker.config(sequelize)); + User.init(UserSchema, User.config(sequelize)); + ProjectCustomer.init( + ProjectCustomerSchema, + ProjectCustomer.config(sequelize), + ); + Abonos.init(AbonosSchema, Abonos.config(sequelize)); + //finances + Prestamo.init(PrestamoSchema, Prestamo.config(sequelize)); + CalculosHugo.init(CalculosHugoSchema, CalculosHugo.config(sequelize)); + + Adjustments.init(AdjustmentSchema, Adjustments.config(sequelize)); + Tools.init(ToolsSchema, Tools.config(sequelize)); + + Project.associate(sequelize.models); Abonos.associate(sequelize.models); + Worker.associate(sequelize.models); + WorkerCost.associate(sequelize.models); } module.exports = setupModels; diff --git a/server/db/models/nominas.model.js b/server/db/models/nominas.model.js new file mode 100644 index 0000000..d6c01fb --- /dev/null +++ b/server/db/models/nominas.model.js @@ -0,0 +1,83 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); +const { PROJECT_TABLE } = require('./proyectos.model'); +const { WORKER_TABLE } = require('./trabajadores.model'); + +const NOMINA_TABLE = 'Nominas'; + +const NominaSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + project_id: { + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: PROJECT_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + worker_id: { + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: WORKER_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + amount_paid: { + allowNull: false, + type: DataTypes.INTEGER, + }, + weeks_worked: { + allowNull: false, + type: DataTypes.INTEGER, + }, + payment_dates: { + type: DataTypes.ARRAY(DataTypes.DATE), + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class Nomina extends Model { + static associate(models) { + this.belongsTo(models.Project, { + foreignKey: 'project_id', + as: 'project', + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); + this.belongsTo(models.Worker, { + foreignKey: 'worker_id', + as: 'worker', // This should match the alias used in your include in the service + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); + } + + static config(sequelize) { + return { + sequelize, + tableName: NOMINA_TABLE, + modelName: 'Nomina', + timestamps: false, + }; + } +} + +module.exports = { + NOMINA_TABLE, + NominaSchema, + Nomina, +}; diff --git a/server/db/models/nominasSemanales.js b/server/db/models/nominasSemanales.js new file mode 100644 index 0000000..12e6aa3 --- /dev/null +++ b/server/db/models/nominasSemanales.js @@ -0,0 +1,112 @@ +const { Model, DataTypes } = require('sequelize'); +const { WORKER_TABLE } = require('./trabajadores.model'); // Asegúrate de importar correctamente el modelo Worker + +const NOMINAS_SEMANALES_TABLE = 'nominas_semanales'; + +const NominasSemanalesSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + fecha_inicio_semana: { + allowNull: false, + type: DataTypes.DATE, + comment: 'Fecha de inicio de la semana de pago', + }, + fecha_fin_semana: { + allowNull: false, + type: DataTypes.DATE, + comment: 'Fecha de fin de la semana de pago', + }, + worker_id: { + field: 'worker_id', + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: WORKER_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + }, + nombre: { + allowNull: false, + type: DataTypes.STRING, + }, + salary_hour: { + allowNull: false, + type: DataTypes.FLOAT, + defaultValue: 0, + }, + horas_trabajadas: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + horas_extra: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + salary: { + allowNull: false, + type: DataTypes.FLOAT, + defaultValue: 0, + }, + isr: { + allowNull: true, + type: DataTypes.FLOAT, + defaultValue: 0, + }, + seguro_social: { + allowNull: true, + type: DataTypes.FLOAT, + defaultValue: 0, + }, + salario_final: { + allowNull: true, + type: DataTypes.FLOAT, + defaultValue: 0, + }, +}; + +class NominasSemanales extends Model { + static associate(models) { + NominasSemanales.belongsTo(models.Worker, { + as: 'worker', + foreignKey: 'worker_id', + }); + } + + static config(sequelize) { + return { + sequelize, + tableName: NOMINAS_SEMANALES_TABLE, + modelName: 'NominasSemanales', + timestamps: false, + hooks: { + beforeCreate: (nominasSemanales) => { + const salarioBase = + nominasSemanales.salary_hour * nominasSemanales.horas_trabajadas; + const salarioExtra = + nominasSemanales.salary_hour * 1.5 * nominasSemanales.horas_extra; + nominasSemanales.salary = salarioBase + salarioExtra; + nominasSemanales.isr = nominasSemanales.salary * 0.04; + nominasSemanales.seguro_social = nominasSemanales.salary * 0.02; + nominasSemanales.salario_final = + nominasSemanales.salary - + nominasSemanales.isr - + nominasSemanales.seguro_social; + }, + }, + }; + } +} + +module.exports = { + NOMINAS_SEMANALES_TABLE, + NominasSemanalesSchema, + NominasSemanales, +}; diff --git a/server/db/models/prestamos.model.js b/server/db/models/prestamos.model.js new file mode 100644 index 0000000..000e3aa --- /dev/null +++ b/server/db/models/prestamos.model.js @@ -0,0 +1,45 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); + +const PRESTAMO_TABLE = 'Prestamos'; + +const PrestamoSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + date_prestamo: { + allowNull: false, + type: DataTypes.DATE, + }, + amount_paid: { + allowNull: false, + type: DataTypes.INTEGER, + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class Prestamo extends Model { + static associate(models) {} + + static config(sequelize) { + return { + sequelize, + tableName: PRESTAMO_TABLE, + modelName: 'Prestamo', + timestamps: false, + }; + } +} + +module.exports = { + PRESTAMO_TABLE, + PrestamoSchema, + Prestamo, +}; diff --git a/server/db/models/proyecto-trabajador.model.js b/server/db/models/proyecto-trabajador.model.js index 5feccca..075d9ad 100644 --- a/server/db/models/proyecto-trabajador.model.js +++ b/server/db/models/proyecto-trabajador.model.js @@ -1,8 +1,8 @@ const { Model, DataTypes, Sequelize } = require('sequelize'); - const { PROJECT_TABLE } = require('./proyectos.model'); const { WORKER_TABLE } = require('./trabajadores.model'); - +const { Nomina, } = require('./nominas.model'); +const { Service } = require('./servicios.model'); const PROJECT_WORKER_TABLE = 'project_has_workers'; const ProjectWorkerSchema = { @@ -70,16 +70,83 @@ class ProjectWorker extends Model { }); } + static async calculateNomina(projectWorker) { + const { project_id, worker_id } = projectWorker; + + const project = await this.sequelize.models.Project.findByPk(project_id); + const worker = await this.sequelize.models.Worker.findByPk(worker_id); + + const amount_paid = worker.salary * project.duracion; + const weeks_worked = project.duracion; + const payment_dates = calculatePaymentDates( + project.fecha_inicio, + project.fecha_fin, + ); + + //this update total project + const newTotal = project.costo + amount_paid; + await project.update({ costo: newTotal }); + + await Nomina.create({ + project_id, + worker_id, + amount_paid, + weeks_worked, + payment_dates, + }); + + // create a cost for each week + for (const paymentDate of payment_dates) { + const serviceDescription = `Pago de ${worker.name} ${ + worker.last_name + } en semana del ${paymentDate.toLocaleDateString()}`; + const cost = worker.salary; + + // Agrega el atributo fecha_costo con la fecha del pago + const fecha_costo = paymentDate; + + await Service.create({ + project_id, + amount: amount_paid, + service: serviceDescription, + cost, + fecha_costo, + }); + } + } + static config(sequelize) { return { sequelize, tableName: PROJECT_WORKER_TABLE, modelName: 'ProjectWorker', timestamps: false, + hooks: { + afterCreate: async (projectWorker, ) => { + await ProjectWorker.calculateNomina(projectWorker); + }, + }, }; } } +function calculatePaymentDates(startDate, endDate) { + const payment_dates = []; + let currentDate = new Date(startDate); + + // Monday (1) + currentDate.setDate( + currentDate.getDate() + ((1 - currentDate.getDay() + 7) % 7), + ); + + while (currentDate <= endDate) { + payment_dates.push(new Date(currentDate)); + currentDate.setDate(currentDate.getDate() + 7); + } + + return payment_dates; +} + module.exports = { PROJECT_WORKER_TABLE, ProjectWorkerSchema, diff --git a/server/db/models/proyectos.model.js b/server/db/models/proyectos.model.js index cf8d7a2..e512e3c 100644 --- a/server/db/models/proyectos.model.js +++ b/server/db/models/proyectos.model.js @@ -1,7 +1,10 @@ const { Model, DataTypes, Sequelize } = require('sequelize'); + const { CUSTOMER_TABLE } = require('./cliente.model'); +const { Op } = require('sequelize'); + const PROJECT_TABLE = 'proyectos'; const ProjectSchema = { @@ -23,11 +26,52 @@ const ProjectSchema = { allowNull: false, type: DataTypes.DATE, }, + duracion: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + + costo_inicial: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + anticipo: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + presupuesto: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, costo: { allowNull: false, type: DataTypes.INTEGER, defaultValue: 0, }, + abonado: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + remaining: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + ganancia: { + allowNull: false, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + status: { + allowNull: false, + type: DataTypes.BOOLEAN, + defaultValue: false, + }, customerId: { field: 'customer_id', allowNull: false, @@ -38,7 +82,7 @@ const ProjectSchema = { }, onUpdate: 'CASCADE', onDelete: 'SET NULL', - defaultValue: 0, + defaultValue: 1, }, createdAt: { allowNull: false, @@ -66,15 +110,152 @@ class Project extends Model { as: 'services', foreignKey: 'project_id', }); + this.hasMany(models.Adjustments,{ + as: 'adjustments', + foreignKey: 'projectId', + }) } + static config(sequelize) { return { sequelize, tableName: PROJECT_TABLE, modelname: 'project', timestamps: false, + hooks: { + beforeCreate: async (project, ) => { + project.costo = project.costo_inicial; + project.abonado = project.anticipo; + + const start = new Date(project.fecha_inicio); + const end = new Date(project.fecha_fin); + + const durationInWeeks = Math.ceil( + (end - start) / (7 * 24 * 60 * 60 * 1000), + ); + project.duracion = durationInWeeks; + project.ganancia = project.abonado - project.costo; + project.remaining = project.presupuesto - project.abonado; + }, + beforeUpdate: async (project, ) => { + // Calcula la duración en semanas antes de la actualización + const start = new Date(project.fecha_inicio); + const end = new Date(project.fecha_fin); + const durationInWeeks = Math.ceil( + (end - start) / (7 * 24 * 60 * 60 * 1000), + ); + project.duracion = durationInWeeks; + project.remaining = project.presupuesto - project.abonado; + + // Verifica si 'remaining' se actualiza y es igual a 0 + if (project.changed('remaining') && project.remaining === 0) { + // Cambia el valor de 'status' a true + project.status = true; + } + + }, + beforeDestroy: async (project,) => { + await sequelize.models.Abonos.destroy({ + where: { proyecto_id: project.id }, + }); + await sequelize.models.ProjectWorker.destroy({ + where: { project_id: project.id }, + }); + await sequelize.models.Service.destroy({ + where: { project_id: project.id }, + }); + await sequelize.models.ProjectCustomer.destroy({ + where: { project_id: project.id }, + }); + + await sequelize.models.Adjustments.destroy({ + where: { projectId: project.id }, + }); + + + + }, + }, }; } + + static async getTotalProjects() { + try { + const totalProjects = await this.count(); + return totalProjects; + } catch (error) { + console.error('Error fetching total projects:', error); + throw error; + } + } + + static async getProjectsByMonth() { + try { + const currentDate = new Date(); + const twelveMonthsAgo = new Date(currentDate); + twelveMonthsAgo.setMonth(currentDate.getMonth() - 12); + + const projectsByMonth = await this.findAll({ + attributes: [ + [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + 'month', + ], + [Sequelize.fn('count', Sequelize.col('*')), 'projectCount'], + ], + where: { + fecha_inicio: { + [Op.between]: [twelveMonthsAgo, currentDate], + }, + }, + group: [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + ], + order: [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + ], + }); + + return projectsByMonth; + } catch (error) { + console.error('Error fetching projects by month:', error); + throw error; + } + } + + static async getExpensesByMonth() { + try { + const currentDate = new Date(); + const twelveMonthsAgo = new Date(currentDate); + twelveMonthsAgo.setMonth(currentDate.getMonth() - 12); + + const expensesByMonth = await this.findAll({ + attributes: [ + [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + 'month', + ], + [Sequelize.fn('sum', Sequelize.col('costo')), 'totalExpense'], + ], + where: { + fecha_inicio: { + [Op.between]: [twelveMonthsAgo, currentDate], + }, + }, + group: [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + ], + order: [ + Sequelize.fn('date_trunc', 'month', Sequelize.col('fecha_inicio')), + ], + }); + + return expensesByMonth; + } catch (error) { + console.error('Error fetching expenses by month:', error); + throw error; + } + } } module.exports = { PROJECT_TABLE, ProjectSchema, Project }; diff --git a/server/db/models/servicios.model.js b/server/db/models/servicios.model.js index ae12b9e..b16f012 100644 --- a/server/db/models/servicios.model.js +++ b/server/db/models/servicios.model.js @@ -2,6 +2,7 @@ const { Model, DataTypes, Sequelize } = require('sequelize'); const { PROJECT_TABLE } = require('./proyectos.model'); + const SERVICES_TABLE = 'services'; const ServiceSchema = { @@ -34,6 +35,11 @@ const ServiceSchema = { allowNull: false, type: DataTypes.INTEGER, }, + fecha_costo: { + allowNull: true, + type: DataTypes.DATE, + default: '2019-01-12T08:28:24.762Z', + }, createdAt: { allowNull: false, type: DataTypes.DATE, @@ -58,18 +64,29 @@ class Service extends Model { tableName: SERVICES_TABLE, modelName: 'Service', timestamps: false, + hooks: { + afterCreate: async (service) => { + const project = await this.sequelize.models.Project.findByPk( + service.project_id, + ); + if (project) { + project.ganancia = project.abonado - project.costo; + await project.save(); + } + }, + afterDestroy: async (service) => { + const project = await this.sequelize.models.Project.findByPk( + service.project_id, + ); + if (project) { + project.ganancia = project.abonado - project.costo; // Actualizar la ganancia + await project.save(); + } + }, + }, }; } - - static async afterCreate(serviceInstance, options) { - const project = await serviceInstance.getProject(); - if (project) { - const totalCost = project.costo + serviceInstance.cost; - await project.update({ costo: totalCost }, options); - } - } } - module.exports = { SERVICES_TABLE, ServiceSchema, diff --git a/server/db/models/tools.model.js b/server/db/models/tools.model.js new file mode 100644 index 0000000..3849649 --- /dev/null +++ b/server/db/models/tools.model.js @@ -0,0 +1,102 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); + +const { WORKER_TABLE } = require('./trabajadores.model'); + +const TOOLS_TABLE = 'Tools'; + +const ToolsSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + worker_id: { + field: 'worker_id', + allowNull: false, + type: DataTypes.INTEGER, + references: { + model: WORKER_TABLE, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + }, + tool_name: { + allowNull: false, + type: DataTypes.STRING, + }, + cost: { + allowNull: false, + type: DataTypes.INTEGER, + }, + fecha_adquisicion: { + allowNull: true, + type: DataTypes.DATE, + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class Tools extends Model { + static associate(models) { + this.belongsTo(models.Worker, { + as: 'Worker', + foreignKey: 'worker_id', + }); + } + + static config(sequelize) { + return { + sequelize, + tableName: TOOLS_TABLE, + modelName: 'tools', + timestamps: false, + hooks: { + afterCreate: async (workerCost, ) => { + const worker = await this.sequelize.models.Worker.findByPk( + workerCost.worker_id, + ); + if (worker) { + worker.WorkerCost += workerCost.cost; + await worker.save(); + } + }, + beforeDestroy: async (workerCost, ) => { + const worker = await this.sequelize.models.Worker.findByPk( + workerCost.worker_id, + ); + if (worker) { + worker.WorkerCost -= workerCost.cost; + await worker.save(); + } + }, + beforeUpdate: async (workerCost,) => { + const worker = await this.sequelize.models.Worker.findByPk( + workerCost.worker_id, + ); + if (worker) { + // Restar el coste antiguo y sumar el nuevo + if (workerCost._previousDataValues.cost !== workerCost.cost) { + worker.WorkerCost = + worker.WorkerCost - + workerCost._previousDataValues.cost + + workerCost.cost; + await worker.save(); + } + } + }, + }, + }; + } +} + +module.exports = { + TOOLS_TABLE, + ToolsSchema, + Tools, +}; diff --git a/server/db/models/trabajadores.model.js b/server/db/models/trabajadores.model.js index 6a82d8d..71be551 100644 --- a/server/db/models/trabajadores.model.js +++ b/server/db/models/trabajadores.model.js @@ -25,9 +25,31 @@ const WorkerSchema = { allowNull: false, type: DataTypes.STRING, }, + salary_hour: { + allowNull: true, + type: DataTypes.FLOAT, + defaultValue: 0, + }, + semanal_hours: { + allowNull: true, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + years_worked: { + allowNull: true, + type: DataTypes.INTEGER, + defaultValue: 0, + }, + // Columna de salario que almacena el resultado de salary_hour * hours_worked salary: { + allowNull: false, + type: DataTypes.FLOAT, + defaultValue: 0, // Asegura que siempre haya un valor por defecto + }, + WorkerCost: { allowNull: false, type: DataTypes.INTEGER, + defaultValue: 0, }, createdAt: { allowNull: false, @@ -38,20 +60,39 @@ const WorkerSchema = { }; class Worker extends Model { - static static(models) { + static associate(models) { this.hasMany(models.ProjectWorker, { as: 'projectWorkers', foreignKey: 'worker_id', include: [{ model: models.Project, as: 'project', attributes: ['name'] }], }); + this.hasMany(models.NominasSemanales, { + as: 'NominasSemanales', + foreignKey: 'worker_id', + }); + this.hasMany(models.WorkerCost, { + as: 'WorkerCosts', + foreignKey: 'worker_id', + }); } static config(sequelize) { return { sequelize, tableName: WORKER_TABLE, - modelname: 'worker', + modelName: 'Worker', timestamps: false, + hooks: { + beforeSave: (worker) => { + worker.salary + if(worker.salary != 0){ + var salarioMensual = worker.salary * 4 + var diario = salarioMensual / worker.semanal_hours + worker.salary_hour = diario + } + + }, + }, }; } } diff --git a/server/db/models/users.model.js b/server/db/models/users.model.js new file mode 100644 index 0000000..1165400 --- /dev/null +++ b/server/db/models/users.model.js @@ -0,0 +1,49 @@ +const { Model, DataTypes, Sequelize } = require('sequelize'); + +const USER_TABLE = 'users'; + +const UserSchema = { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + name: { + allowNull: false, + type: DataTypes.STRING, + }, + email: { + allowNull: false, + type: DataTypes.STRING, + unique: true, + }, + password: { + allowNull: false, + type: DataTypes.STRING, + }, + role: { + allowNull: false, + type: DataTypes.STRING, + defaultValue: 'basic', + }, + createdAt: { + allowNull: false, + type: DataTypes.DATE, + field: 'create_at', + defaultValue: Sequelize.NOW, + }, +}; + +class User extends Model { + static config(sequelize) { + return { + sequelize, + tableName: USER_TABLE, + modelName: 'User', + timestamps: false, + }; + } +} + +module.exports = { USER_TABLE, UserSchema, User }; diff --git a/server/db/stored_Procedures/update_abonado.sql b/server/db/stored_Procedures/update_abonado.sql new file mode 100644 index 0000000..1ec01d4 --- /dev/null +++ b/server/db/stored_Procedures/update_abonado.sql @@ -0,0 +1,8 @@ +-- Crear un procedimiento almacenado en PostgreSQL +CREATE OR REPLACE FUNCTION actualizar_monto_abonado(nuevo_monto INT, proyecto_id INT) RETURNS VOID AS $$ +BEGIN + UPDATE proyectos + SET abonado = abonado + nuevo_monto + WHERE id = proyecto_id; +END; +$$ LANGUAGE plpgsql; diff --git a/server/db/stored_Procedures/update_costo.sql b/server/db/stored_Procedures/update_costo.sql index caa0263..f92b30a 100644 --- a/server/db/stored_Procedures/update_costo.sql +++ b/server/db/stored_Procedures/update_costo.sql @@ -1,19 +1,63 @@ -CREATE OR REPLACE FUNCTION add_service_and_update_project_cost( - p_project_id INT, - p_amount INT, - p_service_name VARCHAR(255), - p_cost INT -) -RETURNS VOID AS $$ -BEGIN - -- Insertar el nuevo servicio - INSERT INTO services (project_id, amount, service, cost, create_at) - VALUES (p_project_id, p_amount, p_service_name, p_cost, NOW()); - - -- Actualizar el costo total del proyecto - UPDATE proyectos - SET costo = costo + p_cost - WHERE id = p_project_id; -END; -$$ LANGUAGE plpgsql; +'use strict'; +module.exports = { + up: async (queryInterface, Sequelize) => { + // Agrega una función para actualizar el 'costo' en la tabla de proyectos al insertar un nuevo servicio + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION actualizar_costo_proyecto_al_insertar_servicio(nuevo_costo INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET costo = costo + nuevo_costo + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + + // Agrega un disparador para invocar la función al insertar un servicio + await queryInterface.sequelize.query(` + CREATE TRIGGER trigger_actualizar_costo_proyecto_al_insertar_servicio + AFTER INSERT ON services + FOR EACH ROW + EXECUTE FUNCTION actualizar_costo_proyecto_al_insertar_servicio(NEW.cost, NEW.project_id); + `); + + // Agrega una función para actualizar el 'costo' en la tabla de proyectos al eliminar un servicio + await queryInterface.sequelize.query(` + CREATE OR REPLACE FUNCTION actualizar_costo_proyecto_al_eliminar_servicio(costo_eliminar INT, proyecto_id INT) RETURNS VOID AS $$ + BEGIN + UPDATE proyectos + SET costo = costo - costo_eliminar + WHERE id = proyecto_id; + END; + $$ LANGUAGE plpgsql; + `); + + // Agrega un disparador para invocar la función al eliminar un servicio + await queryInterface.sequelize.query(` + CREATE TRIGGER trigger_actualizar_costo_proyecto_al_eliminar_servicio + AFTER DELETE ON services + FOR EACH ROW + EXECUTE FUNCTION actualizar_costo_proyecto_al_eliminar_servicio(OLD.cost, OLD.project_id); + `); + }, + + down: async (queryInterface, Sequelize) => { + // Elimina los disparadores + await queryInterface.sequelize.query(` + DROP TRIGGER IF EXISTS trigger_actualizar_costo_proyecto_al_insertar_servicio ON services; + `); + + await queryInterface.sequelize.query(` + DROP TRIGGER IF EXISTS trigger_actualizar_costo_proyecto_al_eliminar_servicio ON services; + `); + + // Elimina las funciones + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS actualizar_costo_proyecto_al_insertar_servicio(INT, INT); + `); + + await queryInterface.sequelize.query(` + DROP FUNCTION IF EXISTS actualizar_costo_proyecto_al_eliminar_servicio(INT, INT); + `); + }, +}; diff --git a/server/index.js b/server/index.js index efbf016..e8f3ff3 100644 --- a/server/index.js +++ b/server/index.js @@ -1,6 +1,9 @@ const express = require('express'); const cors = require('cors'); +const http = require('http'); const routerApi = require('./routes'); +const { checkApiKey } = require('./middlewares/auth.handler'); +const {setupWebSocketServer} = require('./lib/webnsocket'); const { logErrors, @@ -10,8 +13,18 @@ const { } = require('./middlewares/error.handler'); const app = express(); +/* Websocker config */ +const server = http.createServer(app); // Crea un servidor HTTP para Express + +// Inicializa el servidor WebSocket pasando el servidor HTTP +const wss = setupWebSocketServer(server); +module.exports.wss = wss; + +/* Websocker config */ + const port = process.env.PORT || 3000; + app.use(express.json()); console.clear(); @@ -28,10 +41,16 @@ const options = { }; app.use(cors(options)); +require('./utils/auth/index'); + app.get('/', (req, res) => { res.send('Hello server'); }); +app.get('/new-route', checkApiKey, (req, res) => { + res.send('Hello from the new route'); +}); + routerApi(app); app.use(logErrors); @@ -39,6 +58,8 @@ app.use(ormErrorHandler); app.use(boomHandler); app.use(errorHandler); -app.listen(port, () => { +// Cambia app.listen por server.listen (solo si hay websocker) + +server.listen(port, () => { console.log('My port:' + port); }); diff --git a/server/lib/sequelize.js b/server/lib/sequelize.js index 3e4dd4f..869617e 100644 --- a/server/lib/sequelize.js +++ b/server/lib/sequelize.js @@ -1,5 +1,4 @@ const { Sequelize } = require('sequelize'); - const { config } = require('../config/config'); const setupModels = require('../db/models/index'); diff --git a/server/lib/webnsocket.js b/server/lib/webnsocket.js new file mode 100644 index 0000000..58c8680 --- /dev/null +++ b/server/lib/webnsocket.js @@ -0,0 +1,17 @@ +const WebSocket = require('ws'); +let wss; + +function setupWebSocketServer(server) { + wss = new WebSocket.Server({ server }); + console.log('WebSocket Server configurado.'); + + wss.on('connection', (ws) => { + console.log('Un cliente se ha conectado'); + ws.on('message', (message) => { + console.log('Mensaje recibido: %s', message); + }); + // ws.send('Hola desde el back'); + }); +} + +module.exports = { setupWebSocketServer, getWss: () => wss }; diff --git a/server/middlewares/auth.handler.js b/server/middlewares/auth.handler.js new file mode 100644 index 0000000..b0a1709 --- /dev/null +++ b/server/middlewares/auth.handler.js @@ -0,0 +1,34 @@ +const boom = require('@hapi/boom'); +const { config } = require('../config/config'); + +function checkApiKey(req, res, next) { + const apiKey = req.headers['api']; + if (apiKey === config.apikey) { + next(); + } else { + next(boom.unauthorized()); + } +} + +//alternative 1 (just for example jaja) +function checkAdminRole(req, res, next) { + const user = req.user; + if (user.role === 'admin') { + next(); + } else { + next(boom.unauthorized()); + } +} + +function checkRoles(...roles) { + return (req, res, next) => { + const user = req.user; + if (roles.includes(user.role)) { + next(); + } else { + next(boom.unauthorized()); + } + }; +} + +module.exports = { checkApiKey, checkAdminRole, checkRoles }; diff --git a/server/package-lock.json b/server/package-lock.json index 3554e26..c043a64 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,16 +11,26 @@ "dependencies": { "@faker-js/faker": "^8.3.1", "@hapi/boom": "^10.0.1", + "bcrypt": "^5.1.1", "cors": "^2.8.5", + "date-fns": "^3.6.0", "dotenv": "^16.3.1", "express": "^4.18.2", "joi": "^17.11.0", + "jsonwebtoken": "^9.0.2", + "moment": "^2.30.1", "mysql2": "^3.6.5", + "passport": "^0.7.0", + "passport-google-oauth": "^2.0.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "pdfkit": "^0.15.0", "pg": "^8.11.3", "pg-hstore": "^2.3.4", "pg-promise": "^11.5.4", "sequelize": "^6.35.1", - "sequelize-cli": "^6.6.2" + "sequelize-cli": "^6.6.2", + "ws": "^8.16.0" }, "devDependencies": { "eslint": "^8.54.0", @@ -258,6 +268,39 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -350,6 +393,14 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@swc/helpers": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", + "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -385,8 +436,7 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { "version": "1.3.8", @@ -421,6 +471,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -472,12 +533,44 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -499,11 +592,65 @@ "node": ">= 4.0.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -579,7 +726,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -597,6 +743,19 @@ "node": ">=8" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -629,13 +788,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -705,6 +869,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/cli-color": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", @@ -730,6 +902,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -746,6 +926,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -757,8 +945,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/config-chain": { "version": "1.1.13", @@ -769,6 +956,11 @@ "proto-list": "~1.2.1" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -826,6 +1018,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -835,6 +1032,15 @@ "type": "^1.0.1" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -851,6 +1057,37 @@ } } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -892,16 +1129,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -916,6 +1156,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -941,6 +1202,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -974,6 +1248,14 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/editorconfig": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", @@ -1031,6 +1313,44 @@ "node": ">= 0.8" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es5-ext": { "version": "0.10.62", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", @@ -1541,6 +1861,30 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/fontkit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.9.0.tgz", + "integrity": "sha512-HkW/8Lrk8jl18kzQHvAw9aTHe1cqsyx5sDnxncx652+CIfhawokEPkeM3BoIC+z/Xv7a0yMr0f3pRRwhGH455g==", + "dependencies": { + "@swc/helpers": "^0.3.13", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "deep-equal": "^2.0.5", + "dfa": "^1.2.0", + "restructure": "^2.0.1", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.3.1", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -1597,11 +1941,32 @@ "node": ">=10" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -1625,6 +1990,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -1642,15 +2034,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1671,7 +2067,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1736,6 +2131,14 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1746,11 +2149,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1778,6 +2181,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -1804,6 +2226,18 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", @@ -1876,7 +2310,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1892,6 +2325,19 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1900,16 +2346,83 @@ "node": ">= 0.10" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { - "binary-extensions": "^2.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-core-module": { @@ -1923,6 +2436,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -1985,6 +2512,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1994,6 +2532,20 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -2013,6 +2565,46 @@ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -2025,6 +2617,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -2052,6 +2698,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2091,6 +2742,11 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, + "node_modules/jpeg-exif": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", + "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==" + }, "node_modules/js-beautify": { "version": "1.14.11", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", @@ -2216,6 +2872,46 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2238,6 +2934,23 @@ "node": ">= 0.8.0" } }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2258,12 +2971,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -2288,6 +3036,28 @@ "es5-ext": "~0.10.2" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2398,7 +3168,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2414,10 +3183,44 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } @@ -2513,6 +3316,30 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -2622,6 +3449,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2638,6 +3481,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -2653,7 +3536,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -2743,6 +3625,11 @@ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2763,6 +3650,123 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz", + "integrity": "sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA==", + "dependencies": { + "passport-google-oauth1": "1.x.x", + "passport-google-oauth20": "2.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-google-oauth1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz", + "integrity": "sha512-qpCEhuflJgYrdg5zZIpAq/K3gTqa1CtHjbubsEsidIdpBPLkEVq6tB1I8kBNcH89RdSiYbnKpCBXAZXX/dtx1Q==", + "dependencies": { + "passport-oauth1": "1.x.x" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth1": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz", + "integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-oauth2/node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==" + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2776,7 +3780,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2822,6 +3825,23 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/pdfkit": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.15.0.tgz", + "integrity": "sha512-Z0dx0sEPKLW2kbThS1SWZ0iSHlRPoFMpP+oSjNrtwRjsfGivwE+r6emyEFwQG/fx1Ri0AGUHmDcGOSMMlLLnSg==", + "dependencies": { + "crypto-js": "^4.2.0", + "fontkit": "^1.8.1", + "jpeg-exif": "^1.1.4", + "linebreak": "^1.0.2", + "png-js": "^1.0.0" + } + }, "node_modules/pg": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", @@ -2956,6 +3976,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -3115,6 +4148,19 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3127,6 +4173,23 @@ "node": ">=8.10.0" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3160,6 +4223,11 @@ "node": ">=4" } }, + "node_modules/restructure": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", + "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==" + }, "node_modules/retry-as-promised": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", @@ -3179,7 +4247,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -3505,15 +4572,36 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3559,8 +4647,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-update-notifier": { "version": "2.0.0", @@ -3606,6 +4693,25 @@ "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3719,6 +4825,30 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3734,6 +4864,11 @@ "next-tick": "1" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -3783,11 +4918,15 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type": { "version": "1.2.0", @@ -3830,6 +4969,11 @@ "node": ">= 0.6" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", @@ -3857,6 +5001,24 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -3891,6 +5053,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3923,6 +5090,20 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3937,6 +5118,64 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wkx": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", @@ -3981,8 +5220,27 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, "node_modules/xtend": { "version": "4.0.2", diff --git a/server/package.json b/server/package.json index ed955c8..0df3abc 100644 --- a/server/package.json +++ b/server/package.json @@ -9,6 +9,7 @@ "start": "node index.js", "migrations:generate": "sequelize-cli migration:generate --name", "migrations:run": "sequelize-cli db:migrate", + "migrations:runspecific": "sequelize-cli db:migrate --name", "migrations:revert": "sequelize-cli db:migrate:undo", "migrations:revert-single": "sequelize-cli db:migrate:undo --name ", "migrations:delete": "sequelize-cli db:migrate:undo:all", @@ -26,15 +27,25 @@ "dependencies": { "@faker-js/faker": "^8.3.1", "@hapi/boom": "^10.0.1", + "bcrypt": "^5.1.1", "cors": "^2.8.5", + "date-fns": "^3.6.0", "dotenv": "^16.3.1", "express": "^4.18.2", "joi": "^17.11.0", + "jsonwebtoken": "^9.0.2", + "moment": "^2.30.1", "mysql2": "^3.6.5", + "passport": "^0.7.0", + "passport-google-oauth": "^2.0.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "pdfkit": "^0.15.0", "pg": "^8.11.3", "pg-hstore": "^2.3.4", "pg-promise": "^11.5.4", "sequelize": "^6.35.1", - "sequelize-cli": "^6.6.2" + "sequelize-cli": "^6.6.2", + "ws": "^8.16.0" } } diff --git a/server/routes/Adjustments.router.js b/server/routes/Adjustments.router.js new file mode 100644 index 0000000..1f06ce0 --- /dev/null +++ b/server/routes/Adjustments.router.js @@ -0,0 +1,81 @@ +const express = require('express'); +const router = express.Router(); +const CalculosHugoService = require('../services/proyectos.service'); +const service = new CalculosHugoService(); +const validatorHandler = require('../middlewares/validator.handler'); +const { + getCustomersSchema, + updateCustomersSchema, +} = require('../schemas/clientes.schema'); +const { createAdjustmentSchema } = require('../schemas/adjustment.schema'); + + + +router.get('/', async (req, res, next) => { + try { + const customers = await service.findAdjustments(); + res.json(customers); + } catch (error) { + next(error); + } +}); + +router.get( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const customer = await service.findOneAdjustment(id); + res.json(customer); + } catch (error) { + next(error); + } + }, +); + +router.post( + '/', + validatorHandler(createAdjustmentSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newCustomer = await service.createAdjustment(body); + res.status(201).json(newCustomer); + } catch (error) { + next(error); + } + }, +); + +router.patch( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + validatorHandler(updateCustomersSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const { id } = req.params; + const customer = await service.updateAdjustment(id, body); + res.json(customer); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.deleteAdjustment(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +module.exports = router; diff --git a/server/routes/CalculosHugo.router.js b/server/routes/CalculosHugo.router.js new file mode 100644 index 0000000..09e6289 --- /dev/null +++ b/server/routes/CalculosHugo.router.js @@ -0,0 +1,83 @@ +const express = require('express'); +const router = express.Router(); +const CalculosHugoService = require('../services/CalculosHugo.service'); +const service = new CalculosHugoService(); + +const passport = require('passport'); +const { checkRoles } = require('../middlewares/auth.handler'); + +const validatorHandler = require('../middlewares/validator.handler'); +const { + createCustomersSchema, + getCustomersSchema, + updateCustomersSchema, +} = require('../schemas/clientes.schema'); + +router.get('/', async (req, res, next) => { + try { + const customers = await service.find(); + res.json(customers); + } catch (error) { + next(error); + } +}); + +router.get( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const customer = await service.findOne(id); + res.json(customer); + } catch (error) { + next(error); + } + }, +); + +router.post( + '/', + validatorHandler(createCustomersSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newCustomer = await service.create(body); + res.status(201).json(newCustomer); + } catch (error) { + next(error); + } + }, +); + +router.patch( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + validatorHandler(updateCustomersSchema, 'body'), + async (req, res) => { + try { + const body = req.body; + const { id } = req.params; + const customer = await service.update(id, body); + res.json(customer); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/:id', + validatorHandler(getCustomersSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.delete(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +module.exports = router; diff --git a/server/routes/NominasSemanales.router.js b/server/routes/NominasSemanales.router.js new file mode 100644 index 0000000..31170bc --- /dev/null +++ b/server/routes/NominasSemanales.router.js @@ -0,0 +1,123 @@ +const express = require('express'); +const router = express.Router(); +const PDFDocument = require('pdfkit'); +const { models } = require('../lib/sequelize'); + +const NominasSemanalesService = require('../services/NominasSemanales.service'); +const service = new NominasSemanalesService(); + +const passport = require('passport'); +const validatorHandler = require('../middlewares/validator.handler'); +const { checkRoles } = require('../middlewares/auth.handler'); +const { + createWorkerSchema, + getWorkerSchema, + updateWorkerSchema, +} = require('../schemas/trabajadores.schema'); + + +router.get('/generate-pdf', (req, res) => { + // Crear un nuevo documento PDF + const doc = new PDFDocument(); + + // Establecer encabezados para descargar el PDF como un archivo + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', 'attachment; filename=quote.pdf'); + + // Enviar el documento PDF al cliente + doc.pipe(res); + + // Agregar contenido al documento + doc.fontSize(25).text('Esta es tu cotización!', 100, 100); + + // Finalizar el documento + doc.end(); +}); + + +router.get('/', async (req, res, next) => { + try { + const workers = await service.find(); + res.json(workers); + } catch (error) { + next(error); + } +}); + +router.get('/weeklyNominas', async (req, res, next) => { + try { + const workers = await service.findWeeklyNominasManual(); + res.json(workers); + } catch (error) { + next(error); + } +}); + +router.get('/payrollsWeek', async (req, res, next) => { + try { + const payrolls = await service.findPayrollsWeeks(); + res.json(payrolls); + } catch (error) { + next(error); + } +}); + +router.get( + '/:id', + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const worker = await service.findOne(id); + res.json(worker); + } catch (error) { + next(error); + } + }, +); + +router.post('/', async (req, res, next) => { + try { + const body = req.body; + const newPayroll = await service.create(body); + res.status(201).json(newPayroll); + } catch (error) { + next(error); + } +}); + +router.patch( + '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + validatorHandler(updateWorkerSchema, 'body'), + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const body = req.body; + const { id } = req.params; + const worker = await service.update(id, body); + res.json(worker); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.delete(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +module.exports = router; diff --git a/server/routes/auth.router.js b/server/routes/auth.router.js new file mode 100644 index 0000000..4398d0e --- /dev/null +++ b/server/routes/auth.router.js @@ -0,0 +1,24 @@ +const express = require('express'); +const passport = require('passport'); +const jwt = require('jsonwebtoken'); +const { config } = require('../config/config'); +const router = express.Router(); + +router.post( + '/login', + passport.authenticate('local', { session: false }), + async (req, res, next) => { + try { + const user = req.user; + const payload = { + sub: user.id, + role: user.role, + }; + const token = jwt.sign(payload, config.jwtsecret); + res.json({ user, token }); + } catch (error) { + next(error); + } + }, +); +module.exports = router; diff --git a/server/routes/clientes.router.js b/server/routes/clientes.router.js index 628783b..3988ff6 100644 --- a/server/routes/clientes.router.js +++ b/server/routes/clientes.router.js @@ -3,6 +3,9 @@ const router = express.Router(); const CustomersService = require('../services/clientes.service'); const service = new CustomersService(); +const passport = require('passport'); +const { checkRoles } = require('../middlewares/auth.handler'); + const validatorHandler = require('../middlewares/validator.handler'); const { createCustomersSchema, @@ -35,6 +38,8 @@ router.get( router.post( '/', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), validatorHandler(createCustomersSchema, 'body'), async (req, res, next) => { try { diff --git a/server/routes/index.js b/server/routes/index.js index 77dac82..e8c4f68 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,11 +1,23 @@ -const proyectosRouter = require("./proyectos.router") -const clientesRouter = require("./clientes.router") -const trabajadoresRouter = require("./trabajadores.router") +const proyectosRouter = require('./proyectos.router'); +const clientesRouter = require('./clientes.router'); +const trabajadoresRouter = require('./trabajadores.router'); +const usersRouter = require('./users.router'); +const authRouter = require('./auth.router'); +const payrollsRouter = require('./payrolls.router'); +const NominasSemanalesRouter = require('./NominasSemanales.router'); +const CalculosHugoRouter = require('./CalculosHugo.router'); +const AdjustmentsRouter = require('./Adjustments.router'); function routerApi(app) { - app.use("/Api/v1/proyectos", proyectosRouter), - app.use("/Api/v1/clientes", clientesRouter), - app.use("/Api/v1/trabajadores",trabajadoresRouter) + app.use('/Api/v1/proyectos', proyectosRouter), + app.use('/Api/v1/clientes', clientesRouter), + app.use('/Api/v1/trabajadores', trabajadoresRouter), + app.use('/Api/v1/users', usersRouter), + app.use('/Api/v1/payrolls', payrollsRouter), + app.use('/Api/v1/nominasSemanales', NominasSemanalesRouter), + app.use('/Api/v1/auth', authRouter); + app.use('/Api/v1/CalculosHugo', CalculosHugoRouter); + app.use('/Api/v1/ajustes', AdjustmentsRouter); } module.exports = routerApi; diff --git a/server/routes/payrolls.router.js b/server/routes/payrolls.router.js new file mode 100644 index 0000000..b885e78 --- /dev/null +++ b/server/routes/payrolls.router.js @@ -0,0 +1,95 @@ +const express = require('express'); +const router = express.Router(); + +const { models } = require('../lib/sequelize'); + +const payrollService = require('../services/proyectos.service'); +const service = new payrollService(); + +const passport = require('passport'); +const validatorHandler = require('../middlewares/validator.handler'); +const { checkRoles } = require('../middlewares/auth.handler'); +const { + createWorkerSchema, + getWorkerSchema, + updateWorkerSchema, +} = require('../schemas/trabajadores.schema'); +const PayrollService = require('../services/payrolls.service'); + +router.get('/', async (req, res, next) => { + try { + const workers = await service.findPayrolls(); + res.json(workers); + } catch (error) { + next(error); + } +}); + +router.get('/payrollsWeek', async (req, res, next) => { + try { + const payrolls = await service.findPayrollsWeeks(); + res.json(payrolls); + } catch (error) { + next(error); + } +}); + +router.get( + '/:id', + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const worker = await service.findOne(id); + res.json(worker); + } catch (error) { + next(error); + } + }, +); + +router.post('/', async (req, res, next) => { + try { + const body = req.body; + const newPayroll = await service.createPayroll(body); + res.status(201).json(newPayroll); + } catch (error) { + next(error); + } +}); + +router.patch( + '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + validatorHandler(updateWorkerSchema, 'body'), + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const body = req.body; + const { id } = req.params; + const worker = await service.update(id, body); + res.json(worker); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.delete(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +module.exports = router; diff --git a/server/routes/proyectos.router.js b/server/routes/proyectos.router.js index d582a5d..ac53270 100644 --- a/server/routes/proyectos.router.js +++ b/server/routes/proyectos.router.js @@ -2,6 +2,11 @@ const express = require('express'); const router = express.Router(); const ProjectService = require('../services/proyectos.service'); const service = new ProjectService(); +const { models } = require('../lib/sequelize'); +const PDFDocument = require('pdfkit'); +const passport = require('passport'); +const { checkRoles } = require('../middlewares/auth.handler'); +const { format } = require('date-fns'); const validatorHandler = require('../middlewares/validator.handler'); const { @@ -11,17 +16,178 @@ const { addCustomerRESchema, addWorkerRESchema, addServiceSchema, + getCostSchema, + updateCostsSchema, + addAbonoSchema, + getAbonoSchema, + updateAbonoSchema, } = require('../schemas/proyectos.schema'); + router.get('/', async (req, res, next) => { try { - const projects = await service.find(); + const projects = await service.findProjects(); res.json(projects); } catch (error) { next(error); } }); +router.get('/generate-pdf', (req, res) => { + // Crear un nuevo documento PDF + const doc = new PDFDocument(); + + // Establecer encabezados para descargar el PDF como un archivo + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', 'attachment; filename=quote.pdf'); + + // Enviar el documento PDF al cliente + doc.pipe(res); + + // Agregar contenido al documento + doc.fontSize(25).text('Esta es tu cotización!', 100, 100); + + doc.end(); +}); + +router.get('/:id/pdf', async (req, res, next) => { + try { + const { id } = req.params; + const project = await service.findOne(id); + + const doc = new PDFDocument(); + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader( + 'Content-Disposition', + `attachment; filename="${project.name}.pdf"`, + ); + + doc.pipe(res); + + doc.fillColor('#000'); // Establece el color del texto a negro para la sección inicial + doc.fontSize(25).text(`Proyecto: ${project.name}`, { underline: true }); + doc.fontSize(12).moveDown(); + doc.text( + `Fecha de Inicio: ${format( + new Date(project.fecha_inicio), + 'dd/MM/yyyy', + )}`, + ); + doc.text( + `Fecha de Fin: ${format(new Date(project.fecha_fin), 'dd/MM/yyyy')}`, + ); + doc.text(`Duración: ${project.duracion} días`); + doc.text(`Costo Inicial: $${project.costo_inicial}`); + doc.text(`Costo Total: $${project.costo}`); + doc.text(`Total Abonado: $${project.abonado}`); + doc.text(`Pendiente: $${project.remaining}`); + doc.moveDown(2); + + // Abonos + doc.fontSize(18).text('Abonos', { underline: true }); + doc.fontSize(10).moveDown(); + drawTable( + doc, + project.abonos, + ['Fecha', 'Monto'], + (item) => [ + format(new Date(item.fecha_abono), 'dd/MM/yyyy'), + `$${item.monto}`, + ], + { + totalLabel: 'Total Abonado', + totalValue: project.abonado, + }, + ); + + doc.moveDown(2); + + doc.fillColor('#000'); + doc.fontSize(18).text('Costos', { underline: true }); + doc.fontSize(4).moveDown(); + drawTable( + doc, + project.services, + ['Fecha', 'Servicio', 'Costo'], + (item) => [ + format(new Date(item.fecha_costo), 'dd/MM/yyyy'), + `${item.service}`, + `$${item.cost}`, + ], + { + totalLabel: 'Total ', + totalValue: project.costo, + }, + ); + + doc.end(); + } catch (error) { + next(error); + } +}); + +function drawTable(doc, data, headers, rowMapper, options = {}) { + let tableTop = doc.y; + let colWidths = [100, 280, 100]; + doc.fontSize(10); + + // Cabecera + doc.fillColor('#444'); + doc.rect(50, tableTop, sum(colWidths, 0, colWidths.length), 20).fill(); + doc.fillColor('#fff'); + headers.forEach((header, i) => { + doc.text(header, 50 + sum(colWidths, 0, i), tableTop + 6, { + width: colWidths[i], + align: 'center', + }); + }); + + // Filas + tableTop += 20; + doc.fillColor('#000'); + data.forEach((item, index) => { + const row = rowMapper(item); + row.forEach((text, i) => { + doc.text(text, 50 + sum(colWidths, 0, i), tableTop + index * 20 + 6, { + width: colWidths[i], + align: 'center', + }); + }); + }); + + // Total + if (options.totalLabel && options.totalValue) { + doc.fillColor('#000'); + doc + .rect( + 50, + tableTop + data.length * 20 + 6, + sum(colWidths, 0, colWidths.length), + 20, + ) + .fill(); + doc.fillColor('#fff'); + doc.text(options.totalLabel, 50, tableTop + data.length * 20 + 12, { + width: colWidths[0], + align: 'center', + }); + doc.text( + `$${options.totalValue}`, + 50 + sum(colWidths, 0, colWidths.length - 1), + tableTop + data.length * 20 + 12, + { width: colWidths[colWidths.length - 1], align: 'center' }, + ); + } +} + +function sum(arr, start, end) { + let total = 0; + for (let i = start; i < end; i++) { + total += arr[i]; + } + return total; +} + router.get('/projectCustomer', async (req, res, next) => { try { const customerProject = await service.findCustomersProjects(); @@ -43,12 +209,56 @@ router.get('/projectWorker', async (req, res, next) => { router.get('/services', async (req, res, next) => { try { const services = await service.findServices(); + res.json(services); } catch (error) { next(error); } }); +router.get('/abonos', async (req, res, next) => { + try { + const abonos = await service.findAbonos(); + res.json(abonos); + } catch (error) { + next(error); + } +}); + +router.get('/cuentasCobrar', async (req, res, next) => { + try { + const projects = await models.Project.findAll({ + where: { + status: false, + }, + include: [ + { + model: models.ProjectCustomer, + as: 'projectCustomers', + attributes: ['customer_name'], + }, + ], + attributes: ['name', 'remaining'], + }); + + res.json(projects); + } catch (error) { + next(error); + } +}); + +router.get('/project-stats', async (req, res) => { + try { + const totalProjects = await models.Project.getTotalProjects(); + const projectsByMonth = await models.Project.getProjectsByMonth(); + const CostsByMonth = await models.Project.getExpensesByMonth(); + + res.json({ totalProjects, projectsByMonth, CostsByMonth }); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + router.get( '/projectCustomer/:id', validatorHandler(getProjectSchema, 'params'), @@ -63,6 +273,151 @@ router.get( }, ); +router.get('/egresos', async (req, res, next) => { + try { + const weeklyCosts = await service.findWeeklyCosts(); + res.json(weeklyCosts); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + next(error) + } +}); + +router.get('/prestamos', async (req, res, next) => { + try { + const weeklyCosts = await service.findAllPrestamos(); + res.json(weeklyCosts); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + next(error); + } +}); + +router.post('/prestamos', async (req, res, next) => { + try { + const nuevoPrestamo = await service.createPrestamo(req.body); + res.status(201).json(nuevoPrestamo); + } catch (error) { + + res.status(500).json({ error: 'Internal Server Error' }); + next(error); + } +}); + +router.get('/flujo', async (req, res, next) => { + try { + const startDate = new Date('2023-01-01'); + const endDate = new Date(); + + const projects = await models.Project.findAll({ + include: ['abonos', 'services'], + }); + + if (!projects || projects.length === 0) { + return res.status(404).json({ error: 'Projects not found' }); + } + + const prestamos = await service.findAllPrestamos(); + + const weeklyFlows = []; + + let currentDate = new Date(startDate); + + while (currentDate <= endDate) { + const startOfWeek = new Date(currentDate); + startOfWeek.setHours(0, 0, 0, 0); + const endOfWeek = new Date(currentDate); + endOfWeek.setDate(endOfWeek.getDate() + 6); + endOfWeek.setHours(23, 59, 59, 999); + + // Obtener el balance total de la semana pasada + const lastWeekBalance = + weeklyFlows.length > 0 + ? weeklyFlows[weeklyFlows.length - 1]['Balance total'] + : 0; + + // Lógica de cálculo de ingresos semanales + // Lógica de cálculo de ingresos semanales + const weeklyIncomes = projects.reduce((total, project) => { + const projectData = project.toJSON(); + + + // Agregar ingresos semanales de abonos al total + const abonosForWeek = projectData.abonos.filter((abono) => { + const abonoDate = new Date(abono.createdAt); + return abonoDate >= startOfWeek && abonoDate <= endOfWeek; + }); + + const abonosTotalForWeek = abonosForWeek.reduce( + (totalAbono, abono) => totalAbono + abono.monto, + 0, + ); + + return total + abonosTotalForWeek; + }, 0); + + // Lógica de cálculo de egresos semanales + const weeklyExpenses = projects.reduce((total, project) => { + const projectData = project.toJSON(); + const projectWeeklyExpense = projectData.services.reduce( + (totalService, service) => { + const serviceDate = new Date(service.createdAt); + return serviceDate >= startOfWeek && serviceDate <= endOfWeek + ? totalService + service.cost + : totalService; + }, + 0, + ); + return total + projectWeeklyExpense; + }, 0); + + // Lógica de cálculo de préstamos semanales + const weeklyPrestamo = prestamos.reduce((totalPrestamo, prestamo) => { + const prestamoDate = new Date(prestamo.date_prestamo); + return prestamoDate >= startOfWeek && prestamoDate <= endOfWeek + ? totalPrestamo + prestamo.amount_paid + : totalPrestamo; + }, 0); + + // Resto de la lógica para calcular caja, balance de flujo, etc. + + // Crear el objeto de flujo semanal + const weeklyFlow = { + startDate: startOfWeek.toISOString(), + endDate: endOfWeek.toISOString(), + caja: lastWeekBalance, + ingresos: weeklyIncomes, + egresos: weeklyExpenses, + 'balance de flujo': lastWeekBalance + weeklyIncomes - weeklyExpenses, + prestamo: weeklyPrestamo, + 'Balance total': + lastWeekBalance + weeklyIncomes - weeklyExpenses + weeklyPrestamo, + }; + + // Agregar el flujo semanal al array + weeklyFlows.push(weeklyFlow); + + // Mover a la siguiente semana + currentDate.setDate(currentDate.getDate() + 7); + } + + // Enviar la respuesta con los flujos semanales + res.json(weeklyFlows); + } catch (error) { + // Manejar cualquier error que pueda ocurrir + next(error); + } +}); + +router.get('/ingresos', async (req, res, next) => { + try { + const services = await service.findWeeklyAbonos(); + res.json(services); + } catch (error) { + next(error); + } +}); + router.get( '/:id', validatorHandler(getProjectSchema, 'params'), @@ -97,7 +452,7 @@ router.post( async (req, res, next) => { try { const body = req.body; - const newCustomer = await service.addCustomer(body); + const newCustomer = await service.addProjectCustomer(body); res.status(201).json(newCustomer); } catch (error) { next(error); @@ -107,6 +462,8 @@ router.post( router.patch( '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), validatorHandler(getProjectSchema, 'params'), validatorHandler(updateProjectSchema, 'body'), async (req, res, next) => { @@ -148,7 +505,7 @@ router.delete( }, ); -//------ProjectWorkers Router +/**---------------------------------------ProjectWorkers--------------------------------------------------------- */ // puse el get all arriba por que estaba teniendo problemas de asincronismo router.get( '/projectWorker/:id', @@ -192,7 +549,7 @@ router.delete( }, ); -//------Services Router +/**---------------------------------------Services Router--------------------------------------------------------- */ // puse el get all arriba por que estaba teniendo problemas de asincronismo router.get( '/services/:id', @@ -200,7 +557,7 @@ router.get( async (req, res, next) => { try { const { id } = req.params; - const projectWorker = await service.findOneProjectWorker(id); + const projectWorker = await service.findOneService(id); res.json(projectWorker); } catch (error) { next(error); @@ -222,6 +579,24 @@ router.post( }, ); +router.patch( + '/services/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + validatorHandler(getCostSchema, 'params'), + validatorHandler(updateCostsSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const { id } = req.params; + const cost = service.updateCost(id, body); + res.json(cost); + } catch (error) { + next(error); + } + }, +); + router.delete( '/services/:id', validatorHandler(getProjectSchema, 'params'), @@ -236,4 +611,63 @@ router.delete( }, ); +/**--------------------Abonos router---------------------------------------------------------------------------- */ +router.get( + '/abonos/:id', + validatorHandler(getProjectSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const abono = await service.findOneAbono(id); + res.json(abono); + } catch (error) { + next(error); + } + }, +); + +router.post( + '/abonos', + validatorHandler(addAbonoSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const abono = await service.createAbono(body); + res.status(201).json(abono); + } catch (error) { + next(error); + } + }, +); + +router.patch( + '/abonos/:id', + validatorHandler(getAbonoSchema, 'params'), + validatorHandler(updateAbonoSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const { id } = req.params; + const cost = service.updateAbono(id, body); + res.json(cost); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/abonos/:id', + validatorHandler(getAbonoSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.deleteAbono(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + module.exports = router; diff --git a/server/routes/trabajadores.router.js b/server/routes/trabajadores.router.js index 974fbcd..9f03786 100644 --- a/server/routes/trabajadores.router.js +++ b/server/routes/trabajadores.router.js @@ -2,12 +2,16 @@ const express = require('express'); const router = express.Router(); const WorkerService = require('../services/trabajadores.service'); const service = new WorkerService(); - +const passport = require('passport'); const validatorHandler = require('../middlewares/validator.handler'); +const { checkRoles } = require('../middlewares/auth.handler'); const { createWorkerSchema, getWorkerSchema, - updateWorkerSchema + updateWorkerSchema, + createWorkerCostSchema, + updateSalaryWorkerSchema, + createToolSchema } = require('../schemas/trabajadores.schema'); router.get('/', async (req, res, next) => { @@ -19,6 +23,52 @@ router.get('/', async (req, res, next) => { } }); +router.get('/WorkerCosts', async (req, res, next) => { + try { + const workers = await service.findWorkerCost(); + res.json(workers); + } catch (error) { + next(error); + } +}); + +router.get('/tools', async (req, res, next) => { + try { + const workers = await service.findTools(); + res.json(workers); + } catch (error) { + next(error); + } +}); + +router.post( + '/tools', + validatorHandler(createToolSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newTool = await service.createTools(body); + res.status(201).json(newTool); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/tools/:id', + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.deleteTools(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + router.get( '/:id', validatorHandler(getWorkerSchema, 'params'), @@ -33,6 +83,7 @@ router.get( }, ); + router.post( '/', validatorHandler(createWorkerSchema, 'body'), @@ -47,6 +98,35 @@ router.post( }, ); +router.post( + '/WorkerCosts', + + validatorHandler(createWorkerCostSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newWorker = await service.createWorkerCost(body); + res.status(201).json(newWorker); + } catch (error) { + next(error); + } + }, +); + +router.post( + '/tools', + validatorHandler(createToolSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newTool = await service.createTools(body); + res.status(201).json(newTool); + } catch (error) { + next(error); + } + }, +); + router.patch( '/:id', validatorHandler(updateWorkerSchema, 'body'), @@ -65,6 +145,8 @@ router.patch( router.delete( '/:id', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), validatorHandler(getWorkerSchema, 'params'), async (req, res, next) => { try { @@ -77,4 +159,50 @@ router.delete( }, ); +router.delete( + '/WorkerCosts/:id', + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.deleteProjectWorker(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/tools/:id', + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.deleteTools(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +router.patch( + '/SalaryUpdate/:id', + validatorHandler(updateSalaryWorkerSchema, 'body'), + validatorHandler(getWorkerSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const newData = req.body; + + const salary = await service.update(id, newData); + + res.status(201).json(salary); + } catch (err) { + console.log(err); + next(err); + } + }, +); module.exports = router; diff --git a/server/routes/users.router.js b/server/routes/users.router.js new file mode 100644 index 0000000..65d8f1d --- /dev/null +++ b/server/routes/users.router.js @@ -0,0 +1,88 @@ +const express = require('express'); +const UserService = require('./../services/users.service'); +const validatorHandler = require('./../middlewares/validator.handler'); +const { + updateUserSchema, + createUserSchema, + getUserSchema, +} = require('./../schemas/users.schema'); + +const router = express.Router(); +const service = new UserService(); + +const passport = require('passport'); +const { checkRoles } = require('../middlewares/auth.handler'); + +router.get( + '/', + passport.authenticate('jwt', { session: false }), + checkRoles('user', 'admin'), + async (req, res, next) => { + try { + const users = await service.findUsers(); + res.json(users); + } catch (error) { + next(error); + } + }, +); + +router.get( + '/:id', + validatorHandler(getUserSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + const category = await service.findOne(id); + res.json(category); + } catch (error) { + next(error); + } + }, +); + +router.post( + '/', + validatorHandler(createUserSchema, 'body'), + async (req, res, next) => { + try { + const body = req.body; + const newCategory = await service.create(body); + res.status(201).json(newCategory); + } catch (error) { + next(error); + } + }, +); + +router.patch( + '/:id', + validatorHandler(getUserSchema, 'params'), + validatorHandler(updateUserSchema, 'body'), + async (req, res, next) => { + try { + const { id } = req.params; + const body = req.body; + const category = await service.update(id, body); + res.json(category); + } catch (error) { + next(error); + } + }, +); + +router.delete( + '/:id', + validatorHandler(getUserSchema, 'params'), + async (req, res, next) => { + try { + const { id } = req.params; + await service.delete(id); + res.status(201).json({ id }); + } catch (error) { + next(error); + } + }, +); + +module.exports = router; diff --git a/server/schemas/adjustment.schema.js b/server/schemas/adjustment.schema.js new file mode 100644 index 0000000..4966d4a --- /dev/null +++ b/server/schemas/adjustment.schema.js @@ -0,0 +1,37 @@ +const Joi = require('joi'); + +const id = Joi.number().integer(); +const name = Joi.string(); +const email = Joi.string().email(); +const password = Joi.string().min(8); +const role = Joi.string().min(4); + +const monto = Joi.number().integer(); +const fecha_ajuste = Joi.date(); +const projectId = Joi.number().integer(); +const motive = Joi.string(); +const operation = Joi.boolean(); + +const createAdjustmentSchema = Joi.object({ + monto: monto.required(), + fecha_ajuste: fecha_ajuste.required(), + projectId: projectId.required(), + motive: motive.required(), + operation: operation.required(), +}); + +const updateUserSchema = Joi.object({ + name: name, + email: email, + role: role, +}); + +const getUserSchema = Joi.object({ + id: id.required(), +}); + +module.exports = { + createAdjustmentSchema, + updateUserSchema, + getUserSchema, +}; diff --git a/server/schemas/payrolls.schema.js b/server/schemas/payrolls.schema.js new file mode 100644 index 0000000..cc7c6f2 --- /dev/null +++ b/server/schemas/payrolls.schema.js @@ -0,0 +1,34 @@ +const Joi = require('joi'); + +const id = Joi.number().integer(); +const project_id = Joi.number().integer(); +const worker_id = Joi.number().integer(); +const amount_paid = Joi.number().integer(); +const weeks_worked = Joi.number().integer(); +const payment_dates = Joi.date(); + +const createPayrollrSchema = Joi.object({ + name: name.required(), + last_name: last_name.required(), + age: age.required(), + position: position.required(), + salary: salary.required(), +}); + +const updateWorkerSchema = Joi.object({ + name: name, + last_name: last_name, + age: age, + position: position, + salary: salary, +}); + +const getWorkerSchema = Joi.object({ + id: id.required(), +}); + +module.exports = { + createPayrollrSchema, + updateWorkerSchema, + getWorkerSchema, +}; diff --git a/server/schemas/proyectos.schema.js b/server/schemas/proyectos.schema.js index 877a8a7..fb0cd8d 100644 --- a/server/schemas/proyectos.schema.js +++ b/server/schemas/proyectos.schema.js @@ -2,21 +2,34 @@ const Joi = require('joi'); const id = Joi.number().integer(); const name = Joi.string(); -fecha_inicio = Joi.date(); -fecha_fin = Joi.date(); -costo = Joi.number().integer(); +const fecha_inicio = Joi.date(); +const fecha_fin = Joi.date(); +const costo = Joi.number().integer(); const project_id = Joi.number().integer(); const customer_id = Joi.number().integer(); const worker_id = Joi.number().integer(); const cost = Joi.number().integer(); const amount = Joi.number().integer(); const service = Joi.string(); +const costo_inicial = Joi.number().integer(); +const abonado = Joi.string(); +const presupuesto = Joi.number().integer(); +const anticipo = Joi.number().integer(); + +/** ----Abonos-- */ +const monto = Joi.number().integer(); +const fecha_abono = Joi.date(); +const projectId = Joi.number().integer(); +const customerId = Joi.number().integer(); +const fecha_costo = Joi.date(); const createProjectsSchema = Joi.object({ name: name.required(), fecha_inicio: fecha_inicio.required(), fecha_fin: fecha_fin.required(), - costo: costo.required(), + costo_inicial: costo_inicial.required(), + presupuesto: presupuesto.required(), + anticipo: anticipo.required(), }); const updateProjectSchema = Joi.object({ @@ -42,11 +55,41 @@ const addWorkerRESchema = Joi.object({ worker_id: worker_id.required(), }); // ====Servie Schemas---- +const getCostSchema = Joi.object({ + id: id.required(), +}); const addServiceSchema = Joi.object({ project_id: project_id.required(), amount: amount.required(), service: service.required(), cost: cost.required(), + fecha_costo: fecha_costo.required(), +}); + +const updateCostsSchema = Joi.object({ + id: id, + amount: amount, + service: service, + cost: cost, +}); + +/** Abonos Schemas */ +const getAbonoSchema = Joi.object({ + id: id.required(), +}); +const addAbonoSchema = Joi.object({ + monto: monto.required(), + fecha_abono: fecha_abono.required(), + projectId: projectId.required(), + customerId: customerId.required(), +}); + +const updateAbonoSchema = Joi.object({ + id: id, + monto: monto, + fecha_abono: fecha_abono, + projectId: projectId, + customerId: customerId, }); module.exports = { @@ -56,4 +99,9 @@ module.exports = { addCustomerRESchema, addWorkerRESchema, addServiceSchema, + getCostSchema, + updateCostsSchema, + getAbonoSchema, + updateAbonoSchema, + addAbonoSchema, }; diff --git a/server/schemas/trabajadores.schema.js b/server/schemas/trabajadores.schema.js index 2dfc5c5..4b61e6e 100644 --- a/server/schemas/trabajadores.schema.js +++ b/server/schemas/trabajadores.schema.js @@ -1,11 +1,19 @@ -const Joi = require("joi"); - -const id = Joi.number().integer() +const Joi = require('joi'); +const id = Joi.number().integer(); const name = Joi.string(); const last_name = Joi.string(); const age = Joi.number(); const position = Joi.string(); const salary = Joi.number().integer(); +const salary_hour = Joi.number().integer(); +const semanal_hours = Joi.number().integer(); +const worker_id = Joi.number().integer() +const service = Joi.string() +const cost = Joi.number().integer() +const fecha_costo = Joi.date() +//tools +const tool_name = Joi.string(); +const fecha_adquisicion = Joi.date(); const createWorkerSchema = Joi.object({ name: name.required(), @@ -13,6 +21,24 @@ const createWorkerSchema = Joi.object({ age: age.required(), position: position.required(), salary: salary.required(), + salary_hour: salary_hour, + semanal_hours: semanal_hours, +}); + +const createWorkerCostSchema = Joi.object({ + worker_id: worker_id, + service: service, + cost: cost, + fecha_costo: fecha_costo, +}); + + + +const createToolSchema = Joi.object({ + worker_id: worker_id, + tool_name: tool_name, + cost: cost, + fecha_adquisicion: fecha_adquisicion, }); const updateWorkerSchema = Joi.object({ @@ -21,6 +47,16 @@ const updateWorkerSchema = Joi.object({ age: age, position: position, salary: salary, + salary_hour: salary_hour, + semanal_hours: semanal_hours, +}); + +const updateSalaryWorkerSchema = Joi.object({ + name: name, + last_name: last_name, + age: age, + position: position, + salary: salary, }); const getWorkerSchema = Joi.object({ @@ -29,6 +65,10 @@ const getWorkerSchema = Joi.object({ module.exports = { createWorkerSchema, + createWorkerCostSchema, + createToolSchema, + updateSalaryWorkerSchema, updateWorkerSchema, getWorkerSchema, + }; diff --git a/server/schemas/users.schema.js b/server/schemas/users.schema.js new file mode 100644 index 0000000..9503681 --- /dev/null +++ b/server/schemas/users.schema.js @@ -0,0 +1,26 @@ +const Joi = require('joi'); + +const id = Joi.number().integer(); +const name = Joi.string(); +const email = Joi.string().email(); +const password = Joi.string().min(8); +const role = Joi.string().min(4); + +const createUserSchema = Joi.object({ + name: name.required(), + email: email.required(), + password: password.required(), + role: role.required(), +}); + +const updateUserSchema = Joi.object({ + name: name, + email: email, + role: role, +}); + +const getUserSchema = Joi.object({ + id: id.required(), +}); + +module.exports = { createUserSchema, updateUserSchema, getUserSchema }; diff --git a/server/services/CalculosHugo.service.js b/server/services/CalculosHugo.service.js new file mode 100644 index 0000000..dd04dcf --- /dev/null +++ b/server/services/CalculosHugo.service.js @@ -0,0 +1,36 @@ +const { models } = require('../lib/sequelize'); +const boom = require('@hapi/boom'); + +class CalculosHugoService { + async find() { + const rta = await models.CalculosHugoService.findAll(); + return rta; + } + + async findOne(id) { + const calculo = await models.CalculosHugoService.findByPk(id); + if (!calculo) { + throw boom.notFound('Customer not found'); + } + return calculo; + } + + async create(data) { + const newCalculo = await models.CalculosHugoService.create(data); + return newCalculo; + } + + async update(id, changes) { + const calculo = await this.findOne(id); + const rta = await calculo.update(changes); + return rta; + } + + async delete(id) { + const calculo = await this.findOne(id); + await calculo.destroy(); + return { id }; + } +} + +module.exports = CalculosHugoService; diff --git a/server/services/NominasSemanales.service.js b/server/services/NominasSemanales.service.js new file mode 100644 index 0000000..ba13a87 --- /dev/null +++ b/server/services/NominasSemanales.service.js @@ -0,0 +1,115 @@ +const { faker } = require('@faker-js/faker'); +const boom = require('@hapi/boom'); +const { create } = require('../schemas/trabajadores.schema'); +const { models } = require('../lib/sequelize'); +const { Op } = require('sequelize'); + +class NominasSemanalesService { + async find() { + const rta = await models.NominasSemanales.findAll(); + return rta; + } + + async findWeeklyNominasManual() { + const startDate = new Date('2023-01-01'); // Fecha de inicio fija + let endDate = new Date(); // Fecha actual como punto de partida para el cálculo del fin de semana + endDate.setDate(endDate.getDate() + (14 - endDate.getDay())); // Extender al último día de la siguiente semana // Ajusta al último día de la semana actual (sábado, si domingo es el inicio de la semana) + endDate.setHours(23, 59, 59, 999); // Asegura que el fin de semana abarque todo el día + + const nominas = await models.NominasSemanales.findAll({ + where: { + fecha_inicio_semana: { + [Op.gte]: startDate, + }, + fecha_fin_semana: { + [Op.lte]: endDate, + }, + }, + }); + + if (!nominas || nominas.length === 0) { + throw new Error('Nominas not found'); + } + + const weeklyNominas = []; + let currentDate = new Date(startDate); + + while (currentDate <= endDate) { + const startOfWeek = new Date(currentDate); + startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay()); + startOfWeek.setHours(0, 0, 0, 0); + + const endOfWeek = new Date(startOfWeek); + endOfWeek.setDate(endOfWeek.getDate() + 6); + endOfWeek.setHours(23, 59, 59, 999); + + // Filtrar y procesar las nóminas para esta semana específica + const nominasForWeek = nominas + .filter((nomina) => { + const nominaStartDate = new Date(nomina.fecha_inicio_semana); + return nominaStartDate >= startOfWeek && nominaStartDate <= endOfWeek; + }) + .map((nomina) => ({ + workerId: nomina.worker_id, + workerName: nomina.nombre, + salary_hour: nomina.salary_hour, + horas_trabajadas: nomina.horas_trabajadas, + horas_extra: nomina.horas_extra, + salary: nomina.salary, + isr: nomina.isr, + seguro_social: nomina.seguro_social, + salary: nomina.salario_final, + startDate: nomina.fecha_inicio_semana, + endDate: nomina.fecha_fin_semana, + })); + + const totalWeeklySalary = nominasForWeek.reduce( + (total, nomina) => total + nomina.salary, + 0, + ); + + weeklyNominas.push({ + startDate: startOfWeek.toISOString(), + endDate: endOfWeek.toISOString(), + nominas: nominasForWeek, + totalWeeklySalary, + }); + + currentDate.setDate(currentDate.getDate() + 7); + } + + return weeklyNominas; + } + + async findOne(id) { + const worker = await models.Worker.findByPk(id); + if (!worker) { + throw boom.notFound('Worker not found'); + } + return worker; + } + + async create(data) { + const newWorker = await models.NominasSemanales.create(data); + return newWorker; + } + + async update(id, changes) { + const worker = await this.findOne(id); + + if (!worker) { + throw boom.notFound('Worker not found'); + } + + const rta = await worker.update(changes); + return rta; + } + + async delete(id) { + const worker = await this.findOne(id); + + await worker.destroy(); + return { id }; + } +} +module.exports = NominasSemanalesService; diff --git a/server/services/adjustments.service.js b/server/services/adjustments.service.js new file mode 100644 index 0000000..66368f5 --- /dev/null +++ b/server/services/adjustments.service.js @@ -0,0 +1,32 @@ +const { models } = require('../lib/sequelize'); +const boom = require('@hapi/boom'); + +class AdjustmentsService { + + async findOne(id) { + const calculo = await models.Adjustments.findByPk(id); + if (!calculo) { + throw boom.notFound('Customer not found'); + } + return calculo; + } + + async create(data) { + const newCalculo = await models.Adjustments.create(data); + return newCalculo; + } + + async update(id, changes) { + const calculo = await this.findOne(id); + const rta = await calculo.update(changes); + return rta; + } + + async delete(id) { + const calculo = await this.findOne(id); + await calculo.destroy(); + return { id }; + } +} + +module.exports = AdjustmentsService; diff --git a/server/services/clientes.service.js b/server/services/clientes.service.js index 271640d..3043f60 100644 --- a/server/services/clientes.service.js +++ b/server/services/clientes.service.js @@ -1,38 +1,35 @@ - -const {models} = require("../lib/sequelize") -const {use} = require("../routes/clientes.router") -const boom = require("@hapi/boom") +const { models } = require('../lib/sequelize'); +const boom = require('@hapi/boom'); class CustomersService { - async find() { - const rta = await models.Customer.findAll() - return rta + const rta = await models.Customer.findAll(); + return rta; } async findOne(id) { - const customer = await models.Customer.findByPk(id) - if (!customer){ - throw boom.notFound("Customer not found") + const customer = await models.Customer.findByPk(id); + if (!customer) { + throw boom.notFound('Customer not found'); } - return customer + return customer; } async create(data) { - const newCustomer = await models.Customer.create(data) - return newCustomer + const newCustomer = await models.Customer.create(data); + return newCustomer; } async update(id, changes) { - const customer = await this.findOne(id) - const rta = await customer.update(changes) - return rta + const customer = await this.findOne(id); + const rta = await customer.update(changes); + return rta; } async delete(id) { - const customer = await this.findOne(id) - await customer.destroy() - return {id} + const customer = await this.findOne(id); + await customer.destroy(); + return { id }; } } diff --git a/server/services/payrolls.service.js b/server/services/payrolls.service.js new file mode 100644 index 0000000..837eef9 --- /dev/null +++ b/server/services/payrolls.service.js @@ -0,0 +1,36 @@ +const { models } = require('../lib/sequelize'); +const boom = require('@hapi/boom'); + +class PayrollService { + async findOnePayroll(id) { + const payroll = await models.Nomina.findByPk(id); + if (!payroll) { + throw boom.notFound('Payroll not found'); + } + return payroll; + } + + async createPayroll(data) { + const newPayroll = await models.Nomina.create(data); + return newPayroll; + } + + async update(id, changes) { + const payroll = await this.findOne(id); + + if (!payroll) { + throw boom.notFound('payroll not found'); + } + + const rta = await payroll.update(changes); + return rta; + } + + async deletePayroll(id) { + const payroll = await this.findOne(id); + + await payroll.destroy(); + return { id }; + } +} +module.exports = PayrollService; diff --git a/server/services/proyectos.service.js b/server/services/proyectos.service.js index 242e84e..7fb844b 100644 --- a/server/services/proyectos.service.js +++ b/server/services/proyectos.service.js @@ -1,22 +1,69 @@ -const { faker } = require('@faker-js/faker'); const { models } = require('../lib/sequelize'); +const { Abonos } = require('../db/models/abonos.model'); const boom = require('@hapi/boom'); const { addCustomerRESchema, addWorkerRESchema, - addServiceSchema, } = require('../schemas/proyectos.schema'); +const { ProjectSchema } = require('../db/models/proyectos.model'); + +const WebSocket = require('ws'); +const { getWss } = require('../lib/webnsocket'); class ProjectService { - constructor() { - // this.projects = [], this.generate() + async updateCardsWebsocket(proyectoId) { + const wss = getWss(); + + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + console.log('hola'); + } + }); + + const updatedProjectData = await this.findOne(proyectoId); + + if (wss && wss.clients) { + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + // Aquí enviarías el mensaje al cliente + //client.send('¡Hola cliente!'); + //console.log('bien hecho'); + } + }); + } + + if (wss && wss.clients) { + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + // Asegúrate de enviar solo los datos necesarios para actualizar las vistas de las tarjetas. + const dataToSend = { + costo: updatedProjectData.costo, + abonado: updatedProjectData.abonado, + remaining: updatedProjectData.remaining, + presupuesto: updatedProjectData.presupuesto, + }; + console.log('Datos enviados al cliente:', dataToSend); // Registro agregado aquí + client.send(JSON.stringify(dataToSend)); + } + }); + } + + console.log( + 'Monto abonado actualizado correctamente y notificado a los clientes.', + ); } + // -------Project Services! ----- - async find() { + async findProjects() { const rta = await models.Project.findAll(); return rta; } + async findAllPrestamos() { + const rta = await models.Prestamo.findAll(); + return rta; + } + async findOne(id) { const project = await models.Project.findByPk(id, { include: [ @@ -40,6 +87,253 @@ class ProjectService { return project; } + async findSemanalCosts() { + const project = await models.Project.findAll({ + include: ['abonos', 'services'], + }); + + if (!project) { + throw boom.notFound('Project not found'); + } + + return project; + } + + async findWeeklyCosts() { + const startDate = new Date('2023-01-01'); // Fecha de inicio desde el año 2023 + const endDate = new Date(); // Fecha actual + + const projects = await models.Project.findAll({ + include: ['services'], // Excluir 'abonos' y solo incluir 'services' + }); + + if (!projects || projects.length === 0) { + throw boom.notFound('Projects not found'); + } + + const weeklyCosts = []; + + // Iterar sobre cada semana desde 2023 hasta la fecha actual + let currentDate = new Date(startDate); + while (currentDate <= endDate) { + const startOfWeek = new Date(currentDate); + startOfWeek.setHours(0, 0, 0, 0); + const endOfWeek = new Date(currentDate); + endOfWeek.setDate(endOfWeek.getDate() + 6); + endOfWeek.setHours(23, 59, 59, 999); + + const weeklyCost = projects + .filter((project) => { + const projectData = project.toJSON(); + + const projectWeeklyServiceCost = projectData.services.reduce( + (total, service) => { + const serviceDate = new Date(service.createdAt); + return serviceDate >= startOfWeek && serviceDate <= endOfWeek + ? total + service.cost + : total; + }, + 0, + ); + + return projectWeeklyServiceCost > 0; + }) + .map((project) => { + const projectData = project.toJSON(); + + const projectWeeklyServiceCost = projectData.services.reduce( + (total, service) => { + const serviceDate = new Date(service.createdAt); + if (serviceDate >= startOfWeek && serviceDate <= endOfWeek) { + total += service.cost; + } + return total; + }, + 0, + ); + + return { + projectId: projectData.id, + projectName: projectData.name, // Reemplaza 'name' con el campo correcto + weeklyCost: projectWeeklyServiceCost, + }; + }); + + // Calcular la suma total de los costos para la semana + const totalWeeklyCost = weeklyCost.reduce( + (total, entry) => total + entry.weeklyCost, + 0, + ); + + weeklyCosts.push({ + startDate: startOfWeek.toISOString(), + endDate: endOfWeek.toISOString(), + weeklyCost, + totalWeeklyCost, + }); + + // Mover a la siguiente semana + currentDate.setDate(currentDate.getDate() + 7); + } + + return weeklyCosts; + } + + async getPayrollInformation() { + // Obtener todas las nominas + const nominas = await models.Nomina.findAll({ + include: [ + { + model: models.Worker, + as: 'worker', + attributes: ['name', 'last_name', 'salary'], + }, + { model: models.Project, as: 'project', attributes: ['name'] }, + ], + }); + + // Transformar la información + const payrollInfo = nominas.map((nomina) => { + const { name, last_name, salary } = nomina.worker; + const { name: projectName } = nomina.project; + + return { + nombre_empleado: `${name} ${last_name}`, + nombre_del_proyecto: projectName, + salary, + payment_date: nomina.payment_dates, + }; + }); + + return payrollInfo; + } + + async findPayrollsWeeks() { + try { + const payrolls = await models.Nomina.findAll({ + include: [ + { + model: models.Worker, + as: 'worker', + attributes: ['name'], + }, + { + model: models.Project, + as: 'project', + attributes: ['name'], + }, + ], + attributes: ['payment_dates', 'amount_paid'], + }); + + // Map the results to the desired JSON format + const formattedPayrolls = payrolls.map((payroll) => { + return { + fecha_pago: payroll.payment_dates, // Assuming payment_dates is an array of dates + nombre_trabajador: payroll.worker.name, + sueldo: payroll.amount_paid, + nombre_proyecto: payroll.project.name, + }; + }); + + return formattedPayrolls; + } catch (error) { + console.error('Error fetching payrolls:', error); + throw error; + } + } + + async findWeeklyAbonos() { + const startDate = new Date('2023-01-01'); + const endDate = new Date(); + + const abonos = await models.Abonos.findAll({ + include: ['project'], + }); + + if (!abonos || abonos.length === 0) { + throw boom.notFound('Abonos not found'); + } + + const weeklyAbonos = []; + + let currentDate = new Date(startDate); + while (currentDate <= endDate) { + const startOfWeek = new Date(currentDate); + startOfWeek.setHours(0, 0, 0, 0); + const endOfWeek = new Date(currentDate); + endOfWeek.setDate(endOfWeek.getDate() + 6); + endOfWeek.setHours(23, 59, 59, 999); + + const abonosForWeek = abonos + .filter((abono) => { + const abonoData = abono.toJSON(); + const abonoDate = new Date(abonoData.fecha_abono); + return abonoDate >= startOfWeek && abonoDate <= endOfWeek; + }) + .map((abono) => { + const abonoData = abono.toJSON(); + return { + amount: abonoData.monto, + projectName: abonoData.project.name, + date: abonoData.fecha_abono.toISOString(), + }; + }); + + const totalWeeklyAbonos = abonosForWeek.reduce( + (total, entry) => total + entry.amount, + 0, + ); + + weeklyAbonos.push({ + startDate: startOfWeek.toISOString(), + endDate: endOfWeek.toISOString(), + abonos: abonosForWeek, + totalWeeklyAbonos, + }); + + // Mover a la siguiente semana + currentDate.setDate(currentDate.getDate() + 7); + } + + return weeklyAbonos; + } + + async findAdustments() { + const rta = await models.Adjustments.findAll(); + return rta; + } + + async findOneAdjustment(id) { + const calculo = await models.Adjustments.findByPk(id); + if (!calculo) { + throw boom.notFound('Customer not found'); + } + return calculo; + } + + // funcion para egresos + // no sirve + async findEgresos() { + try{ + const nomina = await models.Nomina.findAll(); + + + const nominafind = nomina.map((nomina) => { + return { + fecha_pago:nomina.payment_dates, + monto_pagado: nomina.amount_paid, + }; + + }); + return nominafind; + }catch(err){ + console.error('Error fetching payrolls:', err); + throw err; + } + + } + async update(id, changes) { const project = await this.findOne(id); @@ -55,8 +349,22 @@ class ProjectService { } async create(data) { - const newProject = await models.Project.create(data); - return newProject; + try { + // Obtener el valor predeterminado de costo_inicial del esquema + const defaultInitialCost = ProjectSchema.costo_inicial.defaultValue; + + // Establecer el valor predeterminado de costo_inicial como costo al crear el proyecto + const newProjectData = { + ...data, + costo: data.costo || defaultInitialCost, + }; + + const newProject = await models.Project.create(newProjectData); + return newProject; + } catch (error) { + console.error('Error al crear un proyecto:', error); + throw error; + } } // ---------ProjectCustomers Service!------- async findCustomersProjects() { @@ -74,7 +382,7 @@ class ProjectService { return projectCustomer; } - async addCustomer(data) { + async addProjectCustomer(data) { try { // Validar datos con Joi await addCustomerRESchema.validateAsync(data); @@ -162,28 +470,52 @@ class ProjectService { return service; } + async updateCost(id, changes) { + const cost = await this.findOneService(id); + console.log('cost:',cost) + await this.updateCardsWebsocket(id); + const rta = await cost.update(changes); + return rta; + } + + // En tu lógica de negocio para crear un servicio async createService(data) { + const proyectoId = data.project_id; + console.log(proyectoId); const newService = await models.Service.create(data); - // Después de crear el servicio, actualiza el costo total del proyecto - await this.updateProjectTotalCost(newService.project_id); + // Llamada a la función que actualiza el costo total del proyecto al insertar un servicio + await models.Service.sequelize.query( + 'SELECT actualizar_costo_proyecto_al_insertar_servicio(:cost, :project_id)', + { + replacements: { + cost: newService.cost, + project_id: newService.project_id, + }, + type: models.Service.sequelize.QueryTypes.SELECT, + }, + ); + await this.updateCardsWebsocket(proyectoId); return newService; } - async deleteService(id) { const service = await this.findOneService(id); const projectId = service.project_id; - // Antes de eliminar el servicio, obtén el costo del proyecto - const project = await models.Project.findByPk(projectId); - const initialProjectCost = project.costo; + const costoServicio = service.cost; await service.destroy(); - // Después de eliminar el servicio, actualiza el costo total del proyecto - await this.updateProjectTotalCost(projectId, initialProjectCost); - + // Llamada a la función que actualiza el costo total del proyecto al eliminar un servicio + await models.Service.sequelize.query( + 'SELECT actualizar_costo_proyecto_al_eliminar_servicio(:cost, :project_id)', + { + replacements: { cost: costoServicio, project_id: projectId }, + type: models.Service.sequelize.QueryTypes.SELECT, + }, + ); + await this.updateCardsWebsocket(projectId); return { id }; } @@ -195,7 +527,8 @@ class ProjectService { const totalCostOfServices = await models.Service.sum('cost', { where: { project_id: projectId }, }); - + console.log(totalCostOfServices ) + console.log(project.costo) // Si es el primer servicio, agrega el costo inicial del proyecto const totalCost = totalCostOfServices + project.costo; @@ -203,6 +536,152 @@ class ProjectService { await project.update({ costo: totalCost || 0 }); } } + + /**--------------Abonos--------------------- */ + async findAbonos() { + const rta = await models.Abonos.findAll(); + return rta; + } + + async findOneAbono(id) { + const abono = await models.Abonos.findByPk(id, {}); + + if (!abono) { + throw boom.notFound('Abono not found'); + } + return abono; + } + + async updateAbono(id, changes) { + const abono = await this.findOneAbono(id); + const rta = await abono.update(changes); + + // Lógica para actualizar el monto abonado en el proyecto correspondiente + const nuevoMontoAbono = changes.monto || 0; + const proyectoId = abono.projectId; + + try { + // Acceder a la instancia de Sequelize a través del modelo Abonos + await Abonos.sequelize.query( + 'CALL actualizar_monto_abonado(:nuevo_monto, :proyecto_id)', + { + replacements: { + nuevo_monto: nuevoMontoAbono, + proyecto_id: proyectoId, + }, + type: Abonos.sequelize.QueryTypes.RAW, + }, + ); + await this.updateCardsWebsocket(proyectoId); + console.log('Monto abonado actualizado correctamente'); + } catch (error) { + console.error('Error al actualizar el monto abonado:', error); + } + + return rta; + } + + async createAbono(data) { + try { + const newAbono = await Abonos.create(data); + const nuevoMontoAbono = data.monto || 0; + const proyectoId = data.projectId; + try { + // Actualiza el monto abonado en la base de datos + await Abonos.sequelize.query( + 'SELECT actualizar_monto_abonado(:nuevo_monto, :proyecto_id)', + { + replacements: { + nuevo_monto: nuevoMontoAbono, + proyecto_id: proyectoId, + }, + type: Abonos.sequelize.QueryTypes.SELECT, + }, + ); + + await this.updateCardsWebsocket(proyectoId) + + + + + + } catch (error) { + console.error( + 'Error al actualizar el monto abonado y notificar a los clientes:', + error, + ); + throw error; // Asegúrate de manejar este error adecuadamente en tu aplicación. + } + return newAbono; + } catch (error) { + console.error('Error al crear un abono:', error); + throw error; + } + } + + async deleteAbono(id) { + const abono = await this.findOneAbono(id); + + const nuevoMontoAbono = abono.monto || 0; + const proyectoId = abono.projectId; + + await abono.destroy(); + + try { + // Acceder a la instancia de Sequelize a través del modelo Abonos + await Abonos.sequelize.query( + 'SELECT restar_monto_abonado(:monto_a_restar, :proyecto_id)', + { + replacements: { + monto_a_restar: nuevoMontoAbono, + proyecto_id: proyectoId, + }, + type: Abonos.sequelize.QueryTypes.SELECT, + }, + ); + await this.updateCardsWebsocket(proyectoId); + console.log('Monto abonado actualizado correctamente'); + } catch (error) { + console.error('Error al actualizar el monto abonado:', error); + } + + return { id }; + } + + async createPrestamo(data) { + const nuevoPrestamo = await models.Prestamo.create(data); + return nuevoPrestamo; + } + + /**--------------Ajustes--------------------- */ + async findAdjustments() { + const rta = await models.Adjustments.findAll(); + return rta; + } + + + async createAdjustment(data) { + const newCalculo = await models.Adjustments.create(data); + + const proyectoId = data.projectId; + this.updateCardsWebsocket(proyectoId) + console.log(proyectoId) + + return newCalculo; + } + + async updateAdjustment(id, changes) { + const calculo = await this.findOneAdjustment(id); + const rta = await calculo.update(changes); + return rta; + } + + async deleteAdjustment(id) { + const calculo = await this.findOneAdjustment(id); + await calculo.destroy(); + + return { id }; + } } module.exports = ProjectService; diff --git a/server/services/trabajadores.service.js b/server/services/trabajadores.service.js index fd2434f..ad95d7e 100644 --- a/server/services/trabajadores.service.js +++ b/server/services/trabajadores.service.js @@ -1,46 +1,106 @@ -const { faker } = require('@faker-js/faker'); -const boom = require("@hapi/boom") -const {create} = require("../schemas/trabajadores.schema") -const {models} = require("../lib/sequelize") + +const boom = require('@hapi/boom'); + +const { models } = require('../lib/sequelize'); class WorkerService { + async find() { + const rta = await models.Worker.findAll(); + return rta; + } -async find() { - const rta = await models.Worker.findAll() - return rta -} + async findTools() { + const rta = await models.tools.findAll(); + return rta; + } + async findWorkerCost() { + const rta = await models.WorkerCost.findAll(); + return rta; + } + async findOne(id) { - const worker = await models.Worker.findByPk(id) + const worker = await models.Worker.findByPk(id, { + include: [{ model: models.WorkerCost, as: 'WorkerCosts' }], + }); + if (!worker) { + throw boom.notFound('Worker not found'); + } + return worker; + } + + // async findOneTool(id) { + // const worker = await models.WorkerCost.findByPk(id); + // if (!worker) { + // throw boom.notFound('Worker not found'); + // } + // return worker; + // } + + async findOneTool(id) { + const worker = await models.tools.findByPk(id); if (!worker) { - throw boom.notFound("Worker not found") + throw boom.notFound('Worker not found'); } return worker; } + async createTools(data) { + const tool = await models.tools.create(data); + return tool; + } + + async create(data) { - const newWorker = await models.Worker.create(data) - return newWorker + const newWorker = await models.Worker.create(data); + return newWorker; + } + + //async createTools(data) { + // const newWorker = await models.WorkerCost.create(data); + // return newWorker; + // } + + async createTool(data) { + const tool = await models.tools.create(data); + return tool; } async update(id, changes) { const worker = await this.findOne(id); + console.log(worker); + if (!worker) { - throw boom.notFound("Worker not found"); + throw boom.notFound('Worker not found'); } const rta = await worker.update(changes); return rta; } + async delete(id) { + const worker = await this.findOne(id); + + await worker.destroy(); + return { id }; + } - async delete(id){ - const worker = await this.findOne(id) + async deleteProjectWorker(id) { + const worker = await this.findOneTool(id); - await worker.destroy() - return {id} + await worker.destroy(); + return { id }; } + + async deleteTools(id) { + const worker = await this.findOneTool(id); + + await worker.destroy(); + return { id }; + } + + } module.exports = WorkerService; diff --git a/server/services/users.service.js b/server/services/users.service.js new file mode 100644 index 0000000..ab6eaaa --- /dev/null +++ b/server/services/users.service.js @@ -0,0 +1,51 @@ +const boom = require('@hapi/boom'); +const bcrypt = require('bcrypt'); +const { models } = require('../lib/sequelize'); + +class UserService { + constructor() {} + + async create(data) { + const hash = await bcrypt.hash(data.password, 10); + const newUser = await models.User.create({ ...data, password: hash }); + delete newUser.dataValues.password; //esto evita que al crearse un usuario se regrese la contraseña, importante no borrar!!! + return newUser; + } + + async findUsers() { + const users = await models.User.findAll({ + attributes: ['id', 'name', 'email', 'role'], + }); + return users; + } + + //Esto solo es para la autentificacion + async findByEmail(email) { + const rta = await models.User.findOne({ + where: { email }, + }); + return rta; + } + + async findOne(id) { + const user = await models.User.findByPk(id); + if (!user) { + throw boom.notFound('user not found'); + } + return user; + } + + async update(id, changes) { + const user = await this.findOne(id); + const rta = await user.update(changes); + return rta; + } + + async delete(id) { + const user = await this.findOne(id); + await user.destroy(); + return { id }; + } +} + +module.exports = UserService; diff --git a/server/token-sign.js b/server/token-sign.js new file mode 100644 index 0000000..f591d1f --- /dev/null +++ b/server/token-sign.js @@ -0,0 +1,14 @@ +const jwt = require('jsonwebtoken'); + +const secret = 'myCat'; +const payload = { + sub: 1, + role: 'customer', +}; + +function signToken(payload, secret) { + return jwt.sign(payload, secret); +} + +const token = signToken(payload, secret); +console.log(token); // diff --git a/server/token-verify.js b/server/token-verify.js new file mode 100644 index 0000000..c90b878 --- /dev/null +++ b/server/token-verify.js @@ -0,0 +1,12 @@ +const jwt = require('jsonwebtoken'); + +const secret = 'myCat'; +const token = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsInJvbGUiOiJjdXN0b21lciIsImlhdCI6MTcwNjE2NDYwM30.0E4uJ9u7gzhuU7y5lj9TdvebpbiUXC14A_fEpR8j4TU'; + +function verifyToken(token, secret) { + return jwt.verify(token, secret); +} + +const payload = verifyToken(token, secret); +console.log(payload); // diff --git a/server/utils/auth/index.js b/server/utils/auth/index.js new file mode 100644 index 0000000..3d41416 --- /dev/null +++ b/server/utils/auth/index.js @@ -0,0 +1,7 @@ +const passport = require('passport'); + +const localStrategy = require('./strategies/local.strategy'); +const JwtStrategy = require('./strategies/jwt.strategy'); + +passport.use(localStrategy); +passport.use(JwtStrategy); diff --git a/server/utils/auth/strategies/google.strategy.js b/server/utils/auth/strategies/google.strategy.js new file mode 100644 index 0000000..bee5805 --- /dev/null +++ b/server/utils/auth/strategies/google.strategy.js @@ -0,0 +1,16 @@ +var GoogleStrategy = require('passport-google-oauth20').Strategy; + +passport.use( + new GoogleStrategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: 'http://www.example.com/auth/google/callback', + }, + function (accessToken, refreshToken, profile, cb) { + User.findOrCreate({ googleId: profile.id }, function (err, user) { + return cb(err, user); + }); + }, + ), +); diff --git a/server/utils/auth/strategies/jwt.strategy.js b/server/utils/auth/strategies/jwt.strategy.js new file mode 100644 index 0000000..e797e61 --- /dev/null +++ b/server/utils/auth/strategies/jwt.strategy.js @@ -0,0 +1,14 @@ +const { Strategy, ExtractJwt } = require('passport-jwt'); + +const { config } = require('../../../config/config'); + +const options = { + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: config.jwtsecret, +}; + +const JwtStrategy = new Strategy(options, (payload, done) => { + return done(null, payload); +}); + +module.exports = JwtStrategy; diff --git a/server/utils/auth/strategies/local.strategy.js b/server/utils/auth/strategies/local.strategy.js new file mode 100644 index 0000000..620d851 --- /dev/null +++ b/server/utils/auth/strategies/local.strategy.js @@ -0,0 +1,29 @@ +const { Strategy } = require('passport-local'); +const boom = require('@hapi/boom'); +const userService = require('../../../services/users.service'); +const service = new userService(); +const bcrypt = require('bcrypt'); + +const localStrategy = new Strategy( + { + usernameField: 'email', + passwordField: 'password', + }, + async (email, password, done) => { + try { + const user = await service.findByEmail(email); + if (!user) { + done(boom.unauthorized(), false); + } + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + done(boom.unauthorized(), false); + } + done(null, user); + } catch (error) { + done(error, false); + } + }, +); + +module.exports = localStrategy;