Thai Localization - Government Purchase Request

l10n_th_gov_purchase_request
REPOSITORY
REPOSITORYOCA/l10n-thailand
GIT
GIThttps://github.com/OCA/l10n-thailand.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/l10n-thailand/tree/18.0/l10n_th_gov_purchase_request
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYLocalization / Purchase
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Ecosoft
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Ecosoft
COMMITTERS
COMMITTERSOCA-git-bot, oca-ci, Saran440, ROBBYHOOD9
WEBSITE
WEBSITEhttps://github.com/OCA/l10n-thailand
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:30:13
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/purchase-workflow:
    - purchase_exception
    - purchase_request_exception
    - purchase_request
    - purchase_request_substate
    - purchase_request_to_requisition
OCA/server-tools:
    - base_exception
OCA/server-ux:
    - base_substate
odoo/odoo:
    - hr
    - base_setup
    - base
    - web
    - digest
    - mail
    - bus
    - web_tour
    - html_editor
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - resource
    - phone_validation
    - resource_mail
    - purchase
    - account
    - onboarding
    - product
    - uom
    - analytic
    - purchase_stock
    - stock_account
    - stock
    - barcodes_gs1_nomenclature
    - barcodes
    - purchase_requisition_stock
    - purchase_requisition
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module add following features to Purchase Request document, aimed
to follow Thai Government procurement regulation.

1.  Add following data to Purchase Request  
    1.1 Procurement Type (ประเภท)  
    - ซื้อ/จ้าง/เช่า
    - จ้างที่ปรึกษา
    - จ้างออกแบบและควบคุมงานก่อสร้าง

    1.2 Purchase Type (ระเบียบ)  
    - จัดซื้อจัดจ้างผ่านพัสดุ
    - วงเงินเล็กน้อย
    - 79 วรรคสอง
    - ว.119

    1.3 Procurement Method (วิธีการ)  
    - เฉพาะเจาะจง
    - E-bidding
    - คัดเลือก
    - ประกาศเชิญชวนทั่วไป

2.  Use substate to add an extra step, to verify document before approved  
    The states of normal purchase request is,  
    - Draft \> To Approve \> Approved

    Now changed to  
    - Draft \> To Approve (To Verify) \> To Approve (Verified) \>
      Approved

3.  Add/edit following user fields to keep tracks of who is doing what  
    - Prepared By (create_uid)
    - Requested By (requested_by)
    - Purchase Representative (assigned_to)
    - Verified By (verified_by)
    - Approved By (approved_by)

4.  Add new Committee tab for both Procurement (คณะกรรมการจัดซื้อจัดจ้าง) and
    Work Acceptance (คณะกรรมการตรวจรับ)

5.  Check exception logics when sent to approve  
    The exception logic are based on Thai Government procurement regulation year 2560, in summary,  
    - จ้างที่ปรึกษา ต้องมีกรรมการจัดซื้อจัดจ้างอย่างน้อย 5 คน และกรรมการตรวจรับอย่างน้อย
      5 คน
    - จ้างออกแบบและควบคุมงานก่อสร้าง ต้องมีกรรมการจัดซื้อจัดจ้างอย่างน้อย 3 คน
      และกรรมการตรวจรับอย่างน้อย 3 คน
    - ระเบียบ วงเงินเล็กน้อย ไม่เกิน 100,000 บาท และต้องมีกรรมการตรวจรับอย่างน้อย 1
      คน
    - ระเบียบ 79 วรรคสอง ไม่เกิน 500,000 บาท
    - ระเบียบ ว.119 ไม่เกิน 10,000 บาท และต้องมีกรรมการตรวจรับอย่างน้อย 1 คน
    - ซื้อ/จ้าง/เช่า ไม่เกิน 500,000 บาท ต้องมีกรรมการตรวจรับอย่างน้อย 3 คน
    - ซื้อ/จ้าง/เช่า เกิน 500,000 บาท ต้องมีกรรมการจัดซื้อจัดจ้างอย่างน้อย 3 คน
      และกรรมการตรวจรับอย่างน้อย 3 คน

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
procurement_method_view_form procurement.method.view.form procurement.method form New
procurement_method_view_list procurement.method.view.list procurement.method list New
procurement_type_view_form procurement.type.view.form procurement.type form New
procurement_type_view_list procurement.type.view.list procurement.type list New
purchase_type_view_form purchase.type.view.form purchase.type form New
purchase_type_view_list purchase.type.view.list purchase.type list New
view_purchase_request_form purchase.request.form purchase.request button Inherits purchase_request.view_purchase_request_form
view_purchase_request_form_substate view.purchase.request.form.substate purchase.request field Inherits purchase_request_substate.view_purchase_request_form
view_purchase_request_search purchase.request.list.select purchase.request field Inherits purchase_request.view_purchase_request_search
view_purchase_request_tree purchase.request.tree purchase.request field Inherits purchase_request.view_purchase_request_tree
Models touched (7)

