Enable Dark Mode!
overview-of-multi-company-support-in-mobo-apps-using-odoo.jpg
By: Shalbin MP

Overview of Multi-Company Support in Mobo Apps Using Odoo

Technical Flutter mobo

In the enterprise world, multi-company support isn't just a feature; it's a requirement. Odoo handles this natively in its web interface, allowing users to seamlessly switch contexts and view data across different business units. However, when building custom mobile apps with Flutter, replicating this behavior requires a thoughtful architecture.

If you look at comprehensive Odoo mobile solutions like the Mobo app suite—which splits complex Odoo modules into role-specific, user-friendly mobile applications—managing this context flawlessly is critical.

In this guide, we'll explore how to implement a production-grade multi-company system in Flutter that mimics the native Odoo experience, utilizing the same architectural principles that power apps like Mobo.

The Challenge: Context is Everything

Odoo uses a concept called the Current Context to determine which records a user can access. For multi-company environments, the most critical key in this context is allowed_company_ids.

If your API calls don't explicitly include this list, Odoo defaults to the user's primary company. This means a user could have access to "Company A" and "Company B", but if they select "Company B" in your app and you don't tell the backend, they'll still see data for "Company A".

To fix this, we need a Context-First Architecture where the active company state is:

  • Stored globally in the app session.
  • Injected automatically into every network request.
  • Persisted locally so the user's choice survives app restarts.

Step 1: Modeling the Session

First, in the mobo app, we need to extend our session model to include the user’s selected company. A typical class might look like this:

class AppSession {
  final OdooSession odooSession;
  final int? selectedCompanyId;       // The "Main" active company
  final List<int> allowedCompanyIds;  // All active companies (Main + secondary)
  const AppSession({
    required this.odooSession,
    this.selectedCompanyId,
    this.allowedCompanyIds = const [],
    // ... other session fields
  });
  // Example method to save preferences locally
  Future<void> saveToPreferences() async {
    final prefs = await SharedPreferences.getInstance();
    if (selectedCompanyId != null) {
      await prefs.setInt('selected_company_id', selectedCompanyId!);
    }
    await prefs.setStringList(
      'allowed_company_ids',
      allowedCompanyIds.map((e) => e.toString()).toList(),
    );
  }
}

This ensures that the "company context" is always coupled with the "user authentication" state.

Step 2: The Network Interceptor

The most robust way to handle multi-company support is to make it invisible to your feature code. You shouldn't have to manually pass company IDs in every single repository method.

Instead, create a centralized Request Manager that intercepts every call and injects the context.

class OdooRequestManager {
  // ... client initialization ...
  /// A wrapper for Odoo's 'call_kw' method that automatically injects context
  Future<dynamic> callKw({
    required String model,
    required String method,
    required List args,
    Map<String, dynamic> kwargs = const {},
  }) async {
    final session = await _sessionService.getCurrentSession();
    
    // 1. Get existing context or create new one
    Map<String, dynamic> context = Map.from(kwargs['context'] ?? {});
    // 2. Inject Allowed Companies
    if (session != null && session.allowedCompanyIds.isNotEmpty) {
      context['allowed_company_ids'] = session.allowedCompanyIds;
    }
    // 3. Update kwargs with the new context
    final newKwargs = Map<String, dynamic>.from(kwargs);
    newKwargs['context'] = context;
    // 4. execute the call
    return _client.callKw(
      model, method, args, newKwargs
    );
  }
}

With this in place, a developer writing a ProductRepository simply calls requestManager.callKw(...) without worrying about which company is active. The backend always receives the correct filter.

Step 3: Managing State with a Provider

You'll need a state management solution (like Provider, Riverpod, or Bloc) to handle the logic of switching companies. This provider acts as the brain, ensuring that invalid states (like selecting a company you don't have access to) are impossible.

class CompanyProvider extends ChangeNotifier {
  List<Company> _availableCompanies = [];
  int? _activeCompanyId;
  Future<void> switchCompany(int newCompanyId) async {
    // 1. Validate: Ensure user actually has access to this company
    if (!_availableCompanies.any((c) => c.id == newCompanyId)) return;
    // 2. Update Local State
    _activeCompanyId = newCompanyId;
    
    // 3. Update Session & Persist
    await _sessionService.updateCompanyContext(
      selectedId: newCompanyId,
      // For simple use cases, usually the selected company is the only allowed one.
      // For advanced cases, you might allow multiple selections (Multi-Company mode).
      allowedIds: [newCompanyId], 
    );
    // 4. Notify UI to rebuild
    notifyListeners();
  }
}

Step 4: The UI Switcher

Finally, users need a way to switch contexts. A common pattern in tailored apps like Mobo CRM or Mobo POS is to place a dropdown or a bottom sheet in the app bar.

When designing the UI, remember that Odoo supports two modes:

  1. Single Selection: Switching the main active company.
  2. Multi-Selection: "Checking" additional companies to view their data simultaneously.

For a mobile app, a simple dropdown that switches the primary company is often sufficient for 90% of use cases.

// Simple Company Selector Widget
DropdownButton<int>(
  value: provider.activeCompanyId,
  items: provider.availableCompanies.map((company) {
    return DropdownMenuItem(
      value: company.id,
      child: Text(company.name),
    );
  }).toList(),
  onChanged: (newId) {
    if (newId != null) {
      provider.switchCompany(newId);
    }
  },
)

By baking multi-company support into your application's networking layer, you dramatically reduce complexity and potential bugs. Your UI developers can focus on building features, confident that the data users see is always scoped to the correct business unit.

To read more about How to Implement Offline Mode in Mobo with Odoo Data Sync, refer to our blog How to Implement Offline Mode in Mobo with Odoo Data Sync.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message