first commit
This commit is contained in:
commit
98eca2d0cc
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
__pycache__/
|
||||
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Vendor Payment with Misc Journals - Auto Second Entry
|
||||
|
||||
This module automatically generates a second journal entry when a vendor payment is confirmed using a general (misc) journal.
|
||||
|
||||
## Features
|
||||
|
||||
- Creates a linked second journal entry upon payment confirmation.
|
||||
- Debits the misc journal default account (e.g. 111101).
|
||||
- Credits the branch company cash journal default account (e.g. 111103).
|
||||
- Keeps the second entry in sync with the payment lifecycle (posts, resets to draft, cancels, and unlinks when the payment is updated).
|
||||
- Adds a convenient stat button on the payment form to view the linked second entry.
|
||||
1
__init__.py
Normal file
1
__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import models
|
||||
21
__manifest__.py
Normal file
21
__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
'name': 'Vendor Payment with Misc Journals - Auto Second Entry',
|
||||
'version': '19.0.1.0.0',
|
||||
'category': 'Accounting',
|
||||
'summary': 'Automatically generate second journal entry for vendor payments using misc journals',
|
||||
'description': """
|
||||
This module automatically generates a second journal entry when a vendor payment is confirmed using a general journal.
|
||||
The second entry debits the misc journal default account (111101) and credits the branch company cash journal default account (111103).
|
||||
""",
|
||||
'author': 'Suherdy Yacob',
|
||||
'depends': [
|
||||
'account',
|
||||
'vendor_payment_misc',
|
||||
],
|
||||
'data': [
|
||||
'views/account_payment_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
1
models/__init__.py
Normal file
1
models/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import account_payment
|
||||
135
models/account_payment.py
Normal file
135
models/account_payment.py
Normal file
@ -0,0 +1,135 @@
|
||||
from odoo import models, fields, api, _, Command
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = 'account.payment'
|
||||
|
||||
second_move_id = fields.Many2one(
|
||||
'account.move',
|
||||
string='Second Journal Entry',
|
||||
copy=False,
|
||||
ondelete='set null',
|
||||
help='The second journal entry automatically created to transfer funds from branch cash to Kas Kecil.'
|
||||
)
|
||||
|
||||
def action_post(self):
|
||||
# Call super first to post the main entry
|
||||
super().action_post()
|
||||
|
||||
for payment in self:
|
||||
# Only generate/post the second entry if the journal type is 'general'
|
||||
if payment.journal_id.type == 'general':
|
||||
if not payment.second_move_id:
|
||||
# Find the cash journal for this branch/company
|
||||
cash_journal = self.env['account.journal'].search([
|
||||
('company_id', '=', payment.company_id.id),
|
||||
('type', '=', 'cash')
|
||||
], limit=1)
|
||||
|
||||
if not cash_journal or not cash_journal.default_account_id:
|
||||
raise UserError(_("No cash journal with a default account found for company %s.") % payment.company_id.name)
|
||||
|
||||
if not payment.journal_id.default_account_id:
|
||||
raise UserError(_("No default account found on general journal %s.") % payment.journal_id.name)
|
||||
|
||||
debit_account = payment.journal_id.default_account_id
|
||||
credit_account = cash_journal.default_account_id
|
||||
|
||||
amount = payment.amount
|
||||
currency = payment.currency_id
|
||||
company = payment.company_id
|
||||
|
||||
if currency != company.currency_id:
|
||||
balance = currency._convert(
|
||||
amount,
|
||||
company.currency_id,
|
||||
company,
|
||||
payment.date or fields.Date.today()
|
||||
)
|
||||
else:
|
||||
balance = amount
|
||||
|
||||
debit_line_vals = {
|
||||
'name': _("Second Entry Debit for %s") % payment.name,
|
||||
'account_id': debit_account.id,
|
||||
'debit': balance,
|
||||
'credit': 0.0,
|
||||
'partner_id': payment.partner_id.id,
|
||||
}
|
||||
credit_line_vals = {
|
||||
'name': _("Second Entry Credit for %s") % payment.name,
|
||||
'account_id': credit_account.id,
|
||||
'debit': 0.0,
|
||||
'credit': balance,
|
||||
'partner_id': payment.partner_id.id,
|
||||
}
|
||||
|
||||
if currency != company.currency_id:
|
||||
debit_line_vals.update({
|
||||
'currency_id': currency.id,
|
||||
'amount_currency': amount,
|
||||
})
|
||||
credit_line_vals.update({
|
||||
'currency_id': currency.id,
|
||||
'amount_currency': -amount,
|
||||
})
|
||||
|
||||
line_ids = [
|
||||
Command.create(debit_line_vals),
|
||||
Command.create(credit_line_vals),
|
||||
]
|
||||
|
||||
move_vals = {
|
||||
'move_type': 'entry',
|
||||
'ref': payment.name,
|
||||
'date': payment.date,
|
||||
'journal_id': payment.journal_id.id,
|
||||
'company_id': payment.company_id.id,
|
||||
'partner_id': payment.partner_id.id,
|
||||
'currency_id': payment.currency_id.id,
|
||||
'line_ids': line_ids,
|
||||
}
|
||||
|
||||
# Create and post the second move
|
||||
second_move = self.env['account.move'].create(move_vals)
|
||||
second_move.action_post()
|
||||
payment.write({'second_move_id': second_move.id})
|
||||
elif payment.second_move_id.state == 'draft':
|
||||
# If it already exists and is draft (e.g. after resetting payment to draft), post it
|
||||
payment.second_move_id.action_post()
|
||||
|
||||
def action_draft(self):
|
||||
super().action_draft()
|
||||
for payment in self:
|
||||
if payment.second_move_id:
|
||||
if payment.second_move_id.state != 'draft':
|
||||
payment.second_move_id.button_draft()
|
||||
|
||||
def action_cancel(self):
|
||||
super().action_cancel()
|
||||
for payment in self:
|
||||
if payment.second_move_id:
|
||||
if payment.second_move_id.state == 'draft':
|
||||
payment.second_move_id.unlink()
|
||||
else:
|
||||
payment.second_move_id.button_cancel()
|
||||
|
||||
def unlink(self):
|
||||
for payment in self:
|
||||
if payment.second_move_id:
|
||||
second_move = payment.second_move_id
|
||||
if second_move.state != 'draft':
|
||||
second_move.button_draft()
|
||||
second_move.unlink()
|
||||
return super().unlink()
|
||||
|
||||
def button_open_second_journal_entry(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _("Second Journal Entry"),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'account.move',
|
||||
'context': {'create': False},
|
||||
'view_mode': 'form',
|
||||
'res_id': self.second_move_id.id,
|
||||
}
|
||||
27
views/account_payment_views.xml
Normal file
27
views/account_payment_views.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_account_payment_form_inherit_double_entry" model="ir.ui.view">
|
||||
<field name="name">account.payment.form.inherit.double.entry</field>
|
||||
<field name="model">account.payment</field>
|
||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Add a stat button for the second journal entry in the button_box -->
|
||||
<div name="button_box" position="inside">
|
||||
<button name="button_open_second_journal_entry"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-bars"
|
||||
invisible="not second_move_id"
|
||||
groups="account.group_account_user,account.group_account_readonly">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Second Entry</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Include the field on the form so the invisible attribute of the button works correctly -->
|
||||
<sheet position="inside">
|
||||
<field name="second_move_id" invisible="1"/>
|
||||
</sheet>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user