This commit is contained in:
gsinghpal
2026-05-25 20:11:03 -04:00
parent 67af54b46e
commit 5f372b462a
21 changed files with 444 additions and 833 deletions

View File

@@ -177,6 +177,37 @@ class PlantKanbanController(http.Controller):
'cards': cards,
}
# ----------------------------------------------------------------------
# Pair the current user to a work centre (used by station-QR scan from
# plant_kanban). 2026-05-25 — replaces the legacy
# localStorage-based pairing that shopfloor_landing used; plant_kanban
# reads the pairing from res.users.paired_work_centre_ids server-side,
# so QR scans must persist there.
# ----------------------------------------------------------------------
@http.route('/fp/landing/pair_work_centre', type='jsonrpc', auth='user')
def pair_work_centre(self, work_centre_id=None):
"""Set env.user.paired_work_centre_ids to [work_centre_id], or clear.
MVP holds exactly one row in the M2M (the legacy single-station
picker). Pass work_centre_id=None to unpair.
"""
user = request.env.user.sudo()
if 'paired_work_centre_ids' not in user._fields:
return {'ok': False, 'error': 'paired_work_centre_ids not supported'}
try:
if work_centre_id:
wc_id = int(work_centre_id)
# Verify the work centre exists so we don't write garbage
if not request.env['fp.work.centre'].sudo().browse(wc_id).exists():
return {'ok': False, 'error': 'work centre not found'}
user.write({'paired_work_centre_ids': [(6, 0, [wc_id])]})
else:
user.write({'paired_work_centre_ids': [(5,)]})
except Exception as e:
_logger.warning('pair_work_centre failed: %s', e)
return {'ok': False, 'error': str(e)}
return {'ok': True}
# ===== helpers ==========================================================

View File

@@ -198,9 +198,15 @@ class FpTabletController(http.Controller):
}
if not (shop_branch_ids & set(target.all_group_ids.ids)):
return {'ok': False, 'error': 'no_role'}
# Resolve recipient email — login if email-shaped, else partner.email
email = (target.login if target.login and '@' in target.login
else (target.partner_id.email or ''))
# Resolve recipient email — MUST mirror the mail.template's
# `{{ object.email or object.login }}` priority so the masked
# address shown in the UI matches where the email actually
# lands. (Pre-2026-05-25 these two were inverted: the controller
# showed login but the template delivered to email. Operators
# ended up checking the wrong inbox.)
email = (target.email if target.email and '@' in target.email
else (target.login if target.login and '@' in target.login
else ''))
if not email:
owner = env['res.company'].browse(env.user.company_id.id).sudo()
owner_name = (owner.x_fc_owner_user_id.name
@@ -231,9 +237,14 @@ class FpTabletController(http.Controller):
'error': 'rate_limited',
'wait_minutes': wait_min,
}
# Render the email directly with code in context — the
# _dispatch path doesn't yet propagate ctx.code into the
# mail.template render, so direct send_mail is the safe path.
# Render + send IMMEDIATELY (force_send=True). The legacy
# `force_send=False` queued the mail for the `Mail: Email Queue
# Manager` cron — which runs every 1 HOUR on entech (not per
# minute), so a tech tapping "Send temporary PIN" could wait up
# to 60 min for the code. PIN reset is an interactive flow; the
# user is staring at the screen. Synchronous send adds ~1s of
# latency but the code lands in the inbox before the user can
# tab to their email.
try:
mail_template = env.ref(
'fusion_plating_shopfloor.fp_mail_template_tablet_pin_reset',
@@ -241,7 +252,7 @@ class FpTabletController(http.Controller):
)
if mail_template:
mail_template.sudo().with_context(code=code).send_mail(
target.id, force_send=False,
target.id, force_send=True,
)
except Exception as exc:
_logger.warning(