Modified employee creation to assign a valid emp_no if missing or '/'
This commit is contained in:
parent
dfdfc24c23
commit
b3a92a1543
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="seq_hr_employee_global" model="ir.sequence">
|
||||||
|
<field name="name">Global Employee Number</field>
|
||||||
|
<field name="code">hr.employee.global</field>
|
||||||
|
<field name="prefix">EMP-</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="number_next">1</field>
|
||||||
|
<field name="number_increment">1</field>
|
||||||
|
<field name="company_id" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import hr_employee
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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
|
||||||
|
Loading…
Reference in New Issue