feat: add automated inter-company clearing move generation upon POS session validation

This commit is contained in:
Suherdy Yacob 2026-04-28 14:47:46 +07:00
parent 5caa96c1e3
commit 1f60af1e81
5 changed files with 148 additions and 2 deletions

View File

@ -8,8 +8,10 @@
This module removes the standard restriction that prevents Chart of Accounts (COA) of type 'Bank and Cash' from being shared across multiple companies. This module removes the standard restriction that prevents Chart of Accounts (COA) of type 'Bank and Cash' from being shared across multiple companies.
""", """,
'author': 'Suherdy Yacob', 'author': 'Suherdy Yacob',
'depends': ['account'], 'depends': ['account', 'point_of_sale'],
'data': [], 'data': [
'views/pos_payment_method_views.xml',
],
'installable': True, 'installable': True,
'application': False, 'application': False,
'license': 'LGPL-3', 'license': 'LGPL-3',

View File

@ -1 +1,4 @@
from . import account_account from . import account_account
from . import pos_payment_method
from . import pos_session

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class PosPaymentMethod(models.Model):
_inherit = 'pos.payment.method'
intercompany_clearing_account_id = fields.Many2one(
'account.account',
string='Inter-Company Clearing Account',
domain="[('account_type', 'in', ['asset_receivable', 'liability_payable', 'asset_current'])]",
help="If specified, an automatic clearing entry will be generated when a POS session closes. "
"This is used to transfer the balance from a shared parent bank account to an inter-company account."
)
intercompany_clearing_journal_id = fields.Many2one(
'account.journal',
string='Clearing Journal',
domain="[('type', '=', 'general')]",
help="Journal to use for the automated inter-company clearing entries."
)

102
models/pos_session.py Normal file
View File

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, Command
import logging
_logger = logging.getLogger(__name__)
class PosSession(models.Model):
_inherit = 'pos.session'
def _validate_session(self, balancing_account=False, amount_to_balance=0, bank_payment_method_diffs=None):
res = super(PosSession, self)._validate_session(balancing_account, amount_to_balance, bank_payment_method_diffs)
# After the standard validation and account move creation, we create the inter-company clearing moves
self._create_intercompany_clearing_moves()
return res
def _create_intercompany_clearing_moves(self):
for session in self:
if session.state != 'closed' or not session.move_id:
continue
# Dictionary to accumulate amounts per payment method
# Key: pos.payment.method, Value: float (amount)
clearing_amounts = {}
# Find all payments for this session
orders = session.get_session_orders()
payments = orders.payment_ids
for payment in payments:
pm = payment.payment_method_id
if pm.intercompany_clearing_account_id and pm.intercompany_clearing_journal_id:
if pm not in clearing_amounts:
clearing_amounts[pm] = 0.0
clearing_amounts[pm] += payment.amount
for pm, amount in clearing_amounts.items():
if session.currency_id.compare_amounts(amount, 0) <= 0:
continue
# The outstanding account of the payment method is the Shared Bank Account
shared_bank_account = pm.outstanding_account_id or pm.journal_id.default_account_id
if not shared_bank_account:
_logger.warning("No outstanding account found on payment method %s, cannot create clearing entry.", pm.name)
continue
intercompany_account = pm.intercompany_clearing_account_id
clearing_journal = pm.intercompany_clearing_journal_id
# Convert amount to company currency if needed
amount_company_curr = amount
if session.currency_id != session.company_id.currency_id:
amount_company_curr = session.currency_id._convert(
amount, session.company_id.currency_id, session.company_id, fields.Date.context_today(session)
)
# We need to CREDIT the shared bank account (to clear it in the Branch)
# and DEBIT the intercompany account
move_vals = {
'journal_id': clearing_journal.id,
'date': fields.Date.context_today(session),
'ref': f"Inter-company clearing for {session.name} ({pm.name})",
'pos_session_id': session.id, # Link back to session if pos_session_id field exists on move
'line_ids': [
Command.create({
'name': f"Clearing: {pm.name}",
'account_id': shared_bank_account.id,
'credit': amount_company_curr,
'debit': 0.0,
'currency_id': session.currency_id.id,
'amount_currency': -amount if session.currency_id != session.company_id.currency_id else 0.0,
}),
Command.create({
'name': f"Due from Parent: {pm.name}",
'account_id': intercompany_account.id,
'credit': 0.0,
'debit': amount_company_curr,
'currency_id': session.currency_id.id,
'amount_currency': amount if session.currency_id != session.company_id.currency_id else 0.0,
})
]
}
# Check if 'pos_session_id' exists on account.move, if not remove it
if 'pos_session_id' not in self.env['account.move']._fields:
move_vals.pop('pos_session_id', None)
clearing_move = self.env['account.move'].sudo().with_company(session.company_id).create(move_vals)
clearing_move._post()
# Attempt to auto-reconcile the credit line with the debit line from the main POS move
try:
clearing_credit_line = clearing_move.line_ids.filtered(lambda l: l.account_id == shared_bank_account and l.credit > 0)
pos_debit_lines = session.move_id.line_ids.filtered(lambda l: l.account_id == shared_bank_account and l.debit > 0)
if clearing_credit_line and pos_debit_lines:
(clearing_credit_line + pos_debit_lines).reconcile()
except Exception as e:
_logger.warning("Could not auto-reconcile inter-company clearing lines for session %s: %s", session.name, e)

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="pos_payment_method_view_form_inherit_intercompany" model="ir.ui.view">
<field name="name">pos.payment.method.form.inherit.intercompany</field>
<field name="model">pos.payment.method</field>
<field name="inherit_id" ref="point_of_sale.pos_payment_method_view_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='Payment methods']" position="after">
<group name="intercompany_clearing" string="Inter-Company Clearing">
<field name="intercompany_clearing_account_id"/>
<field name="intercompany_clearing_journal_id"
invisible="not intercompany_clearing_account_id"
required="intercompany_clearing_account_id"/>
</group>
</xpath>
</field>
</record>
</odoo>