#!/usr/bin/env python3 # -*- coding: utf-8 -*- import odoorpc import argparse import sys from tqdm import tqdm def parse_arguments(): parser = argparse.ArgumentParser( description="Update contacts to add salesperson and their manager as followers" ) parser.add_argument("--host", required=True, help="Odoo server host") parser.add_argument("--port", type=int, default=8069, help="Odoo server port") parser.add_argument("--db", required=True, help="Database name") parser.add_argument("--user", required=True, help="Username") parser.add_argument("--password", required=True, help="Password") parser.add_argument( "--limit", type=int, help="Limit the number of contacts to process" ) parser.add_argument( "--batch-size", type=int, default=100, help="Batch size for processing" ) return parser.parse_args() def main(): args = parse_arguments() # Connect to Odoo print(f"Connecting to {args.host}:{args.port}, database {args.db}...") try: odoo = odoorpc.ODOO(args.host, port=args.port) odoo.login(args.db, args.user, args.password) print("Connected successfully") except Exception as e: print(f"Connection failed: {e}") sys.exit(1) # Get all contacts with salespersons contact_domain = [("user_id", "!=", False)] # Get total count contact_count = odoo.env["res.partner"].search_count(contact_domain) print(f"Found {contact_count} contacts with assigned salespersons") # Apply limit if specified if args.limit and args.limit > 0: print(f"Processing only {args.limit} contacts due to limit argument") contact_count = min(contact_count, args.limit) # Search contacts contact_ids = odoo.env["res.partner"].search(contact_domain, limit=args.limit) # Process in batches to avoid memory issues batch_size = args.batch_size total_updated = 0 # Pre-fetch user and employee data print("Pre-fetching user and employee data...") user_partner_cache = {} # Maps user_id to partner_id user_manager_partners_cache = {} # Maps user_id to list of manager partner_ids # Setup progress bar total_batches = (len(contact_ids) + batch_size - 1) // batch_size pbar = tqdm(total=total_batches, desc="Processing batches") for i in range(0, len(contact_ids), batch_size): batch_ids = contact_ids[i : i + batch_size] batch_contacts = odoo.env["res.partner"].browse(batch_ids) batch_updated = 0 # Collect all salesperson IDs in this batch salesperson_ids = [ contact.user_id.id for contact in batch_contacts if contact.user_id ] unique_salesperson_ids = list(set(salesperson_ids)) # Bulk fetch salesperson partner IDs if not in cache new_user_ids = [ uid for uid in unique_salesperson_ids if uid not in user_partner_cache ] if new_user_ids: users = odoo.env["res.users"].browse(new_user_ids) for user in users: user_partner_cache[user.id] = user.partner_id.id # Bulk fetch employee data for salespeople not in cache new_employee_fetch = [ uid for uid in unique_salesperson_ids if uid not in user_manager_partners_cache ] if new_employee_fetch: # Find employee records for these users employee_domain = [("user_id", "in", new_employee_fetch)] employee_ids = odoo.env["hr.employee"].search(employee_domain) # Map employee records to their user_ids for easier lookup employee_by_user = {} for emp_id in employee_ids: emp = odoo.env["hr.employee"].browse(emp_id) if emp.user_id: employee_by_user[emp.user_id.id] = emp # Process each salesperson that needs manager data for salesperson_id in new_employee_fetch: user_manager_partners_cache[salesperson_id] = [] # Skip if no employee record found if salesperson_id not in employee_by_user: continue # Start with the employee record current_employee = employee_by_user[salesperson_id] # Walk up the management chain while current_employee.parent_id: manager = current_employee.parent_id if manager.user_id: # Cache the manager's partner ID if not already cached if manager.user_id.id not in user_partner_cache: manager_user = odoo.env["res.users"].browse( manager.user_id.id ) user_partner_cache[manager.user_id.id] = ( manager_user.partner_id.id ) # Add this manager's partner ID to the salesperson's manager list user_manager_partners_cache[salesperson_id].append( user_partner_cache[manager.user_id.id] ) # Move up the chain current_employee = manager # Process each contact in the batch updates_by_partner = {} for contact in batch_contacts: salesperson_id = contact.user_id.id if not salesperson_id: continue # Get the salesperson's partner ID salesperson_partner_id = user_partner_cache.get(salesperson_id) if not salesperson_partner_id: continue # Get the manager partner IDs for this salesperson manager_partner_ids = user_manager_partners_cache.get(salesperson_id, []) # Combine salesperson and manager partner IDs partner_ids = [salesperson_partner_id] + manager_partner_ids # Get current followers current_followers = [f.partner_id.id for f in contact.message_follower_ids] # Only add partners that are not already followers partners_to_add = [p for p in partner_ids if p not in current_followers] if partners_to_add: updates_by_partner[contact.id] = partners_to_add # Batch process the updates for contact_id, partners in updates_by_partner.items(): try: odoo.env["res.partner"].browse(contact_id).message_subscribe( partner_ids=partners ) print(f"Updated contact {contact.name} with partners {partners}") batch_updated += 1 except Exception as e: contact_name = odoo.env["res.partner"].browse(contact_id).name print(f"Error updating contact {contact_name} (ID: {contact_id}): {e}") total_updated += batch_updated pbar.update(1) pbar.set_postfix({"updated": batch_updated, "total_updated": total_updated}) pbar.close() print( f"Process completed. Updated {total_updated} out of {contact_count} contacts." ) if __name__ == "__main__": main()