From d900ecb313c95dd161e02cbd8a9888406fbcddf9 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 10 Jun 2026 00:22:41 +0200 Subject: [PATCH] Fase 1: ProjectDAO, formProject, formProjectList, PROJECTDOCUMENT, PROJECTBUDGET en sqltable.h --- data/sqltable.h | 194 ++++++------------ src/dao/projectdao.cpp | 328 ++++++++++++++++++++++++++++++ src/dao/projectdao.h | 84 ++++++++ src/gui/forms/formproject.cpp | 261 ++++++++++++++++++++++++ src/gui/forms/formproject.h | 48 +++++ src/gui/forms/formprojectlist.cpp | 92 +++++++++ src/gui/forms/formprojectlist.h | 30 +++ 7 files changed, 904 insertions(+), 133 deletions(-) create mode 100644 src/dao/projectdao.cpp create mode 100644 src/dao/projectdao.h create mode 100644 src/gui/forms/formproject.cpp create mode 100644 src/gui/forms/formproject.h create mode 100644 src/gui/forms/formprojectlist.cpp create mode 100644 src/gui/forms/formprojectlist.h diff --git a/data/sqltable.h b/data/sqltable.h index 60885dc..315c1a2 100644 --- a/data/sqltable.h +++ b/data/sqltable.h @@ -148,98 +148,6 @@ const QString tElemento = "CREATE TABLE ELEMENT (" "WIDTH FLOAT, " // 21. ID proveedor por defecto "LENGHT FLOAT, " // 22. ID proveedor por defecto -/* Interesa?? - // Proveedores: - "SUPPLIER_ID_0 VARCHAR(10), " // 23. ID del proveedor - "SUPPLIER_NAME_0 VARCHAR(60), " // 24. Nombre del proveedor - "SUPPLIER_REFERENCE_0 VARCHAR(20), " // 25. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_0 FLOAT, " // 26. PVP - "SUPPLIER_DISCOUNT_0 FLOAT, " // 27. Descuento - "SUPPLIER_PURCHASE_PRICE_0 FLOAT, " // 28. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_0 FLOAT, " // 29. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_0 DATE, " // 30. Fecha de actualización del precio - - "SUPPLIER_ID_1 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_1 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_1 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_1 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_1 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_1 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_1 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_1 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_2 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_2 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_2 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_2 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_2 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_2 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_2 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_2 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_3 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_3 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_3 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_3 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_3 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_3 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_3 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_3 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_4 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_4 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_4 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_4 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_4 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_4 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_4 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_4 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_5 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_5 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_5 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_5 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_5 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_5 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_5 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_5 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_6 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_6 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_6 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_6 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_6 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_6 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_6 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_6 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_7 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_7 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_7 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_7 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_7 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_7 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_7 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_7 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_8 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_8 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_8 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_8 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_8 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_8 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_8 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_8 DATE, " // 25. Fecha de actualización del precio - - "SUPPLIER_ID_9 VARCHAR(10), " // 18. ID del proveedor - "SUPPLIER_NAME_9 VARCHAR(60), " // 19. Nombre del proveedor - "SUPPLIER_REFERENCE_9 VARCHAR(20), " // 20. Ref. del elemento que tiene el proveedor - "SUPPLIER_REAL_PRICE_9 FLOAT, " // 21. PVP - "SUPPLIER_DISCOUNT_9 FLOAT, " // 22. Descuento - "SUPPLIER_PURCHASE_PRICE_9 FLOAT, " // 23. Precio compra - "SUPPLIER_MINIMUM_AMOUNT_9 FLOAT, " // 24. Cantidad mínima de pedido - "SUPPLIER_DATE_UPDATE_9 DATE, " // 97. Fecha de actualización del precio -*/ "SUPPLIER_DEFAULT VARCHAR(10), " // 98. CODE proveedor por defecto "CREATEDAT DATE DEFAULT CURRENT_DATE, " // 25. Fecha de creación @@ -417,7 +325,7 @@ const QString tDataDocVenta = "CREATE TABLE SALEDOCUMENTDATA (" //---------------------- DOCUMENTOS DE COMPRA -------------------------------- const QString tDocCompra = "CREATE TABLE BUYDOCUMENT (" "ID INTEGER PRIMARY KEY AUTOINCREMENT, " // 0. ID - "TYPE VARCHAR(2) NOT NULL, " // 1. Tipo de documento: + "TYPE VARCHAR(2)\tNOT NULL, " // 1. Tipo de documento: "CODE VARCHAR(20) NOT NULL, " // 2. Código documento "SUPPLIER_CODE VARCHAR(20) NOT NULL, " // 3. Código proveedor "SUPPLIER_NAME VARCHAR(100) NOT NULL, " // 4. Nombre proveedor @@ -439,7 +347,7 @@ const QString tDocCompra = "CREATE TABLE BUYDOCUMENT (" const QString tDataDocCompra = "CREATE TABLE BUYDOCUMENTDATA (" "ID INTEGER NOT NULL, " // 0. ID linea - "BUYDOCUMENT_CODE VARCHAR(10) NOT NULL, " // 1. ID Documento de compra + "BUYDOCUMENT_CODE\tVARCHAR(10)\tNOT NULL, " // 1. ID Documento de compra "ELEMENT_CODE VARCHAR(20), " // 2. ID del elemento "ELEMENT_SUPPLIER_CODE VARCHAR(20), " // 3. ID del elemento que tiene el proveedor "ELEMENT_NAME VARCHAR(100), " // 4. Descripción del elemento @@ -458,24 +366,24 @@ const QString tDataDocCompra = "CREATE TABLE BUYDOCUMENTDATA (" //----------------------- PROYECTO ----------------------------------------- const QString tProyecto = "CREATE TABLE PROJECT (" - "ID INTEGER PRIMARY KEY AUTOINCREMENT, " // 0. ID - "CODE VARCHAR(20) NOT NULL, " // 1. ID - "TITLE VARCHAR(100) NOT NULL, " // 2. Título - "CUSTOMER_CODE VARCHAR(10) NOT NULL, " // 3. ID Cliente - "CUSTOMER_NAME VARCHAR(60) NOT NULL, " // 4. Nombre del cliente - "START_DATE DATE DEFAULT CURRENT_DATE, " // 5. Fecha de Inicio - "DURATION DATE, " // 6. Fecha de Inicio - "STATE VARCHAR(10), " // 7. Estado + "ID\tINTEGER PRIMARY KEY AUTOINCREMENT, " // 0. ID + "CODE\tVARCHAR(20) NOT NULL, " // 1. ID + "TITLE\tVARCHAR(100) NOT NULL, " // 2. Título + "CUSTOMER_CODE\tVARCHAR(10) NOT NULL, " // 3. ID Cliente + "CUSTOMER_NAME\tVARCHAR(60) NOT NULL, " // 4. Nombre del cliente + "START_DATE\tDATE DEFAULT CURRENT_DATE, " // 5. Fecha de Inicio + "DURATION\tDATE, " // 6. Fecha de Inicio + "STATE\tVARCHAR(10), " // 7. Estado // Dirección de la obra - "CONTACT VARCHAR(60), " // 8. Contacto - "ADDRESS1 VARCHAR(40), " // 9. Dir L1 - "ADDRESS2 VARCHAR(40), " // 10. Dir L2 - "POSTCODE VARCHAR(9), " // 11. CP - "CITY VARCHAR(80), " // 12. Ciudad - "PHONE VARCHAR(20), " // 13. Telefono - "MOBILE VARCHAR(20), " // 14. Movil - "EMAIL VARCHAR(60), " // 15. e-mail + "CONTACT\tVARCHAR(60), " // 8. Contacto + "ADDRESS1\tVARCHAR(40), " // 9. Dir L1 + "ADDRESS2\tVARCHAR(40), " // 10. Dir L2 + "POSTCODE\tVARCHAR(9), " // 11. CP + "CITY\tVARCHAR(80), " // 12. Ciudad + "PHONE\tVARCHAR(20), " // 13. Telefono + "MOBILE\tVARCHAR(20), " // 14. Movil + "EMAIL\tVARCHAR(60), " // 15. e-mail "pyGantt BLOB" // 16. Imagen @@ -484,15 +392,15 @@ const QString tProyecto = "CREATE TABLE PROJECT (" ");"; const QString tProjectControl = "CREATE TABLE PROJECTCONTROL (" - "ID INTEGER PRIMARY KEY AUTOINCREMENT, " // 01. ID - "pcPYCODE VARCHAR(20) NOT NULL, " // 02. ID - "pcELID VARCHAR(20) NOT NULL, " // 03. ID + "ID\tINTEGER PRIMARY KEY AUTOINCREMENT, " // 01. ID + "pcPYCODE\tVARCHAR(20) NOT NULL, " // 02. ID + "pcELID\tVARCHAR(20) NOT NULL, " // 03. ID "pcELTITLE VARCHAR(100) NOT NULL, " // 04. ID Cliente - "pcELTOTAL FLOAT, " // 05. Nombre del cliente + "pcELTOTAL\tFLOAT, " // 05. Nombre del cliente "pcELINSTALLED FLOAT, " // 06. Fecha de Inicio - "pcELREST FLOAT, " // 07. Fecha de Inicio - "pcELUNIT VARCHAR(2), " // 08. Estado - "pcELDATE DATE DEFAULT CURRENT_DATE, " // 09. Estado + "pcELREST\tFLOAT, " // 07. Fecha de Inicio + "pcELUNIT\tVARCHAR(2), " // 08. Estado + "pcELDATE\tDATE DEFAULT CURRENT_DATE, " // 09. Estado "pcELTYPE VARCHAR(3), " "pcELNODEINDEX INTEGER, " "pcTASK VARCHAR(50), " @@ -501,14 +409,34 @@ const QString tProjectControl = "CREATE TABLE PROJECTCONTROL (" "CREATEDAT DATETIME DEFAULT CURRENT_DATE" ");"; +//----------------------- PROJECTDOCUMENT ------------------------------------ +const QString tProjectDocument = "CREATE TABLE PROJECTDOCUMENT (" + "ID INTEGER PRIMARY KEY AUTOINCREMENT, " + "PROJECT_CODE VARCHAR(20) NOT NULL, " + "DOC_TYPE VARCHAR(2) NOT NULL, " + "DOC_CODE VARCHAR(20) NOT NULL, " + "CREATEDBY VARCHAR(100), " + "CREATEDAT DATETIME DEFAULT CURRENT_TIMESTAMP" + ");"; + +//----------------------- PROJECTBUDGET -------------------------------------- +const QString tProjectBudget = "CREATE TABLE PROJECTBUDGET (" + "ID INTEGER PRIMARY KEY AUTOINCREMENT, " + "PROJECT_CODE VARCHAR(20) NOT NULL, " + "CONCEPT VARCHAR(200), " + "AMOUNT FLOAT, " + "CREATEDBY VARCHAR(100), " + "CREATEDAT DATETIME DEFAULT CURRENT_TIMESTAMP" + ");"; + //----------------------- TRABAJADOR --------------------------------------- const QString tTrabajador = "CREATE TABLE EMPLOYEE (" "ID INTEGER PRIMARY KEY AUTOINCREMENT, " // 01. ID "SURNAME VARCHAR(60) NOT NULL, " // 02. Apellidos "NAME VARCHAR(60) NOT NULL, " // 03. Nombre - "STATE BOOLEAN DEFAULT FALSE, " // 04. Activo o inactivo + "STATE\tBOOLEAN\tDEFAULT FALSE, " // 04. Activo o inactivo "CATEGORY VARCHAR(30), " // 05. Categoría - "PRICEPERHOUR FLOAT, " // 06. Precio Hora + "PRICEPERHOUR\tFLOAT, " // 06. Precio Hora "NOTES BLOB," // 07. Notas "PICTURE BLOB," @@ -518,19 +446,19 @@ const QString tTrabajador = "CREATE TABLE EMPLOYEE (" //----------------------- CONTACTOS ----------------------------------------- const QString tContact = "CREATE TABLE CONTACT (" - "coID INTEGER PRIMARY KEY AUTOINCREMENT, " // 01. ID - "coCode VARCHAR(20) NOT NULL, " // 02. Code - "coNombre VARCHAR(60) NOT NULL, " // 03. Nombre - "coApellido VARCHAR(60) NOT NULL, " // 04. Apellidos - "coForma VARCHAR(10), " // 05. Forma + "coID\tINTEGER PRIMARY KEY AUTOINCREMENT, " // 01. ID + "coCode\tVARCHAR(20) NOT NULL, " // 02. Code + "coNombre\tVARCHAR(60) NOT NULL, " // 03. Nombre + "coApellido\tVARCHAR(60) NOT NULL, " // 04. Apellidos + "coForma\tVARCHAR(10), " // 05. Forma - "coPuesto VARCHAR(20), " // 06. Puesto - "coclCode VARCHAR(20), " // 07. Código Empresa + "coPuesto\tVARCHAR(20), " // 06. Puesto + "coclCode\tVARCHAR(20), " // 07. Código Empresa "coclNom VARCHAR(100), " // 08. Nombre Empresa - "coTel VARCHAR(20), " // 09. Teléfono fijo - "coMovil VARCHAR(20), " // 10. Teléfono móvil - "coFax VARCHAR(20), " // 11. Fax - "coEMail VARCHAR(60)," // 12. e-mail + "coTel\tVARCHAR(20), " // 09. Teléfono fijo + "coMovil\tVARCHAR(20), " // 10. Teléfono móvil + "coFax\tVARCHAR(20), " // 11. Fax + "coEMail\tVARCHAR(60)," // 12. e-mail "coImage BLOB," // 13. Imagen "CREATEDAT DATE DEFAULT CURRENT_DATE, " // 25. Fecha de creación @@ -559,9 +487,9 @@ const QString tTemplate = "CREATE TABLE TEMPLATE (" ");"; const QStringList dbTables = {tDBInfo, tEmpresaInfo, tThird, tElemento, tElemComp, tUnidad, tPropuestaVenta, tDataPropuestaVenta, - tDocVenta, tDataDocVenta, tDocCompra, tDataDocCompra, tContact, tTemplate}; + tDocVenta, tDataDocVenta, tDocCompra, tDataDocCompra, tContact, tTemplate, tProjectDocument, tProjectBudget}; const QStringList dbTableNames = {"DBINFO", "ENTERPRISESINFO", "THIRD", "ELEMENT", "ELEMENTCOMPOSITION", "UNIT", "SALEPROPOSAL", "SALEPROPOSALDATA", - "SALEDOCUMENT", "SALEDOCUMENTDATA", "BUYDOCUMENT", "BUYDOCUMENTDATA", "CONTACT", "TEMPLATE"}; + "SALEDOCUMENT", "SALEDOCUMENTDATA", "BUYDOCUMENT", "BUYDOCUMENTDATA", "CONTACT", "TEMPLATE", "PROJECTDOCUMENT", "PROJECTBUDGET"}; -#endif // SQLTABLE_H +#endif // SQLTABLE_H \ No newline at end of file diff --git a/src/dao/projectdao.cpp b/src/dao/projectdao.cpp new file mode 100644 index 0000000..3524d88 --- /dev/null +++ b/src/dao/projectdao.cpp @@ -0,0 +1,328 @@ +#include "projectdao.h" + +// --------------- Project CRUD --------------- + +bool ProjectDAO::create(Project &proj) +{ + QSqlQuery query; + query.prepare("INSERT INTO PROJECT (CODE, TITLE, CUSTOMER_CODE, CUSTOMER_NAME, " + "START_DATE, DURATION, STATE, CONTACT, ADDRESS1, ADDRESS2, " + "POSTCODE, CITY, PHONE, MOBILE, EMAIL, CREATEDBY) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + query.addBindValue(proj.code); + query.addBindValue(proj.title); + query.addBindValue(proj.customerCode); + query.addBindValue(proj.customerName); + query.addBindValue(proj.startDate); + query.addBindValue(proj.duration); + query.addBindValue(proj.state); + query.addBindValue(proj.contact); + query.addBindValue(proj.address1); + query.addBindValue(proj.address2); + query.addBindValue(proj.postcode); + query.addBindValue(proj.city); + query.addBindValue(proj.phone); + query.addBindValue(proj.mobile); + query.addBindValue(proj.email); + query.addBindValue("user"); + + if (!query.exec()) { + qWarning() << "ProjectDAO::create - Error:" << query.lastError().text(); + return false; + } + proj.id = query.lastInsertId().toInt(); + return true; +} + +bool ProjectDAO::update(const Project &proj) +{ + QSqlQuery query; + query.prepare("UPDATE PROJECT SET TITLE=?, CUSTOMER_CODE=?, CUSTOMER_NAME=?, " + "START_DATE=?, DURATION=?, STATE=?, CONTACT=?, ADDRESS1=?, ADDRESS2=?, " + "POSTCODE=?, CITY=?, PHONE=?, MOBILE=?, EMAIL=? WHERE CODE=?"); + query.addBindValue(proj.title); + query.addBindValue(proj.customerCode); + query.addBindValue(proj.customerName); + query.addBindValue(proj.startDate); + query.addBindValue(proj.duration); + query.addBindValue(proj.state); + query.addBindValue(proj.contact); + query.addBindValue(proj.address1); + query.addBindValue(proj.address2); + query.addBindValue(proj.postcode); + query.addBindValue(proj.city); + query.addBindValue(proj.phone); + query.addBindValue(proj.mobile); + query.addBindValue(proj.email); + query.addBindValue(proj.code); + + if (!query.exec()) { + qWarning() << "ProjectDAO::update - Error:" << query.lastError().text(); + return false; + } + return query.numRowsAffected() > 0; +} + +bool ProjectDAO::remove(const QString &code) +{ + QSqlQuery query; + query.prepare("DELETE FROM PROJECT WHERE CODE = ?"); + query.addBindValue(code); + if (!query.exec()) { + qWarning() << "ProjectDAO::remove - Error:" << query.lastError().text(); + return false; + } + return true; +} + +Project ProjectDAO::getByCode(const QString &code) +{ + Project proj; + QSqlQuery query; + query.prepare("SELECT ID, CODE, TITLE, CUSTOMER_CODE, CUSTOMER_NAME, " + "START_DATE, DURATION, STATE, CONTACT, ADDRESS1, ADDRESS2, " + "POSTCODE, CITY, PHONE, MOBILE, EMAIL FROM PROJECT WHERE CODE = ?"); + query.addBindValue(code); + if (query.exec() && query.next()) { + proj.id = query.value(0).toInt(); + proj.code = query.value(1).toString(); + proj.title = query.value(2).toString(); + proj.customerCode = query.value(3).toString(); + proj.customerName = query.value(4).toString(); + proj.startDate = query.value(5).toDate(); + proj.duration = query.value(6).toDate(); + proj.state = query.value(7).toString(); + proj.contact = query.value(8).toString(); + proj.address1 = query.value(9).toString(); + proj.address2 = query.value(10).toString(); + proj.postcode = query.value(11).toString(); + proj.city = query.value(12).toString(); + proj.phone = query.value(13).toString(); + proj.mobile = query.value(14).toString(); + proj.email = query.value(15).toString(); + } + return proj; +} + +Project ProjectDAO::getById(int id) +{ + Project proj; + QSqlQuery query; + query.prepare("SELECT ID, CODE, TITLE, CUSTOMER_CODE, CUSTOMER_NAME, " + "START_DATE, DURATION, STATE, CONTACT, ADDRESS1, ADDRESS2, " + "POSTCODE, CITY, PHONE, MOBILE, EMAIL FROM PROJECT WHERE ID = ?"); + query.addBindValue(id); + if (query.exec() && query.next()) { + proj.id = query.value(0).toInt(); + proj.code = query.value(1).toString(); + proj.title = query.value(2).toString(); + proj.customerCode = query.value(3).toString(); + proj.customerName = query.value(4).toString(); + proj.startDate = query.value(5).toDate(); + proj.duration = query.value(6).toDate(); + proj.state = query.value(7).toString(); + proj.contact = query.value(8).toString(); + proj.address1 = query.value(9).toString(); + proj.address2 = query.value(10).toString(); + proj.postcode = query.value(11).toString(); + proj.city = query.value(12).toString(); + proj.phone = query.value(13).toString(); + proj.mobile = query.value(14).toString(); + proj.email = query.value(15).toString(); + } + return proj; +} + +QVector ProjectDAO::getAll() +{ + QVector result; + QSqlQuery query("SELECT ID, CODE, TITLE, CUSTOMER_CODE, CUSTOMER_NAME, " + "START_DATE, DURATION, STATE, CONTACT, ADDRESS1, ADDRESS2, " + "POSTCODE, CITY, PHONE, MOBILE, EMAIL FROM PROJECT ORDER BY CODE"); + while (query.next()) { + Project proj; + proj.id = query.value(0).toInt(); + proj.code = query.value(1).toString(); + proj.title = query.value(2).toString(); + proj.customerCode = query.value(3).toString(); + proj.customerName = query.value(4).toString(); + proj.startDate = query.value(5).toDate(); + proj.duration = query.value(6).toDate(); + proj.state = query.value(7).toString(); + proj.contact = query.value(8).toString(); + proj.address1 = query.value(9).toString(); + proj.address2 = query.value(10).toString(); + proj.postcode = query.value(11).toString(); + proj.city = query.value(12).toString(); + proj.phone = query.value(13).toString(); + proj.mobile = query.value(14).toString(); + proj.email = query.value(15).toString(); + result.append(proj); + } + return result; +} + +QStringList ProjectDAO::getAllCodes() +{ + QStringList codes; + QSqlQuery query("SELECT CODE FROM PROJECT ORDER BY CODE"); + while (query.next()) + codes.append(query.value(0).toString()); + return codes; +} + +bool ProjectDAO::exists(const QString &code) +{ + QSqlQuery query; + query.prepare("SELECT COUNT(*) FROM PROJECT WHERE CODE = ?"); + query.addBindValue(code); + if (query.exec() && query.next()) + return query.value(0).toInt() > 0; + return false; +} + +// --------------- Project status --------------- + +bool ProjectDAO::setState(const QString &code, const QString &state) +{ + QSqlQuery query; + query.prepare("UPDATE PROJECT SET STATE = ? WHERE CODE = ?"); + query.addBindValue(state); + query.addBindValue(code); + return query.exec(); +} + +QStringList ProjectDAO::getStates() +{ + return {"draft", "active", "paused", "completed", "cancelled"}; +} + +// --------------- Project documents --------------- + +bool ProjectDAO::attachDocument(const QString &projectCode, const QString &docType, const QString &docCode) +{ + QSqlQuery query; + query.prepare("INSERT INTO PROJECTDOCUMENT (PROJECT_CODE, DOC_TYPE, DOC_CODE, CREATEDBY) " + "VALUES (?, ?, ?, ?)"); + query.addBindValue(projectCode); + query.addBindValue(docType); + query.addBindValue(docCode); + query.addBindValue("user"); + + if (!query.exec()) { + qWarning() << "ProjectDAO::attachDocument - Error:" << query.lastError().text(); + return false; + } + return true; +} + +bool ProjectDAO::removeDocument(int docId) +{ + QSqlQuery query; + query.prepare("DELETE FROM PROJECTDOCUMENT WHERE ID = ?"); + query.addBindValue(docId); + return query.exec(); +} + +QVector ProjectDAO::getDocuments(const QString &projectCode) +{ + QVector result; + QSqlQuery query; + query.prepare("SELECT ID, PROJECT_CODE, DOC_TYPE, DOC_CODE FROM PROJECTDOCUMENT WHERE PROJECT_CODE = ?"); + query.addBindValue(projectCode); + if (query.exec()) { + while (query.next()) { + ProjectDocument doc; + doc.id = query.value(0).toInt(); + doc.projectCode = query.value(1).toString(); + doc.docType = query.value(2).toString(); + doc.docCode = query.value(3).toString(); + result.append(doc); + } + } + return result; +} + +// --------------- Project budget --------------- + +bool ProjectDAO::addBudgetLine(const QString &projectCode, const QString &concept, double amount) +{ + QSqlQuery query; + query.prepare("INSERT INTO PROJECTBUDGET (PROJECT_CODE, CONCEPT, AMOUNT, CREATEDBY) VALUES (?, ?, ?, ?)"); + query.addBindValue(projectCode); + query.addBindValue(concept); + query.addBindValue(amount); + query.addBindValue("user"); + + if (!query.exec()) { + qWarning() << "ProjectDAO::addBudgetLine - Error:" << query.lastError().text(); + return false; + } + return true; +} + +bool ProjectDAO::removeBudgetLine(int budgetId) +{ + QSqlQuery query; + query.prepare("DELETE FROM PROJECTBUDGET WHERE ID = ?"); + query.addBindValue(budgetId); + return query.exec(); +} + +QVector ProjectDAO::getBudget(const QString &projectCode) +{ + QVector result; + QSqlQuery query; + query.prepare("SELECT ID, PROJECT_CODE, CONCEPT, AMOUNT FROM PROJECTBUDGET WHERE PROJECT_CODE = ?"); + query.addBindValue(projectCode); + if (query.exec()) { + while (query.next()) { + ProjectBudget b; + b.id = query.value(0).toInt(); + b.projectCode = query.value(1).toString(); + b.concept = query.value(2).toString(); + b.amount = query.value(3).toDouble(); + result.append(b); + } + } + return result; +} + +double ProjectDAO::getTotalBudget(const QString &projectCode) +{ + QSqlQuery query; + query.prepare("SELECT COALESCE(SUM(AMOUNT), 0) FROM PROJECTBUDGET WHERE PROJECT_CODE = ?"); + query.addBindValue(projectCode); + if (query.exec() && query.next()) + return query.value(0).toDouble(); + return 0.0; +} + +// --------------- Financial summary --------------- + +double ProjectDAO::getTotalCost(const QString &projectCode) +{ + QSqlQuery query; + query.prepare("SELECT COALESCE(SUM(PRICE), 0) FROM SALEDOCUMENT WHERE PROJECT_CODE = ?"); + query.addBindValue(projectCode); + if (query.exec() && query.next()) + return query.value(0).toDouble(); + return 0.0; +} + +double ProjectDAO::getTotalInvoiced(const QString &projectCode) +{ + QSqlQuery query; + query.prepare("SELECT COALESCE(SUM(PRICE), 0) FROM SALEDOCUMENT WHERE PROJECT_CODE = ? AND TYPE = 'FA'"); + query.addBindValue(projectCode); + if (query.exec() && query.next()) + return query.value(0).toDouble(); + return 0.0; +} + +QPair ProjectDAO::getFinancialSummary(const QString &projectCode) +{ + double totalBudget = getTotalBudget(projectCode); + double totalCost = getTotalCost(projectCode); + return {totalBudget, totalCost}; +} \ No newline at end of file diff --git a/src/dao/projectdao.h b/src/dao/projectdao.h new file mode 100644 index 0000000..64fa85d --- /dev/null +++ b/src/dao/projectdao.h @@ -0,0 +1,84 @@ +#ifndef PROJECTDAO_H +#define PROJECTDAO_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct Project { + int id = 0; + QString code; + QString title; + QString customerCode; + QString customerName; + QDate startDate; + QDate duration; + QString state; + QString contact; + QString address1; + QString address2; + QString postcode; + QString city; + QString phone; + QString mobile; + QString email; +}; + +struct ProjectDocument { + int id = 0; + QString projectCode; + QString docType; // PR, FI, FC, OT + QString docCode; +}; + +struct ProjectBudget { + int id = 0; + QString projectCode; + QString concept; + double amount = 0.0; +}; + +class ProjectDAO : public QObject +{ + Q_OBJECT + +public: + explicit ProjectDAO(QObject *parent = nullptr) : QObject(parent) {} + + // Project CRUD + static bool create(Project &proj); + static bool update(const Project &proj); + static bool remove(const QString &code); + static Project getByCode(const QString &code); + static Project getById(int id); + static QVector getAll(); + static QStringList getAllCodes(); + static bool exists(const QString &code); + + // Project status management + static bool setState(const QString &code, const QString &state); + static QStringList getStates(); + + // Project documents + static bool attachDocument(const QString &projectCode, const QString &docType, const QString &docCode); + static bool removeDocument(int docId); + static QVector getDocuments(const QString &projectCode); + + // Project budget + static bool addBudgetLine(const QString &projectCode, const QString &concept, double amount); + static bool removeBudgetLine(int budgetId); + static QVector getBudget(const QString &projectCode); + static double getTotalBudget(const QString &projectCode); + + // Financial summary + static double getTotalCost(const QString &projectCode); + static double getTotalInvoiced(const QString &projectCode); + static QPair getFinancialSummary(const QString &projectCode); +}; + +#endif // PROJECTDAO_H \ No newline at end of file diff --git a/src/gui/forms/formproject.cpp b/src/gui/forms/formproject.cpp new file mode 100644 index 0000000..8722f5f --- /dev/null +++ b/src/gui/forms/formproject.cpp @@ -0,0 +1,261 @@ +#include "formproject.h" +#include "ui_formproject.h" +#include "projectdao.h" +#include +#include + +formProject::formProject(QWidget *parent) + : QMainWindow(parent), ui(new Ui::formProject), m_isNew(true) +{ + ui->setupUi(this); + setupConnections(); + clearForm(); + + // Populate state combo + ui->stateCombo->addItems(ProjectDAO::getStates()); + ui->docTypeCombo->addItems({"PR", "FI", "FC", "OT"}); +} + +formProject::~formProject() +{ + delete ui; +} + +void formProject::setupConnections() +{ + connect(ui->saveButton, &QPushButton::clicked, this, &formProject::saveProject); + connect(ui->addDocButton, &QPushButton::clicked, this, &formProject::addDocument); + connect(ui->removeDocButton, &QPushButton::clicked, this, &formProject::removeDocument); + connect(ui->addBudgetButton, &QPushButton::clicked, this, &formProject::addBudgetLine); + connect(ui->removeBudgetButton, &QPushButton::clicked, this, &formProject::removeBudgetLine); +} + +void formProject::setNewMode() +{ + m_isNew = true; + m_project = Project(); + clearForm(); + ui->codeEdit->setEnabled(true); + ui->codeEdit->setFocus(); +} + +void formProject::clearForm() +{ + ui->codeEdit->clear(); + ui->titleEdit->clear(); + ui->customerCodeEdit->clear(); + ui->customerNameEdit->clear(); + ui->startDateEdit->setDate(QDate::currentDate()); + ui->durationDateEdit->setDate(QDate::currentDate().addYears(1)); + ui->stateCombo->setCurrentIndex(0); + ui->contactEdit->clear(); + ui->address1Edit->clear(); + ui->address2Edit->clear(); + ui->postcodeEdit->clear(); + ui->cityEdit->clear(); + ui->phoneEdit->clear(); + ui->mobileEdit->clear(); + ui->emailEdit->clear(); +} + +void formProject::loadProject(const QString &code) +{ + m_isNew = false; + m_project = ProjectDAO::getByCode(code); + ui->codeEdit->setEnabled(false); + populateForm(); + refreshDocuments(); + refreshBudget(); + refreshFinancialSummary(); +} + +void formProject::populateForm() +{ + ui->codeEdit->setText(m_project.code); + ui->titleEdit->setText(m_project.title); + ui->customerCodeEdit->setText(m_project.customerCode); + ui->customerNameEdit->setText(m_project.customerName); + ui->startDateEdit->setDate(m_project.startDate); + ui->durationDateEdit->setDate(m_project.duration); + int stateIdx = ui->stateCombo->findText(m_project.state); + if (stateIdx >= 0) ui->stateCombo->setCurrentIndex(stateIdx); + ui->contactEdit->setText(m_project.contact); + ui->address1Edit->setText(m_project.address1); + ui->address2Edit->setText(m_project.address2); + ui->postcodeEdit->setText(m_project.postcode); + ui->cityEdit->setText(m_project.city); + ui->phoneEdit->setText(m_project.phone); + ui->mobileEdit->setText(m_project.mobile); + ui->emailEdit->setText(m_project.email); +} + +void formProject::readForm() +{ + m_project.code = ui->codeEdit->text(); + m_project.title = ui->titleEdit->text(); + m_project.customerCode = ui->customerCodeEdit->text(); + m_project.customerName = ui->customerNameEdit->text(); + m_project.startDate = ui->startDateEdit->date(); + m_project.duration = ui->durationDateEdit->date(); + m_project.state = ui->stateCombo->currentText(); + m_project.contact = ui->contactEdit->text(); + m_project.address1 = ui->address1Edit->text(); + m_project.address2 = ui->address2Edit->text(); + m_project.postcode = ui->postcodeEdit->text(); + m_project.city = ui->cityEdit->text(); + m_project.phone = ui->phoneEdit->text(); + m_project.mobile = ui->mobileEdit->text(); + m_project.email = ui->emailEdit->text(); +} + +void formProject::saveProject() +{ + readForm(); + + if (m_project.code.isEmpty() || m_project.title.isEmpty()) { + QMessageBox::warning(this, tr("Aviso"), tr("Código y Título son obligatorios.")); + return; + } + + bool ok; + if (m_isNew) { + if (ProjectDAO::exists(m_project.code)) { + QMessageBox::warning(this, tr("Error"), tr("Ya existe un proyecto con ese código.")); + return; + } + ok = ProjectDAO::create(m_project); + } else { + ok = ProjectDAO::update(m_project); + } + + if (ok) { + QMessageBox::information(this, tr("Éxito"), tr("Proyecto guardado correctamente.")); + m_isNew = false; + ui->codeEdit->setEnabled(false); + emit projectSaved(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo guardar el proyecto.")); + } +} + +void formProject::onStateChanged(int index) +{ + Q_UNUSED(index); +} + +// --------------- Documents tab --------------- + +void formProject::addDocument() +{ + if (m_project.code.isEmpty()) { + QMessageBox::warning(this, tr("Aviso"), tr("Guarda el proyecto primero.")); + return; + } + QString docType = ui->docTypeCombo->currentText(); + QString docCode = ui->docCodeEdit->text(); + if (docCode.isEmpty()) { + QMessageBox::warning(this, tr("Aviso"), tr("Introduce el código del documento.")); + return; + } + if (ProjectDAO::attachDocument(m_project.code, docType, docCode)) { + ui->docCodeEdit->clear(); + refreshDocuments(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo adjuntar el documento.")); + } +} + +void formProject::removeDocument() +{ + QModelIndex idx = ui->documentsTable->currentIndex(); + if (!idx.isValid()) { + QMessageBox::information(this, tr("Info"), tr("Selecciona un documento.")); + return; + } + int row = idx.row(); + int docId = ui->documentsTable->model()->data(ui->documentsTable->model()->index(row, 0)).toInt(); + if (ProjectDAO::removeDocument(docId)) { + refreshDocuments(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo eliminar el documento.")); + } +} + +void formProject::refreshDocuments() +{ + QSqlQueryModel *model = new QSqlQueryModel(this); + QSqlQuery query; + query.prepare("SELECT ID, DOC_TYPE, DOC_CODE FROM PROJECTDOCUMENT WHERE PROJECT_CODE = ?"); + query.addBindValue(m_project.code); + query.exec(); + model->setQuery(query); + model->setHeaderData(0, Qt::Horizontal, tr("ID")); + model->setHeaderData(1, Qt::Horizontal, tr("Tipo")); + model->setHeaderData(2, Qt::Horizontal, tr("Código")); + ui->documentsTable->setModel(model); + ui->documentsTable->setColumnHidden(0, true); +} + +// --------------- Budget tab --------------- + +void formProject::addBudgetLine() +{ + if (m_project.code.isEmpty()) { + QMessageBox::warning(this, tr("Aviso"), tr("Guarda el proyecto primero.")); + return; + } + QString concept = ui->budgetConceptEdit->text(); + double amount = ui->budgetAmountSpin->value(); + if (concept.isEmpty() || amount <= 0) { + QMessageBox::warning(this, tr("Aviso"), tr("Introduce concepto y cantidad.")); + return; + } + if (ProjectDAO::addBudgetLine(m_project.code, concept, amount)) { + ui->budgetConceptEdit->clear(); + ui->budgetAmountSpin->setValue(0); + refreshBudget(); + refreshFinancialSummary(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo añadir la línea de presupuesto.")); + } +} + +void formProject::removeBudgetLine() +{ + QModelIndex idx = ui->budgetTable->currentIndex(); + if (!idx.isValid()) { + QMessageBox::information(this, tr("Info"), tr("Selecciona una línea de presupuesto.")); + return; + } + int row = idx.row(); + int budgetId = ui->budgetTable->model()->data(ui->budgetTable->model()->index(row, 0)).toInt(); + if (ProjectDAO::removeBudgetLine(budgetId)) { + refreshBudget(); + refreshFinancialSummary(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo eliminar la línea.")); + } +} + +void formProject::refreshBudget() +{ + QSqlQueryModel *model = new QSqlQueryModel(this); + QSqlQuery query; + query.prepare("SELECT ID, CONCEPT, AMOUNT FROM PROJECTBUDGET WHERE PROJECT_CODE = ?"); + query.addBindValue(m_project.code); + query.exec(); + model->setQuery(query); + model->setHeaderData(0, Qt::Horizontal, tr("ID")); + model->setHeaderData(1, Qt::Horizontal, tr("Concepto")); + model->setHeaderData(2, Qt::Horizontal, tr("Cantidad")); + ui->budgetTable->setModel(model); + ui->budgetTable->setColumnHidden(0, true); +} + +void formProject::refreshFinancialSummary() +{ + QPair summary = ProjectDAO::getFinancialSummary(m_project.code); + ui->totalBudgetLabel->setText(QString::number(summary.first, 'f', 2) + " €"); + ui->totalCostLabel->setText(QString::number(summary.second, 'f', 2) + " €"); + ui->balanceLabel->setText(QString::number(summary.first - summary.second, 'f', 2) + " €"); +} \ No newline at end of file diff --git a/src/gui/forms/formproject.h b/src/gui/forms/formproject.h new file mode 100644 index 0000000..0aed04b --- /dev/null +++ b/src/gui/forms/formproject.h @@ -0,0 +1,48 @@ +#ifndef FORMPROJECT_H +#define FORMPROJECT_H + +#include +#include +#include "projectdao.h" + +namespace Ui { +class formProject; +} + +class formProject : public QMainWindow +{ + Q_OBJECT + +public: + explicit formProject(QWidget *parent = nullptr); + ~formProject(); + + void loadProject(const QString &code); + void setNewMode(); + +signals: + void projectSaved(); + +private slots: + void saveProject(); + void onStateChanged(int index); + void addDocument(); + void removeDocument(); + void addBudgetLine(); + void removeBudgetLine(); + void refreshDocuments(); + void refreshBudget(); + void refreshFinancialSummary(); + +private: + Ui::formProject *ui; + Project m_project; + bool m_isNew; + + void populateForm(); + void readForm(); + void clearForm(); + void setupConnections(); +}; + +#endif // FORMPROJECT_H \ No newline at end of file diff --git a/src/gui/forms/formprojectlist.cpp b/src/gui/forms/formprojectlist.cpp new file mode 100644 index 0000000..7c343ec --- /dev/null +++ b/src/gui/forms/formprojectlist.cpp @@ -0,0 +1,92 @@ +#include "formprojectlist.h" +#include "ui_formprojectlist.h" +#include "formproject.h" +#include "projectdao.h" +#include +#include + +formProjectList::formProjectList(QWidget *parent) + : QMainWindow(parent), ui(new Ui::formProjectList) +{ + ui->setupUi(this); + setupModel(); + + connect(ui->refreshButton, &QPushButton::clicked, this, &formProjectList::refreshList); + connect(ui->newButton, &QPushButton::clicked, this, &formProjectList::createNewProject); + connect(ui->editButton, &QPushButton::clicked, this, &formProjectList::editProject); + connect(ui->deleteButton, &QPushButton::clicked, this, &formProjectList::deleteProject); +} + +formProjectList::~formProjectList() +{ + delete ui; +} + +void formProjectList::setupModel() +{ + QSqlQueryModel *model = new QSqlQueryModel(this); + model->setQuery("SELECT CODE, TITLE, CUSTOMER_NAME, STATE, START_DATE FROM PROJECT ORDER BY CODE"); + model->setHeaderData(0, Qt::Horizontal, tr("Código")); + model->setHeaderData(1, Qt::Horizontal, tr("Título")); + model->setHeaderData(2, Qt::Horizontal, tr("Cliente")); + model->setHeaderData(3, Qt::Horizontal, tr("Estado")); + model->setHeaderData(4, Qt::Horizontal, tr("Fecha Inicio")); + ui->projectTable->setModel(model); +} + +void formProjectList::refreshList() +{ + QSqlQueryModel *model = qobject_cast(ui->projectTable->model()); + if (model) + model->setQuery(model->query().lastQuery()); +} + +void formProjectList::createNewProject() +{ + formProject *form = new formProject(this); + form->setNewMode(); + connect(form, &formProject::projectSaved, this, [this]() { + refreshList(); + }); + form->show(); +} + +void formProjectList::editProject() +{ + QModelIndex idx = ui->projectTable->currentIndex(); + if (!idx.isValid()) { + QMessageBox::information(this, tr("Info"), tr("Selecciona un proyecto.")); + return; + } + int row = idx.row(); + QString code = ui->projectTable->model()->data(ui->projectTable->model()->index(row, 0)).toString(); + + formProject *form = new formProject(this); + form->loadProject(code); + connect(form, &formProject::projectSaved, this, [this]() { + refreshList(); + }); + form->show(); +} + +void formProjectList::deleteProject() +{ + QModelIndex idx = ui->projectTable->currentIndex(); + if (!idx.isValid()) { + QMessageBox::information(this, tr("Info"), tr("Selecciona un proyecto.")); + return; + } + int row = idx.row(); + QString code = ui->projectTable->model()->data(ui->projectTable->model()->index(row, 0)).toString(); + + int ret = QMessageBox::question(this, tr("Confirmar"), tr("¿Eliminar el proyecto %1?").arg(code), + QMessageBox::Yes | QMessageBox::No); + if (ret == QMessageBox::Yes) { + if (ProjectDAO::remove(code)) { + QMessageBox::information(this, tr("Éxito"), tr("Proyecto eliminado.")); + refreshList(); + } else { + QMessageBox::critical(this, tr("Error"), tr("No se pudo eliminar el proyecto.")); + } + } +} \ No newline at end of file diff --git a/src/gui/forms/formprojectlist.h b/src/gui/forms/formprojectlist.h new file mode 100644 index 0000000..67d73a6 --- /dev/null +++ b/src/gui/forms/formprojectlist.h @@ -0,0 +1,30 @@ +#ifndef FORMPROJECTLIST_H +#define FORMPROJECTLIST_H + +#include +#include "projectdao.h" + +namespace Ui { +class formProjectList; +} + +class formProjectList : public QMainWindow +{ + Q_OBJECT + +public: + explicit formProjectList(QWidget *parent = nullptr); + ~formProjectList(); + +public slots: + void refreshList(); + void createNewProject(); + void editProject(); + void deleteProject(); + +private: + void setupModel(); + Ui::formProjectList *ui; +}; + +#endif // FORMPROJECTLIST_H \ No newline at end of file