New fields (9)
  • approve_role Selection
    required=True selection=[('chairman', 'Chairman'), ('committee', 'Committee')] string='Role'
  • committee_type Selection
    selection=[('procurement', 'Procurement Committee'), ('work_acceptance', 'Work Acceptance Committee')]
  • department_id Many2one
    related='employee_id.department_id'
  • email Char
    related='employee_id.work_email'
  • employee_id Many2one → hr.employee
    comodel_name='hr.employee' index=True ondelete='restrict' string='Employee'
  • name Char
    compute='_compute_default_name' index=True readonly=False required=True store=True string='Committee Name'
  • note Text
  • phone Char
    related='employee_id.work_phone'
  • request_id Many2one → purchase.request
    comodel_name='purchase.request' index=True ondelete='cascade' string='Purchase Request'
Public methods (0)

No public methods.

New fields (4)
  • active Boolean
    default=True
  • description Text
    translate=True
  • name Char
    required=True
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (5)
  • active Boolean
    default=True
  • description Text
    translate=True
  • name Char
    required=True
  • product_id Many2one → product.product
    comodel_name='product.product' help='Default product for purchase request line'
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (14)
  • approved_by Many2one → res.users
    comodel_name='res.users' copy=False index=True tracking=True
  • assigned_to Many2one
    copy=False string='Purchase Representative'
  • date_approved Date
    copy=False string='Approved Date'
  • date_verified Date
    copy=False string='Verified Date'
  • expense_reason Text
    string='Reason'
  • procurement_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'procurement')] inverse_name='request_id' string='Procurement Committees'
  • procurement_method_id Many2one → procurement.method
    comodel_name='procurement.method' index=True ondelete='restrict' string='Procurement Method'
  • procurement_method_ids Many2many
    related='purchase_type_id.procurement_method_ids'
  • procurement_type_id Many2one → procurement.type
    comodel_name='procurement.type' index=True ondelete='restrict' string='Procurement Type'
  • purchase_type_id Many2one → purchase.type
    comodel_name='purchase.type' default=<expr> domain=<expr> index=True ondelete='restrict' string='Purchase Type'
  • substate_sequence Integer
    related='substate_id.sequence'
  • to_create Selection
    related='purchase_type_id.to_create'
  • verified_by Many2one → res.users
    comodel_name='res.users' copy=False index=True tracking=True
  • work_acceptance_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'work_acceptance')] inverse_name='request_id' string='Work Acceptance Committees'
Public methods (5)
  • action_to_substate(self)
  • button_approved(self)
  • button_draft(self)
    Allows the Procurement to reset documents after procurement approved by procurement only.
  • button_rejected(self)
    Allows the Procurement to reject documents after procurement approved by procurement only.
  • get_estimated_cost_currency(self, date=False)
    Get estimated cost with currency

New fields (1)
  • product_id Many2one
    compute='_compute_default_product_id' readonly=False store=True
Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (1)
  • default_get(self, fields)
    @api.model
    Do not allow, if, - Some PRs is not approved - Some PRs' purchase type is not for Purchase Agreement

