[IMP] account: Allow spreading amount over several dates from Cut-Off wizard.

closes odoo/odoo#59246

Task: 2347881
Related: odoo/upgrade#1989
Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
Laurent Smet
2020-12-07 15:57:50 +00:00
parent f0ae2288e9
commit 7e3e99adb4
8 changed files with 524 additions and 318 deletions
@@ -126,6 +126,7 @@ access_account_payment,account.payment,model_account_payment,account.group_accou
access_account_payment_register,access.account.payment.register,model_account_payment_register,account.group_account_invoice,1,1,1,0
access_account_bank_statement_closebalance,access.account.bank.statement.closebalance,model_account_bank_statement_closebalance,account.group_account_user,1,1,1,0
access_account_automatic_entry_wizard,access.account.automatic.entry.wizard,model_account_automatic_entry_wizard,account.group_account_user,1,1,1,0
access_account_automatic_entry_wizard_line,access.account.automatic.entry.wizard.line,model_account_automatic_entry_wizard_line,account.group_account_user,1,1,1,1
access_account_unreconcile,access.account.unreconcile,model_account_unreconcile,account.group_account_user,1,1,1,0
access_account_resequence,access.account.resequence.wizard,model_account_resequence_wizard,account.group_account_manager,1,1,1,0
access_validate_account_move,access.validate.account.move,model_validate_account_move,account.group_account_invoice,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
126 access_account_analytic_default access_account_invoice_send account.analytic.default access.account.invoice.send model_account_analytic_default model_account_invoice_send account.group_account_readonly account.group_account_invoice 1 0 1 0 1 0
127 access_account_analytic_default_analytic access_account_analytic_default account.analytic.default analytic account.analytic.default model_account_analytic_default analytic.group_analytic_accounting account.group_account_readonly 1 0 0 0
128 access_account_analytic_default_invoice access_account_analytic_default_analytic account.analytic.default invoice account.analytic.default analytic model_account_analytic_default account.group_account_invoice analytic.group_analytic_accounting 1 1 0 1 0 1 0
129 access_account_analytic_default_invoice account.analytic.default invoice model_account_analytic_default account.group_account_invoice 1 1 1 1
130 access_account_tour_upload_bill account.tour.upload.bill model_account_tour_upload_bill account.group_account_manager 1 1 1 0
131 access_account_tour_upload_bill_email_confirm account.tour.upload.bill.email.confirm model_account_tour_upload_bill_email_confirm account.group_account_manager 1 1 1 0
132
+1
View File
@@ -2,6 +2,7 @@
from . import test_account_move_reconcile
from . import test_account_move_payments_widget
from . import test_account_automatic_entry_wizard_period
from . import test_account_move_out_invoice
from . import test_account_move_out_refund
from . import test_account_move_in_invoice
@@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo import fields
from odoo.tests import tagged
@tagged('post_install', '-at_install')
class TestAccountAutomaticEntryWizardPeriod(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'partner_id': cls.partner_a.id,
'invoice_date': '2017-01-01',
'invoice_line_ids': [
(0, 0, {
'name': "line1",
'quantity': 1,
'price_unit': 1000.0,
'account_id': cls.company_data['default_account_revenue'].id,
}),
(0, 0, {
'name': "line2",
'quantity': 1,
'price_unit': 200.0,
'account_id': cls.company_data['default_account_revenue'].id,
}),
]
})
cls.invoice.action_post()
cls.accrual_account = cls.env['account.account'].create({
'name': "test_accrual_account",
'code': "TEST",
'user_type_id': cls.env.ref('account.data_account_type_revenue').id,
'reconcile': True,
'company_id': cls.company_data['company'].id,
})
cls.income_account = cls.company_data['default_account_revenue']
cls.Wizard = cls.env['account.automatic.entry.wizard']\
.with_context(active_model='account.move.line', active_ids=cls.invoice.invoice_line_ids.ids)
def test_chg_period_100_percent_no_time_division(self):
''' Test smoothing the invoice in the period without the time division. '''
to_date = lambda string: fields.Date.from_string(string)
wizard = self.Wizard.create({
'action': 'change_period',
'journal_id': self.company_data['default_journal_misc'].id,
'revenue_accrual_account': self.accrual_account.id,
'chg_period_date_from': '2017-06-01',
'chg_period_date_to': '2017-12-31', # Large period to check the rounding issues.
'chg_period_percentage': 100.0,
'chg_period_time_division': False,
})
wizard_res = wizard.do_action()
lines = self.env['account.move'].browse(wizard_res['domain'][0][2]).line_ids.sorted(lambda line: (line.date, -abs(line.balance), -line.balance))
dest_lines = lines.filtered(lambda line: line.date == self.invoice.date)
accrual_lines = lines.filtered(lambda line: line.date != self.invoice.date)
self.assertRecordValues(dest_lines, [
{'debit': 1000.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 1000.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
{'debit': 200.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 200.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
])
self.assertRecordValues(accrual_lines, [
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-30')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-30')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-30')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-30')},
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-31')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-31')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-31')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-31')},
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-31')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-31')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-31')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-31')},
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-30')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-30')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-30')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-30')},
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-10-31')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-10-31')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-10-31')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-10-31')},
{'debit': 142.86, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-11-30')},
{'debit': 0.0, 'credit': 142.86, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-11-30')},
{'debit': 28.57, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-11-30')},
{'debit': 0.0, 'credit': 28.57, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-11-30')},
{'debit': 142.84, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-12-31')},
{'debit': 0.0, 'credit': 142.84, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-12-31')},
{'debit': 28.58, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-12-31')},
{'debit': 0.0, 'credit': 28.58, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-12-31')},
])
def test_chg_period_100_percent_time_division(self):
''' Same as test_chg_period_100_percent_no_time_division using the time division meaning the smoothing is made
to different months considering their number of days.
'''
to_date = lambda string: fields.Date.from_string(string)
wizard = self.Wizard.create({
'action': 'change_period',
'journal_id': self.company_data['default_journal_misc'].id,
'revenue_accrual_account': self.accrual_account.id,
'chg_period_date_from': '2017-06-01',
'chg_period_date_to': '2017-12-31', # Large period to check the rounding issues.
'chg_period_percentage': 100.0,
'chg_period_time_division': True,
})
wizard_res = wizard.do_action()
lines = self.env['account.move'].browse(wizard_res['domain'][0][2]).line_ids.sorted(lambda line: (line.date, -abs(line.balance), -line.balance))
dest_lines = lines.filtered(lambda line: line.date == self.invoice.date)
accrual_lines = lines.filtered(lambda line: line.date != self.invoice.date)
self.assertRecordValues(dest_lines, [
{'debit': 1000.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 1000.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
{'debit': 200.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 200.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
])
self.assertRecordValues(accrual_lines, [
{'debit': 140.1, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-30')},
{'debit': 0.0, 'credit': 140.1, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-30')},
{'debit': 28.02, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-30')},
{'debit': 0.0, 'credit': 28.02, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-30')},
{'debit': 144.93, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-31')},
{'debit': 0.0, 'credit': 144.93, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-31')},
{'debit': 28.99, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-31')},
{'debit': 0.0, 'credit': 28.99, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-31')},
{'debit': 144.93, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-31')},
{'debit': 0.0, 'credit': 144.93, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-31')},
{'debit': 28.99, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-31')},
{'debit': 0.0, 'credit': 28.99, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-31')},
{'debit': 140.1, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-30')},
{'debit': 0.0, 'credit': 140.1, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-30')},
{'debit': 28.02, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-30')},
{'debit': 0.0, 'credit': 28.02, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-30')},
{'debit': 144.93, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-10-31')},
{'debit': 0.0, 'credit': 144.93, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-10-31')},
{'debit': 28.99, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-10-31')},
{'debit': 0.0, 'credit': 28.99, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-10-31')},
{'debit': 140.1, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-11-30')},
{'debit': 0.0, 'credit': 140.1, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-11-30')},
{'debit': 28.02, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-11-30')},
{'debit': 0.0, 'credit': 28.02, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-11-30')},
{'debit': 144.91, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-12-31')},
{'debit': 0.0, 'credit': 144.91, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-12-31')},
{'debit': 28.97, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-12-31')},
{'debit': 0.0, 'credit': 28.97, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-12-31')},
])
def test_chg_period_manual_periods(self):
''' Test the smoothing by manual lines. '''
to_date = lambda string: fields.Date.from_string(string)
wizard = self.Wizard.create({
'action': 'change_period',
'journal_id': self.company_data['default_journal_misc'].id,
'revenue_accrual_account': self.accrual_account.id,
'chg_period_line_ids': [
(0, 0, {'date': '2017-06-01', 'balance': 120.0}),
(0, 0, {'date': '2017-07-01', 'balance': 300.0}),
(0, 0, {'date': '2017-08-01', 'balance': 700.0}),
(0, 0, {'date': '2017-09-01', 'balance': 80.0}),
],
})
wizard_res = wizard.do_action()
lines = self.env['account.move'].browse(wizard_res['domain'][0][2]).line_ids.sorted(lambda line: (line.date, -abs(line.balance), -line.balance))
dest_lines = lines.filtered(lambda line: line.date == self.invoice.date)
accrual_lines = lines.filtered(lambda line: line.date != self.invoice.date)
self.assertRecordValues(dest_lines, [
{'debit': 1000.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 1000.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
{'debit': 200.0, 'credit': 0.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-01-01')},
{'debit': 0.0, 'credit': 200.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-01-01')},
])
self.assertRecordValues(accrual_lines, [
{'debit': 100.0, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-01')},
{'debit': 0.0, 'credit': 100.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-01')},
{'debit': 20.0, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-06-01')},
{'debit': 0.0, 'credit': 20.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-06-01')},
{'debit': 250.0, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-01')},
{'debit': 0.0, 'credit': 250.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-01')},
{'debit': 50.0, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-07-01')},
{'debit': 0.0, 'credit': 50.0, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-07-01')},
{'debit': 583.33, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-01')},
{'debit': 0.0, 'credit': 583.33, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-01')},
{'debit': 116.67, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-08-01')},
{'debit': 0.0, 'credit': 116.67, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-08-01')},
{'debit': 66.67, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-01')},
{'debit': 0.0, 'credit': 66.67, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-01')},
{'debit': 13.33, 'credit': 0.0, 'account_id': self.accrual_account.id, 'reconciled': True, 'date': to_date('2017-09-01')},
{'debit': 0.0, 'credit': 13.33, 'account_id': self.income_account.id, 'reconciled': False, 'date': to_date('2017-09-01')},
])
@@ -1656,108 +1656,3 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_total' : self.move_vals['amount_total'],
'amount_untaxed' : self.move_vals['amount_untaxed'],
})
def test_in_invoice_change_period_accrual_1(self):
move = self.env['account.move'].create({
'move_type': 'in_invoice',
'date': '2017-01-01',
'partner_id': self.partner_a.id,
'invoice_date': fields.Date.from_string('2017-01-01'),
'currency_id': self.currency_data['currency'].id,
'invoice_payment_term_id': self.pay_terms_a.id,
'invoice_line_ids': [
(0, None, {
'name': self.product_line_vals_1['name'],
'product_id': self.product_line_vals_1['product_id'],
'product_uom_id': self.product_line_vals_1['product_uom_id'],
'quantity': self.product_line_vals_1['quantity'],
'price_unit': self.product_line_vals_1['price_unit'],
'tax_ids': self.product_line_vals_1['tax_ids'],
}),
(0, None, {
'name': self.product_line_vals_2['name'],
'product_id': self.product_line_vals_2['product_id'],
'product_uom_id': self.product_line_vals_2['product_uom_id'],
'quantity': self.product_line_vals_2['quantity'],
'price_unit': self.product_line_vals_2['price_unit'],
'tax_ids': self.product_line_vals_2['tax_ids'],
}),
]
})
move.action_post()
wizard = self.env['account.automatic.entry.wizard']\
.with_context(active_model='account.move.line', active_ids=move.invoice_line_ids.ids).create({
'action': 'change_period',
'date': '2018-01-01',
'percentage': 60,
'journal_id': self.company_data['default_journal_misc'].id,
'expense_accrual_account': self.env['account.account'].create({
'name': 'Accrual Expense Account',
'code': '234567',
'user_type_id': self.env.ref('account.data_account_type_expenses').id,
'reconcile': True,
}).id,
'revenue_accrual_account': self.env['account.account'].create({
'name': 'Accrual Revenue Account',
'code': '765432',
'user_type_id': self.env.ref('account.data_account_type_expenses').id,
'reconcile': True,
}).id,
})
wizard_res = wizard.do_action()
self.assertInvoiceValues(move, [
{
**self.product_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'amount_currency': 800.0,
'debit': 400.0,
'credit': 0.0,
},
{
**self.product_line_vals_2,
'currency_id': self.currency_data['currency'].id,
'amount_currency': 160.0,
'debit': 80.0,
'credit': 0.0,
},
{
**self.tax_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'amount_currency': 144.0,
'debit': 72.0,
'credit': 0.0,
},
{
**self.tax_line_vals_2,
'currency_id': self.currency_data['currency'].id,
'amount_currency': 24.0,
'debit': 12.0,
'credit': 0.0,
},
{
**self.term_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'amount_currency': -1128.0,
'debit': 0.0,
'credit': 564.0,
'date_maturity': fields.Date.from_string('2017-01-01'),
},
], {
**self.move_vals,
'currency_id': self.currency_data['currency'].id,
'date': fields.Date.from_string('2017-01-01'),
})
accrual_lines = self.env['account.move'].browse(wizard_res['domain'][0][2]).line_ids.sorted('date')
self.assertRecordValues(accrual_lines, [
{'amount_currency': -480.0, 'debit': 0.0, 'credit': 240.0, 'account_id': self.product_line_vals_1['account_id'], 'reconciled': False},
{'amount_currency': 480.0, 'debit': 240.0, 'credit': 0.0, 'account_id': wizard.expense_accrual_account.id, 'reconciled': True},
{'amount_currency': -96.0, 'debit': 0.0, 'credit': 48.0, 'account_id': self.product_line_vals_2['account_id'], 'reconciled': False},
{'amount_currency': 96.0, 'debit': 48.0, 'credit': 0.0, 'account_id': wizard.expense_accrual_account.id, 'reconciled': True},
{'amount_currency': 480.0, 'debit': 240.0, 'credit': 0.0, 'account_id': self.product_line_vals_1['account_id'], 'reconciled': False},
{'amount_currency': -480.0, 'debit': 0.0, 'credit': 240.0, 'account_id': wizard.expense_accrual_account.id, 'reconciled': True},
{'amount_currency': 96.0, 'debit': 48.0, 'credit': 0.0, 'account_id': self.product_line_vals_2['account_id'], 'reconciled': False},
{'amount_currency': -96.0, 'debit': 0.0, 'credit': 48.0, 'account_id': wizard.expense_accrual_account.id, 'reconciled': True},
])
@@ -2424,113 +2424,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_untaxed' : self.move_vals['amount_untaxed'],
})
def test_out_invoice_change_period_accrual_1(self):
move = self.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'partner_id': self.partner_a.id,
'invoice_date': fields.Date.from_string('2017-01-01'),
'currency_id': self.currency_data['currency'].id,
'invoice_payment_term_id': self.pay_terms_a.id,
'invoice_line_ids': [
(0, None, {
'name': self.product_line_vals_1['name'],
'product_id': self.product_line_vals_1['product_id'],
'product_uom_id': self.product_line_vals_1['product_uom_id'],
'quantity': self.product_line_vals_1['quantity'],
'price_unit': self.product_line_vals_1['price_unit'],
'tax_ids': self.product_line_vals_1['tax_ids'],
}),
(0, None, {
'name': self.product_line_vals_2['name'],
'product_id': self.product_line_vals_2['product_id'],
'product_uom_id': self.product_line_vals_2['product_uom_id'],
'quantity': self.product_line_vals_2['quantity'],
'price_unit': self.product_line_vals_2['price_unit'],
'tax_ids': self.product_line_vals_2['tax_ids'],
}),
]
})
move.action_post()
wizard = self.env['account.automatic.entry.wizard']\
.with_context(active_model='account.move.line', active_ids=move.invoice_line_ids.ids).create({
'action': 'change_period',
'date': '2018-01-01',
'percentage': 60,
'journal_id': self.company_data['default_journal_misc'].id,
'expense_accrual_account': self.env['account.account'].create({
'name': 'Accrual Expense Account',
'code': '234567',
'user_type_id': self.env.ref('account.data_account_type_expenses').id,
'reconcile': True,
}).id,
'revenue_accrual_account': self.env['account.account'].create({
'name': 'Accrual Revenue Account',
'code': '765432',
'user_type_id': self.env.ref('account.data_account_type_expenses').id,
'reconcile': True,
}).id,
})
wizard_res = wizard.do_action()
self.assertInvoiceValues(move, [
{
**self.product_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'amount_currency': -1000.0,
'debit': 0.0,
'credit': 500.0,
},
{
**self.product_line_vals_2,
'currency_id': self.currency_data['currency'].id,
'amount_currency': -200.0,
'debit': 0.0,
'credit': 100.0,
},
{
**self.tax_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'amount_currency': -180.0,
'debit': 0.0,
'credit': 90.0,
},
{
**self.tax_line_vals_2,
'currency_id': self.currency_data['currency'].id,
'amount_currency': -30.0,
'debit': 0.0,
'credit': 15.0,
},
{
**self.term_line_vals_1,
'currency_id': self.currency_data['currency'].id,
'name': 'INV/2017/01/0001',
'amount_currency': 1410.0,
'debit': 705.0,
'credit': 0.0,
'date_maturity': fields.Date.from_string('2017-01-01'),
},
], {
**self.move_vals,
'currency_id': self.currency_data['currency'].id,
'date': fields.Date.from_string('2017-01-01'),
'payment_reference': 'INV/2017/01/0001',
})
accrual_lines = self.env['account.move'].browse(wizard_res['domain'][0][2]).line_ids.sorted('date')
self.assertRecordValues(accrual_lines, [
{'amount_currency': 600.0, 'debit': 300.0, 'credit': 0.0, 'account_id': self.product_line_vals_1['account_id'], 'reconciled': False},
{'amount_currency': -600.0, 'debit': 0.0, 'credit': 300.0, 'account_id': wizard.revenue_accrual_account.id, 'reconciled': True},
{'amount_currency': 120.0, 'debit': 60.0, 'credit': 0.0, 'account_id': self.product_line_vals_2['account_id'], 'reconciled': False},
{'amount_currency': -120.0, 'debit': 0.0, 'credit': 60.0, 'account_id': wizard.revenue_accrual_account.id, 'reconciled': True},
{'amount_currency': -600.0, 'debit': 0.0, 'credit': 300.0, 'account_id': self.product_line_vals_1['account_id'], 'reconciled': False},
{'amount_currency': 600.0, 'debit': 300.0, 'credit': 0.0, 'account_id': wizard.revenue_accrual_account.id, 'reconciled': True},
{'amount_currency': -120.0, 'debit': 0.0, 'credit': 60.0, 'account_id': self.product_line_vals_2['account_id'], 'reconciled': False},
{'amount_currency': 120.0, 'debit': 60.0, 'credit': 0.0, 'account_id': wizard.revenue_accrual_account.id, 'reconciled': True},
])
def test_out_invoice_filter_zero_balance_lines(self):
zero_balance_payment_term = self.env['account.payment.term'].create({
'name': 'zero_balance_payment_term',
+1 -1
View File
@@ -1006,7 +1006,7 @@
<button name="action_automatic_entry"
type="object"
icon="fa-calendar"
string="Cut-Off"
string="Re-allocate"
aria-label="Change Period"
class="float-right"
attrs="{'invisible': [('account_internal_group', 'not in', ('income', 'expense'))], 'column_invisible': [('parent.move_type', '=', 'entry')]}"
@@ -5,8 +5,10 @@ from odoo.tools.misc import format_date, formatLang
from collections import defaultdict
from itertools import groupby
from dateutil.relativedelta import relativedelta
import json
class AutomaticEntryWizard(models.TransientModel):
_name = 'account.automatic.entry.wizard'
_description = 'Create Automatic Entries'
@@ -16,18 +18,21 @@ class AutomaticEntryWizard(models.TransientModel):
move_data = fields.Text(compute="_compute_move_data", help="JSON value of the moves to be created")
preview_move_data = fields.Text(compute="_compute_preview_move_data", help="JSON value of the data to be displayed in the previewer")
move_line_ids = fields.Many2many('account.move.line')
date = fields.Date(required=True, default=lambda self: fields.Date.context_today(self))
company_id = fields.Many2one('res.company', required=True, readonly=True)
company_currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
percentage = fields.Float("Percentage", compute='_compute_percentage', readonly=False, store=True, help="Percentage of each line to execute the action on.")
total_amount = fields.Monetary(compute='_compute_total_amount', store=True, readonly=False, currency_field='company_currency_id', help="Total amount impacted by the automatic entry.")
total_amount = fields.Monetary(
compute='_compute_from_move_line_ids',
currency_field='company_currency_id',
help="Total amount impacted by the automatic entry.")
journal_id = fields.Many2one('account.journal', required=True, readonly=False, string="Journal",
domain="[('company_id', '=', company_id), ('type', '=', 'general')]",
related="company_id.automatic_entry_default_journal_id",
help="Journal where to create the entry.")
# change period
account_type = fields.Selection([('income', 'Revenue'), ('expense', 'Expense')], compute='_compute_account_type', store=True)
account_type = fields.Selection(
selection=[('income', 'Revenue'), ('expense', 'Expense')],
compute='_compute_from_move_line_ids')
expense_accrual_account = fields.Many2one('account.account', readonly=False,
domain="[('company_id', '=', company_id),"
"('internal_type', 'not in', ('receivable', 'payable')),"
@@ -38,38 +43,105 @@ class AutomaticEntryWizard(models.TransientModel):
"('internal_type', 'not in', ('receivable', 'payable')),"
"('is_off_balance', '=', False)]",
related="company_id.revenue_accrual_account_id")
chg_period_date_from = fields.Date(
string="Date from",
default=lambda self: fields.Date.context_today(self),
help="Date at which the smoothing of the balance will start.")
chg_period_date_to = fields.Date(
string="Date to",
default=lambda self: fields.Date.context_today(self),
help="Date at which the smoothing of the balance will end.")
chg_period_time_division = fields.Boolean(
string="Time Division",
help="Allocate the balance to different periods considering their number of days.")
chg_period_percentage = fields.Float(string="Percentage", default=100.0)
chg_period_line_ids = fields.One2many(
comodel_name='account.automatic.entry.wizard.line',
inverse_name='wizard_id',
string='Lines',
store=True, readonly=False,
compute='_compute_chg_period_line_ids')
chg_period_warning_message = fields.Text(
string="Warning",
compute='_compute_chg_period_warning_message')
# change account
date = fields.Date(string="Transfer Date", default=lambda self: fields.Date.context_today(self))
destination_account_id = fields.Many2one(string="To", comodel_name='account.account', help="Account to transfer to.")
display_currency_helper = fields.Boolean(string="Currency Conversion Helper", compute='_compute_display_currency_helper',
help="Technical field. Used to indicate whether or not to display the currency conversion tooltip. The tooltip informs a currency conversion will be performed with the transfer.")
@api.constrains('percentage', 'action')
def _constraint_percentage(self):
for record in self:
if not (0.0 < record.percentage <= 100.0):
raise UserError(_("Percentage must be between 0 and 100"))
if record.percentage != 100 and record.action != 'change_period':
raise UserError(_("Percentage can only be set for Change Period method"))
def _get_total_balance(self):
self.ensure_one()
return sum(line.amount_residual if line.account_id.reconcile else line.balance for line in self.move_line_ids)
@api.depends('percentage', 'move_line_ids')
def _compute_total_amount(self):
for record in self:
record.total_amount = (record.percentage or 100) * sum(record.move_line_ids.mapped('balance')) / 100
@api.depends('chg_period_date_from', 'chg_period_date_to', 'chg_period_time_division', 'chg_period_percentage')
def _compute_chg_period_line_ids(self):
''' Create the lines representing the way the balance will be smoothed per period. '''
for wizard in self:
one2many_commands = [(5, 0)]
@api.depends('total_amount', 'move_line_ids')
def _compute_percentage(self):
for record in self:
total = (sum(record.move_line_ids.mapped('balance')) or record.total_amount)
if total != 0:
record.percentage = (record.total_amount / total) * 100
date_from = wizard.chg_period_date_from
date_to = wizard.chg_period_date_to
percentage = (wizard.chg_period_percentage or 0.0) / 100.0
if date_from and date_to and percentage:
total_balance = wizard.company_currency_id.round(wizard._get_total_balance() * percentage)
total_days = 0.0
date_distribution = []
while date_from <= date_to:
next_month_date = (date_from + relativedelta(months=1)).replace(day=1)
period_date = next_month_date - relativedelta(days=1)
period_days = (period_date - date_from).days
date_distribution.append((period_date, period_days))
date_from = next_month_date
total_days += period_days
total_reported_balance = 0.0
for period_date, nb_days in date_distribution:
if wizard.chg_period_time_division:
period_balance = wizard.company_currency_id.round(total_balance * nb_days / total_days)
else:
period_balance = wizard.company_currency_id.round(total_balance / len(date_distribution))
one2many_commands.append((0, 0, {
'wizard_id': wizard.id,
'date': period_date,
'balance': period_balance,
}))
total_reported_balance += period_balance
# When the percentage is 100%, ensure the full balance is dispatched into the periods.
# To avoid a huge gap in the last line, we ensure the rounding error is also smoothed.
# For example: smoothing 100 into 12 months:
# 12 lines of 8.33 are generated at this point. Since 8.33 * 12 = 99.96, the error of 0.04 will be
# smoothed by adding 0.01 to the last 4 periods.
if wizard.chg_period_percentage == 100.0:
total_rounding_error = wizard.company_currency_id.round(total_balance - total_reported_balance)
nber_rounding_steps = int(abs(total_rounding_error / wizard.company_currency_id.rounding))
rounding_error = round(
nber_rounding_steps and total_rounding_error / nber_rounding_steps or 0.0,
wizard.company_currency_id.decimal_places,
)
nber_periods = len(one2many_commands) - 1
if len(one2many_commands) > nber_rounding_steps:
for i in range(nber_rounding_steps):
one2many_commands[nber_periods - i][2]['balance'] += rounding_error
wizard.chg_period_line_ids = one2many_commands
@api.depends('chg_period_date_from', 'chg_period_date_to')
def _compute_chg_period_warning_message(self):
for wizard in self:
if wizard.chg_period_date_from and wizard.chg_period_date_to and wizard.chg_period_date_from > wizard.chg_period_date_to:
wizard.chg_period_warning_message = _("Check the dates. The period cannot start later than its own end.")
else:
record.percentage = 100
wizard.chg_period_warning_message = False
@api.depends('move_line_ids')
def _compute_account_type(self):
for record in self:
record.account_type = 'income' if sum(record.move_line_ids.mapped('balance')) < 0 else 'expense'
@api.depends('move_line_ids.balance')
def _compute_from_move_line_ids(self):
for wizard in self:
wizard.total_amount = wizard._get_total_balance()
wizard.account_type = 'income' if wizard.total_amount < 0.0 else 'expense'
@api.depends('destination_account_id')
def _compute_display_currency_helper(self):
@@ -167,80 +239,113 @@ class AutomaticEntryWizard(models.TransientModel):
def _get_move_dict_vals_change_period(self):
# set the change_period account on the selected journal items
accrual_account = self.revenue_accrual_account if self.account_type == 'income' else self.expense_accrual_account
total_percentage = sum(self.chg_period_line_ids.mapped('percentage'))
move_data = {'new_date': {
'currency_id': self.journal_id.currency_id.id or self.journal_id.company_id.currency_id.id,
'move_type': 'entry',
'line_ids': [],
'ref': _('Adjusting Entry'),
'date': fields.Date.to_string(self.date),
'journal_id': self.journal_id.id,
}}
# complete the account.move data
for date, grouped_lines in groupby(self.move_line_ids, lambda m: m.move_id.date):
move_data = {
'original_dates': {},
'new_dates': {},
}
# ==== Prepare journal entries (account.move) ====
for date, grouped_lines in groupby(self.move_line_ids, lambda line: line.date):
grouped_lines = list(grouped_lines)
amount = sum(l.balance for l in grouped_lines)
move_data[date] = {
'currency_id': self.journal_id.currency_id.id or self.journal_id.company_id.currency_id.id,
move_data['original_dates'][date] = {
'move_type': 'entry',
'line_ids': [],
'ref': self._format_strings(_('Adjusting Entry of {date} ({percent:f}% recognized on {new_date})'), grouped_lines[0].move_id, amount),
'date': fields.Date.to_string(date),
'journal_id': self.journal_id.id,
'line_ids': [],
}
# compute the account.move.lines and the total amount per move
for line in self.chg_period_line_ids:
ref = _("Adjusting Entry made on {date} ({percent:f}%)").format(
date=format_date(self.env, line.date),
percent=line.percentage,
)
move_data['new_dates'][line.date] = {
'move_type': 'entry',
'ref': ref,
'date': fields.Date.to_string(line.date),
'journal_id': self.journal_id.id,
'line_ids': [],
}
# ==== Prepare journal items (account.move.line) ====
for aml in self.move_line_ids:
# account.move.line data
reported_debit = aml.company_id.currency_id.round((self.percentage / 100) * aml.debit)
reported_credit = aml.company_id.currency_id.round((self.percentage / 100) * aml.credit)
reported_amount_currency = aml.currency_id.round((self.percentage / 100) * aml.amount_currency)
move_data['new_date']['line_ids'] += [
total_reported_balance = 0.0
total_reported_amount_currency = 0.0
# Avoid rounding issues with 100%.
if round(total_percentage, 2) == 100.0:
total_balance_to_report = aml.balance
total_amount_currency_to_report = aml.amount_currency
else:
total_balance_to_report = aml.company_currency_id.round((total_percentage / 100) * aml.balance)
total_amount_currency_to_report = aml.currency_id.round((total_percentage / 100) * aml.amount_currency)
for i, line in enumerate(self.chg_period_line_ids):
# Avoid rounding issue on the last line.
if i == len(self.chg_period_line_ids) - 1:
reported_balance = total_balance_to_report - total_reported_balance
reported_amount_currency = total_amount_currency_to_report - total_reported_amount_currency
else:
reported_balance = aml.company_currency_id.round((line.percentage / 100) * aml.balance)
reported_amount_currency = aml.currency_id.round((line.percentage / 100) * aml.amount_currency)
total_reported_balance += reported_balance
total_reported_amount_currency += reported_amount_currency
move_data['new_dates'][line.date]['line_ids'] += [
(0, 0, {
'name': aml.name or '',
'currency_id': aml.currency_id.id,
'account_id': aml.account_id.id,
'partner_id': aml.partner_id.id,
'debit': reported_balance if reported_balance > 0.0 else 0.0,
'credit': -reported_balance if reported_balance < 0.0 else 0.0,
'amount_currency': reported_amount_currency,
}),
(0, 0, {
'name': _('Adjusting Entry'),
'currency_id': aml.currency_id.id,
'account_id': accrual_account.id,
'partner_id': aml.partner_id.id,
'debit': -reported_balance if reported_balance < 0.0 else 0.0,
'credit': reported_balance if reported_balance > 0.0 else 0.0,
'amount_currency': -reported_amount_currency,
}),
]
move_data['original_dates'][aml.date]['line_ids'] += [
(0, 0, {
'name': aml.name or '',
'debit': reported_debit,
'credit': reported_credit,
'amount_currency': reported_amount_currency,
'currency_id': aml.currency_id.id,
'account_id': aml.account_id.id,
'partner_id': aml.partner_id.id,
'debit': -total_reported_balance if total_reported_balance < 0.0 else 0.0,
'credit': total_reported_balance if total_reported_balance > 0.0 else 0.0,
'amount_currency': -total_reported_amount_currency,
}),
(0, 0, {
'name': _('Adjusting Entry'),
'debit': reported_credit,
'credit': reported_debit,
'amount_currency': -reported_amount_currency,
'currency_id': aml.currency_id.id,
'account_id': accrual_account.id,
'partner_id': aml.partner_id.id,
}),
]
move_data[aml.move_id.date]['line_ids'] += [
(0, 0, {
'name': aml.name or '',
'debit': reported_credit,
'credit': reported_debit,
'amount_currency': -reported_amount_currency,
'currency_id': aml.currency_id.id,
'account_id': aml.account_id.id,
'partner_id': aml.partner_id.id,
}),
(0, 0, {
'name': _('Adjusting Entry'),
'debit': reported_debit,
'credit': reported_credit,
'amount_currency': reported_amount_currency,
'currency_id': aml.currency_id.id,
'account_id': accrual_account.id,
'partner_id': aml.partner_id.id,
'debit': total_reported_balance if total_reported_balance > 0.0 else 0.0,
'credit': -total_reported_balance if total_reported_balance < 0.0 else 0.0,
'amount_currency': total_reported_amount_currency,
}),
]
move_vals = [m for m in move_data.values()]
return move_vals
return list(move_data['original_dates'].values()) + list(move_data['new_dates'].values())
@api.depends('move_line_ids', 'journal_id', 'revenue_accrual_account', 'expense_accrual_account', 'percentage', 'date', 'account_type', 'action', 'destination_account_id')
@api.depends('move_line_ids', 'journal_id', 'revenue_accrual_account', 'expense_accrual_account',
'date', 'account_type', 'action', 'destination_account_id',
'chg_period_line_ids')
def _compute_move_data(self):
for record in self:
if record.action == 'change_period':
@@ -287,28 +392,44 @@ class AutomaticEntryWizard(models.TransientModel):
def _do_action_change_period(self, move_vals):
accrual_account = self.revenue_accrual_account if self.account_type == 'income' else self.expense_accrual_account
# ==== Create new journal entries ====
created_moves = self.env['account.move'].create(move_vals)
created_moves._post()
# ==== Manage chatters ====
destination_move = created_moves[0]
destination_messages = []
accrual_moves = created_moves[1:]
messages_from = [_("<li>%(link)s</li>", link=self._format_move_link(move)) for move in self.move_line_ids.move_id]
messages_from = ''.join([_("Adjusting Entry generated for these journal entries:<ul>")] + messages_from + ["</ul>"])
messages_to = [_("Adjusting Entries created:<ul>")]
messages_to.append(self._format_strings(
_("<li>%(link)s cancelling {percent:f}%% of {amount}</li>", link=self._format_move_link(destination_move)),
destination_move, self.total_amount,
))
for accrual_move in accrual_moves:
messages_to.append(self._format_strings(
_("<li>%(link)s postponing it to {new_date}</li>", link=self._format_move_link(accrual_move)),
accrual_move, 0.0,
))
messages_to = ''.join(messages_to + ["</ul>"])
for move in self.move_line_ids.move_id:
amount = sum((self.move_line_ids._origin & move.line_ids).mapped('balance'))
accrual_move = created_moves[1:].filtered(lambda m: m.date == move.date)
move.message_post(body=messages_to)
destination_move.message_post(body=messages_from)
for accrual_move in accrual_moves:
accrual_move.message_post(body=messages_from)
if accrual_account.reconcile:
to_reconcile = (accrual_move + destination_move).mapped('line_ids').filtered(lambda line: line.account_id == accrual_account)
to_reconcile.reconcile()
move.message_post(body=self._format_strings(_('Adjusting Entries have been created for this invoice:<ul><li>%(link1)s cancelling '
'{percent:f}%% of {amount}</li><li>%(link0)s postponing it to {new_date}</li></ul>',
link0=self._format_move_link(destination_move),
link1=self._format_move_link(accrual_move),
), move, amount))
destination_messages += [self._format_strings(_('Adjusting Entry {link}: {percent:f}% of {amount} recognized from {date}'), move, amount)]
accrual_move.message_post(body=self._format_strings(_('Adjusting Entry for {link}: {percent:f}% of {amount} recognized on {new_date}'), move, amount))
destination_move.message_post(body='<br/>\n'.join(destination_messages))
# ==== Reconcile ====
if accrual_account.reconcile and all(move.state == 'posted' for move in created_moves):
created_moves.line_ids.filtered(lambda line: line.account_id == accrual_account and not line.reconciled).reconcile()
# ==== Redirect ====
# open the generated entries
action = {
'name': _('Generated Entries'),
'domain': [('id', 'in', created_moves.ids)],
@@ -389,7 +510,7 @@ class AutomaticEntryWizard(models.TransientModel):
def _format_strings(self, string, move, amount):
return string.format(
percent=self.percentage,
percent=sum(self.chg_period_line_ids.mapped('percentage')),
name=move.name,
id=move.id,
amount=formatLang(self.env, abs(amount), currency_obj=self.company_id.currency_id),
@@ -399,3 +520,43 @@ class AutomaticEntryWizard(models.TransientModel):
new_date=self.date and format_date(self.env, self.date) or _('[Not set]'),
account_target_name=self.destination_account_id.display_name,
)
class AutomaticEntryWizardLine(models.TransientModel):
_name = "account.automatic.entry.wizard.line"
_description = "Create Automatic Entries Line"
wizard_id = fields.Many2one(
comodel_name='account.automatic.entry.wizard',
required=True, readonly=True, ondelete='cascade')
date = fields.Date(
string="Date",
required=True,
default=lambda self: fields.Date.context_today(self))
percentage = fields.Float(
string="Percentage",
store=True, readonly=False,
compute='_compute_percentage',
help="Percentage of each line to execute the action on.")
balance = fields.Monetary(
string="Amount",
store=True, readonly=False,
compute='_compute_balance',
currency_field='company_currency_id',
help="Total amount impacted by the automatic entry.")
# ==== Display purpose fields ====
company_currency_id = fields.Many2one(
comodel_name='res.currency',
related='wizard_id.company_id.currency_id')
@api.depends('balance')
def _compute_percentage(self):
for line in self:
total = abs(line.wizard_id._get_total_balance())
line.percentage = total and (abs(line.balance) / total) * 100.0 or 0.0
@api.depends('percentage')
def _compute_balance(self):
for line in self:
line.balance = line.company_currency_id.round((line.percentage / 100.0) * line.wizard_id._get_total_balance())
@@ -9,34 +9,85 @@
<field name="account_type" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="display_currency_helper" invisible="1"/>
<div attrs="{'invisible': [('display_currency_helper', '=', False)]}" class="alert alert-info text-center" role="status">
The selected destination account is set to use a specific currency. Every entry transferred to it will be converted into this currency, causing
the loss of any pre-existing foreign currency amount.
</div>
<div class="alert alert-warning"
role="alert"
style="margin-bottom:0px;"
attrs="{'invisible': ['|', ('action', '!=', 'change_period'), ('chg_period_warning_message', '=', False)]}">
<field name="chg_period_warning_message" nolabel="1"/>
</div>
<field name="action" invisible="context.get('hide_automatic_options')" widget="radio" options="{'horizontal': true}"/>
<group>
<group>
<field name="total_amount"/>
<field name="journal_id"/>
</group>
<group attrs="{'invisible': [('action', '!=', 'change_period')]}">
<field name="date" string="Recognition Date"/>
<field name="expense_accrual_account" string="Accrued Account"
<field name="expense_accrual_account"
string="Accrued Account"
attrs="{'invisible': [('account_type', '!=', 'expense')], 'required': [('account_type', '=', 'expense'), ('action', '=', 'change_period')]}"/>
<field name="revenue_accrual_account" string="Accrued Account"
<field name="revenue_accrual_account"
string="Accrued Account"
attrs="{'invisible': [('account_type', '!=', 'income')], 'required': [('account_type', '=', 'income'), ('action', '=', 'change_period')]}"/>
</group>
<group attrs="{'invisible': [('action', '!=', 'change_account')]}">
<field name="date" string="Transfer Date"/>
<field name="destination_account_id" attrs="{'required': [('action', '=', 'change_account')]}" domain="[('company_id', '=', company_id)]"/>
</group>
<group>
<label for="total_amount" string="Adjusting Amount" attrs="{'invisible': [('action', '!=', 'change_period')]}"/>
<div attrs="{'invisible': [('action', '!=', 'change_period')]}">
<field name="percentage" style="width:40% !important" class="oe_inline" attrs="{'readonly': [('action', '!=', 'change_period')]}"/>%<span class="px-3"></span>(<field name="total_amount" class="oe_inline"/>)
</div>
<field name="total_amount" readonly="1" attrs="{'invisible': [('action', '=', 'change_period')]}"/>
<field name="journal_id"/>
<field name="date"/>
<field name="destination_account_id"
attrs="{'required': [('action', '=', 'change_account')]}"
domain="[('company_id', '=', company_id)]"/>
</group>
</group>
<label for="preview_move_data" string="The following Journal Entries will be generated"/>
<field name="preview_move_data" widget="grouped_view_widget"/>
<notebook>
<page string="Automatic re-allocation"
attrs="{'invisible': [('action', '!=', 'change_period')]}">
<span>Create one journal entry per month automatically</span>
<group>
<group>
<label for="chg_period_date_from" string="From"/>
<div class="d-flex">
<field name="chg_period_date_from"/>
<span class="oe_inline o_form_label mx-3"> to </span>
<field name="chg_period_date_to"/>
</div>
<field name="chg_period_time_division"/>
</group>
<group>
<label for="chg_period_percentage" string="Re-allocation percentage"/>
<div class="d-flex">
<field name="chg_period_percentage"/>
<span class="oe_inline o_form_label mx-3">%</span>
</div>
</group>
</group>
</page>
<page string="Manual re-allocation" attrs="{'invisible': [('action', '!=', 'change_period')]}">
<field name="chg_period_line_ids">
<tree editable="bottom" default_order="date desc">
<field name="company_currency_id" invisible="1"/>
<field name="date"/>
<field name="percentage" sum="Total Percentage"/>
<field name="balance" sum="Total Balance"/>
</tree>
</field>
</page>
<page string="Preview">
<label for="preview_move_data" string="The following Journal Entries will be generated"/>
<field name="preview_move_data" widget="grouped_view_widget"/>
</page>
</notebook>
<footer>
<button string="Create Journal Entries" name="do_action" type="object" class="oe_highlight"/>
<button string="Cancel" class="btn btn-secondary" special="cancel"/>