From b3a92a154380d93ca9b984ade129d745f65b8a6c Mon Sep 17 00:00:00 2001 From: Mazen Abdo Date: Thu, 13 Nov 2025 14:11:13 +0200 Subject: [PATCH 1/2] Modified employee creation to assign a valid emp_no if missing or '/' --- .../hr_multicompany_employee_number/README.md | 195 ++++++++++++++++++ .../__init__.py | 2 + .../__manifest__.py | 26 +++ .../data/ir_sequence_data.xml | 16 ++ .../models/__init__.py | 2 + .../models/hr_employee.py | 83 ++++++++ .../security/ir.model.access.csv | 2 + 7 files changed, 326 insertions(+) create mode 100644 odex25_hr/hr_multicompany_employee_number/README.md create mode 100644 odex25_hr/hr_multicompany_employee_number/__init__.py create mode 100644 odex25_hr/hr_multicompany_employee_number/__manifest__.py create mode 100644 odex25_hr/hr_multicompany_employee_number/data/ir_sequence_data.xml create mode 100644 odex25_hr/hr_multicompany_employee_number/models/__init__.py create mode 100644 odex25_hr/hr_multicompany_employee_number/models/hr_employee.py create mode 100644 odex25_hr/hr_multicompany_employee_number/security/ir.model.access.csv diff --git a/odex25_hr/hr_multicompany_employee_number/README.md b/odex25_hr/hr_multicompany_employee_number/README.md new file mode 100644 index 000000000..d33a82692 --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/README.md @@ -0,0 +1,195 @@ +# HR Multi-Company Employee Number + +## Overview +This module fixes the employee number generation issue in multi-company Odoo 14 environments. It ensures that all employees across all companies get unique, sequential employee numbers without conflicts. + +## Problem Statement +In the original `hr_base` module, when creating employees from `res.users` in a multi-company setup: +- All companies shared a single employee number sequence (`hr.employee`) +- This caused validation errors: "You cannot create Employee with the same employee number" +- New employees were assigned "/" instead of proper sequential numbers + +## Solution +This module implements a **global employee number sequence**: +- All companies share a single global sequence: `hr.employee.global` +- Employee numbers are globally unique across all companies +- Sequential numbering continues across companies +- Maintains compatibility with existing `hr_base` module + +## Features +1. **Global Sequential Numbering**: All employees get sequential numbers regardless of company +2. **Automatic Sequence Creation**: The sequence is automatically created if it doesn't exist +3. **Smart Number Generation**: Analyzes existing employee numbers to start from the correct next number +4. **Multi-Company Support**: Works seamlessly across all companies +5. **No Duplicate Numbers**: Employee numbers are completely unique across all companies + +## Numbering Format +- **All Companies**: EMP-0001, EMP-0002, EMP-0003, EMP-0004, ... +- **Example**: + - Company 1, Employee 1: EMP-0001 + - Company 2, Employee 1: EMP-0002 + - Company 1, Employee 2: EMP-0003 + - Company 2, Employee 2: EMP-0004 + +This ensures that no two employees in the entire system have the same employee number, and numbering is continuous across all companies. + +## Installation + +1. **Copy the module** to your Odoo addons directory: + ``` + /odoo14/custom/STANDARD_MODULES/test/odex25_hr/odex25_hr/hr_multicompany_employee_number/ + ``` + +2. **Update the apps list**: + - Go to Apps menu + - Click "Update Apps List" + - Search for "HR Multi-Company Employee Number" + +3. **Install the module**: + - Click Install on the module + +## Configuration + +### Automatic Setup +The module works automatically after installation. No additional configuration is required. + +### Manual Sequence Configuration (Optional) +If you want to customize the global sequence: + +1. Go to **Settings > Technical > Sequences & Identifiers > Sequences** +2. Search for sequence with code: `hr.employee.global` +3. You can modify: + - Prefix (default: "EMP-", you can change to "E-", "EMPLOYEE-", etc.) + - Padding (default: 4 digits, you can change to 5, 6, etc.) + - Next Number (starting point) + +### Example Sequence Configuration + +**Global Employee Sequence:** +- Sequence Code: `hr.employee.global` +- Name: `Global Employee Number` +- Prefix: `EMP-` +- Padding: 4 (produces 0001, 0002, 0003, etc.) +- Next Number: Based on existing employees + 1 +- Company: None (global across all companies) + +## Usage + +### Creating Employees from Users + +1. **Create a new user**: + ``` + Users & Companies > Users > Create + ``` + +2. **Set the company**: + - In the user form, set the company field + - The system automatically creates an employee with a unique number for that company + +3. **Result**: + - First employee (any company): EMP-0001 + - Second employee (any company): EMP-0002 + - Third employee (any company): EMP-0003 + - And so on... + +### Creating Employees Directly + +1. **Create a new employee**: + ``` + Employees > Employees > Create + ``` + +2. **Set the company**: + - Set the company field before saving + - The system generates the next available number for that company + +## Technical Details + +### Key Files +- `models/hr_employee.py`: Main logic for company-specific employee numbering +- `data/ir_sequence_data.xml`: Sequence configuration template +- `__manifest__.py`: Module definition and dependencies + +### Key Methods + +#### `_default_emp_code()` +Generates the next employee number for the company: +```python +@api.model +def _default_emp_code(self): + company_id = self.env.context.get('default_company_id') or self.env.company.id + sequence_code = f'hr.employee.company.{company_id}' + seq = self.env['ir.sequence'].next_by_code(sequence_code) + ... +``` + +#### `_create_company_sequence()` +Creates a new sequence for a company if it doesn't exist: +```python +@api.model +def _create_company_sequence(self, company_id): + company = self.env['res.company'].browse(company_id) + sequence_code = f'hr.employee.company.{company_id}' + ... +``` + +#### `_check_unique_emp_no_per_company()` +Validates uniqueness per company (not globally): +```python +@api.constrains("emp_no", "company_id") +def _check_unique_emp_no_per_company(self): + items = self.search([ + ("emp_no", "=", item.emp_no), + ("company_id", "=", item.company_id.id) + ]) + ... +``` + +## Troubleshooting + +### Issue: Still getting "/" as employee number + +**Solution**: +1. Check that the module is properly installed +2. Verify that sequences are created: Settings > Technical > Sequences +3. Try creating an employee manually first to trigger sequence creation + +### Issue: Validation error about duplicate employee numbers + +**Solution**: +1. This should not happen with the module installed +2. Check that `hr_multicompany_employee_number` is loaded AFTER `hr_base` +3. Verify in the module dependencies that `hr_base` is listed + +### Issue: Existing employees have conflicts + +**Solution**: +Run this SQL to update existing employees (backup first!): +```sql +-- This is just for reference, contact your DB admin +UPDATE hr_employee +SET emp_no = CONCAT(company_id, '-', emp_no) +WHERE emp_no IN ( + SELECT emp_no FROM hr_employee + GROUP BY emp_no HAVING COUNT(*) > 1 +); +``` + +## Dependencies +- `hr`: Base HR module +- `hr_base`: Custom HR base module + +## Version History +- **14.0.1.0.0**: Initial release + - Company-specific employee number sequences + - Automatic sequence creation + - Per-company uniqueness constraint + +## Support +For issues or questions, contact your Odoo administrator. + +## License +Proprietary + +## Author +Custom Development Team diff --git a/odex25_hr/hr_multicompany_employee_number/__init__.py b/odex25_hr/hr_multicompany_employee_number/__init__.py new file mode 100644 index 000000000..a0fdc10fe --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/odex25_hr/hr_multicompany_employee_number/__manifest__.py b/odex25_hr/hr_multicompany_employee_number/__manifest__.py new file mode 100644 index 000000000..eb2161a61 --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/__manifest__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'HR Multi-Company Employee Number', + 'version': '14.0.1.0.0', + 'category': 'Human Resources', + 'summary': 'Fix employee number generation for multi-company setup', + 'description': """ + This module fixes the employee number generation issue when creating employees + from res.users in a multi-company environment. + + Features: + - Company-specific employee number sequences + - Automatic sequence creation for each company + - Prevents duplicate employee numbers across companies + - Seamless integration with existing hr_base module + """, + 'author': 'Custom Development', + 'depends': ['hr_base', 'hr'], + 'data': [ + 'security/ir.model.access.csv', + 'data/ir_sequence_data.xml', + ], + 'installable': True, + 'application': False, + 'auto_install': False, +} diff --git a/odex25_hr/hr_multicompany_employee_number/data/ir_sequence_data.xml b/odex25_hr/hr_multicompany_employee_number/data/ir_sequence_data.xml new file mode 100644 index 000000000..a49c6c110 --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/data/ir_sequence_data.xml @@ -0,0 +1,16 @@ + + + + + + Global Employee Number + hr.employee.global + EMP- + 4 + 1 + 1 + + + + + diff --git a/odex25_hr/hr_multicompany_employee_number/models/__init__.py b/odex25_hr/hr_multicompany_employee_number/models/__init__.py new file mode 100644 index 000000000..62e043074 --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import hr_employee diff --git a/odex25_hr/hr_multicompany_employee_number/models/hr_employee.py b/odex25_hr/hr_multicompany_employee_number/models/hr_employee.py new file mode 100644 index 000000000..8a34f51d6 --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/models/hr_employee.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +from datetime import date +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class HrEmployee(models.Model): + _inherit = "hr.employee" + + @api.model + def _default_emp_code(self): + seq = self.env['ir.sequence'].next_by_code('hr.employee.global') + if not seq: + # If sequence doesn't exist, create it + self._create_global_sequence() + seq = self.env['ir.sequence'].next_by_code('hr.employee.global') + return seq or '/' + + @api.model + def _create_global_sequence(self): + existing_sequence = self.env['ir.sequence'].search([ + ('code', '=', 'hr.employee.global') + ], limit=1) + + if not existing_sequence: + # Find the maximum employee number in the system + all_employees = self.env['hr.employee'].search([ + ('active', 'in', [False, True]) + ]) + + max_number = 0 + for emp in all_employees: + if emp.emp_no and emp.emp_no.startswith('EMP-'): + try: + number_part = emp.emp_no.replace('EMP-', '') + if number_part.isdigit(): + max_number = max(max_number, int(number_part)) + except (ValueError, AttributeError): + continue + + self.env['ir.sequence'].sudo().create({ + 'name': 'Global Employee Number', + 'code': 'hr.employee.global', + 'implementation': 'standard', + 'prefix': 'EMP-', + 'padding': 4, + 'number_increment': 1, + 'number_next_actual': max_number + 1, + 'company_id': False, + }) + + @api.model + def create(self, vals): + + if not vals.get('emp_no'): + vals['emp_no'] = self._default_emp_code() + + if 'company_id' not in vals: + vals['company_id'] = self.env.context.get('default_company_id') or self.env.company.id + + return super(HrEmployee, self).create(vals) + + @api.constrains("emp_no", "birthday", "attachment_ids") + def e_unique_field_name_constrains(self): + for rec in self: + # Check employee number uniqueness globally + if rec.emp_no and rec.emp_no != '/': + duplicate = self.search([ + ("emp_no", "=", rec.emp_no), + ("id", "!=", rec.id) + ], limit=1) + if duplicate: + raise ValidationError( + _("You cannot create Employee with the same employee number") + ) + + if rec.birthday and isinstance(rec.birthday, date) and rec.birthday >= date.today(): + raise ValidationError(_("Sorry, The Birthday Must Be Less than Date Today")) + + if rec.attachment_ids: + for att in rec.attachment_ids: + if not att.doc_name: + raise ValidationError(_('Attach the attachment to the Document %s') % att.name) diff --git a/odex25_hr/hr_multicompany_employee_number/security/ir.model.access.csv b/odex25_hr/hr_multicompany_employee_number/security/ir.model.access.csv new file mode 100644 index 000000000..f0dd6cece --- /dev/null +++ b/odex25_hr/hr_multicompany_employee_number/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_employee_multicompany,hr.employee.multicompany,hr.model_hr_employee,base.group_user,1,1,1,1 From 8a7a3755cd2bd9b83af42aee88f6de825feded83 Mon Sep 17 00:00:00 2001 From: Mazen Abdo Date: Thu, 13 Nov 2025 14:12:41 +0200 Subject: [PATCH 2/2] Modified employee creation to assign a valid emp_no if missing or '/' --- .../hr_multicompany_employee_number/README.md | 195 ------------------ .../__manifest__.py | 6 - 2 files changed, 201 deletions(-) delete mode 100644 odex25_hr/hr_multicompany_employee_number/README.md diff --git a/odex25_hr/hr_multicompany_employee_number/README.md b/odex25_hr/hr_multicompany_employee_number/README.md deleted file mode 100644 index d33a82692..000000000 --- a/odex25_hr/hr_multicompany_employee_number/README.md +++ /dev/null @@ -1,195 +0,0 @@ -# HR Multi-Company Employee Number - -## Overview -This module fixes the employee number generation issue in multi-company Odoo 14 environments. It ensures that all employees across all companies get unique, sequential employee numbers without conflicts. - -## Problem Statement -In the original `hr_base` module, when creating employees from `res.users` in a multi-company setup: -- All companies shared a single employee number sequence (`hr.employee`) -- This caused validation errors: "You cannot create Employee with the same employee number" -- New employees were assigned "/" instead of proper sequential numbers - -## Solution -This module implements a **global employee number sequence**: -- All companies share a single global sequence: `hr.employee.global` -- Employee numbers are globally unique across all companies -- Sequential numbering continues across companies -- Maintains compatibility with existing `hr_base` module - -## Features -1. **Global Sequential Numbering**: All employees get sequential numbers regardless of company -2. **Automatic Sequence Creation**: The sequence is automatically created if it doesn't exist -3. **Smart Number Generation**: Analyzes existing employee numbers to start from the correct next number -4. **Multi-Company Support**: Works seamlessly across all companies -5. **No Duplicate Numbers**: Employee numbers are completely unique across all companies - -## Numbering Format -- **All Companies**: EMP-0001, EMP-0002, EMP-0003, EMP-0004, ... -- **Example**: - - Company 1, Employee 1: EMP-0001 - - Company 2, Employee 1: EMP-0002 - - Company 1, Employee 2: EMP-0003 - - Company 2, Employee 2: EMP-0004 - -This ensures that no two employees in the entire system have the same employee number, and numbering is continuous across all companies. - -## Installation - -1. **Copy the module** to your Odoo addons directory: - ``` - /odoo14/custom/STANDARD_MODULES/test/odex25_hr/odex25_hr/hr_multicompany_employee_number/ - ``` - -2. **Update the apps list**: - - Go to Apps menu - - Click "Update Apps List" - - Search for "HR Multi-Company Employee Number" - -3. **Install the module**: - - Click Install on the module - -## Configuration - -### Automatic Setup -The module works automatically after installation. No additional configuration is required. - -### Manual Sequence Configuration (Optional) -If you want to customize the global sequence: - -1. Go to **Settings > Technical > Sequences & Identifiers > Sequences** -2. Search for sequence with code: `hr.employee.global` -3. You can modify: - - Prefix (default: "EMP-", you can change to "E-", "EMPLOYEE-", etc.) - - Padding (default: 4 digits, you can change to 5, 6, etc.) - - Next Number (starting point) - -### Example Sequence Configuration - -**Global Employee Sequence:** -- Sequence Code: `hr.employee.global` -- Name: `Global Employee Number` -- Prefix: `EMP-` -- Padding: 4 (produces 0001, 0002, 0003, etc.) -- Next Number: Based on existing employees + 1 -- Company: None (global across all companies) - -## Usage - -### Creating Employees from Users - -1. **Create a new user**: - ``` - Users & Companies > Users > Create - ``` - -2. **Set the company**: - - In the user form, set the company field - - The system automatically creates an employee with a unique number for that company - -3. **Result**: - - First employee (any company): EMP-0001 - - Second employee (any company): EMP-0002 - - Third employee (any company): EMP-0003 - - And so on... - -### Creating Employees Directly - -1. **Create a new employee**: - ``` - Employees > Employees > Create - ``` - -2. **Set the company**: - - Set the company field before saving - - The system generates the next available number for that company - -## Technical Details - -### Key Files -- `models/hr_employee.py`: Main logic for company-specific employee numbering -- `data/ir_sequence_data.xml`: Sequence configuration template -- `__manifest__.py`: Module definition and dependencies - -### Key Methods - -#### `_default_emp_code()` -Generates the next employee number for the company: -```python -@api.model -def _default_emp_code(self): - company_id = self.env.context.get('default_company_id') or self.env.company.id - sequence_code = f'hr.employee.company.{company_id}' - seq = self.env['ir.sequence'].next_by_code(sequence_code) - ... -``` - -#### `_create_company_sequence()` -Creates a new sequence for a company if it doesn't exist: -```python -@api.model -def _create_company_sequence(self, company_id): - company = self.env['res.company'].browse(company_id) - sequence_code = f'hr.employee.company.{company_id}' - ... -``` - -#### `_check_unique_emp_no_per_company()` -Validates uniqueness per company (not globally): -```python -@api.constrains("emp_no", "company_id") -def _check_unique_emp_no_per_company(self): - items = self.search([ - ("emp_no", "=", item.emp_no), - ("company_id", "=", item.company_id.id) - ]) - ... -``` - -## Troubleshooting - -### Issue: Still getting "/" as employee number - -**Solution**: -1. Check that the module is properly installed -2. Verify that sequences are created: Settings > Technical > Sequences -3. Try creating an employee manually first to trigger sequence creation - -### Issue: Validation error about duplicate employee numbers - -**Solution**: -1. This should not happen with the module installed -2. Check that `hr_multicompany_employee_number` is loaded AFTER `hr_base` -3. Verify in the module dependencies that `hr_base` is listed - -### Issue: Existing employees have conflicts - -**Solution**: -Run this SQL to update existing employees (backup first!): -```sql --- This is just for reference, contact your DB admin -UPDATE hr_employee -SET emp_no = CONCAT(company_id, '-', emp_no) -WHERE emp_no IN ( - SELECT emp_no FROM hr_employee - GROUP BY emp_no HAVING COUNT(*) > 1 -); -``` - -## Dependencies -- `hr`: Base HR module -- `hr_base`: Custom HR base module - -## Version History -- **14.0.1.0.0**: Initial release - - Company-specific employee number sequences - - Automatic sequence creation - - Per-company uniqueness constraint - -## Support -For issues or questions, contact your Odoo administrator. - -## License -Proprietary - -## Author -Custom Development Team diff --git a/odex25_hr/hr_multicompany_employee_number/__manifest__.py b/odex25_hr/hr_multicompany_employee_number/__manifest__.py index eb2161a61..8a29534ae 100644 --- a/odex25_hr/hr_multicompany_employee_number/__manifest__.py +++ b/odex25_hr/hr_multicompany_employee_number/__manifest__.py @@ -7,12 +7,6 @@ 'description': """ This module fixes the employee number generation issue when creating employees from res.users in a multi-company environment. - - Features: - - Company-specific employee number sequences - - Automatic sequence creation for each company - - Prevents duplicate employee numbers across companies - - Seamless integration with existing hr_base module """, 'author': 'Custom Development', 'depends': ['hr_base', 'hr'],