nordabiz/blueprints/public/routes_company_edit.py
Maciej Pienczyn 7e570e0492
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
feat: Support multiple websites per company (max 5)
Add CompanyWebsite model with label, is_primary flag, and backward
compatibility sync to company.website. Dynamic form in company edit,
separate buttons in contact bar, additional banners in detail view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 07:45:22 +01:00

310 lines
11 KiB
Python

"""
Company Edit Routes
===================
Routes for editing company profiles by authorized users.
"""
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from blueprints.public import bp
from sqlalchemy import or_
from database import SessionLocal, Company, CompanyContact, CompanySocialMedia, CompanyWebsite, Category
from utils.helpers import sanitize_input, sanitize_html, validate_email, ensure_url
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
EMPLOYEE_COUNT_WHITELIST = ['1-5', '6-10', '11-25', '26-50', '51-100', '101-250', '250+', '']
VALID_SOCIAL_PLATFORMS = ['facebook', 'linkedin', 'instagram', 'youtube', 'twitter', 'tiktok']
EDITABLE_SOURCES = [None, 'manual_edit', 'manual']
@bp.route('/firma/edytuj')
@bp.route('/firma/edytuj/<int:company_id>')
@login_required
def company_edit(company_id=None):
"""Display the company profile edit form."""
target_company_id = company_id or current_user.company_id
if not target_company_id or not current_user.can_manage_company(target_company_id):
flash('Edycja profilu firmy jest dostępna tylko dla kadry zarządzającej. '
'Skontaktuj się z osobą zarządzającą Twoją firmą.', 'warning')
return redirect(url_for('public.dashboard'))
db = SessionLocal()
try:
company = db.query(Company).get(target_company_id)
if not company:
flash('Nie znaleziono firmy.', 'error')
return redirect(url_for('public.dashboard'))
categories = db.query(Category).order_by(Category.name).all()
contacts = db.query(CompanyContact).filter_by(
company_id=company.id
).order_by(
CompanyContact.contact_type,
CompanyContact.is_primary.desc()
).all()
social_media = db.query(CompanySocialMedia).filter_by(
company_id=company.id
).all()
company_websites = db.query(CompanyWebsite).filter_by(
company_id=company.id
).order_by(CompanyWebsite.is_primary.desc()).all()
permissions = {
'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]
return render_template(
'company_edit.html',
company=company,
categories=categories,
contacts=editable_contacts,
all_contacts=contacts,
social_media=social_media,
company_websites=company_websites,
permissions=permissions,
)
finally:
db.close()
@bp.route('/firma/edytuj', methods=['POST'])
@bp.route('/firma/edytuj/<int:company_id>', methods=['POST'])
@login_required
def company_edit_save(company_id=None):
"""Save company profile edits."""
target_company_id = company_id or current_user.company_id
if not target_company_id or not current_user.can_manage_company(target_company_id):
flash('Edycja profilu firmy jest dostępna tylko dla kadry zarządzającej. '
'Skontaktuj się z osobą zarządzającą Twoją firmą.', 'warning')
return redirect(url_for('public.dashboard'))
db = SessionLocal()
try:
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', company_id=company.id):
_save_description(db, company)
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', company_id=company.id):
_save_contacts(db, company)
elif active_tab == 'social' and current_user.can_edit_company_field('social', company_id=company.id):
_save_social_media(db, company)
db.commit()
flash('Dane firmy zostały zaktualizowane.', 'success')
return redirect(url_for('public.company_detail', company_id=company.id))
except Exception as e:
db.rollback()
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', company_id=company_id))
finally:
db.close()
def _save_description(db, company):
"""Save description tab fields."""
company.description_short = sanitize_input(
request.form.get('description_short', ''), max_length=500
) or None
company.description_full = sanitize_html(
request.form.get('description_full', '')
) or None
company.founding_history = sanitize_html(
request.form.get('founding_history', '')
) or None
company.core_values = sanitize_html(
request.form.get('core_values', '')
) or None
category_id_raw = request.form.get('category_id', '')
if category_id_raw:
try:
category_id = int(category_id_raw)
category = db.query(Category).get(category_id)
if category:
company.category_id = category_id
except (ValueError, TypeError):
pass
else:
company.category_id = None
def _save_services(company):
"""Save services tab fields."""
company.services_offered = sanitize_html(
request.form.get('services_offered', '')
) or None
company.technologies_used = sanitize_html(
request.form.get('technologies_used', '')
) or None
company.operational_area = sanitize_input(
request.form.get('operational_area', ''), max_length=500
) or None
company.languages_offered = sanitize_input(
request.form.get('languages_offered', ''), max_length=200
) or None
employee_count = request.form.get('employee_count_range', '')
if employee_count in EMPLOYEE_COUNT_WHITELIST:
company.employee_count_range = employee_count or None
def _save_contacts(db, company):
"""Save contacts tab fields."""
_save_websites(db, company)
email_raw = sanitize_input(request.form.get('email', ''), max_length=255)
if email_raw:
if validate_email(email_raw):
company.email = email_raw
else:
company.email = None
phone_raw = sanitize_input(request.form.get('phone', ''), max_length=50)
company.phone = phone_raw or None
company.address_street = sanitize_input(
request.form.get('address_street', ''), max_length=255
) or None
company.address_city = sanitize_input(
request.form.get('address_city', ''), max_length=100
) or None
company.address_postal = sanitize_input(
request.form.get('address_postal', ''), max_length=10
) or None
# Delete existing editable contacts (source is NULL, 'manual_edit', or 'manual')
db.query(CompanyContact).filter(
CompanyContact.company_id == company.id,
or_(
CompanyContact.source.in_(['manual_edit', 'manual']),
CompanyContact.source.is_(None)
)
).delete(synchronize_session='fetch')
# Add new contacts from form
contact_types = request.form.getlist('contact_types[]')
contact_values = request.form.getlist('contact_values[]')
contact_purposes = request.form.getlist('contact_purposes[]')
for i, value in enumerate(contact_values):
value = sanitize_input(value, max_length=255)
if not value:
continue
contact_type = sanitize_input(contact_types[i], max_length=20) if i < len(contact_types) else 'phone'
purpose = sanitize_input(contact_purposes[i], max_length=100) if i < len(contact_purposes) else ''
db.add(CompanyContact(
company_id=company.id,
contact_type=contact_type,
value=value,
purpose=purpose or None,
source='manual_edit',
))
def _save_social_media(db, company):
"""Save social media tab fields."""
# Delete existing editable social media (source is NULL, 'manual_edit', or 'manual')
db.query(CompanySocialMedia).filter(
CompanySocialMedia.company_id == company.id,
or_(
CompanySocialMedia.source.in_(['manual_edit', 'manual']),
CompanySocialMedia.source.is_(None)
)
).delete(synchronize_session='fetch')
# Add new social media from form
social_platforms = request.form.getlist('social_platforms[]')
social_urls = request.form.getlist('social_urls[]')
for i, url in enumerate(social_urls):
url = sanitize_input(url, max_length=500)
if not url:
continue
platform = social_platforms[i] if i < len(social_platforms) else ''
if platform not in VALID_SOCIAL_PLATFORMS:
continue
db.add(CompanySocialMedia(
company_id=company.id,
platform=platform,
url=ensure_url(url),
source='manual_edit',
verified_at=datetime.now(),
))
def _save_websites(db, company):
"""Save multiple website URLs from the contacts tab."""
# Delete existing editable websites
db.query(CompanyWebsite).filter(
CompanyWebsite.company_id == company.id,
or_(
CompanyWebsite.source.in_(['manual_edit', 'manual', 'migration']),
CompanyWebsite.source.is_(None)
)
).delete(synchronize_session='fetch')
website_urls = request.form.getlist('website_urls[]')
website_labels = request.form.getlist('website_labels[]')
primary_idx_raw = request.form.get('website_primary', '0')
try:
primary_idx = int(primary_idx_raw)
except (ValueError, TypeError):
primary_idx = 0
added = 0
primary_url = None
for i, url_raw in enumerate(website_urls):
if added >= 5:
break
url_raw = sanitize_input(url_raw, max_length=500)
if not url_raw:
continue
url = ensure_url(url_raw)
label = sanitize_input(website_labels[i], max_length=100) if i < len(website_labels) else ''
is_primary = (i == primary_idx)
if is_primary:
primary_url = url
db.add(CompanyWebsite(
company_id=company.id,
url=url,
label=label or None,
is_primary=is_primary,
source='manual_edit',
))
added += 1
# Sync company.website with primary for backward compatibility
if primary_url:
company.website = primary_url
elif added > 0:
# No explicit primary — first one becomes primary
company.website = ensure_url(sanitize_input(website_urls[0], max_length=500))
else:
company.website = None