first commit

This commit is contained in:
Suherdy Yacob 2026-05-21 09:41:38 +07:00
commit 6801b2e86c
8 changed files with 212 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
# Editors and IDEs
.idea/
.vscode/
*.swp
*.swo
.DS_Store

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# POS Edit Payment Method
An Odoo 19 module designed to allow **POS Managers** to safely edit the payment method used in Point of Sale Orders under specific conditions, while maintaining strict control over other financial fields (like nominal amount and date).
## Key Features
- 👤 **Access Limitation**: Only users belonging to the **POS Manager** group (`point_of_sale.group_pos_manager`) can edit the payment method. For standard POS Users, the fields remain completely read-only.
- 🔒 **Dynamic Checks & Rules**: Editing is only permitted if:
- The POS Session of the order is **not closed**.
- The POS Order state is **not Done or Cancelled**.
- The order **does not have a posted invoice**.
- None of the payments in the order have a **posted accounting journal entry**.
- 🚫 **Tamper Prevention**: Other financial fields, such as **amount (nominal)**, **date**, and **card details** are strictly read-only and cannot be manually edited. Standard creation or deletion of payments from the backend list view is disabled (`create="0"` and `delete="0"`).
- 💬 **Odoo Chatter Integration**: Seamlessly leverages Odoo's native change tracking system to automatically log changes in the POS Order chatter (e.g., *• BCA changed to BTN for Rp 17,001.00*).
## Installation
1. Place the `pos_edit_payment_method` directory inside your custom Odoo addons path.
2. Restart your Odoo server instance.
3. Log in to your Odoo database as an Administrator.
4. Activate **Developer Mode** (Settings -> Activate the developer mode).
5. Navigate to the **Apps** menu and click **Update Apps List**.
6. Search for `POS Edit Payment Method` in the Apps search bar.
7. Click **Activate** (or **Install**).
## Usage Guide
1. Go to **Point of Sale** -> **Orders** -> **Orders**.
2. Open a POS order whose session is still in progress (not closed).
3. Under the **Payments** tab, double-click or inline-edit the **Payment Method** field. Note that other fields (like **Amount** and **Date**) are strictly locked.
4. Save the order.
5. Review the **Chatter** on the right to see the automatic change log entry.
6. Test using an order whose session has already been closed. Verify that the payment method field is locked and cannot be edited.
7. Test logging in as a standard POS User (non-manager). Verify that all fields in the payments tab are locked.
## Technical Details
- **Dependencies**: `point_of_sale`
- **Category**: `Point of Sale`
- **License**: `LGPL-3`
- **Author**: `Suherdy Yacob`
- **Version**: `19.0.1.0.0`

1
__init__.py Normal file
View File

@ -0,0 +1 @@
from . import models

19
__manifest__.py Normal file
View File

@ -0,0 +1,19 @@
{
'name': 'POS Edit Payment Method',
'version': '19.0.1.0.0',
'category': 'Point of Sale',
'summary': 'Allows editing the payment method of a POS Order under specific conditions',
'description': """
This module allows users to edit the payment method used in POS orders:
- Only editable if the POS session is not closed and the accounting journal/entry is not posted.
- Other payment fields (like amount/nominal) remain strictly read-only.
- Log payment method changes in the POS Order's chatter.
""",
'author': 'Suherdy Yacob',
'depends': ['point_of_sale'],
'data': [
'views/pos_order_views.xml',
],
'installable': True,
'license': 'LGPL-3',
}

2
models/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from . import pos_order
from . import pos_payment

33
models/pos_order.py Normal file
View File

@ -0,0 +1,33 @@
from odoo import models, fields, api
class PosOrder(models.Model):
_inherit = 'pos.order'
is_payment_editable = fields.Boolean(
compute='_compute_is_payment_editable',
string='Is Payment Editable'
)
@api.depends('session_id.state', 'state', 'account_move', 'payment_ids.account_move_id.state')
def _compute_is_payment_editable(self):
is_manager = self.env.user.has_group('point_of_sale.group_pos_manager')
for order in self:
# Only POS Managers can edit
if not is_manager:
order.is_payment_editable = False
# 1. POS session must not be closed
elif order.session_id.state == 'closed':
order.is_payment_editable = False
# 2. POS order state must not be done or cancel
elif order.state in ('done', 'cancel'):
order.is_payment_editable = False
# 3. Order must not have a posted invoice
elif order.account_move and order.account_move.state == 'posted':
order.is_payment_editable = False
else:
# 4. None of the payments must have a posted accounting journal entry (account_move_id)
is_any_payment_posted = any(
p.account_move_id and p.account_move_id.state == 'posted'
for p in order.payment_ids
)
order.is_payment_editable = not is_any_payment_posted

23
models/pos_payment.py Normal file
View File

@ -0,0 +1,23 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
class PosPayment(models.Model):
_inherit = 'pos.payment'
def write(self, vals):
if 'payment_method_id' in vals:
# Check if current user is not a POS Manager
if not self.env.user.has_group('point_of_sale.group_pos_manager'):
raise ValidationError(_(
"Only POS Managers are allowed to edit payment methods."
))
for payment in self:
# Enforce the validation rules
if not payment.pos_order_id.is_payment_editable:
raise ValidationError(_(
"You cannot edit the payment method because the POS session is closed "
"or the accounting journal has already been posted."
))
return super(PosPayment, self).write(vals)

59
views/pos_order_views.xml Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_pos_pos_form_inherit_edit_payment" model="ir.ui.view">
<field name="name">pos.order.form.inherit.edit.payment</field>
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
<field name="arch" type="xml">
<!-- Add is_payment_editable field so the view can evaluate its value -->
<xpath expr="//field[@name='partner_id']" position="after">
<field name="is_payment_editable" invisible="1"/>
</xpath>
<!-- Make payment_ids dynamically editable based on our computed field -->
<xpath expr="//field[@name='payment_ids']" position="attributes">
<attribute name="readonly">not is_payment_editable</attribute>
</xpath>
<!-- Inside the list view of payment_ids, make the sub-fields readonly as requested, except payment_method_id -->
<!-- Also set create="0" and delete="0" on list to prevent adding or deleting payments manually -->
<xpath expr="//field[@name='payment_ids']/list" position="attributes">
<attribute name="create">0</attribute>
<attribute name="delete">0</attribute>
<attribute name="editable">bottom</attribute>
</xpath>
<!-- Make date readonly -->
<xpath expr="//field[@name='payment_ids']/list/field[@name='payment_date']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<!-- Make payment method editable -->
<xpath expr="//field[@name='payment_ids']/list/field[@name='payment_method_id']" position="attributes">
<attribute name="readonly">0</attribute>
</xpath>
<!-- Make amount/nominal readonly -->
<xpath expr="//field[@name='payment_ids']/list/field[@name='amount']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<!-- Make optional card and mode fields readonly -->
<xpath expr="//field[@name='payment_ids']/list/field[@name='payment_method_payment_mode']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//field[@name='payment_ids']/list/field[@name='card_no']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//field[@name='payment_ids']/list/field[@name='card_brand']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//field[@name='payment_ids']/list/field[@name='cardholder_name']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
</field>
</record>
</odoo>