odoo-18-docker-compose/addons/epr/views/epr_purchase_request_views.xml
mtpc4s9 0570680d58 Thêm PO
Thêm wizard gom nhiều RFQs để tạo POs (những RFQs cùng vendor)
2025-12-14 13:33:10 +07:00

370 lines
21 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!--
===================================================================
1. SEARCH VIEW
Bộ lọc và thanh tìm kiếm
===================================================================
-->
<record id="view_epr_purchase_request_search" model="ir.ui.view">
<field name="name">epr.purchase.request.search</field>
<field name="model">epr.purchase.request</field>
<field name="arch" type="xml">
<search string="Search Purchase Request">
<field name="name" string="Reference"/>
<field name="employee_id"/>
<field name="department_id"/>
<!-- Filters -->
<filter string="My Requests" name="my_requests" domain="[('employee_id.user_id', '=', uid)]"/>
<filter string="To Approve by Me" name="to_approve_by_me" domain="[('approver_ids', 'in', uid)]"/>
<separator/>
<filter string="To Approve" name="to_approve" domain="[('state', '=', 'to_approve')]"/>
<filter string="Approved" name="approved" domain="[('state', '=', 'approved')]"/>
<separator/>
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]"/>
<!-- Group By -->
<group expand="0" string="Group By">
<filter string="Employee" name="employee" domain="[]" context="{'group_by': 'employee_id'}"/>
<filter string="Department" name="department" domain="[]" context="{'group_by': 'department_id'}"/>
<filter string="Status" name="status" domain="[]" context="{'group_by': 'state'}"/>
</group>
</search>
</field>
</record>
<!--
===================================================================
2. KANBAN VIEW
Hiển thị dạng thẻ theo quy trình (Pipeline)
===================================================================
-->
<record id="view_epr_purchase_request_kanban" model="ir.ui.view">
<field name="name">epr.purchase.request.kanban</field>
<field name="model">epr.purchase.request</field>
<field name="arch" type="xml">
<!--
default_group_by="state": Kích hoạt chế độ cột theo trạng thái.
records_draggable="false": Chặn kéo thả nếu bạn muốn quy trình chặt chẽ (chỉ đổi trạng thái bằng nút bấm).
Nếu muốn cho phép kéo thả để duyệt nhanh, hãy bỏ attribute này.
-->
<kanban default_group_by="state"
class="o_kanban_small_column"
quick_create="false"
sample="1">
<!-- Các field cần dùng trong logic hiển thị -->
<field name="state"/>
<field name="currency_id"/>
<field name="activity_state"/>
<field name="id"/>
<field name="employee_id"/>
<field name="department_id"/>
<field name="date_required"/>
<field name="priority"/>
<field name="estimated_total"/>
<field name="activity_ids"/>
<!-- Thanh tiến độ (Progress Bar) trên đầu mỗi cột -->
<progressbar field="state"
colors='{"draft": "secondary", "to_approve": "warning", "approved": "success", "rejected": "danger"}'/>
<templates>
<t t-name="kanban-box">
<!-- Khung thẻ: oe_kanban_global_click giúp click vào đâu cũng mở form -->
<div class="oe_kanban_global_click oe_kanban_card d-flex flex-column">
<!-- HEADER: Tên + Menu 3 chấm -->
<div class="o_kanban_record_top mb-2">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<field name="name"/>
</strong>
<span class="float-end badge"
t-attf-class="badge-{{record.state.raw_value == 'approved' ? 'success' : (record.state.raw_value == 'rejected' ? 'danger' : 'info')}}">
<field name="state"/>
</span>
</div>
<!-- Dropdown Menu (Odoo 18 dùng Bootstrap 5) -->
<div class="o_dropdown_kanban dropdown">
<a class="dropdown-toggle o-no-caret btn" role="button" data-bs-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<a role="menuitem" type="edit" class="dropdown-item">Edit</a>
<a role="menuitem" type="delete" class="dropdown-item">Delete</a>
</div>
</div>
</div>
<!-- BODY: Thông tin chính -->
<div class="o_kanban_record_body tags-section mb-2">
<div class="d-flex align-items-center mb-1">
<field name="employee_id" widget="many2one_avatar_user"/>
<span class="ms-2 text-muted small">
<field name="department_id"/>
</span>
</div>
<!-- Hiển thị ngày cần hàng nếu có -->
<div t-if="record.date_required.raw_value" class="text-muted small">
<i class="fa fa-clock-o me-1"/> <field name="date_required"/>
</div>
</div>
<!-- FOOTER: Priority + Activity + Total -->
<div class="o_kanban_record_bottom mt-auto d-flex justify-content-between align-items-center">
<div class="oe_kanban_bottom_left d-flex align-items-center">
<field name="priority" widget="priority"/>
<field name="activity_ids" widget="kanban_activity" class="ms-2"/>
</div>
<div class="oe_kanban_bottom_right">
<field name="estimated_total" widget="monetary" options="{'currency_field': 'currency_id'}" class="fw-bold"/>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!--
===================================================================
2. LIST VIEW
Danh sách tổng quan
===================================================================
-->
<record id="view_epr_purchase_request_list" model="ir.ui.view">
<field name="name">epr.purchase.request.list</field>
<field name="model">epr.purchase.request</field>
<field name="arch" type="xml">
<list string="Purchase Requests"
decoration-muted="state == 'cancel'"
decoration-info="state == 'to_approve'"
decoration-danger="state == 'rejected'"
sample="1">
<field name="name"/>
<field name="date_required"/>
<field name="employee_id" widget="many2one_avatar_user"/>
<field name="department_id" optional="show"/>
<field name="priority" widget="priority" optional="hide"/>
<field name="estimated_total" widget="monetary" options="{'currency_field': 'currency_id'}" sum="Total"/>
<field name="currency_id" invisible="1"/>
<field name="state"
widget="badge"
decoration-success="state == 'approved' or state == 'done'"
decoration-warning="state == 'to_approve'"
decoration-danger="state == 'rejected'"/>
</list>
</field>
</record>
<!--
===================================================================
3. FORM VIEW
Giao diện chi tiết
===================================================================
-->
<record id="view_epr_purchase_request_form" model="ir.ui.view">
<field name="name">epr.purchase.request.form</field>
<field name="model">epr.purchase.request</field>
<field name="arch" type="xml">
<form string="Purchase Request">
<header>
<!-- Nút Submit: Chỉ hiện khi Draft -->
<button name="action_submit"
string="Submit for Approval"
type="object"
class="oe_highlight"
invisible="state != 'draft'"/>
<!-- Nút Approve: Dành cho Manager -->
<button name="action_approve"
string="Approve"
type="object"
class="oe_highlight btn-success"
invisible="state != 'to_approve'"
groups="epr.group_epr_manager"/>
<!-- Nút Reject: Gọi Wizard -->
<button name="action_reject_wizard"
string="Reject"
type="object"
class="btn-danger"
invisible="state != 'to_approve'"
groups="epr.group_epr_manager"/>
<field name="is_owner" invisible="1"/>
<!-- Nút Reset: Cho phép PR's owner sửa lại khi đã submit nhầm -->
<button name="action_reset_to_draft"
string="Reset to Draft"
type="object"
class="btn-warning"
invisible="state not in ['to_approve', 'rejected'] or not is_owner"/>
<field name="state" widget="statusbar" statusbar_visible="draft,to_approve,approved,done"/>
</header>
<sheet>
<!--
BUTTON BOX: Khu vực chứa các nút thống kê/liên kết
Thường đặt ở góc trên bên phải của sheet
-->
<div name="button_box" class="oe_button_box">
<button name="action_view_rfqs"
type="object"
class="oe_stat_button"
icon="fa-list-alt"
invisible="rfq_count == 0">
<field name="rfq_count" widget="statinfo" string="RFQs"/>
</button>
</div>
<!-- Ribbons trạng thái -->
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" invisible="active"/>
<widget name="web_ribbon" title="Rejected" bg_color="bg-danger" invisible="state != 'rejected'"/>
<div class="oe_title">
<label for="name" string="Request Reference"/>
<h1>
<field name="name" placeholder="e.g. PR/2023/001" readonly="1"/>
</h1>
</div>
<group>
<group>
<field name="employee_id" options="{'no_create': True}"/>
<field name="department_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</group>
<group>
<field name="date_required"/>
<field name="priority" widget="priority"/>
</group>
</group>
<notebook>
<page string="Products" name="products">
<!-- List sản phẩm (One2many) -->
<field name="line_ids"
readonly="state in ['to_approve', 'approved', 'done', 'rejected', 'cancel']">
<list editable="bottom">
<!-- Free Text cho Staff nhập -->
<field name="name" string="Product Name"/>
<field name="product_description" optional="show"/>
<field name="quantity"/>
<field name="uom_name" string="UoM"/>
<field name="estimated_price"/>
<field name="subtotal_estimated" sum="Total"/>
<field name="currency_id" invisible="1"/>
<!-- ================= VENDOR SECTION ================= -->
<!-- 1. VENDOR LIST (User Search) -->
<!-- options="{'no_create': True}": Ngăn User tạo rác dữ liệu -->
<field name="user_vendor_id"
placeholder="Select existing vendor..."
options="{'no_create': True, 'no_create_edit': True}"
widget="many2one"/>
<!-- 2. VENDOR TEXT (User Type)
"user_vendor_id": Tự động điền vào trường text nếu đã chọn được vendor trong danh bạ
"required="not user_vendor_id": Bắt buộc nhập nếu chưa chọn ID.
-->
<field name="suggested_vendor_name"
placeholder="...or type new vendor name"
required="not user_vendor_id"/>
<!-- 3. FINAL VENDOR (Purchasing Only)
"groups="...": Chỉ hiện cột này cho Purchasing Officer.
User thường sẽ hoàn toàn không thấy cột này.
"optional="show": Cho phép ẩn/hiện trong menu 3 chấm nếu cần.
-->
<field name="final_vendor_id"
groups="epr.group_epr_purchasing_officer"
widget="many2one"
optional="show"/>
<!-- Product ID ẩn, dành cho Purchasing map sau này -->
<field name="product_id" optional="hide" groups="epr.group_epr_purchasing_officer"/>
</list>
</field>
<group class="oe_subtotal_footer oe_right">
<field name="estimated_total" widget="monetary"/>
</group>
</page>
<page string="Other Information" name="other_info">
<group>
<group string="Approvals">
<field name="date_submitted" readonly="1"/>
<field name="date_approved" readonly="1"/>
<field name="approved_by_id" readonly="1"/>
<field name="approver_ids" widget="many2many_tags" readonly="1"/>
</group>
</group>
<group>
<group string="Rejection" invisible="not date_rejected">
<field name="date_rejected" readonly="1"/>
<field name="rejected_by_id" readonly="1"/>
<field name="rejection_reason"
readonly="1"
class="text-danger"/>
</group>
</group>
</page>
</notebook>
</sheet>
<!-- Chatter -->
<chatter reload_on_post="True"/>
</form>
</field>
</record>
<!--
===================================================================
4. WINDOW ACTION
Định nghĩa hành động mở view (Được gọi bởi menu)
===================================================================
-->
<record id="action_epr_purchase_request" model="ir.actions.act_window">
<field name="name">Purchase Requests</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">epr.purchase.request</field>
<field name="view_mode">kanban,list,form,search</field>
<field name="search_view_id" ref="view_epr_purchase_request_search"/>
<field name="context">{'search_default_my_requests': 1}</field>
<!-- <field name="context">{'search_default_my_requests': 1, 'search_default_to_approve_by_me': 1}</field> -->
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first Purchase Request!
</p>
</field>
</record>
<!-- Action cho Manager: Chỉ hiện PR cần duyệt -->
<record id="action_epr_purchase_request_to_approve" model="ir.actions.act_window">
<field name="name">To Approve</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">epr.purchase.request</field>
<field name="view_mode">kanban,list,form,search</field>
<field name="search_view_id" ref="view_epr_purchase_request_search"/>
<field name="context">{'search_default_to_approve_by_me': 1, 'search_default_to_approve': 1}</field>
<field name="domain">[('state', '=', 'to_approve')]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No requests waiting for your approval!
</p>
</field>
</record>
</data>
</odoo>