feat(fusion_login_audit): smart button + Login Activity tab on res.users
Adds four x_fc_* fields on res.users: login_audit_ids (One2many), login_audit_count (compute), last_successful_login (compute, stored), last_login_ip (compute, stored). action_fc_view_login_audit returns a window action scoped to the current user. View inheritance adds a smart button to the button box and a "Login Activity" page to the notebook, both gated by base.group_system on the inner XML nodes (NOT on the view record — Odoo 19 forbids that; see CLAUDE.md rule #11). Tests (2 new, 18 total green): test_computed_last_successful_login — uses registry cursor to commit the audit row so the stored compute picks it up across the TransactionCase boundary. test_action_view_login_audit_returns_window_action — smart-button action shape + domain scoping. CLAUDE.md rule #11 added: inherited ir.ui.view records cannot have groups/group_ids on the record; the gate must be on the inner XML nodes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
fusion_login_audit/views/res_users_views.xml
Normal file
56
fusion_login_audit/views/res_users_views.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_users_form_inherit_login_audit" model="ir.ui.view">
|
||||
<field name="name">res.users.form.inherit.fusion_login_audit</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<!-- Odoo 19: groups MUST be on the inherited XML nodes (button + page
|
||||
below), NOT on the ir.ui.view record itself. Setting `group_ids`
|
||||
on the record raises ParseError "Inherited view cannot have
|
||||
'groups' defined on the record. Use 'groups' attributes inside
|
||||
the view definition". -->
|
||||
<field name="arch" type="xml">
|
||||
<!-- Smart button -->
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_fc_view_login_audit"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-key"
|
||||
groups="base.group_system">
|
||||
<field name="x_fc_login_audit_count" widget="statinfo"
|
||||
string="Logins"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<!-- Login Activity tab appended at the end of the notebook -->
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Login Activity"
|
||||
name="fc_login_activity"
|
||||
groups="base.group_system">
|
||||
<group>
|
||||
<field name="x_fc_last_successful_login" readonly="1"/>
|
||||
<field name="x_fc_last_login_ip" readonly="1"/>
|
||||
</group>
|
||||
<field name="x_fc_login_audit_ids" readonly="1"
|
||||
context="{'create': False, 'edit': False, 'delete': False}">
|
||||
<list create="false" edit="false" delete="false"
|
||||
limit="30" default_order="event_time desc">
|
||||
<field name="event_time"/>
|
||||
<field name="result" decoration-success="result=='success'"
|
||||
decoration-danger="result=='failure'"
|
||||
widget="badge"/>
|
||||
<field name="failure_reason"/>
|
||||
<field name="ip_address"/>
|
||||
<field name="country_code"/>
|
||||
<field name="city"/>
|
||||
<field name="browser"/>
|
||||
<field name="os"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user