-- MARINE MAINTENANCE SYSTEM -- Database Schema v2.0 PRAGMA foreign_keys = ON; -- COMPAÑÍAS CREATE TABLE IF NOT EXISTS companies ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, address TEXT, phone TEXT, email TEXT, website TEXT, logo_path TEXT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- USUARIOS (roles: superadmin | admin | technician) CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, full_name TEXT, role TEXT NOT NULL DEFAULT 'technician', is_active INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_login TIMESTAMP ); -- EMBARCACIONES CREATE TABLE IF NOT EXISTS vessels ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), name TEXT NOT NULL, registration TEXT, vessel_type TEXT, make TEXT, model TEXT, year INTEGER, length_ft REAL, flag TEXT, port_of_registry TEXT, engine_type TEXT, engine_hours REAL DEFAULT 0, owner_name TEXT, owner_phone TEXT, owner_email TEXT, captain_name TEXT, captain_phone TEXT, captain_email TEXT, notes TEXT, photo_path TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- EQUIPOS DE LA EMBARCACIÓN CREATE TABLE IF NOT EXISTS vessel_equipment ( id INTEGER PRIMARY KEY AUTOINCREMENT, vessel_id INTEGER NOT NULL REFERENCES vessels(id) ON DELETE CASCADE, name TEXT NOT NULL, equipment_type TEXT, make TEXT, model TEXT, serial_number TEXT, year INTEGER, position TEXT, engine_hours REAL DEFAULT 0, last_service_date DATE, last_service_hours REAL, notes TEXT, is_active INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- DOCUMENTOS ADJUNTOS (por embarcación / equipo) CREATE TABLE IF NOT EXISTS vessel_documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, vessel_id INTEGER NOT NULL REFERENCES vessels(id) ON DELETE CASCADE, equipment_id INTEGER REFERENCES vessel_equipment(id), doc_type TEXT NOT NULL DEFAULT 'other', title TEXT NOT NULL, description TEXT, filename TEXT NOT NULL, original_filename TEXT, file_size INTEGER, uploaded_by TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- CATEGORÍAS DE REPUESTOS CREATE TABLE IF NOT EXISTS parts_categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT ); -- INVENTARIO CREATE TABLE IF NOT EXISTS parts ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), category_id INTEGER REFERENCES parts_categories(id), part_number TEXT, name TEXT NOT NULL, description TEXT, brand TEXT, location TEXT, quantity REAL DEFAULT 0, unit TEXT DEFAULT 'pcs', min_quantity REAL DEFAULT 0, cost_price REAL DEFAULT 0, sale_price REAL DEFAULT 0, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- PROVEEDORES CREATE TABLE IF NOT EXISTS suppliers ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), name TEXT NOT NULL, contact_name TEXT, phone TEXT, email TEXT, address TEXT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- COMPRAS CREATE TABLE IF NOT EXISTS purchases ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), supplier_id INTEGER REFERENCES suppliers(id), vessel_id INTEGER REFERENCES vessels(id), -- embarcación destino work_order_id INTEGER REFERENCES work_orders(id), -- WO relacionada (opcional) purchase_number TEXT, -- número interno invoice_number TEXT, -- número factura proveedor purchase_date DATE NOT NULL, delivery_date DATE, -- fecha entrega esperada received_date DATE, -- fecha recibido real status TEXT DEFAULT 'requested', -- requested|approved|ordered|received|paid|cancelled requested_by TEXT, approved_by TEXT, payment_method TEXT, -- cash|transfer|check|credit payment_reference TEXT, invoice_photo TEXT, -- archivo adjunto factura total_amount REAL DEFAULT 0, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS purchase_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, purchase_id INTEGER NOT NULL REFERENCES purchases(id) ON DELETE CASCADE, part_id INTEGER REFERENCES parts(id), description TEXT, part_number TEXT, -- número de parte fabricante quantity REAL NOT NULL, unit_cost REAL NOT NULL, total_cost REAL GENERATED ALWAYS AS (quantity * unit_cost) STORED, quantity_received REAL DEFAULT 0, -- lo que realmente llegó notes TEXT ); -- HISTORIAL DE MOVIMIENTOS DE INVENTARIO CREATE TABLE IF NOT EXISTS inventory_movements ( id INTEGER PRIMARY KEY AUTOINCREMENT, part_id INTEGER NOT NULL REFERENCES parts(id), movement_type TEXT NOT NULL, -- in|out|adjustment quantity REAL NOT NULL, -- positivo=entrada, negativo=salida reference_type TEXT, -- purchase|work_order|adjustment reference_id INTEGER, -- id de la compra o WO vessel_id INTEGER REFERENCES vessels(id), notes TEXT, created_by TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ÓRDENES DE TRABAJO CREATE TABLE IF NOT EXISTS work_orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, vessel_id INTEGER NOT NULL REFERENCES vessels(id), equipment_id INTEGER REFERENCES vessel_equipment(id), order_number TEXT UNIQUE, status TEXT DEFAULT 'open', work_type TEXT, scope TEXT, description TEXT, root_cause TEXT, repairs_done TEXT, technician TEXT, start_date DATE, end_date DATE, engine_hours_start REAL, engine_hours_end REAL, labor_hours REAL DEFAULT 0, labor_rate REAL DEFAULT 0, billing_type TEXT DEFAULT 'labor_materials', -- lump_sum | labor_only | labor_materials total_parts_cost REAL DEFAULT 0, total_labor_cost REAL GENERATED ALWAYS AS (labor_hours * labor_rate) STORED, notes TEXT, invoice_exported INTEGER DEFAULT 0, system_id INTEGER REFERENCES systems(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS work_order_parts ( id INTEGER PRIMARY KEY AUTOINCREMENT, work_order_id INTEGER NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, part_id INTEGER REFERENCES parts(id), description TEXT, quantity REAL NOT NULL, unit_cost REAL NOT NULL, total_cost REAL GENERATED ALWAYS AS (quantity * unit_cost) STORED ); CREATE TABLE IF NOT EXISTS work_order_photos ( id INTEGER PRIMARY KEY AUTOINCREMENT, work_order_id INTEGER NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, photo_type TEXT NOT NULL, filename TEXT NOT NULL, caption TEXT, taken_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS maintenance_schedules ( id INTEGER PRIMARY KEY AUTOINCREMENT, vessel_id INTEGER NOT NULL REFERENCES vessels(id), equipment_id INTEGER REFERENCES vessel_equipment(id), task_name TEXT NOT NULL, description TEXT, frequency_days INTEGER, frequency_hours REAL, last_done_date DATE, last_done_hours REAL, next_due_date DATE, next_due_hours REAL, is_active INTEGER DEFAULT 1, notes TEXT ); -- SISTEMAS (predefinidos + personalizados) CREATE TABLE IF NOT EXISTS systems ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), name TEXT NOT NULL, description TEXT, is_default INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- EQUIPOS TRABAJADOS EN UNA ORDEN (múltiples por WO) CREATE TABLE IF NOT EXISTS work_order_equipment ( id INTEGER PRIMARY KEY AUTOINCREMENT, work_order_id INTEGER NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, equipment_id INTEGER REFERENCES vessel_equipment(id), description TEXT, notes TEXT, labor_hours REAL DEFAULT 0, labor_rate REAL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Sistemas predefinidos INSERT OR IGNORE INTO systems (id, name, is_default) VALUES (1, 'Propulsión', 1), (2, 'Generación', 1), (3, 'Navegación y Comunicaciones', 1), (4, 'Sistema Eléctrico', 1), (5, 'Baterías y Carga', 1), (6, 'Hidráulico', 1), (7, 'HVAC / Climatización', 1), (8, 'Plomería / Agua', 1), (9, 'Seguridad', 1), (10, 'Casco y Estructura', 1), (11, 'Otro', 1); INSERT OR IGNORE INTO parts_categories (name) VALUES ('Motor'),('Hidráulico'),('Eléctrico'),('Electrónico'), ('Casco y cubierta'),('Plomería / Sistema de agua'), ('HVAC'),('Seguridad'),('Varios'); -- CONFIGURACIÓN EMAIL POR COMPAÑÍA CREATE TABLE IF NOT EXISTS email_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), smtp_host TEXT, smtp_port INTEGER DEFAULT 587, smtp_user TEXT, smtp_password TEXT, from_name TEXT, from_email TEXT, use_tls INTEGER DEFAULT 1, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- FIRMAS Y PDF GUARDADO en work_orders (columnas extra) -- Se agregan via ALTER TABLE al iniciar si no existen -- LOG DE EMAILS ENVIADOS CREATE TABLE IF NOT EXISTS email_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, work_order_id INTEGER REFERENCES work_orders(id), sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, to_email TEXT NOT NULL, to_name TEXT, subject TEXT, lang TEXT DEFAULT 'es', pdf_filename TEXT, status TEXT DEFAULT 'sent', error_msg TEXT, sent_by TEXT ); CREATE TABLE IF NOT EXISTS po_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, po_id INTEGER NOT NULL REFERENCES purchase_orders(id) ON DELETE CASCADE, rfq_item_id INTEGER REFERENCES rfq_items(id), description TEXT NOT NULL, quantity REAL DEFAULT 1, unit TEXT DEFAULT 'pcs', unit_cost REAL DEFAULT 0, total_cost REAL GENERATED ALWAYS AS (quantity * unit_cost) STORED, part_id INTEGER REFERENCES parts(id), received_qty REAL DEFAULT 0, notes TEXT ); -- MOVIMIENTOS DE INVENTARIO CREATE TABLE IF NOT EXISTS inventory_movements ( id INTEGER PRIMARY KEY AUTOINCREMENT, part_id INTEGER REFERENCES parts(id), po_id INTEGER REFERENCES purchase_orders(id), work_order_id INTEGER REFERENCES work_orders(id), vessel_id INTEGER REFERENCES vessels(id), movement_type TEXT NOT NULL, -- in|out|assign_vessel|assign_wo quantity REAL NOT NULL, unit_cost REAL DEFAULT 0, notes TEXT, created_by TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ═══════════════════════════════════════════════════════ -- MÓDULO ISM — PROCEDIMIENTOS DE TRABAJO SEGURO -- ═══════════════════════════════════════════════════════ -- PROCEDIMIENTOS MAESTRO CREATE TABLE IF NOT EXISTS swp ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), code TEXT NOT NULL, -- SWP-001, SWP-002... title TEXT NOT NULL, category TEXT NOT NULL, -- electrical|mechanical|chemical|confined|height|welding|hull|other status TEXT DEFAULT 'active', -- active|archived current_version_id INTEGER, -- FK a swp_versions (se actualiza al aprobar) created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- VERSIONES DEL PROCEDIMIENTO CREATE TABLE IF NOT EXISTS swp_versions ( id INTEGER PRIMARY KEY AUTOINCREMENT, swp_id INTEGER NOT NULL REFERENCES swp(id) ON DELETE CASCADE, version TEXT NOT NULL, -- v1.0, v1.1, v2.0 purpose TEXT, scope TEXT, hazards TEXT, -- JSON array ppe TEXT, -- JSON array tools TEXT, -- JSON array (herramientas y materiales) steps TEXT, -- JSON array numerado emergency TEXT, ref_standards TEXT, -- JSON array (ISM, SOLAS, OSHA...) status TEXT DEFAULT 'draft', -- draft|active|superseded|archived change_reason TEXT, diff_summary TEXT, created_by TEXT, approved_by TEXT, approved_at TIMESTAMP, effective_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- FIRMA DEL TÉCNICO AL LEER EL PROCEDIMIENTO EN UNA WO CREATE TABLE IF NOT EXISTS swp_acknowledgements ( id INTEGER PRIMARY KEY AUTOINCREMENT, swp_id INTEGER NOT NULL REFERENCES swp(id), swp_version_id INTEGER NOT NULL REFERENCES swp_versions(id), work_order_id INTEGER NOT NULL REFERENCES work_orders(id), technician TEXT NOT NULL, signature TEXT, -- filename de firma acknowledged_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, notes TEXT ); -- MSDS — FICHAS TÉCNICAS DE SEGURIDAD CREATE TABLE IF NOT EXISTS msds ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_id INTEGER REFERENCES companies(id), part_id INTEGER REFERENCES parts(id), -- vinculada a inventario product_name TEXT NOT NULL, manufacturer TEXT, version TEXT DEFAULT 'v1.0', hazard_class TEXT, -- GHS class hazards TEXT, -- descripción de riesgos first_aid TEXT, ppe_required TEXT, handling TEXT, storage TEXT, spill_procedure TEXT, disposal TEXT, ref_standards TEXT, pdf_filename TEXT, -- PDF oficial del fabricante created_by TEXT, updated_by TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );