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