From 2e6eca55e7b1182e25c537fd1eb680018038db07 Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Fri, 6 Feb 2026 19:13:10 +0100 Subject: [PATCH] feat(multi-company): Allow users to be associated with multiple companies Adds user_companies table with BEFORE/AFTER triggers to sync primary company to users.company_id. Dashboard shows all user's companies with edit buttons. Company edit routes accept optional company_id parameter. Admin API endpoints for managing user-company associations. Co-Authored-By: Claude Opus 4.6 --- blueprints/admin/routes_membership.py | 12 +- blueprints/admin/routes_users_api.py | 171 ++++++++++++++++++++- blueprints/public/routes.py | 13 ++ blueprints/public/routes_company_edit.py | 38 +++-- database.py | 123 ++++++++++++--- database/migrations/050_user_companies.sql | 108 +++++++++++++ templates/company_detail.html | 2 +- templates/company_edit.html | 2 +- templates/dashboard.html | 45 +++++- 9 files changed, 469 insertions(+), 45 deletions(-) create mode 100644 database/migrations/050_user_companies.sql diff --git a/blueprints/admin/routes_membership.py b/blueprints/admin/routes_membership.py index aac4a52..a600f88 100644 --- a/blueprints/admin/routes_membership.py +++ b/blueprints/admin/routes_membership.py @@ -17,7 +17,7 @@ from . import bp from database import ( SessionLocal, MembershipApplication, CompanyDataRequest, Company, Category, User, UserNotification, Person, CompanyPerson, CompanyPKD, - SystemRole + SystemRole, UserCompany ) from krs_api_service import get_company_from_krs from utils.decorators import role_required @@ -354,6 +354,16 @@ def admin_membership_approve(app_id): user.company_id = company.id user.is_norda_member = True + # Create user-company association (multi-company support) + is_first_company = not db.query(UserCompany).filter_by(user_id=user.id).first() + user_company = UserCompany( + user_id=user.id, + company_id=company.id, + role=user.company_role or 'MANAGER', + is_primary=is_first_company, + ) + db.add(user_company) + db.commit() logger.info( diff --git a/blueprints/admin/routes_users_api.py b/blueprints/admin/routes_users_api.py index 8574bc9..8e9cae1 100644 --- a/blueprints/admin/routes_users_api.py +++ b/blueprints/admin/routes_users_api.py @@ -12,12 +12,13 @@ import re import secrets import string import tempfile +from datetime import datetime from flask import jsonify, request from flask_login import current_user, login_required from werkzeug.security import generate_password_hash -from database import SessionLocal, User, Company, SystemRole, CompanyRole, UserCompanyPermissions +from database import SessionLocal, User, Company, SystemRole, CompanyRole, UserCompanyPermissions, UserCompany from utils.decorators import role_required import gemini_service from . import bp @@ -349,6 +350,15 @@ def admin_users_change_role(): user.company_role = 'NONE' # OFFICE_MANAGER and ADMIN keep their company_role unchanged + # Sync role to user_companies table (primary company) + if user.company_id and new_role in ['MANAGER', 'EMPLOYEE']: + uc = db.query(UserCompany).filter_by( + user_id=user.id, company_id=user.company_id + ).first() + if uc: + uc.role = new_role + uc.updated_at = datetime.now() + # Create default permissions for EMPLOYEE if they have a company if new_role == 'EMPLOYEE' and user.company_id: existing_perms = db.query(UserCompanyPermissions).filter_by( @@ -399,3 +409,162 @@ def admin_users_get_roles(): ] return jsonify({'success': True, 'roles': roles}) + + +# ============================================================ +# USER-COMPANY ASSOCIATIONS (Multi-company support) +# ============================================================ + +@bp.route('/users-api//companies', methods=['GET']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_user_companies_list(user_id): + """List all companies associated with a user.""" + db = SessionLocal() + try: + user = db.query(User).get(user_id) + if not user: + return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404 + + associations = db.query(UserCompany).filter_by(user_id=user_id).all() + result = [] + for uc in associations: + company = db.query(Company).get(uc.company_id) + result.append({ + 'id': uc.id, + 'company_id': uc.company_id, + 'company_name': company.name if company else '(usunięta)', + 'role': uc.role, + 'is_primary': uc.is_primary, + 'created_at': uc.created_at.isoformat() if uc.created_at else None, + }) + + return jsonify({'success': True, 'companies': result}) + finally: + db.close() + + +@bp.route('/users-api//companies', methods=['POST']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_user_company_add(user_id): + """Add a company association to a user.""" + db = SessionLocal() + try: + user = db.query(User).get(user_id) + if not user: + return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404 + + data = request.get_json() + company_id = data.get('company_id') + role = data.get('role', 'MANAGER') + + if not company_id: + return jsonify({'success': False, 'error': 'Brak company_id'}), 400 + + company = db.query(Company).get(company_id) + if not company: + return jsonify({'success': False, 'error': 'Firma nie znaleziona'}), 404 + + # Check if already associated + existing = db.query(UserCompany).filter_by( + user_id=user_id, company_id=company_id + ).first() + if existing: + return jsonify({'success': False, 'error': 'Użytkownik jest już powiązany z tą firmą'}), 409 + + # If user has no companies yet, make this the primary + has_companies = db.query(UserCompany).filter_by(user_id=user_id).first() + is_primary = not has_companies + + uc = UserCompany( + user_id=user_id, + company_id=company_id, + role=role, + is_primary=is_primary, + ) + db.add(uc) + db.commit() + + logger.info(f"Admin {current_user.email} added company {company_id} ({company.name}) to user {user.email} with role {role}") + + return jsonify({ + 'success': True, + 'message': f'Firma {company.name} przypisana do użytkownika', + 'association_id': uc.id, + 'is_primary': is_primary, + }) + except Exception as e: + db.rollback() + logger.error(f"Error adding company to user: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 + finally: + db.close() + + +@bp.route('/users-api//companies/', methods=['DELETE']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_user_company_remove(user_id, company_id): + """Remove a company association from a user.""" + db = SessionLocal() + try: + uc = db.query(UserCompany).filter_by( + user_id=user_id, company_id=company_id + ).first() + if not uc: + return jsonify({'success': False, 'error': 'Powiązanie nie znalezione'}), 404 + + company_name = '' + company = db.query(Company).get(company_id) + if company: + company_name = company.name + + db.delete(uc) + db.commit() + + logger.info(f"Admin {current_user.email} removed company {company_id} ({company_name}) from user {user_id}") + + return jsonify({ + 'success': True, + 'message': f'Usunięto powiązanie z firmą {company_name}', + }) + except Exception as e: + db.rollback() + logger.error(f"Error removing company from user: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 + finally: + db.close() + + +@bp.route('/users-api//companies//primary', methods=['PUT']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_user_company_set_primary(user_id, company_id): + """Set a company as the user's primary company.""" + db = SessionLocal() + try: + uc = db.query(UserCompany).filter_by( + user_id=user_id, company_id=company_id + ).first() + if not uc: + return jsonify({'success': False, 'error': 'Powiązanie nie znalezione'}), 404 + + uc.is_primary = True + uc.updated_at = datetime.now() + # Trigger will handle clearing other primaries and syncing users.company_id + db.commit() + + company = db.query(Company).get(company_id) + logger.info(f"Admin {current_user.email} set company {company_id} as primary for user {user_id}") + + return jsonify({ + 'success': True, + 'message': f'Firma {company.name if company else company_id} ustawiona jako główna', + }) + except Exception as e: + db.rollback() + logger.error(f"Error setting primary company: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 + finally: + db.close() diff --git a/blueprints/public/routes.py b/blueprints/public/routes.py index 039fbb1..c6e8c42 100644 --- a/blueprints/public/routes.py +++ b/blueprints/public/routes.py @@ -44,6 +44,7 @@ from database import ( ForumTopic, Classified, UserNotification, + UserCompany, ) from utils.helpers import sanitize_input from extensions import limiter @@ -594,6 +595,17 @@ def dashboard(): except Exception: pass # MembershipApplication table may not exist yet + # Load user's company associations (multi-company support) + from sqlalchemy.orm import joinedload + user_companies = db.query(UserCompany).options( + joinedload(UserCompany.company) + ).filter_by( + user_id=current_user.id + ).order_by(UserCompany.is_primary.desc(), UserCompany.created_at.asc()).all() + # Force-load company names before session closes + for uc in user_companies: + _ = uc.company.name if uc.company else None + # Widget 1: Upcoming events (3 nearest future events) upcoming_events = db.query(NordaEvent).filter( NordaEvent.event_date >= date.today() @@ -641,6 +653,7 @@ def dashboard(): has_pending_application=has_pending_application, has_draft_application=has_draft_application, pending_application=pending_application, + user_companies=user_companies, unread_notifications=unread_notifications, upcoming_events_count=upcoming_events_count, user_forum_topics_count=user_forum_topics_count, diff --git a/blueprints/public/routes_company_edit.py b/blueprints/public/routes_company_edit.py index f76bae3..fb35071 100644 --- a/blueprints/public/routes_company_edit.py +++ b/blueprints/public/routes_company_edit.py @@ -22,16 +22,19 @@ EDITABLE_SOURCES = [None, 'manual_edit', 'manual'] @bp.route('/firma/edytuj') +@bp.route('/firma/edytuj/') @login_required -def company_edit(): +def company_edit(company_id=None): """Display the company profile edit form.""" - if not current_user.can_edit_company(): + target_company_id = company_id or current_user.company_id + + if not target_company_id or not current_user.can_edit_company(target_company_id): flash('Nie masz uprawnień do edycji profilu firmy.', 'error') return redirect(url_for('public.dashboard')) db = SessionLocal() try: - company = db.query(Company).get(current_user.company_id) + company = db.query(Company).get(target_company_id) if not company: flash('Nie znaleziono firmy.', 'error') return redirect(url_for('public.dashboard')) @@ -50,10 +53,10 @@ def company_edit(): ).all() permissions = { - 'description': current_user.can_edit_company_field('description'), - 'services': current_user.can_edit_company_field('services'), - 'contacts': current_user.can_edit_company_field('contacts'), - 'social': current_user.can_edit_company_field('social'), + 'description': current_user.can_edit_company_field('description', company_id=company.id), + 'services': current_user.can_edit_company_field('services', company_id=company.id), + 'contacts': current_user.can_edit_company_field('contacts', company_id=company.id), + 'social': current_user.can_edit_company_field('social', company_id=company.id), } editable_contacts = [c for c in contacts if c.source in EDITABLE_SOURCES] @@ -72,32 +75,35 @@ def company_edit(): @bp.route('/firma/edytuj', methods=['POST']) +@bp.route('/firma/edytuj/', methods=['POST']) @login_required -def company_edit_save(): +def company_edit_save(company_id=None): """Save company profile edits.""" - if not current_user.can_edit_company(): + target_company_id = company_id or current_user.company_id + + if not target_company_id or not current_user.can_edit_company(target_company_id): flash('Nie masz uprawnień do edycji profilu firmy.', 'error') return redirect(url_for('public.dashboard')) db = SessionLocal() try: - company = db.query(Company).get(current_user.company_id) + company = db.query(Company).get(target_company_id) if not company: flash('Nie znaleziono firmy.', 'error') return redirect(url_for('public.dashboard')) active_tab = request.form.get('active_tab', 'description') - if active_tab == 'description' and current_user.can_edit_company_field('description'): + if active_tab == 'description' and current_user.can_edit_company_field('description', company_id=company.id): _save_description(db, company) - elif active_tab == 'services' and current_user.can_edit_company_field('services'): + elif active_tab == 'services' and current_user.can_edit_company_field('services', company_id=company.id): _save_services(company) - elif active_tab == 'contacts' and current_user.can_edit_company_field('contacts'): + elif active_tab == 'contacts' and current_user.can_edit_company_field('contacts', company_id=company.id): _save_contacts(db, company) - elif active_tab == 'social' and current_user.can_edit_company_field('social'): + elif active_tab == 'social' and current_user.can_edit_company_field('social', company_id=company.id): _save_social_media(db, company) db.commit() @@ -106,9 +112,9 @@ def company_edit_save(): except Exception as e: db.rollback() - logger.error(f"Error saving company edit for company_id={current_user.company_id}: {e}") + logger.error(f"Error saving company edit for company_id={target_company_id}: {e}") flash('Wystąpił błąd podczas zapisywania zmian. Spróbuj ponownie.', 'error') - return redirect(url_for('public.company_edit')) + return redirect(url_for('public.company_edit', company_id=company_id)) finally: db.close() diff --git a/database.py b/database.py index a989c38..8a481ff 100644 --- a/database.py +++ b/database.py @@ -377,12 +377,52 @@ class User(Base, UserMixin): """ return self.has_role(SystemRole.MEMBER) + def get_companies(self, session=None): + """ + Get all companies associated with this user. + + Returns: + List of UserCompany objects (uses relationship if loaded, else queries). + """ + if self.company_associations: + return self.company_associations + if session: + return session.query(UserCompany).filter_by(user_id=self.id).all() + return [] + + def get_company_role(self, company_id: int, session=None) -> 'CompanyRole': + """ + Get user's role for a specific company from user_companies table. + + Args: + company_id: The company to check. + session: SQLAlchemy session (optional, uses relationship if loaded). + + Returns: + CompanyRole enum value, or CompanyRole.NONE if not associated. + """ + # Check loaded relationships first + for assoc in (self.company_associations or []): + if assoc.company_id == company_id: + return assoc.role_enum + # Fallback to query + if session: + assoc = session.query(UserCompany).filter_by( + user_id=self.id, company_id=company_id + ).first() + if assoc: + return assoc.role_enum + # Legacy fallback: if company_id matches primary, use company_role + if self.company_id == company_id: + return self.company_role_enum + return CompanyRole.NONE + def can_edit_company(self, company_id: int = None) -> bool: """ Check if user can edit a company's profile. Args: - company_id: Company to check. If None, checks user's own company. + company_id: Company to check. If None, checks user's primary company. Returns: True if user can edit the company. @@ -391,20 +431,20 @@ class User(Base, UserMixin): if self.has_role(SystemRole.OFFICE_MANAGER): return True - # Check user's own company target_company = company_id or self.company_id - if not target_company or self.company_id != target_company: + if not target_company: return False - # EMPLOYEE or MANAGER of the company can edit - return self.company_role_enum >= CompanyRole.EMPLOYEE + # Check role via user_companies (supports multi-company) + role = self.get_company_role(target_company) + return role >= CompanyRole.EMPLOYEE def can_manage_company(self, company_id: int = None) -> bool: """ Check if user can manage a company (including user management). Args: - company_id: Company to check. If None, checks user's own company. + company_id: Company to check. If None, checks user's primary company. Returns: True if user has full management rights. @@ -413,13 +453,13 @@ class User(Base, UserMixin): if self.has_role(SystemRole.ADMIN): return True - # Check user's own company target_company = company_id or self.company_id - if not target_company or self.company_id != target_company: + if not target_company: return False - # Only MANAGER of the company can manage users - return self.company_role_enum >= CompanyRole.MANAGER + # Check role via user_companies (supports multi-company) + role = self.get_company_role(target_company) + return role >= CompanyRole.MANAGER def can_manage_users(self) -> bool: """ @@ -442,9 +482,9 @@ class User(Base, UserMixin): """ return self.has_role(SystemRole.OFFICE_MANAGER) - def has_delegated_permission(self, permission: str, session=None) -> bool: + def has_delegated_permission(self, permission: str, company_id: int = None, session=None) -> bool: """ - Check if user has a specific delegated permission for their company. + Check if user has a specific delegated permission for a company. This checks UserCompanyPermissions table for fine-grained permissions granted by a MANAGER. @@ -452,30 +492,35 @@ class User(Base, UserMixin): Args: permission: One of: 'edit_description', 'edit_services', 'edit_contacts', 'edit_social', 'manage_classifieds', 'post_forum', 'view_analytics' + company_id: Company to check. If None, checks user's primary company. session: SQLAlchemy session (optional, uses relationship if available) Returns: True if user has this specific permission """ + target_company = company_id or self.company_id + # Managers have all permissions by default - if self.company_role_enum >= CompanyRole.MANAGER: + role = self.get_company_role(target_company) if target_company else self.company_role_enum + if role >= CompanyRole.MANAGER: return True # Check delegated permissions if self.company_permissions: for perm in self.company_permissions: - if perm.company_id == self.company_id: + if perm.company_id == target_company: attr_name = f'can_{permission}' return getattr(perm, attr_name, False) return False - def can_edit_company_field(self, field_category: str) -> bool: + def can_edit_company_field(self, field_category: str, company_id: int = None) -> bool: """ Check if user can edit a specific category of company fields. Args: field_category: 'description', 'services', 'contacts', or 'social' + company_id: Company to check. If None, checks user's primary company. Returns: True if user can edit fields in this category @@ -484,17 +529,20 @@ class User(Base, UserMixin): if self.has_role(SystemRole.OFFICE_MANAGER): return True - # Must have company - if not self.company_id: + target_company = company_id or self.company_id + if not target_company: return False + # Check role via user_companies (supports multi-company) + role = self.get_company_role(target_company) + # Managers can edit everything in their company - if self.company_role_enum >= CompanyRole.MANAGER: + if role >= CompanyRole.MANAGER: return True # Employees need delegated permission - if self.company_role_enum >= CompanyRole.EMPLOYEE: - return self.has_delegated_permission(f'edit_{field_category}') + if role >= CompanyRole.EMPLOYEE: + return self.has_delegated_permission(f'edit_{field_category}', company_id=target_company) return False @@ -579,6 +627,41 @@ class UserCompanyPermissions(Base): return perms +class UserCompany(Base): + """ + Association between users and companies (multi-company support). + + Allows a user to be linked to multiple companies with different roles. + One association per user can be marked as is_primary, which syncs + to users.company_id via database trigger. + """ + __tablename__ = 'user_companies' + + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False) + company_id = Column(Integer, ForeignKey('companies.id', ondelete='CASCADE'), nullable=False) + role = Column(String(20), nullable=False, default='MANAGER') + is_primary = Column(Boolean, default=False) + created_at = Column(DateTime, default=datetime.now) + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + + # Relationships + user = relationship('User', backref='company_associations') + company = relationship('Company', backref='user_associations') + + __table_args__ = ( + UniqueConstraint('user_id', 'company_id', name='uq_user_company'), + ) + + @property + def role_enum(self) -> CompanyRole: + """Get the CompanyRole enum value.""" + return CompanyRole.from_string(self.role or 'NONE') + + def __repr__(self): + return f'' + + # ============================================================ # COMPANY DIRECTORY (existing schema from SQL) # ============================================================ diff --git a/database/migrations/050_user_companies.sql b/database/migrations/050_user_companies.sql new file mode 100644 index 0000000..af72f9f --- /dev/null +++ b/database/migrations/050_user_companies.sql @@ -0,0 +1,108 @@ +-- Migration 050: User-Company associations (multi-company support) +-- Allows one user to be associated with multiple companies +-- Maintains backward compatibility via trigger syncing users.company_id + +BEGIN; + +-- 1. Create user_companies table +CREATE TABLE IF NOT EXISTS user_companies ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + company_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE CASCADE, + role VARCHAR(20) NOT NULL DEFAULT 'MANAGER', + is_primary BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(user_id, company_id) +); + +-- 2. Indexes +CREATE INDEX IF NOT EXISTS idx_user_companies_user_id ON user_companies(user_id); +CREATE INDEX IF NOT EXISTS idx_user_companies_company_id ON user_companies(company_id); +-- No partial unique index on is_primary - enforced by BEFORE trigger instead +-- (AFTER trigger can't clear old primary before unique constraint check) + +-- 3. Migrate existing data from users table +INSERT INTO user_companies (user_id, company_id, role, is_primary, created_at) +SELECT id, company_id, + COALESCE(NULLIF(company_role, 'NONE'), 'MANAGER'), + TRUE, + COALESCE(created_at, NOW()) +FROM users +WHERE company_id IS NOT NULL +ON CONFLICT (user_id, company_id) DO NOTHING; + +-- 4. BEFORE trigger: enforce single primary + clear others +CREATE OR REPLACE FUNCTION sync_user_primary_company() +RETURNS TRIGGER AS $$ +BEGIN + -- On INSERT or UPDATE with is_primary = TRUE + IF TG_OP IN ('INSERT', 'UPDATE') AND NEW.is_primary = TRUE THEN + -- Clear other primary flags for this user BEFORE the row is written + UPDATE user_companies + SET is_primary = FALSE, updated_at = NOW() + WHERE user_id = NEW.user_id + AND id != NEW.id + AND is_primary = TRUE; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trg_sync_user_primary_company ON user_companies; +CREATE TRIGGER trg_sync_user_primary_company + BEFORE INSERT OR UPDATE ON user_companies + FOR EACH ROW + EXECUTE FUNCTION sync_user_primary_company(); + +-- 5. AFTER trigger: sync to users table +CREATE OR REPLACE FUNCTION sync_user_primary_to_users() +RETURNS TRIGGER AS $$ +BEGIN + -- On INSERT or UPDATE with is_primary = TRUE -> sync to users + IF TG_OP IN ('INSERT', 'UPDATE') AND NEW.is_primary = TRUE THEN + UPDATE users + SET company_id = NEW.company_id, + company_role = NEW.role + WHERE id = NEW.user_id; + END IF; + + -- On DELETE of primary association + IF TG_OP = 'DELETE' AND OLD.is_primary = TRUE THEN + -- Try to promote another association to primary + UPDATE user_companies + SET is_primary = TRUE, updated_at = NOW() + WHERE id = ( + SELECT id FROM user_companies + WHERE user_id = OLD.user_id AND id != OLD.id + ORDER BY created_at ASC + LIMIT 1 + ); + + -- If no other association exists, clear users table + IF NOT FOUND THEN + UPDATE users + SET company_id = NULL, company_role = 'NONE' + WHERE id = OLD.user_id; + END IF; + END IF; + + IF TG_OP = 'DELETE' THEN + RETURN OLD; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trg_sync_user_primary_to_users ON user_companies; +CREATE TRIGGER trg_sync_user_primary_to_users + AFTER INSERT OR UPDATE OR DELETE ON user_companies + FOR EACH ROW + EXECUTE FUNCTION sync_user_primary_to_users(); + +-- 6. Permissions +GRANT ALL ON TABLE user_companies TO nordabiz_app; +GRANT USAGE, SELECT ON SEQUENCE user_companies_id_seq TO nordabiz_app; + +COMMIT; diff --git a/templates/company_detail.html b/templates/company_detail.html index a9addd1..8ce0405 100755 --- a/templates/company_detail.html +++ b/templates/company_detail.html @@ -615,7 +615,7 @@ -
+ diff --git a/templates/dashboard.html b/templates/dashboard.html index 6979565..b582d02 100755 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -520,13 +520,13 @@ -
Twoja firma
- {% if current_user.company_id %} -

{{ current_user.company.name if current_user.company else 'Przypisana' }}

+
{{ 'Twoje firmy' if user_companies|length > 1 else 'Twoja firma' }}
+ {% if user_companies %} +

{{ user_companies|length }}

{% else %}

Brak

{% endif %} - Profil firmowy + {{ 'Profile firmowe' if user_companies|length > 1 else 'Profil firmowy' }} @@ -570,8 +570,43 @@ + {# User's companies section #} + {% if user_companies %} +
+ {% endif %} + {# Membership CTA for users without company #} - {% if not current_user.company_id %} + {% if not user_companies %} {% if has_pending_application %}