feat: enhance purchase UI/UX with Odoo standards

- Reorganize Purchase Request form with logical field grouping
- Add separators for Request Information and Vendor sections
- Implement radio widgets and proper button styling
- Add icons to smart buttons and notebook pages
- Apply decoration patterns to tree views
- Enhance Purchase Requisition form organization
- Add Purchase Configuration and Department sections
- Standardize button classes (btn-primary, btn-secondary)
- Include proper placeholders and labels

Synced with latest dev_odex25_purchase on Fri Nov 14 08:38:08 +03 2025
This commit is contained in:
maltayyar2 2025-11-14 08:38:08 +03:00
parent 7c6e3e6bce
commit 64fe02a0ae
2 changed files with 125 additions and 99 deletions

View File

@ -30,16 +30,17 @@
attrs="{'invisible' : ['|',('state' , '!=' , 'waiting'),('purchase_create' , '=' , True)]}"/>
<button name="action_refuse" type="object" string="Refuse" id="prc_request_refuse"
class="btn-secondary"
groups="purchase_requisition_custom.group_refuse_purchase"
attrs="{'invisible' : [('state' , 'not in' , ('direct_manager','waiting'))]}"/>
<button name="action_done" type="object" string="Done" class="oe_stat_button"
<button name="action_done" type="object" string="Done" class="oe_highlight"
groups="purchase.group_purchase_manager" states="waiting"/>
<button name="action_draft" type="object" string="Reset To Draft" class="oe_highlight"
<button name="action_draft" type="object" string="Reset To Draft" class="btn-secondary"
states="refuse" groups="purchase_requisition_custom.group_purchase_set_to_draft"/>
<field name="state" widget="statusbar"/>
<field name="state" widget="statusbar" statusbar_visible="draft,direct_manager,waiting,done"/>
</header>
<sheet>
@ -47,74 +48,92 @@
<field name="is_requisition" invisible="1"/>
<field name="is_creator" invisible="1"/>
<button class="oe_stat_button" name="open_requisition" type="object"
string="Purchase Agreement" icon="fa-list-ol"
icon="fa-handshake-o"
attrs="{'invisible' : [('is_requisition' , '=' , False)]}"
groups="purchase.group_purchase_user,purchase.group_purchase_manager"/>
groups="purchase.group_purchase_user,purchase.group_purchase_manager">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">Purchase Agreement</span>
</div>
</button>
<button class="oe_stat_button" name="open_purchase" type="object"
string="Purchase Orders" icon="fa-list-ol"
icon="fa-shopping-cart"
attrs="{'invisible' : [('purchase_create' , '=' , False)]}"
groups="purchase.group_purchase_user,purchase.group_purchase_manager"/>
groups="purchase.group_purchase_user,purchase.group_purchase_manager">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">Purchase Orders</span>
</div>
</button>
</div>
<div class="oe_title">
<!-- <label for="name" class="oe_edit_only"/>-->
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" required="1" readonly="1" class="oe_inline"/>
</h1>
<h3>
<field name="purchase_purpose" required="1" placeholder="Request Purpose..."
attrs="{'readonly':[('state' , '!=' , 'draft')]}"/>
</h3>
</div>
<group col="4" colspan="3">
<field name="by_purchase" groups="purchase.group_purchase_user"/>
<field name="picking_type_id" invisible="1"/>
<field name="department_id" force_save="1"
attrs="{'readonly' : ['|',('state' , '!=' , 'draft'),('by_purchase' , '!=' , True)] , 'required' : [('by_purchase' , '=' , True)]}"
readonly="1" context="{'show_department_short': True}"/>
<field name="branch_id" readonly="1" context="{'show_branch_short': True}"/>
<group>
<group string="Requester Information" name="requester_info">
<field name="employee_id" attrs="{'readonly':[('by_purchase' , '=' , False)]}" required="1"
options="{'no_create' : True , 'no_edit' : True ,'no_open' : True}"/>
<field name="product_category_ids" widget="many2many_tags"/>
<field name="purchase_purpose" required="1"
attrs="{'readonly':[('state' , '!=' , 'draft')]}"/>
<field name="department_id" force_save="1"
attrs="{'readonly' : ['|',('state' , '!=' , 'draft'),('by_purchase' , '!=' , True)] , 'required' : [('by_purchase' , '=' , True)]}"
readonly="1"/>
<field name="branch_id" readonly="1"/>
<field name="date" readonly="1"/>
<field name="purchase_create" invisible="1"/>
</group>
<group string="Request Details" name="request_details">
<field name="type" widget="radio" options="{'horizontal': true}"/>
<field name="product_category_ids" widget="many2many_tags"/>
<field name="by_purchase" groups="purchase.group_purchase_user"/>
<field name="purchase_state_summary" readonly="1"
attrs="{'invisible': [('purchase_state_summary', '=', False)]}"/>
</group>
</group>
<group string="Vendor &amp; Accounting" name="vendor_accounting"
attrs="{'invisible': [('state', '=', 'draft')]}">
<group>
<field name="partner_id"
domain="[('supplier_rank','>', 0)]"
options="{'no_create' : True , 'no_edit' : True ,'no_open' : True}"
attrs="{'readonly':['|',('edit_partner_id' , '=' , False),('state' , '!=' , 'waiting')]}"/>
<!-- <div>-->
<!-- <label for="use_analytic"/>-->
<field name="use_analytic"/>
</group>
<group>
<field name="account_analytic_id"
options="{'no_create' : True , 'no_edit' : True ,'no_open' : True}"
attrs="{'invisible':[('use_analytic' , '=' , False)],'required':[('use_analytic' , '=' , True)],'readonly':[('state' , '!=' , 'draft')]}"/>
<!-- </div>-->
</group>
</group>
<!-- Hidden fields -->
<field name="picking_type_id" invisible="1"/>
<field name="purchase_create" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="edit_partner_id" invisible="1"/>
</group>
<notebook>
<page string="Items" name="purchase_request_info">
<page string="Requested Items" name="purchase_request_info" icon="fa-list">
<field name="line_ids" attrs="{'readonly':[('state' , '!=' , 'draft')]}">
<tree editable="bottom">
<tree editable="bottom" decoration-info="qty > 0" decoration-muted="qty == 0">
<field name="product_id"
domain="[('purchase_ok', '=', True)]"
options="{'no_create' : True , 'no_edit' : True ,'no_open' : True}"
optional="show"
required="1"/>
<field name="description" required="1"/>
<field name="qty" sum="Total Quantity"
attrs="{'column_required':[('parent.state' , '!=' , 'draft')]}"/>
<field name="uom_id"/>
<field name="description"/>
<field name="qty"
attrs="{'column_required':[('parent.state' , '!=' , 'draft')]}"
optional="show"/>
<!-- <field name="price_unit" attrs="{'column_required':[('parent.state' , '!=', 'draft')]}" optional="show"/>-->
<!-- <field name="sum_total" force_save="1" sum="Total amount" optional="show"/>-->
<field name="account_id" invisible="1"/>
</tree>
</field>
</page>
<page string="Note">
<field name="note"/>
<page string="Notes" name="notes" icon="fa-sticky-note-o">
<field name="note" placeholder="Additional notes or special requirements..."/>
</page>
</notebook>
</sheet>
@ -135,8 +154,8 @@
<field name="name"/>
<field name="date"/>
<field name="employee_id"/>
<field name="department_id" context="{'show_department_short': True}"/>
<field name="branch_id" context="{'show_branch_short': True}"/>
<field name="department_id"/>
<field name="branch_id"/>
<field name="product_category_ids" widget="many2many_tags"/>
<field name="state"/>
<field name="purchase_state_summary" optional="show"/>
@ -152,8 +171,8 @@
<search string="Purchase Request">
<field name="name" string="Request Number"/>
<field name="employee_id" string="Employee"/>
<field name="department_id" string="Department" context="{'show_department_short': True}"/>
<field name="branch_id" string="Branch" context="{'show_branch_short': True}"/>
<field name="department_id" string="Department"/>
<field name="branch_id" string="Branch"/>
<field name="purchase_purpose" string="Purpose"/>
<field name="partner_id" string="Vendor"/>
@ -176,16 +195,7 @@
domain="[('employee_id.user_id','=',uid)]"/>
<filter string="My Department" name="my_department"
domain="[('department_id.manager_id.user_id','=',uid)]"/>
<filter string="Pending Approval" name="pending_approval"
domain="[('state','in',['draft','direct_manager'])]"/>
<filter string="In Progress" name="in_progress"
domain="[('state','in',['waiting'])]"/>
<separator/>
<filter string="Project Requests" name="project_type"
domain="[('type','=','project')]"/>
<filter string="Operational Requests" name="operational_type"
domain="[('type','=','operational')]"/>
<separator/>
<filter string="With Vendor" name="with_vendor"
@ -197,11 +207,9 @@
<group expand="0" string="Group By">
<filter string="State" name="group_state" context="{'group_by':'state'}"/>
<filter string="Employee" name="group_employee" context="{'group_by':'employee_id'}"/>
<filter string="Department" name="group_department" context="{'group_by':'department_id', 'show_department_short': True}"/>
<filter string="Department" name="group_department" context="{'group_by':'department_id'}"/>
<filter string="Branch" name="group_branch" context="{'group_by':'branch_id'}"/>
<filter string="Vendor" name="group_vendor" context="{'group_by':'partner_id'}"/>
<filter string="Request Type" name="group_type" context="{'group_by':'type'}"/>
<filter string="Product Categories" name="group_categories" context="{'group_by':'product_category_ids'}"/>
</group>
</search>
</field>

