from flask_login import UserMixin from app import db from datetime import datetime class Company(db.Model): __tablename__ = 'companies' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False) type = db.Column(db.String(20), nullable=False) # 'management', 'owner' email = db.Column(db.String(100)) phone = db.Column(db.String(20)) created_at = db.Column(db.DateTime, default=datetime.utcnow) class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) password_hash = db.Column(db.String(200), nullable=False) company_id = db.Column(db.Integer, db.ForeignKey('companies.id')) role = db.Column(db.String(20), default='admin') # 'admin', 'captain' is_active = db.Column(db.Boolean, default=True) is_super_admin = db.Column(db.Boolean, default=False) company = db.relationship('Company', backref='users') class Vessel(db.Model): __tablename__ = 'vessels' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) hin = db.Column(db.String(50)) make = db.Column(db.String(50)) model = db.Column(db.String(50)) length = db.Column(db.Float) engines = db.Column(db.String(200)) fuel_consumption_14knots = db.Column(db.Float) # L/h owner_company_id = db.Column(db.Integer, db.ForeignKey('companies.id')) management_company_id = db.Column(db.Integer, db.ForeignKey('companies.id')) management_company = db.relationship('Company', foreign_keys=[management_company_id], backref='managed_vessels') plan_id = db.Column(db.Integer) charter_percentage = db.Column(db.Float, default=25.0) # % para management base_rate_4h = db.Column(db.Float) hourly_rate_extra = db.Column(db.Float) max_passengers = db.Column(db.Integer, default=12) is_active = db.Column(db.Boolean, default=True) class Captain(db.Model): __tablename__ = 'captains' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) phone = db.Column(db.String(20)) license_number = db.Column(db.String(50)) hourly_rate = db.Column(db.Float) company_id = db.Column(db.Integer, db.ForeignKey('companies.id')) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) license_type = db.Column(db.String(20), default='private') # 'private' | 'six_pack' | 'master' class Route(db.Model): __tablename__ = 'routes' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) distance_nm = db.Column(db.Float) time_hours_14knots = db.Column(db.Float) points_of_interest = db.Column(db.Text) deviation_charge_usd = db.Column(db.Float, default=50) class Charter(db.Model): __tablename__ = 'charters' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id')) route_id = db.Column(db.Integer, db.ForeignKey('routes.id')) charterer_name = db.Column(db.String(100)) charterer_phone = db.Column(db.String(20)) charterer_email = db.Column(db.String(100)) start_datetime = db.Column(db.DateTime) hours = db.Column(db.Float) total_base_rate = db.Column(db.Float) management_percentage = db.Column(db.Float) management_earnings = db.Column(db.Float) owner_earnings = db.Column(db.Float) status = db.Column(db.String(20), default='draft') # draft, signed, completed, paid contract_pdf = db.Column(db.String(200)) completed_at = db.Column(db.DateTime) insurance_rider_number = db.Column(db.String(60)) insurer_name = db.Column(db.String(100)) coverage_amount = db.Column(db.Float) damage_waiver = db.Column(db.Float, default=0) captain_id = db.Column(db.Integer, db.ForeignKey('captains.id')) captain = db.relationship('Captain', backref='charters') vessel = db.relationship('Vessel', backref='charters') route = db.relationship('Route', backref='charters') class WorkOrder(db.Model): __tablename__ = 'work_orders' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id')) requested_by_company_id = db.Column(db.Integer, db.ForeignKey('companies.id')) approved_by_owner_id = db.Column(db.Integer) description = db.Column(db.Text) estimated_cost = db.Column(db.Float) actual_cost = db.Column(db.Float) status = db.Column(db.String(20), default='pending') # pending, approved, done, rejected priority = db.Column(db.String(20), default='normal') # normal, urgente, emergencia invoice_number = db.Column(db.String(60)) # número de factura/invoice notified_at = db.Column(db.DateTime) approved_at = db.Column(db.DateTime) # timestamp de aprobacion del owner approved_by_name = db.Column(db.String(100)) # nombre del owner que aprobó rejected_at = db.Column(db.DateTime) rejection_reason = db.Column(db.String(300)) created_at = db.Column(db.DateTime, default=datetime.utcnow) completed_at = db.Column(db.DateTime) vessel = db.relationship('Vessel', backref='work_orders') class Voucher(db.Model): __tablename__ = 'vouchers' id = db.Column(db.Integer, primary_key=True) charter_id = db.Column(db.Integer, db.ForeignKey('charters.id')) total_charged = db.Column(db.Float) fuel_actual_liters = db.Column(db.Float) deviation_charged = db.Column(db.Float, default=0) speed_extra_charged = db.Column(db.Float, default=0) tip_amount = db.Column(db.Float) tip_percentage = db.Column(db.Float, default=18) issued_at = db.Column(db.DateTime, default=datetime.utcnow) paid_at = db.Column(db.DateTime) charter = db.relationship('Charter', backref='voucher') class AccountingVessel(db.Model): __tablename__ = 'accounting_vessel' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id')) month = db.Column(db.Integer) year = db.Column(db.Integer) charter_revenue = db.Column(db.Float, default=0) plan_revenue = db.Column(db.Float, default=0) fuel_cost = db.Column(db.Float, default=0) maintenance_cost = db.Column(db.Float, default=0) cleaning_cost = db.Column(db.Float, default=0) detailing_cost = db.Column(db.Float, default=0) teak_cost = db.Column(db.Float, default=0) net_profit = db.Column(db.Float, default=0) # ── Ledger de transacciones por embarcación ────────────────────────── class AccountingEntry(db.Model): __tablename__ = 'accounting_entries' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id'), nullable=False) date = db.Column(db.Date, nullable=False) entry_type = db.Column(db.String(10), nullable=False) # 'income' | 'expense' category = db.Column(db.String(30), nullable=False) # income: 'charter' | 'plan_subscription' | 'other_income' # expense: 'work_order' | 'fuel' | 'cleaning' | 'detailing' | 'teak' | 'marina' | 'other_expense' description = db.Column(db.String(300)) amount = db.Column(db.Float, nullable=False) invoice_number = db.Column(db.String(60)) reference_type = db.Column(db.String(20)) # 'charter' | 'work_order' | 'fuel_entry' | None reference_id = db.Column(db.Integer) notes = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow) vessel = db.relationship('Vessel', backref='accounting_entries') # ── Registro de combustible ────────────────────────────────────────── class FuelEntry(db.Model): __tablename__ = 'fuel_entries' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id'), nullable=False) charter_id = db.Column(db.Integer, db.ForeignKey('charters.id'), nullable=True) date = db.Column(db.Date, nullable=False) liters = db.Column(db.Float) price_per_liter = db.Column(db.Float) total_cost = db.Column(db.Float, nullable=False) supplier = db.Column(db.String(100)) invoice_number = db.Column(db.String(60)) notes = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow) vessel = db.relationship('Vessel', backref='fuel_entries') charter = db.relationship('Charter', backref='fuel_entries') # ── Documentos adjuntos (invoices, contratos, fotos) ───────────────── class Document(db.Model): __tablename__ = 'documents' id = db.Column(db.Integer, primary_key=True) vessel_id = db.Column(db.Integer, db.ForeignKey('vessels.id')) reference_type = db.Column(db.String(20)) # 'charter'|'work_order'|'fuel_entry'|'general' reference_id = db.Column(db.Integer) doc_type = db.Column(db.String(20)) # 'invoice'|'receipt'|'contract'|'photo'|'other' filename = db.Column(db.String(200)) original_name = db.Column(db.String(200)) file_size = db.Column(db.Integer) uploaded_at = db.Column(db.DateTime, default=datetime.utcnow) notes = db.Column(db.Text) vessel = db.relationship('Vessel', backref='documents')