feat(fusion_plating_shopfloor): /fp/tablet/set_pin + /fp/tablet/reset_pin_for endpoints (P6.1.2)
set_pin is self-service: requires old PIN if a hash exists, validates 4-digit format. reset_pin_for is manager-only (enforced server-side via has_group); clears the hash + posts to chatter. Both endpoints log INFO on success and WARNING on access-control denials. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,3 +8,4 @@ from . import tank_status
|
||||
from . import move_controller
|
||||
from . import workspace_controller
|
||||
from . import landing_controller
|
||||
from . import tablet_controller
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
"""JSON-RPC endpoints for the tablet PIN gate (Phase 6 tablet redesign).
|
||||
|
||||
POST /fp/tablet/tiles — list of tiles for the lock screen
|
||||
POST /fp/tablet/unlock — verify PIN + clear/increment failure counter
|
||||
POST /fp/tablet/set_pin — self-service set/change PIN
|
||||
POST /fp/tablet/reset_pin_for — manager-only reset of another user's PIN
|
||||
POST /fp/tablet/ping — bump server-side last-active timestamp
|
||||
|
||||
Spec: docs/superpowers/specs/2026-05-22-shopfloor-pin-gate-design.md
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import _, fields, http
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_manager(env):
|
||||
"""True if calling user is in the fusion_plating manager group."""
|
||||
return env.user.has_group('fusion_plating.group_fusion_plating_manager')
|
||||
|
||||
|
||||
class FpTabletController(http.Controller):
|
||||
"""Tablet PIN gate endpoints. All require an authenticated Odoo
|
||||
session (the tablet logs in once as a 'shopfloor service' user).
|
||||
"""
|
||||
|
||||
# ======================================================================
|
||||
# /fp/tablet/set_pin — self-service set or change
|
||||
# ======================================================================
|
||||
@http.route('/fp/tablet/set_pin', type='jsonrpc', auth='user')
|
||||
def set_pin(self, new_pin, old_pin=None):
|
||||
env = request.env
|
||||
user = env.user
|
||||
existing_hash = user.sudo().x_fc_tablet_pin_hash
|
||||
if existing_hash:
|
||||
if not old_pin:
|
||||
return {'ok': False, 'error': _('Current PIN is required to change it.')}
|
||||
if not user.verify_tablet_pin(old_pin):
|
||||
return {'ok': False, 'error': _('Current PIN is incorrect.')}
|
||||
try:
|
||||
user.set_tablet_pin(new_pin)
|
||||
except UserError as e:
|
||||
return {'ok': False, 'error': str(e.args[0]) if e.args else str(e)}
|
||||
_logger.info(
|
||||
"Tablet PIN set/changed for uid %s by self", user.id,
|
||||
)
|
||||
return {'ok': True}
|
||||
|
||||
# ======================================================================
|
||||
# /fp/tablet/reset_pin_for — manager-only
|
||||
# ======================================================================
|
||||
@http.route('/fp/tablet/reset_pin_for', type='jsonrpc', auth='user')
|
||||
def reset_pin_for(self, user_id):
|
||||
env = request.env
|
||||
if not _is_manager(env):
|
||||
_logger.warning(
|
||||
"Non-manager uid %s attempted to reset PIN for user %s",
|
||||
env.uid, user_id,
|
||||
)
|
||||
return {'ok': False, 'error': _('Manager privilege required.')}
|
||||
target = env['res.users'].browse(int(user_id))
|
||||
if not target.exists():
|
||||
return {'ok': False, 'error': _('User not found.')}
|
||||
target.clear_tablet_pin()
|
||||
_logger.info(
|
||||
"Tablet PIN reset for uid %s by manager uid %s",
|
||||
target.id, env.uid,
|
||||
)
|
||||
return {'ok': True}
|
||||
Reference in New Issue
Block a user