View File

@ -98,7 +98,7 @@
<xpath expr="//field[@name='partner_id']" position="after">
<field name="recommendation_order" string="Recommend Order"/>
<field name="branch_id" context="{'from_branch_field': True}"/>
<field name="branch_id"/>
</xpath>
@ -187,7 +187,7 @@
<button type="object" name="action_refuse" groups="purchase_requisition_custom.committe_member"
attrs="{'invisible':['|',('state','in',['done','purchase','waiting','cancel']),('can_committee_vote', '=', False)]}"
string="Refuse"/>
string="Refuse" class="btn-secondary"/>
<button type="object" name="action_recommend"
groups="purchase_requisition_custom.group_select_recommended_offer"
confirm="Are you sure you want to process ?"
@ -299,15 +299,25 @@
<attribute name="attrs">{'readonly':[('state','in',('done','cancel','waiting'))]}</attribute>
</xpath>
<xpath expr="//field[@name='currency_id']" position="after">
<field name="state_of_delivery"/>
<separator string="Request Information"/>
<group col="4">
<field name="request_id" readonly="1" colspan="2"/>
<field name="purchase_request_employee_id" colspan="2"/>
<field name="department_id" invisible="0"/>
<field name="branch_id" readonly="1"/>
<field name="purpose" attrs="{'readonly': [('state','in',['done','cancel'])]}" colspan="2"/>
</group>
<separator string="Status &amp; Tracking"/>
<group col="4">
<field name="state_of_delivery" colspan="2"/>
<field name="no_of_approve" readonly="1" colspan="2"/>
</group>
<!-- Hidden fields -->
<field name="is_purchase_budget" invisible="1"/>
<field name="no_of_approve" readonly="1"/>
<!--here -->
<field name="purchase_cost" invisible="1"/>
<field name="department_id" invisible="0" context="{'show_department_short': True}"/>
<field name="branch_id" readonly="1" context="{'show_branch_short': True}"/>
<field name="purchase_request_employee_id"/>
<field name="purpose" attrs="{'readonly': [('state','in',['done','cancel'])]}"/>
</xpath>
<xpath expr="//field[@name='order_line']" position="before">
<button name="action_select_all" class="oe_highlight" type="object"
@ -412,7 +422,7 @@
</xpath>
<xpath expr="//field[@name='line_ids']/tree[1]/field[@name='product_qty']" position="before">
<field name="department_id" attrs="{'column_invisible': [('parent.purchase_cost', '=', 'default')],
'readonly': ['|', ('parent.state', '=', 'accept'), ('parent.order_count', '>', 0)]}" context="{'show_department_short': True}"/>
'readonly': ['|', ('parent.state', '=', 'accept'), ('parent.order_count', '>', 0)]}"/>
</xpath>
<xpath expr="/form/header/button[@name='action_done']" position="replace">
@ -452,7 +462,7 @@
<xpath expr="/form/header/button[@name='action_cancel']" position="replace">
<button name="action_cancel" groups="purchase_requisition_custom.group_cancel_purchase_requisition"
states="draft,in_progress,ongoing,checked,purchase_manager,rejected_by_committee"
string="Cancel" type="object"/>
string="Cancel" type="object" class="btn-secondary"/>
</xpath>
<xpath expr="/form/header/button[@name='action_draft']" position="attributes">
<attribute name="attrs">{'invisible':[('state','!=','cancel')]}</attribute>
@ -474,25 +484,33 @@
<!-- <field name="city" attrs="{'readonly': [('state','!=', 'draft')]}"/>-->
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="check_request" invisible="1"/>
<separator string="Purchase Configuration"/>
<group col="4">
<field name="category_ids" required="1"
attrs="{'readonly':['|',('state','in',('cancel','checked','done','waiting')),('check_request','=',True)]}"
widget="many2many_tags"/>
widget="many2many_tags" colspan="4"/>
<field name="purchase_cost" required="1"
attrs="{'readonly':[('state','in',('cancel','checked','done','waiting'))]}"/>
<field name="purchase_cost" required="1" widget="radio" options="{'horizontal': true}"
attrs="{'readonly':[('state','in',('cancel','checked','done','waiting'))]}" colspan="4"/>
<field name="department_id" attrs="{'required':[('purchase_cost','=', 'department')], 'invisible':[('purchase_cost','=', 'product_line')],
'readonly':['|',('state','in',('cancel','checked','done','waiting')),('check_request','=',True)]}" context="{'show_department_short': True}"/>
<field name="purpose" required="1"
attrs="{'readonly':['|',('state','in',('cancel','checked','done','waiting')),('check_request','=',True)]}"/>
<field name="type" required="1"
attrs="{'readonly':[('state','in',('cancel','checked','done','waiting'))]}"/>
<field name="project_id"
<field name="type" required="1" widget="radio" options="{'horizontal': true}"
attrs="{'readonly':[('state','in',('cancel','checked','done','waiting'))]}" colspan="2"/>
<field name="project_id" colspan="2"
attrs="{'invisible':[('type','!=', 'project')], 'required':[('type','=', 'project')], 'readonly':[('state' , '!=' , 'draft')]}"/>
</group>
<field name="request_id" readonly="1"/>
<separator string="Department &amp; Request Details"/>
<group col="4">
<field name="department_id" attrs="{'required':[('purchase_cost','=', 'department')], 'invisible':[('purchase_cost','=', 'product_line')],
'readonly':['|',('state','in',('cancel','checked','done','waiting')),('check_request','=',True)]}" colspan="2"/>
<field name="request_id" readonly="1" colspan="2"/>
<field name="purpose" required="1" colspan="4"
attrs="{'readonly':['|',('state','in',('cancel','checked','done','waiting')),('check_request','=',True)]}"/>
</group>
<!-- Hidden fields -->
<field name="check_request" invisible="1"/>
<field name="type_exclusive" invisible="1"/>
</xpath>
<xpath expr="//field[@name='vendor_id']" position="attributes">
@ -619,8 +637,8 @@
<field name="origin" invisible="1"/>
<field name="reject_reason" required="1"/>
<footer>
<button name="action_reject" string="Confirm" type="object" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel"/>
<button name="action_reject" string="Confirm" type="object" class="btn-secondary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
@ -651,8 +669,8 @@
<field name="order_id" invisible="1"/>
<field name="refuse_reason" required="1"/>
<footer>
<button name="action_refuse" string="Confirm" type="object" class="oe_highlight"/>
<button string="Cancel" class="btn btn-default" special="cancel"/>
<button name="action_refuse" string="Confirm" type="object" class="btn-secondary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>