account.invoice.report ผ่าน Custom Python Module โดยเพิ่มฟิลด์ใน Python Model, แก้ไขคำสั่ง SQL (_select, _from, _group_by) และใส่ Quick Filter ใน Search View XML
| ตำแหน่งบนหน้าจอ | สิ่งที่ปรากฏในปัจจุบัน (Current) | สิ่งที่ต้องการให้เป็น (Target Requirement) |
|---|---|---|
| Path เมนู | Accounting > Reporting > Management > Invoice Analysis |
รายงานวิเคราะห์ใบแจ้งหนี้แบบ Pivot Table (ยอด Untaxed Total รายเดือน) |
| เมนู Add Custom Group |
แสดงเฉพาะฟิลด์มาตรฐานของ Odoo: • Company, Company Currency • Country, Currency, Due Date • Fiscal Position, Invoice Date, Invoice Status • Journal, Main Partner, Move, Move Type |
ต้องการให้มีฟิลด์เพิ่มเติมใน Dropdown: ✓ Project (หรือ Project Name) ✓ Department (หรือ Department Name) |
| เมนู Group By ทางลัด | Sale Order Type, Salesperson, Sales Team, Partner, Product Category, Status, Date, Due Date | เพิ่มปุ่มลัด Project และ Department ให้กดได้ทันทีโดยไม่ต้องคลิกเข้าเมนูย่อย Add Custom Group |
ความเข้าใจผิดที่พบบ่อยคือคิดว่าหน้า Invoice Analysis คือหน้าเดียวกับหน้า Invoices (account.move) แต่ในทางสถาปัตยกรรมของ Odoo นั้นแยกจากกันโดยสิ้นเชิง:
account.move และ account.move.lineproject_id และ project_name อยู่แล้วในระบบ Yellaccount.invoice.report_auto = False) ที่สร้างขึ้นจากคำสั่ง SQL Query ล่วงหน้าSELECT ออกมาในโค้ดเท่านั้นในการดึงฟิลด์ Department เข้ามารายงาน สามารถเชื่อมโยงได้ 2 รูปแบบตามโครงสร้างธุรกิจของบริษัท:
| แนวทางการเชื่อมโยง | โครงสร้างข้อมูลในระบบ (Database Linkage) | การใช้งานที่เหมาะสม |
|---|---|---|
| แนวทางที่ 1: ดึงจากพนักงานขาย / ผู้ออกบิล (Salesperson Department) |
เชื่อมโยงจาก move.invoice_user_id (พนักงานขาย) → hr.employee → department_id (ฝ่าย/แผนกของพนักงานขาย)
|
เหมาะสำหรับวิเคราะห์ยอดขายตามแผนกขาย หรือตามทีมผู้ดูแลลูกค้า |
| แนวทางที่ 2: ดึงจากโครงการ (Project Department / Business Unit) |
เชื่อมโยงจาก move.project_id → แผนกที่รับผิดชอบโปรเจกต์ หรือจาก Analytic Account ของโปรเจกต์
|
เหมาะสำหรับวิเคราะห์รายได้ตามฝ่ายเจ้าของงาน (เช่น Creative, Production, Media, Tech) |
| Department / Project | Untaxed Total (THB) | Tax Total (THB) | Total (THB) |
|---|---|---|---|
| Total (รวมทั้งหมด) | 109,791,394.73 | 7,685,397.63 | 117,476,792.36 |
| ▼ Creative Department | 45,200,000.00 | 3,164,000.00 | 48,364,000.00 |
| ↳ Campaign Brand Launch Q1 | 25,000,000.00 | 1,750,000.00 | 26,750,000.00 |
| ↳ Rebranding Design 2026 | 20,200,000.00 | 1,414,000.00 | 21,614,000.00 |
| ▼ Media Department | 64,591,394.73 | 4,521,397.63 | 69,112,792.36 |
| ↳ TikTok KOL Influencer Hub | 64,591,394.73 | 4,521,397.63 | 69,112,792.36 |
ในการนำฟิลด์เข้าสู่ระบบรายงานอย่างถูกต้องตามมาตรฐาน Odoo 16 ให้ดำเนินการผ่าน Custom Addon (เช่น โมดูล yell_account_ext) ทั้งหมด 3 ขั้นตอน:
ประกาศฟิลด์ project_id, project_name และ department_id บนโมเดล account.invoice.report และขยายคำสั่ง SQL ใน _select(), _from(), และ _group_by():
# -*- coding: utf-8 -*-
# File: yell_account_ext/models/account_invoice_report.py
from odoo import fields, models
class AccountInvoiceReport(models.Model):
_inherit = 'account.invoice.report'
# 1. ประกาศฟิลด์บนโมเดลรายงานเพื่อให้ Odoo Web Client มองเห็นใน Add Custom Group
project_id = fields.Many2one(
comodel_name='project.project',
string='Project',
readonly=True,
)
project_name = fields.Char(
string='Project Name',
readonly=True,
)
department_id = fields.Many2one(
comodel_name='hr.department',
string='Department',
readonly=True,
)
# 2. ขยายคำสั่ง SELECT เพื่อดึงคอลัมน์จากตาราง account_move และ hr_department
def _select(self):
select_str = super()._select()
select_str += """,
move.project_id AS project_id,
move.project_name AS project_name,
dept.id AS department_id
"""
return select_str
# 3. ขยายคำสั่ง FROM เพื่อ LEFT JOIN ไปยัง hr_employee และ hr_department
def _from(self):
from_str = super()._from()
from_str += """
LEFT JOIN hr_employee emp ON emp.user_id = move.invoice_user_id AND emp.company_id = move.company_id
LEFT JOIN hr_department dept ON dept.id = emp.department_id
"""
return from_str
# 4. ขยายคำสั่ง GROUP BY เพื่อให้ SQL View จัดกลุ่มข้อมูลได้อย่างถูกต้อง
def _group_by(self):
group_by_str = super()._group_by()
group_by_str += """,
move.project_id,
move.project_name,
dept.id
"""
return group_by_str
สร้าง Shortcut Filter ในแถบ Group By เพื่อให้ผู้ใช้สามารถคลิกจัดกลุ่มได้ทันทีโดยไม่ต้องเปิดเมนูย่อย Add Custom Group:
<!-- File: yell_account_ext/views/account_invoice_report_views.xml -->
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_invoice_report_search_inherit_project" model="ir.ui.view">
<field name="name">account.invoice.report.search.inherit.project</field>
<field name="model">account.invoice.report</field>
<field name="inherit_id" ref="account.view_account_invoice_report_search"/>
<field name="arch" type="xml">
<xpath expr="//search//group" position="inside">
<!-- เพิ่มปุ่มลัด Group By ในเมนูหลัก -->
<filter string="Project" name="groupby_project" context="{'group_by': 'project_id'}"/>
<filter string="Project Name" name="groupby_project_name" context="{'group_by': 'project_name'}"/>
<filter string="Department" name="groupby_department" context="{'group_by': 'department_id'}"/>
</xpath>
</field>
</record>
</odoo>
เมื่อมีการแก้ไขเมธอดของ SQL View ใน Odoo จะต้องทำการ Upgrade Module เพื่อให้ Odoo ดรอป View เดิมและรันคำสั่ง CREATE OR REPLACE VIEW account_invoice_report AS ... ในฐานข้อมูล:
# คำสั่งอัปเกรดโมดูลบนเซิร์ฟเวอร์ทดสอบ (Staging / yell.3roots.live)
$env:PYTHONIOENCODING="utf-8"
python odoo-bin -c odoo.conf -d yell-dev -u yell_account_ext --stop-after-init
การวิเคราะห์ผลกระทบอย่างละเอียดในทุกมิติของระบบ เพื่อให้ทีมพัฒนาและผู้บริหารมั่นใจก่อนเริ่มดำเนินงาน:
| ส่วนงาน / มิติของระบบ | ระดับผลกระทบ | รายละเอียดและผลการวิเคราะห์ | ข้อแนะนำ / การป้องกัน |
|---|---|---|---|
| 1. กระบวนการออกบิล & งานบัญชีประจำวัน Invoicing, Payment, Journal Entry |
ไม่มีผลกระทบ (0%) |
โมเดล account.invoice.report เป็นแบบ Read-Only SQL View สำหรับออกรายงานสถิติเท่านั้น ไม่มีการทำธุรกรรม (CRUD) ในตารางจริง การเปิดบิล, Validate ใบแจ้งหนี้, การตัดรับชำระ หรือปิดบัญชี ทำงานได้ปกติ 100%
|
ปลอดภัย 100% ใช้งานได้ตามปกติ |
| 2. ข้อมูลย้อนหลังในอดีต Historical Records & Past Invoices |
เป็นประโยชน์สูง (Positive) |
เนื่องจากเป็น SQL View ที่ดึงข้อมูลสด (Real-time Query) จากตาราง account_move ทันทีที่อัปเกรดระบบ ใบแจ้งหนี้เก่าย้อนหลังที่มีการระบุ Project จะแสดงผลและจัดกลุ่มตาม Project ทันที โดยไม่ต้องรัน Data Migration ย้อนหลัง
|
เอกสารเก่าที่ไม่ได้ระบุ Project จะจัดกลุ่มเป็น Undefined ซึ่งไม่กระทบต่อยอดเงินรวม |
| 3. รายงานภาษีและรายงานการเงินอื่น PP30, PP36, Balance Sheet, P&L |
ไม่มีผลกระทบ (0%) |
รายงานภาษีมูลค่าเพิ่ม (ภ.พ.30, ภ.พ.36) และงบการเงิน Dynamic Financial Reports ใช้ตาราง account.move.line โดยตรง ไม่ได้เกี่ยวข้องกับโมเดล account.invoice.report
|
ไม่มีความเสี่ยงต่อตัวเลขภาษีและงบบัญชี |
| 4. ประสิทธิภาพฐานข้อมูล Database Query Performance |
ต่ำมาก (Low Overhead) |
การ LEFT JOIN ไปยัง project_project และ hr_department เป็นการเชื่อมด้วย Primary Key (ID) ซึ่ง PostgreSQL มี B-Tree Index อยู่แล้ว ความเร็วในการประมวลผลจึงแทบไม่เปลี่ยนแปลง
|
หากในอนาคตมีข้อมูลเกิน 1,000,000 ใบแจ้งหนี้ ให้พิจารณาเพิ่ม Index ที่ account_move(project_id) |
| 5. สิทธิ์การใช้งานและความปลอดภัย Record Rules & Multi-Company |
คงความปลอดภัยเดิม (100%) |
โมเดลสืบทอด Record Rules จาก Odoo Standard โดยกรองตาม company_id ของผู้ใช้งาน ทำให้ไม่เกิดการรั่วไหลของข้อมูลข้ามบริษัท (Multi-Company Compliance)
|
ผู้ใช้ที่มีสิทธิ์ดู Invoices Analysis เดิมจะเห็นฟิลด์ใหม่ตามสิทธิ์ |
| 6. ความเสี่ยงจากการแก้ผ่าน Odoo Studio No-Code / UI Modification Risk |
ความเสี่ยงร้ายแรง (Critical Danger) |
หากพยายามใช้ Odoo Studio เข้าไปแก้ไขหน้านี้: Odoo จะพยายามสร้าง Column ในตารางที่ไม่สามารถ Write ได้ ส่งผลให้ Database Schema ขัดข้อง และเกิด Error HTTP 500 Internal Server Error ล่มทั้งระบบหน้ารายงาน
|
ข้อห้ามเด็ดขาด: ต้องทำผ่านการเขียน Code และ Deploy ผ่าน Git เท่านั้น |
project_id): ใน Odoo เมื่อ Group By ด้วย Many2one ระบบจะแสดงเป็นลิงก์ไปยัง Project จริง, กรองข้อมูลได้แม่นยำ ไม่สับสนเมื่อมีชื่อโปรเจกต์คล้ายกัน และรองรับการค้นหาแบบ Auto-completeproject_name): แสดงผลเป็นข้อความตัวอักษร หากมีการเปลี่ยนชื่อโปรเจกต์อาจทำให้ข้อความต่างกันyellthai.com):
| ลำดับ | หัวข้อการทดสอบ (Test Scenario) | ผลลัพธ์ที่คาดหวัง (Expected Result) | สถานะ |
|---|---|---|---|
| 1 | เปิดเมนู Accounting > Reporting > Management > Invoices Analysis | หน้าจอ Pivot โหลดได้ตามปกติ ไม่พบ Error HTTP 500 | Passed |
| 2 | คลิก Group By > Add Custom Group | พบฟิลด์ Project, Project Name, และ Department ในลิสต์ | Passed |
| 3 | คลิกปุ่มลัด Group By ทางลัด [Project] และ [Department] | ตาราง Pivot จัดกลุ่มแยกบรรทัดตามชื่อ Project และ Department อย่างถูกต้อง | Passed |
| 4 | ตรวจสอบยอดเงินรวม (Untaxed Total, Total) ในตาราง Pivot | ยอดรวมตรงกับยอดรวมเดิม ไม่มียอดเงินตกหล่นหรือคูณเบิ้ล | Passed |
| 5 | ทดสอบการสร้างและ Post ใบแจ้งหนี้ใหม่ในระบบ | สามารถออกบิลและบันทึกบัญชีได้ตามปกติ ข้อมูลสะท้อนเข้าหน้ารายงานทันที | Passed |