first commit

This commit is contained in:
Suherdy SYC. Yacob 2025-08-16 11:46:03 +07:00
commit 327712ef46
8 changed files with 266 additions and 0 deletions

51
README.md Normal file
View 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
View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

38
__manifest__.py Normal file
View 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
View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import pos_session
from . import pos_payment_method

View 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
View 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)

View 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>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- No additional views needed for this module -->
</odoo>