first commit
This commit is contained in:
commit
327712ef46
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Split Pendapatan Payment
|
||||
|
||||
This module modifies Point of Sale transactions to split income/pendapatan journal entries per payment method used in the transaction.
|
||||
|
||||
## Overview
|
||||
|
||||
In standard Odoo Point of Sale, all sales income is recorded in a single journal entry line regardless of the payment methods used. This module changes that behavior by creating separate income lines for each payment method used in the transactions.
|
||||
|
||||
For example, if a session has sales paid with both cash and credit card, instead of creating one income line for all sales, the module will create separate income lines:
|
||||
- Sales - Cash
|
||||
- Sales - Credit Card
|
||||
|
||||
## Features
|
||||
|
||||
- Splits income journal entries by payment method
|
||||
- Maintains proper accounting reconciliation
|
||||
- Works with all payment methods (cash, bank transfers, credit cards, etc.)
|
||||
- Preserves tax calculations and reporting
|
||||
- Allows linking specific income accounts to payment methods
|
||||
|
||||
## Configuration
|
||||
|
||||
### Payment Method Configuration
|
||||
1. Go to Point of Sale > Configuration > Payment Methods
|
||||
2. Edit a payment method
|
||||
3. In the Accounting section, set the "Income Account" field to specify which account should be used for sales paid with this method
|
||||
4. If left empty, the default product income account will be used
|
||||
|
||||
## How it works
|
||||
|
||||
The module inherits from the `pos.session` model and overrides two key methods:
|
||||
|
||||
1. `_accumulate_amounts()` - Modifies how sales amounts are accumulated to include payment method information and use specific income accounts
|
||||
2. `_get_sale_vals()` - Customizes the journal entry line descriptions to include payment method names
|
||||
|
||||
The module also extends the `pos.payment.method` model to add an "Income Account" field that allows you to specify which account should be used for sales paid with each payment method.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Place the module in your Odoo addons directory
|
||||
2. Update the Apps list in Odoo
|
||||
3. Install the "Split Pendapatan Payment" module
|
||||
|
||||
## Compatibility
|
||||
|
||||
This module is designed for Odoo 17.0 and is compatible with the standard Point of Sale module.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Only affects non-invoiced POS orders (invoiced orders follow standard Odoo accounting)
|
||||
- May require adjustments if used with other POS customization modules that also modify journal entries
|
||||
3
__init__.py
Normal file
3
__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
38
__manifest__.py
Normal file
38
__manifest__.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Split Pendapatan Payment",
|
||||
|
||||
'summary': "Split income journal entries per payment method in Point of Sale transactions",
|
||||
|
||||
'description': """
|
||||
This module modifies Point of Sale transactions to split income/pendapatan
|
||||
journal entries per payment method used in the transaction. Instead of
|
||||
creating a single income line for all sales, separate income lines are
|
||||
created for each payment method.
|
||||
""",
|
||||
|
||||
'author': "Your Company",
|
||||
'website': "https://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
'category': 'Point of Sale',
|
||||
'version': '17.0.1.0.0',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['point_of_sale'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
# 'security/ir.model.access.csv',
|
||||
'views/pos_session_views.xml',
|
||||
'views/pos_payment_method_views.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
# 'demo/demo.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
4
models/__init__.py
Normal file
4
models/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import pos_session
|
||||
from . import pos_payment_method
|
||||
13
models/pos_payment_method.py
Normal file
13
models/pos_payment_method.py
Normal file
@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class PosPaymentMethod(models.Model):
|
||||
_inherit = 'pos.payment.method'
|
||||
|
||||
income_account_id = fields.Many2one(
|
||||
'account.account',
|
||||
string='Income Account',
|
||||
domain=[('deprecated', '=', False)],
|
||||
help='Account used for income lines when splitting by payment method. '
|
||||
'If empty, the default income account from the product will be used.')
|
||||
140
models/pos_session.py
Normal file
140
models/pos_session.py
Normal file
@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from collections import defaultdict
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_is_zero, float_compare
|
||||
|
||||
|
||||
class PosSession(models.Model):
|
||||
_inherit = 'pos.session'
|
||||
|
||||
def _accumulate_amounts(self, data):
|
||||
# Call the original method to get all the standard accumulations
|
||||
data = super(PosSession, self)._accumulate_amounts(data)
|
||||
|
||||
# Get all orders in this session
|
||||
closed_orders = self._get_closed_orders()
|
||||
|
||||
# If no orders, return original data
|
||||
if not closed_orders:
|
||||
return data
|
||||
|
||||
# Get the original sales data
|
||||
sales = data.get('sales', {})
|
||||
if not sales:
|
||||
return data
|
||||
|
||||
# Create new sales data structure split by payment method
|
||||
split_sales = defaultdict(lambda: {'amount': 0.0, 'amount_converted': 0.0, 'tax_amount': 0.0})
|
||||
|
||||
# For each sale entry, we need to distribute it across payment methods
|
||||
for sale_key, sale_amounts in sales.items():
|
||||
# Skip if this is a tax key (we only want to split actual sales)
|
||||
if len(sale_key) < 4: # Not a standard sales key
|
||||
continue
|
||||
|
||||
total_amount = sale_amounts['amount']
|
||||
total_amount_converted = sale_amounts['amount_converted']
|
||||
tax_amount = sale_amounts.get('tax_amount', 0.0) # Get tax amount if it exists
|
||||
|
||||
if float_is_zero(total_amount, precision_rounding=self.currency_id.rounding):
|
||||
continue
|
||||
|
||||
# Distribute this sales amount across all orders based on their payment methods
|
||||
total_payment_amount = sum(sum(payment.amount for payment in order.payment_ids) for order in closed_orders)
|
||||
|
||||
if float_is_zero(total_payment_amount, precision_rounding=self.currency_id.rounding):
|
||||
continue
|
||||
|
||||
# Distribute the sales amount across all orders proportionally to their payments
|
||||
for order in closed_orders:
|
||||
if order.is_invoiced:
|
||||
continue # Skip invoiced orders
|
||||
|
||||
order_payments = order.payment_ids
|
||||
order_payment_total = sum(payment.amount for payment in order_payments)
|
||||
|
||||
if float_is_zero(order_payment_total, precision_rounding=order.currency_id.rounding):
|
||||
continue
|
||||
|
||||
# For each payment in this order, create a split sales entry
|
||||
for payment in order_payments:
|
||||
# Calculate the proportion of this payment relative to all payments
|
||||
payment_proportion = payment.amount / total_payment_amount
|
||||
payment_amount = total_amount * payment_proportion
|
||||
payment_amount_converted = total_amount_converted * payment_proportion
|
||||
payment_tax_amount = tax_amount * payment_proportion
|
||||
|
||||
if float_is_zero(payment_amount, precision_rounding=self.currency_id.rounding):
|
||||
continue
|
||||
|
||||
# Use the payment method's income account if specified, otherwise use original account
|
||||
income_account_id = sale_key[0] # default account
|
||||
if payment.payment_method_id.income_account_id and payment.payment_method_id.income_account_id.id:
|
||||
income_account_id = payment.payment_method_id.income_account_id.id
|
||||
|
||||
# Ensure we have a valid account ID
|
||||
if not income_account_id:
|
||||
continue
|
||||
|
||||
# Create a new key that includes the payment method
|
||||
new_sale_key = (
|
||||
# account (use payment method account if specified)
|
||||
income_account_id,
|
||||
# sign (same as original)
|
||||
sale_key[1],
|
||||
# payment method
|
||||
payment.payment_method_id.id,
|
||||
# for taxes (same as original)
|
||||
sale_key[2],
|
||||
# base tags (same as original)
|
||||
sale_key[3],
|
||||
)
|
||||
|
||||
# Update the split sales data
|
||||
split_sales[new_sale_key]['amount'] += payment_amount
|
||||
split_sales[new_sale_key]['amount_converted'] += payment_amount_converted
|
||||
split_sales[new_sale_key]['tax_amount'] += payment_tax_amount
|
||||
|
||||
# Replace the original sales data with our split sales data
|
||||
data['sales'] = split_sales
|
||||
return data
|
||||
|
||||
def _get_sale_vals(self, key, amount, amount_converted):
|
||||
""" Override to add payment method information to the sales line description """
|
||||
# Check if this key includes payment method information
|
||||
if len(key) >= 5 and isinstance(key[2], int): # Has payment method ID
|
||||
account_id, sign, payment_method_id, tax_keys, base_tag_ids = key
|
||||
# Try to get the payment method name
|
||||
try:
|
||||
payment_method = self.env['pos.payment.method'].browse(payment_method_id)
|
||||
payment_method_name = payment_method.name
|
||||
except:
|
||||
payment_method_name = "Unknown Payment"
|
||||
else:
|
||||
# Original format
|
||||
account_id, sign, tax_keys, base_tag_ids = key
|
||||
payment_method_name = None
|
||||
|
||||
tax_ids = set(tax[0] for tax in tax_keys) if tax_keys else set()
|
||||
applied_taxes = self.env['account.tax'].browse(tax_ids)
|
||||
title = _('Sales') if sign == 1 else _('Refund')
|
||||
|
||||
# Create name with payment method information
|
||||
if payment_method_name:
|
||||
name = _('%s - %s', title, payment_method_name)
|
||||
if applied_taxes:
|
||||
name = _('%s with %s - %s', title, ', '.join([tax.name for tax in applied_taxes]), payment_method_name)
|
||||
else:
|
||||
name = _('%s untaxed', title)
|
||||
if applied_taxes:
|
||||
name = _('%s with %s', title, ', '.join([tax.name for tax in applied_taxes]))
|
||||
|
||||
partial_vals = {
|
||||
'name': name,
|
||||
'account_id': account_id,
|
||||
'move_id': self.move_id.id,
|
||||
'tax_ids': [(6, 0, tax_ids)],
|
||||
'tax_tag_ids': [(6, 0, base_tag_ids)] if base_tag_ids else [],
|
||||
}
|
||||
return self._credit_amounts(partial_vals, amount, amount_converted)
|
||||
13
views/pos_payment_method_views.xml
Normal file
13
views/pos_payment_method_views.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_pos_payment_method_form_inherit" model="ir.ui.view">
|
||||
<field name="name">pos.payment.method.form.inherit</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']/group" position="inside">
|
||||
<field name="income_account_id" domain="[('deprecated', '=', False)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
4
views/pos_session_views.xml
Normal file
4
views/pos_session_views.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- No additional views needed for this module -->
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user