diff --git a/__manifest__.py b/__manifest__.py index 335da40..4b177e1 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -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. """, 'author': 'Suherdy Yacob', - 'depends': ['account'], - 'data': [], + 'depends': ['account', 'point_of_sale'], + 'data': [ + 'views/pos_payment_method_views.xml', + ], 'installable': True, 'application': False, 'license': 'LGPL-3', diff --git a/models/__init__.py b/models/__init__.py index 5a5ba4f..f3225ed 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,4 @@ from . import account_account +from . import pos_payment_method +from . import pos_session + diff --git a/models/pos_payment_method.py b/models/pos_payment_method.py new file mode 100644 index 0000000..ef76c44 --- /dev/null +++ b/models/pos_payment_method.py @@ -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." + ) diff --git a/models/pos_session.py b/models/pos_session.py new file mode 100644 index 0000000..8cd3d5b --- /dev/null +++ b/models/pos_session.py @@ -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) diff --git a/views/pos_payment_method_views.xml b/views/pos_payment_method_views.xml new file mode 100644 index 0000000..92cb06d --- /dev/null +++ b/views/pos_payment_method_views.xml @@ -0,0 +1,18 @@ + + + + pos.payment.method.form.inherit.intercompany + pos.payment.method + + + + + + + + + + +