New fields (10)
  • active Boolean
    default=True
  • code Char
  • description Text
    translate=True
  • is_default Boolean
    help='Default purchase type on the purchase request' string='Default'
  • name Char
    required=True
  • procurement_method_ids Many2many → procurement.method
    comodel_name='procurement.method' help='This field will help to filter procurement method in each purchase type on the purchase request' required=True string='Allowed Procurement Method'
  • sequence Integer
    default=10
  • to_create Selection
    help='Create purchase agreement or expense when pr is approved' required=True selection=[('purchase_agreement', 'Purchase Agreement'), ('expense', 'Expense')]
  • visible_on_expense Boolean
    string='Expense'
  • visible_on_purchase_request Boolean
    string='Purchase Request'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/l10n-thailand
GIT
GIThttps://github.com/OCA/l10n-thailand.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/l10n-thailand/tree/15.0/l10n_th_gov_purchase_request
VERSION
VERSION 2.0.1
CATEGORY
CATEGORYLocalization / Purchase
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Ecosoft
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Ecosoft
COMMITTERS
COMMITTERSOCA-git-bot, oca-ci, Saran440
WEBSITE
WEBSITEhttps://github.com/OCA/l10n-thailand
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:46:35
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/purchase-workflow:
    - purchase_exception
    - purchase_request_exception
    - purchase_request
    - purchase_request_substate
    - purchase_request_to_requisition
OCA/server-tools:
    - base_exception
OCA/server-ux:
    - base_substate
odoo/odoo:
    - hr
    - base_setup
    - base
    - web
    - mail
    - bus
    - web_tour
    - resource
    - purchase
    - account
    - product
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - purchase_stock
    - stock_account
    - stock
    - barcodes
    - purchase_requisition_stock
    - purchase_requisition
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
procurement_method_view_form procurement.method.view.form procurement.method form New
procurement_method_view_tree procurement.method.view.tree procurement.method tree New
procurement_type_view_form procurement.type.view.form procurement.type form New
procurement_type_view_tree procurement.type.view.tree procurement.type tree New
purchase_type_view_form purchase.type.view.form purchase.type form New
purchase_type_view_tree purchase.type.view.tree purchase.type tree New
view_purchase_request_form purchase.request.form purchase.request button Inherits purchase_request.view_purchase_request_form
view_purchase_request_form_substate view.purchase.request.form.substate purchase.request field Inherits purchase_request_substate.view_purchase_request_form
view_purchase_request_search purchase.request.list.select purchase.request field Inherits purchase_request.view_purchase_request_search
view_purchase_request_tree purchase.request.tree purchase.request field Inherits purchase_request.view_purchase_request_tree
Models touched (7)

New fields (9)
  • approve_role Selection
    required=True selection=[('chairman', 'Chairman'), ('committee', 'Committee')] string='Role'
  • committee_type Selection
    selection=[('procurement', 'Procurement Committee'), ('work_acceptance', 'Work Acceptance Committee')]
  • department_id Many2one
    related='employee_id.department_id'
  • email Char
    related='employee_id.work_email'
  • employee_id Many2one → hr.employee
    comodel_name='hr.employee' index=True ondelete='restrict' string='Employee'
  • name Char
    compute='_compute_default_name' index=True readonly=False required=True store=True string='Committee Name'
  • note Text
  • phone Char
    related='employee_id.work_phone'
  • request_id Many2one → purchase.request
    comodel_name='purchase.request' index=True ondelete='cascade' string='Purchase Request'
Public methods (0)

No public methods.

New fields (4)
  • active Boolean
    default=True
  • description Text
    translate=True
  • name Char
    required=True
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (5)
  • active Boolean
    default=True
  • description Text
    translate=True
  • name Char
    required=True
  • product_id Many2one → product.product
    comodel_name='product.product' help='Default product for purchase request line'
  • sequence Integer
    default=10
Public methods (0)

No public methods.

