refactor: replace custom employee_pin field with Odoo's standard pin field and update UI for inline regeneration
This commit is contained in:
parent
ea9bf82a6c
commit
406f353b98
41
README.md
41
README.md
@ -1,52 +1,41 @@
|
|||||||
Employee PIN Generator
|
Employee PIN Generator
|
||||||
======================
|
======================
|
||||||
|
|
||||||
.. contents::
|
This Odoo 19 module automatically assigns a unique, randomly generated 6-digit PIN to every employee record using Odoo's standard PIN Code field. The PIN is enforced to be globally unique across all companies in the database via a SQL UNIQUE constraint.
|
||||||
:local:
|
|
||||||
:depth: 1
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
This Odoo 19 module automatically assigns a unique, randomly generated 6-digit PIN to every employee record. The PIN is enforced to be globally unique across **all companies** in the same Odoo database via a SQL UNIQUE constraint.
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- Auto-generates a 6-digit numeric PIN when a new employee is created.
|
- Auto-generates a unique 6-digit numeric PIN when a new employee is created.
|
||||||
- PIN uniqueness is enforced at the database level (UNIQUE constraint on ``employee_pin``).
|
- Reuses Odoo's standard PIN Code field (used in Point of Sale and HR Attendance).
|
||||||
- No two employees — even in different companies — can share the same PIN.
|
- PIN uniqueness is enforced at the database level across all companies.
|
||||||
- A **Regenerate PIN** button is available on the employee form (Private Information tab) for HR managers and users.
|
- A Regenerate PIN button is available inline with the PIN Code field on the employee form.
|
||||||
- A confirmation wizard is shown before replacing the current PIN, displaying the existing value.
|
- A confirmation wizard is shown before replacing the current PIN.
|
||||||
|
|
||||||
Technical Details
|
Technical Details
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- Model extended: ``hr.employee``
|
- Model extended: hr.employee
|
||||||
- New field: ``employee_pin`` (Char, size 6, unique)
|
- Field modified: pin (Odoo's standard PIN Code)
|
||||||
- New transient model: ``hr.employee.regenerate.pin.wizard``
|
- New transient model: hr.employee.regenerate.pin.wizard
|
||||||
- PIN generation retries up to 1,000 times to avoid collision in a crowded pool.
|
- PIN generation retries up to 1,000 times to avoid collision.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
1. Copy ``hr_employee_pin`` into your custom addons path.
|
1. Copy hr_employee_pin into your custom addons path.
|
||||||
2. Update the addons list in Odoo (Settings → Activate Developer Mode → Update App List).
|
2. Update the addons list in Odoo.
|
||||||
3. Install **Employee PIN Generator** from the Apps menu.
|
3. Install Employee PIN Generator.
|
||||||
4. Existing employees will NOT receive a PIN automatically — use the **Regenerate PIN** button on each record, or run a one-time migration script.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
When creating a new employee the PIN is generated automatically and shown in the **Private Information** tab under the *Employee PIN* section.
|
|
||||||
|
|
||||||
To regenerate the PIN:
|
To regenerate the PIN:
|
||||||
|
|
||||||
1. Open an employee record.
|
1. Open an employee record.
|
||||||
2. Go to the **Private Information** tab.
|
2. Go to the Settings tab under the Attendance/Point of Sale section.
|
||||||
3. Find the *Employee PIN* section and click **Regenerate PIN**.
|
3. Click the Regenerate PIN button inline with the PIN Code field.
|
||||||
4. Confirm in the dialog.
|
4. Confirm in the dialog.
|
||||||
5. A success notification will show the new PIN.
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
|
|||||||
@ -10,19 +10,11 @@ _logger = logging.getLogger(__name__)
|
|||||||
class HrEmployee(models.Model):
|
class HrEmployee(models.Model):
|
||||||
_inherit = 'hr.employee'
|
_inherit = 'hr.employee'
|
||||||
|
|
||||||
employee_pin = fields.Char(
|
|
||||||
string='Employee PIN',
|
|
||||||
size=6,
|
|
||||||
copy=False,
|
|
||||||
help='Unique 6-digit PIN for this employee. '
|
|
||||||
'Globally unique across all companies.',
|
|
||||||
)
|
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
(
|
(
|
||||||
'employee_pin_unique',
|
'pin_unique',
|
||||||
'UNIQUE(employee_pin)',
|
'UNIQUE(pin)',
|
||||||
'The Employee PIN must be unique across all employees and companies.',
|
'The Employee PIN Code must be unique across all employees and companies.',
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -34,7 +26,7 @@ class HrEmployee(models.Model):
|
|||||||
def _generate_unique_pin(self):
|
def _generate_unique_pin(self):
|
||||||
"""Return a random 6-digit string that is not yet used by any employee."""
|
"""Return a random 6-digit string that is not yet used by any employee."""
|
||||||
existing = set(
|
existing = set(
|
||||||
self.sudo().search([('employee_pin', '!=', False)]).mapped('employee_pin')
|
self.sudo().search([('pin', '!=', False)]).mapped('pin')
|
||||||
)
|
)
|
||||||
for _attempt in range(1000):
|
for _attempt in range(1000):
|
||||||
pin = '{:06d}'.format(random.randint(0, 999999))
|
pin = '{:06d}'.format(random.randint(0, 999999))
|
||||||
@ -52,8 +44,8 @@ class HrEmployee(models.Model):
|
|||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
for vals in vals_list:
|
for vals in vals_list:
|
||||||
if not vals.get('employee_pin'):
|
if not vals.get('pin'):
|
||||||
vals['employee_pin'] = self._generate_unique_pin()
|
vals['pin'] = self._generate_unique_pin()
|
||||||
return super().create(vals_list)
|
return super().create(vals_list)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@ -64,7 +56,7 @@ class HrEmployee(models.Model):
|
|||||||
"""Regenerate a new unique PIN for this employee (called from button)."""
|
"""Regenerate a new unique PIN for this employee (called from button)."""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
new_pin = self._generate_unique_pin()
|
new_pin = self._generate_unique_pin()
|
||||||
self.employee_pin = new_pin
|
self.pin = new_pin
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.client',
|
'type': 'ir.actions.client',
|
||||||
'tag': 'display_notification',
|
'tag': 'display_notification',
|
||||||
|
|||||||
@ -8,16 +8,16 @@
|
|||||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
|
||||||
<!-- Insert employee PIN after the existing 'PIN Code' field
|
<!-- Replace the standard PIN field to make it readonly and add a Regenerate button inline -->
|
||||||
inside the Attendance/Point of Sale group (Settings tab). -->
|
<field name="pin" position="replace">
|
||||||
<field name="pin" position="after">
|
<label for="pin" string="PIN Code"/>
|
||||||
<field name="employee_pin" string="Employee PIN (6-digit)" readonly="1"/>
|
<div class="o_row">
|
||||||
<div>
|
<field name="pin" readonly="1" class="oe_inline"/>
|
||||||
<button name="%(hr_employee_pin.action_regenerate_pin_wizard)d"
|
<button name="%(hr_employee_pin.action_regenerate_pin_wizard)d"
|
||||||
string="Regenerate PIN"
|
string="Regenerate PIN"
|
||||||
type="action"
|
type="action"
|
||||||
icon="fa-refresh"
|
icon="fa-refresh"
|
||||||
class="btn-secondary"
|
class="btn btn-link"
|
||||||
context="{'default_employee_id': id}"/>
|
context="{'default_employee_id': id}"/>
|
||||||
</div>
|
</div>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@ -15,7 +15,7 @@ class RegeneratePinWizard(models.TransientModel):
|
|||||||
)
|
)
|
||||||
current_pin = fields.Char(
|
current_pin = fields.Char(
|
||||||
string='Current PIN',
|
string='Current PIN',
|
||||||
related='employee_id.employee_pin',
|
related='employee_id.pin',
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user