370 lines
21 KiB
XML
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> |