New fields (14)
  • approved_by Many2one → res.users
    comodel_name='res.users' copy=False index=True tracking=True
  • assigned_to Many2one
    copy=False string='Purchase Representative'
  • date_approved Date
    copy=False string='Approved Date'
  • date_verified Date
    copy=False string='Verified Date'
  • expense_reason Text
    string='Reason'
  • procurement_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'procurement')] inverse_name='request_id' string='Procurement Committees'
  • procurement_method_id Many2one → procurement.method
    comodel_name='procurement.method' index=True ondelete='restrict' string='Procurement Method'
  • procurement_method_ids Many2many
    related='purchase_type_id.procurement_method_ids'
  • procurement_type_id Many2one → procurement.type
    comodel_name='procurement.type' index=True ondelete='restrict' string='Procurement Type'
  • purchase_type_id Many2one → purchase.type
    comodel_name='purchase.type' default=<expr> domain=<expr> index=True ondelete='restrict' string='Purchase Type'
  • substate_sequence Integer
    related='substate_id.sequence'
  • to_create Selection
    related='purchase_type_id.to_create'
  • verified_by Many2one → res.users
    comodel_name='res.users' copy=False index=True tracking=True
  • work_acceptance_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'work_acceptance')] inverse_name='request_id' string='Work Acceptance Committees'
Public methods (5)
  • action_to_substate(self)
  • button_approved(self)
  • button_draft(self)
    Allows the Procurement to reset documents after procurement approved by procurement only.
  • button_rejected(self)
    Allows the Procurement to reject documents after procurement approved by procurement only.
  • get_estimated_cost_currency(self, date=False)
    Get estimated cost with currency

New fields (1)
  • product_id Many2one
    compute='_compute_default_product_id' readonly=False store=True
Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (1)
  • view_init(self, fields)
    @api.model
    Do not allow, if, - Some PRs is not approved - Some PRs' purchase type is not for Purchase Agreement

New fields (10)
  • active Boolean
    default=True
  • code Char
  • description Text
    translate=True
  • is_default Boolean
    default=False help='Default purchase type on the purchase request' string='Default'
  • name Char
    required=True
  • procurement_method_ids Many2many → procurement.method
    comodel_name='procurement.method' help='This field will help to filter procurement method in each purchase type on the purchase request' required=True string='Allowed Procurement Method'
  • sequence Integer
    default=10
  • to_create Selection
    help='Create purchase agreement or expense when pr is approved' required=True selection=[('purchase_agreement', 'Purchase Agreement'), ('expense', 'Expense')]
  • visible_on_expense Boolean
    string='Expense'
  • visible_on_purchase_request Boolean
    string='Purchase Request'
Public methods (0)

No public methods.

REPOSITORY
REPOSITORYOCA/l10n-thailand
GIT
GIThttps://github.com/OCA/l10n-thailand.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/l10n-thailand/tree/14.0/l10n_th_gov_purchase_request
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYLocalization / Purchase
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Ecosoft
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Ecosoft
COMMITTERS
COMMITTERSoca-travis, OCA-git-bot, Saran440
WEBSITE
WEBSITEhttps://github.com/OCA/l10n-thailand
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:41:02
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/purchase-workflow:
    - purchase_request_exception
    - purchase_request
    - purchase_request_substate
    - purchase_request_to_requisition
OCA/server-tools:
    - base_exception
OCA/server-ux:
    - base_substate
odoo/odoo:
    - hr
    - base_setup
    - base
    - web
    - mail
    - bus
    - web_tour
    - resource
    - purchase
    - account
    - product
    - uom
    - analytic
    - portal
    - web_editor
    - http_routing
    - auth_signup
    - digest
    - purchase_stock
    - stock_account
    - stock
    - barcodes
    - purchase_requisition
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (10)
XML IDNameModelTypeStatus
procurement_method_view_form procurement.method.view.form procurement.method form New
procurement_method_view_tree procurement.method.view.tree procurement.method tree New
procurement_type_view_form procurement.type.view.form procurement.type form New
procurement_type_view_tree procurement.type.view.tree procurement.type tree New
purchase_type_view_form purchase.type.view.form purchase.type form New
purchase_type_view_tree purchase.type.view.tree purchase.type tree New
view_purchase_request_form purchase.request.form purchase.request field Inherits purchase_request.view_purchase_request_form
view_purchase_request_form_substate view.purchase.request.form.substate purchase.request field Inherits purchase_request_substate.view_purchase_request_form
view_purchase_request_search purchase.request.list.select purchase.request field Inherits purchase_request.view_purchase_request_search
view_purchase_request_tree purchase.request.tree purchase.request field Inherits purchase_request.view_purchase_request_tree
Models touched (7)

