# models.py - UPDATED from flask_sqlalchemy import SQLAlchemy from datetime import datetime, date from sqlalchemy import Text import json db = SQLAlchemy() # Business Card Model class BusinessCard(db.Model): __tablename__ = 'business_cards' id = db.Column(db.Integer, primary_key=True) json_id = db.Column(db.String(64), unique=True, nullable=False) owner_name = db.Column(db.String(200), nullable=False) company_name = db.Column(db.String(200)) email = db.Column(db.String(120)) phone_number = db.Column(db.String(50)) address = db.Column(db.Text) source_document = db.Column(db.String(500)) user_hash = db.Column(db.String(64), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.json_id, 'Owner Name': self.owner_name, 'Company Name': self.company_name, 'Email': self.email, 'Number': self.phone_number, 'Address': self.address, 'source_document': self.source_document, 'image_filename': f"{self.json_id}.png", 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # Brochure Model class Brochure(db.Model): __tablename__ = 'brochures' id = db.Column(db.Integer, primary_key=True) json_id = db.Column(db.String(64), unique=True, nullable=False) company_name = db.Column(db.String(200), nullable=False) raw_text = db.Column(db.Text) source_document = db.Column(db.String(500)) user_hash = db.Column(db.String(64), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) contacts = db.relationship('Contact', backref='brochure', lazy=True, cascade='all, delete-orphan') def to_dict(self): return { 'id': self.json_id, 'company_name': self.company_name, 'raw_text': self.raw_text, 'contacts': [contact.to_dict() for contact in self.contacts], 'image_filename': f"{self.json_id}.pdf", 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # Contact Model (for Brochures) class Contact(db.Model): __tablename__ = 'brochure_contacts' id = db.Column(db.Integer, primary_key=True) json_id = db.Column(db.String(64), unique=True, nullable=False) brochure_id = db.Column(db.Integer, db.ForeignKey('brochures.id'), nullable=False) owner_name = db.Column(db.String(200), nullable=False) email = db.Column(db.String(120)) phone_number = db.Column(db.String(50)) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.json_id, 'Owner Name': self.owner_name, 'Email': self.email, 'Number': self.phone_number, 'brochure_id': self.brochure_id, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # User Model for CRM - ENHANCED class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password_hash = db.Column(db.String(255), nullable=False) email = db.Column(db.String(120)) name = db.Column(db.String(100)) phone = db.Column(db.String(20)) department = db.Column(db.String(100)) role = db.Column(db.String(20), default='employee') status = db.Column(db.String(20), default='active') dark_mode = db.Column(db.Boolean, default=False) # NEW: Dark mode preference created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) last_login = db.Column(db.DateTime) # Relationships contacts_assigned = db.relationship('CRMContact', foreign_keys='CRMContact.assigned_to', backref='assigned_user', lazy=True) deals_assigned = db.relationship('Deal', foreign_keys='Deal.assigned_to', backref='assigned_user', lazy=True) tasks_assigned = db.relationship('Task', foreign_keys='Task.assigned_to', backref='assigned_user', lazy=True) activities = db.relationship('Activity', backref='user', lazy=True) comments = db.relationship('Comment', backref='user', lazy=True) notifications = db.relationship('Notification', backref='user', lazy=True) # In User model task_assignments_created = db.relationship( 'TaskAssignment', foreign_keys='TaskAssignment.assigned_by', backref='assigner', lazy=True ) task_assignments_received = db.relationship( 'TaskAssignment', foreign_keys='TaskAssignment.user_id', backref='assignee', lazy=True ) def to_dict(self): return { 'id': self.id, 'username': self.username, 'email': self.email, 'name': self.name, 'phone': self.phone, 'department': self.department, 'role': self.role, 'status': self.status, 'dark_mode': self.dark_mode, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, 'last_login': self.last_login.isoformat() if self.last_login else None } # Company Model for CRM - ENHANCED class Company(db.Model): __tablename__ = 'companies' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), unique=True, nullable=False) industry = db.Column(db.String(100)) size = db.Column(db.String(50)) website = db.Column(db.String(200)) description = db.Column(db.Text) tags = db.Column(db.String(500)) # NEW: Tags for companies created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'name': self.name, 'industry': self.industry, 'size': self.size, 'website': self.website, 'description': self.description, 'tags': self.tags, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # CRM Contact Model - ENHANCED class CRMContact(db.Model): __tablename__ = 'crm_contacts' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False) email = db.Column(db.String(120)) phone = db.Column(db.String(50)) company = db.Column(db.String(200)) position = db.Column(db.String(100)) tags = db.Column(db.String(500)) status = db.Column(db.String(50), nullable=True) source = db.Column(db.String(100), default='manual') assigned_to = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) notes = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships deals = db.relationship('Deal', backref='contact', lazy=True) tasks = db.relationship('Task', backref='contact', lazy=True) activities = db.relationship('Activity', backref='contact', lazy=True) comments = db.relationship('Comment', backref='contact', lazy=True) attachments = db.relationship('Attachment', backref='contact', lazy=True) def to_dict(self): return { 'id': self.id, 'name': self.name, 'email': self.email, 'phone': self.phone, 'company': self.company, 'position': self.position, 'tags': self.tags, 'status': self.status, 'source': self.source, 'assigned_to': self.assigned_to, 'notes': self.notes, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # Deal Model - ENHANCED with tags class Deal(db.Model): __tablename__ = 'deals' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(200), nullable=False) company = db.Column(db.String(200)) value = db.Column(db.Float, default=0.0) stage = db.Column(db.String(50), default='lead') probability = db.Column(db.Integer, default=0) contact_id = db.Column(db.Integer, db.ForeignKey('crm_contacts.id')) assigned_to = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) expected_close = db.Column(db.Date) tags = db.Column(db.String(500)) # ENHANCED: Tags for deals description = db.Column(db.Text) created_date = db.Column(db.Date, default=datetime.utcnow) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships activities = db.relationship('Activity', backref='deal', lazy=True) comments = db.relationship('Comment', backref='deal', lazy=True) attachments = db.relationship('Attachment', backref='deal', lazy=True) tasks = db.relationship('Task', backref='deal', lazy=True) def to_dict(self): return { 'id': self.id, 'title': self.title, 'company': self.company, 'value': self.value, 'stage': self.stage, 'probability': self.probability, 'contact_id': self.contact_id, 'assigned_to': self.assigned_to, 'expected_close': self.expected_close.isoformat() if self.expected_close else None, 'tags': self.tags, 'description': self.description, 'created_date': self.created_date.isoformat() if self.created_date else None, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # Task Model - ENHANCED with assignment and mentions class Task(db.Model): __tablename__ = 'tasks' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(200), nullable=False) description = db.Column(db.Text) due_date = db.Column(db.Date) due_time = db.Column(db.Time) priority = db.Column(db.String(20), default='medium') assigned_to = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) contact_id = db.Column(db.Integer, db.ForeignKey('crm_contacts.id')) deal_id = db.Column(db.Integer, db.ForeignKey('deals.id')) completed = db.Column(db.Boolean, default=False) completed_date = db.Column(db.DateTime) tags = db.Column(db.String(500)) # ENHANCED: Tags for tasks created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Calendar fields event_type = db.Column(db.String(20), default='task') location = db.Column(db.String(200)) is_all_day = db.Column(db.Boolean, default=False) reminder_minutes = db.Column(db.Integer, default=30) # NEW: Mentions for tagging users mentions = db.Column(db.String(500)) # Store mentioned user IDs def to_dict(self): return { 'id': self.id, 'title': self.title, 'description': self.description, 'due_date': self.due_date.isoformat() if self.due_date else None, 'due_time': self.due_time.isoformat() if self.due_time else None, 'priority': self.priority, 'assigned_to': self.assigned_to, 'contact_id': self.contact_id, 'deal_id': self.deal_id, 'completed': self.completed, 'completed_date': self.completed_date.isoformat() if self.completed_date else None, 'tags': self.tags, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, 'event_type': self.event_type, 'location': self.location, 'is_all_day': self.is_all_day, 'reminder_minutes': self.reminder_minutes, 'mentions': self.mentions } # Task Assignment Model - NEW class TaskAssignment(db.Model): __tablename__ = 'task_assignments' id = db.Column(db.Integer, primary_key=True) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) assigned_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) due_date = db.Column(db.Date) notes = db.Column(db.Text) status = db.Column(db.String(20), default='assigned') created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'task_id': self.task_id, 'user_id': self.user_id, 'assigned_by': self.assigned_by, 'due_date': self.due_date.isoformat() if self.due_date else None, 'notes': self.notes, 'status': self.status, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # Activity Model (Audit Log) - ENHANCED class Activity(db.Model): __tablename__ = 'activities' id = db.Column(db.Integer, primary_key=True) contact_id = db.Column(db.Integer, db.ForeignKey('crm_contacts.id')) deal_id = db.Column(db.Integer, db.ForeignKey('deals.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) action = db.Column(db.String(100), nullable=False) description = db.Column(db.Text) entity_type = db.Column(db.String(50)) # NEW: contact, deal, task, company entity_id = db.Column(db.Integer) # NEW: ID of the entity old_values = db.Column(db.Text) # NEW: JSON string of old values new_values = db.Column(db.Text) # NEW: JSON string of new values timestamp = db.Column(db.DateTime, default=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'contact_id': self.contact_id, 'deal_id': self.deal_id, 'task_id': self.task_id, 'user_id': self.user_id, 'action': self.action, 'description': self.description, 'entity_type': self.entity_type, 'entity_id': self.entity_id, 'old_values': json.loads(self.old_values) if self.old_values else None, 'new_values': json.loads(self.new_values) if self.new_values else None, 'timestamp': self.timestamp.isoformat() if self.timestamp else None } # Attachment Model - ENHANCED class Attachment(db.Model): __tablename__ = 'attachments' id = db.Column(db.Integer, primary_key=True) contact_id = db.Column(db.Integer, db.ForeignKey('crm_contacts.id')) deal_id = db.Column(db.Integer, db.ForeignKey('deals.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) filename = db.Column(db.String(255), nullable=False) file_path = db.Column(db.String(500), nullable=False) file_size = db.Column(db.Integer) file_type = db.Column(db.String(100)) uploaded_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) uploaded_at = db.Column(db.DateTime, default=datetime.utcnow) description = db.Column(db.Text) def to_dict(self): return { 'id': self.id, 'contact_id': self.contact_id, 'deal_id': self.deal_id, 'task_id': self.task_id, 'filename': self.filename, 'file_path': self.file_path, 'file_size': self.file_size, 'file_type': self.file_type, 'uploaded_by': self.uploaded_by, 'uploaded_at': self.uploaded_at.isoformat() if self.uploaded_at else None, 'description': self.description } # Comment Model - ENHANCED with mentions class Comment(db.Model): __tablename__ = 'comments' id = db.Column(db.Integer, primary_key=True) contact_id = db.Column(db.Integer, db.ForeignKey('crm_contacts.id')) deal_id = db.Column(db.Integer, db.ForeignKey('deals.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) text = db.Column(db.Text, nullable=False) mentions = db.Column(db.String(500)) # NEW: Store mentioned user IDs timestamp = db.Column(db.DateTime, default=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'contact_id': self.contact_id, 'deal_id': self.deal_id, 'task_id': self.task_id, 'user_id': self.user_id, 'text': self.text, 'mentions': self.mentions, 'timestamp': self.timestamp.isoformat() if self.timestamp else None } # Notification Model - ENHANCED class Notification(db.Model): __tablename__ = 'notifications' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) title = db.Column(db.String(200), nullable=False) message = db.Column(db.Text, nullable=False) type = db.Column(db.String(50), default='info') read = db.Column(db.Boolean, default=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow) link = db.Column(db.String(500)) category = db.Column(db.String(50), default='system') is_urgent = db.Column(db.Boolean, default=False) # NEW: For task assignments and mentions related_entity_type = db.Column(db.String(50)) # task, deal, contact related_entity_id = db.Column(db.Integer) def to_dict(self): return { 'id': self.id, 'user_id': self.user_id, 'title': self.title, 'message': self.message, 'type': self.type, 'read': self.read, 'timestamp': self.timestamp.isoformat() if self.timestamp else None, 'link': self.link, 'category': self.category, 'is_urgent': self.is_urgent, 'related_entity_type': self.related_entity_type, 'related_entity_id': self.related_entity_id } # Reminder Model - NEW class Reminder(db.Model): __tablename__ = 'reminders' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) title = db.Column(db.String(200), nullable=False) description = db.Column(db.Text) remind_at = db.Column(db.DateTime, nullable=False) is_completed = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'user_id': self.user_id, 'title': self.title, 'description': self.description, 'remind_at': self.remind_at.isoformat() if self.remind_at else None, 'is_completed': self.is_completed, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # System Settings Model class SystemSetting(db.Model): __tablename__ = 'system_settings' id = db.Column(db.Integer, primary_key=True) key = db.Column(db.String(100), unique=True, nullable=False) value = db.Column(db.Text) description = db.Column(db.String(500)) category = db.Column(db.String(50), default='general') updated_by = db.Column(db.Integer, db.ForeignKey('users.id')) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'key': self.key, 'value': self.value, 'description': self.description, 'category': self.category, 'updated_by': self.updated_by, 'updated_at': self.updated_at.isoformat() if self.updated_at else None } # AI Chat History Model class AIChatHistory(db.Model): __tablename__ = 'ai_chat_history' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) question = db.Column(db.Text, nullable=False) answer = db.Column(db.Text, nullable=False) category = db.Column(db.String(50), default='general') timestamp = db.Column(db.DateTime, default=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'user_id': self.user_id, 'question': self.question, 'answer': self.answer, 'category': self.category, 'timestamp': self.timestamp.isoformat() if self.timestamp else None } # AI Insights Model class AIInsight(db.Model): __tablename__ = 'ai_insights' id = db.Column(db.Integer, primary_key=True) insight_type = db.Column(db.String(50), nullable=False) title = db.Column(db.String(200), nullable=False) description = db.Column(db.Text, nullable=False) data = db.Column(db.Text) confidence = db.Column(db.Float, default=0.0) is_active = db.Column(db.Boolean, default=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) expires_at = db.Column(db.DateTime) def to_dict(self): return { 'id': self.id, 'insight_type': self.insight_type, 'title': self.title, 'description': self.description, 'data': json.loads(self.data) if self.data else {}, 'confidence': self.confidence, 'is_active': self.is_active, 'created_at': self.created_at.isoformat() if self.created_at else None, 'expires_at': self.expires_at.isoformat() if self.expires_at else None } # Bulk Message Campaign Model - NEW class BulkMessageCampaign(db.Model): __tablename__ = 'bulk_message_campaigns' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False) message_type = db.Column(db.String(20), nullable=False) # email, whatsapp subject = db.Column(db.String(300)) message = db.Column(db.Text, nullable=False) contact_ids = db.Column(db.Text) # JSON array of contact IDs filters = db.Column(db.Text) # JSON filter criteria sent_count = db.Column(db.Integer, default=0) total_contacts = db.Column(db.Integer, default=0) status = db.Column(db.String(20), default='draft') # draft, sending, completed, failed created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) sent_at = db.Column(db.DateTime) def to_dict(self): return { 'id': self.id, 'name': self.name, 'message_type': self.message_type, 'subject': self.subject, 'message': self.message, 'contact_ids': json.loads(self.contact_ids) if self.contact_ids else [], 'filters': json.loads(self.filters) if self.filters else {}, 'sent_count': self.sent_count, 'total_contacts': self.total_contacts, 'status': self.status, 'created_by': self.created_by, 'created_at': self.created_at.isoformat() if self.created_at else None, 'sent_at': self.sent_at.isoformat() if self.sent_at else None } # Export History Model - NEW class ExportHistory(db.Model): __tablename__ = 'export_history' id = db.Column(db.Integer, primary_key=True) export_type = db.Column(db.String(50), nullable=False) # contacts, companies, deals, tasks, master file_format = db.Column(db.String(20), nullable=False) # excel, pdf file_path = db.Column(db.String(500)) filters = db.Column(db.Text) # JSON filter criteria record_count = db.Column(db.Integer, default=0) exported_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) exported_at = db.Column(db.DateTime, default=datetime.utcnow) def to_dict(self): return { 'id': self.id, 'export_type': self.export_type, 'file_format': self.file_format, 'file_path': self.file_path, 'filters': json.loads(self.filters) if self.filters else {}, 'record_count': self.record_count, 'exported_by': self.exported_by, 'exported_at': self.exported_at.isoformat() if self.exported_at else None }