New fields (9)
  • approve_role Selection
    required=True selection=[('chairman', 'Chairman'), ('committee', 'Committee')] string='Role'
  • committee_type Selection
    selection=[('procurement', 'Procurement Committee'), ('work_acceptance', 'Work Acceptance Committee')] string='Committee Type'
  • department_id Many2one
    related='employee_id.department_id'
  • email Char
    related='employee_id.work_email'
  • employee_id Many2one → hr.employee
    comodel_name='hr.employee' index=True ondelete='restrict' required=True string='Employee'
  • name Char
    related='employee_id.name'
  • note Text
    string='Note'
  • phone Char
    related='employee_id.work_phone'
  • request_id Many2one → purchase.request
    comodel_name='purchase.request' index=True ondelete='cascade' string='Purchase Request'
Public methods (0)

No public methods.

New fields (4)
  • active Boolean
    default=True string='Active'
  • description Text
    string='Description' translate=True
  • name Char
    required=True string='Name'
  • sequence Integer
    default=10 string='Sequence'
Public methods (0)

No public methods.

New fields (5)
  • active Boolean
    default=True string='Active'
  • description Text
    string='Description' translate=True
  • name Char
    required=True string='Name'
  • product_id Many2one → product.product
    comodel_name='product.product' help='Default product for purchase request line'
  • sequence Integer
    default=10 string='Sequence'
Public methods (0)

No public methods.

New fields (14)
  • approved_by Many2one → res.users
    comodel_name='res.users' copy=False index=True string='Approved By' tracking=True
  • assigned_to Many2one
    copy=False string='Purchase Representative'
  • date_approved Date
    copy=False string='Approved Date'
  • date_verified Date
    copy=False string='Verified Date'
  • expense_reason Text
    string='Reason'
  • procurement_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'procurement')] inverse_name='request_id' string='Procurement Committees'
  • procurement_method_id Many2one → procurement.method
    comodel_name='procurement.method' index=True ondelete='restrict' string='Procurement Method'
  • procurement_method_ids Many2many
    related='purchase_type_id.procurement_method_ids'
  • procurement_type_id Many2one → procurement.type
    comodel_name='procurement.type' index=True ondelete='restrict' string='Procurement Type'
  • purchase_type_id Many2one → purchase.type
    comodel_name='purchase.type' default=<expr> index=True ondelete='restrict' string='Purchase Type'
  • substate_sequence Integer
    related='substate_id.sequence'
  • to_create Selection
    related='purchase_type_id.to_create'
  • verified_by Many2one → res.users
    comodel_name='res.users' copy=False index=True string='Verified By' tracking=True
  • work_acceptance_committee_ids One2many → procurement.committee
    comodel_name='procurement.committee' copy=True domain=[('committee_type', '=', 'work_acceptance')] inverse_name='request_id' string='Work Acceptance Committees'
Public methods (2)
  • action_to_substate(self)
  • button_approved(self)

New fields (1)
  • product_id Many2one
    compute='_compute_default_product_id' readonly=False store=True
Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (1)
  • view_init(self, fields)
    @api.model
    Do not allow, if, - Some PRs is not approved - Some PRs' purchase type is not for Purchase Agreement

New fields (7)
  • active Boolean
    default=True string='Active'
  • description Text
    string='Description' translate=True
  • is_default Boolean
    default=False help='Default purchase type on the purchase request' string='Default'
  • name Char
    required=True string='Name'
  • procurement_method_ids Many2many → procurement.method
    comodel_name='procurement.method' help='This field will help to filter procurement method in each purchase type on the purchase request' required=True string='Allowed Procurement Method'
  • sequence Integer
    default=10 string='Sequence'
  • to_create Selection
    help='Create purchase agreement or expense when pr is approved' required=True selection=[('purchase_agreement', 'Purchase Agreement'), ('expense', 'Expense')] string='To Create'
Public methods (0)

No public methods.