diff --git a/l10n_ro_account_period_close/__init__.py b/l10n_ro_account_period_close/__init__.py new file mode 100644 index 000000000..aee8895e7 --- /dev/null +++ b/l10n_ro_account_period_close/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/l10n_ro_account_period_close/__manifest__.py b/l10n_ro_account_period_close/__manifest__.py new file mode 100644 index 000000000..b02b4d13e --- /dev/null +++ b/l10n_ro_account_period_close/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2018 Forest and Biomass Romania +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Romania - Account Period Closing', + 'summary': 'Romania - Account Period Closing', + 'version': '11.0.1.0.0', + 'category': 'Localization', + 'author': 'Forest and Biomass Romania, ' + 'Odoo Community Association (OCA)', + 'website': 'https://www.forbiom.eu', + 'license': 'AGPL-3', + 'installable': True, + 'depends': ['account'], + 'data': [ + 'views/account_period_close_view.xml', + 'wizards/wizard_account_period_closing_view.xml', + 'security/account_security.xml', + 'security/ir.model.access.csv', + ], +} diff --git a/l10n_ro_account_period_close/models/__init__.py b/l10n_ro_account_period_close/models/__init__.py new file mode 100644 index 000000000..1e271ee79 --- /dev/null +++ b/l10n_ro_account_period_close/models/__init__.py @@ -0,0 +1,2 @@ +from . import account +from . import account_period_close diff --git a/l10n_ro_account_period_close/models/account.py b/l10n_ro_account_period_close/models/account.py new file mode 100644 index 000000000..af4de75d9 --- /dev/null +++ b/l10n_ro_account_period_close/models/account.py @@ -0,0 +1,22 @@ +# Copyright 2018 Forest and Biomass Romania +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class Account(models.Model): + _inherit = 'account.account' + + close_check = fields.Boolean( + 'Bypass Closing Side Check', + help='By checking this when you close a period, it will not respect ' + 'the side of closing, meaning: expenses closed on credit side, ' + 'incomed closed on debit side. \n You should check the 711xxx ' + 'accounts.') + + +class AccountMove(models.Model): + _inherit = 'account.move' + + close_id = fields.Many2one( + 'account.period.closing', 'Closed Account Period') diff --git a/l10n_ro_account_period_close/models/account_period_close.py b/l10n_ro_account_period_close/models/account_period_close.py new file mode 100644 index 000000000..edc1b77ac --- /dev/null +++ b/l10n_ro_account_period_close/models/account_period_close.py @@ -0,0 +1,229 @@ +# Copyright 2018 Forest and Biomass Romania +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class AccountPeriodClosing(models.Model): + _name = 'account.period.closing' + _description = 'Account Period Closing' + + name = fields.Char('Name', required=True) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + type = fields.Selection( + [ + ('income', 'Incomes'), + ('expense', 'Expenses'), + ('selected', 'Selected') + ], string='Type', required=True) + close_result = fields.Boolean('Close debit and credit accounts') + journal_id = fields.Many2one( + 'account.journal', string='Journal', required=True) + account_ids = fields.Many2many( + 'account.account', string='Accounts to close') + debit_account_id = fields.Many2one( + 'account.account', + 'Closing account, debit', + required=True, + domain="[('company_id', '=', company_id)]" + ) + credit_account_id = fields.Many2one( + 'account.account', + 'Closing account, credit', + required=True, + domain="[('company_id', '=', company_id)]" + ) + move_ids = fields.One2many('account.move', 'close_id', 'Closing Moves') + + @api.onchange('company_id', 'type') + def _onchange_type(self): + acc_type = False + accounts = self.env['account.account'] + if self.type == 'income': + acc_type = self.env.ref( + 'account.data_account_type_revenue').id + elif self.type == 'expense': + acc_type = self.env.ref( + 'account.data_account_type_expenses').id + if acc_type: + accounts = self.env['account.account'].search([ + ('user_type_id', '=', acc_type), + ('company_id', '=', self.company_id.id) + ]) + self.account_ids = accounts + + def _get_accounts(self, accounts, display_account): + """ compute the balance, debit and credit for the provided accounts + :Arguments: + `accounts`: list of accounts record, + `display_account`: it's used to display either all accounts or + those accounts which balance is > 0 + :Returns a list of dict of Accounts with following key and value + `name`: Account name, + `code`: Account code, + `credit`: total amount of credit, + `debit`: total amount of debit, + `balance`: total amount of balance, + """ + + account_result = {} + # Prepare sql query base on selected parameters from wizard + tables, where_clause, where_params = self.env[ + 'account.move.line']._query_get() + tables = tables.replace('"', '') + if not tables: + tables = 'account_move_line' + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + # compute the balance, debit and credit for the provided accounts + request = ("SELECT account_id AS id, " + "SUM(debit) AS debit, " + "SUM(credit) AS credit, " + "(SUM(debit) - SUM(credit)) AS balance" + + " FROM " + tables + + " WHERE account_id IN %s " + filters + + " GROUP BY account_id") + params = (tuple(accounts.ids),) + tuple(where_params) + self.env.cr.execute(request, params) + for row in self.env.cr.dictfetchall(): + account_result[row.pop('id')] = row + + account_res = [] + for account in accounts: + res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance']) + currency = account.currency_id if account.currency_id else \ + account.company_id.currency_id + res['id'] = account.id + res['code'] = account.code + res['name'] = account.name + if account.id in account_result: + res['debit'] = account_result[account.id].get('debit') + res['credit'] = account_result[account.id].get('credit') + res['balance'] = account_result[account.id].get('balance') + if display_account == 'all': + account_res.append(res) + if display_account == 'not_zero' and \ + not currency.is_zero(res['balance']): + account_res.append(res) + if display_account == 'movement' and \ + (not currency.is_zero(res['debit']) or + not currency.is_zero(res['credit'])): + account_res.append(res) + return account_res + + @api.multi + def close(self, date_from=None, date_to=None): + """ This method will create the closing move for the + date interval selected.""" + account_obj = self.env['account.account'] + journal_id = self.journal_id.id + for closing in self: + ctx = self.env.context.copy() + ctx['strict_range'] = True + ctx['date_from'] = date_from + ctx['date_to'] = date_to + account_res = self.with_context(ctx)._get_accounts( + closing.account_ids, 'not_zero') + move = self.env['account.move'].create({ + 'date': date_to, + 'journal_id': journal_id, + 'close_id': closing.id, + 'company_id': closing.company_id.id + }) + amount = 0.0 + for account in account_res: + if account['balance'] != 0.0: + balance = account['balance'] + check = account_obj.browse(account['id']).close_check + if closing.type == 'expense' and not check: + val = { + 'name': 'Closing ' + closing.name, + 'move_id': move.id, + 'account_id': account['id'], + 'credit': balance or 0.0, + 'debit': 0.0, + } + elif closing.type == 'income' and not check: + val = { + 'name': 'Closing ' + closing.name, + 'move_id': move.id, + 'account_id': account['id'], + 'credit': 0.0, + 'debit': (-1 * balance) or 0.0, + } + else: + val = { + 'name': 'Closing ' + closing.name, + 'move_id': move.id, + 'account_id': account['id'], + 'credit': balance if balance > 0.0 else 0.0, + 'debit': -balance if balance < 0.0 else 0.0, + } + amount += balance + self.env['account.move.line'].with_context( + check_move_validity=False).create(val) + + diff_line = { + 'name': 'Closing ' + closing.name, + 'move_id': move.id, + 'account_id': + closing.debit_account_id.id if + amount >= 0 else closing.credit_account_id.id, + 'credit': -amount if amount <= 0.0 else 0.0, + 'debit': amount if amount >= 0.0 else 0.0, + } + self.env['account.move.line'].with_context( + check_move_validity=False).create(diff_line) + if self.close_result and amount != 0.0: + debit_acc = closing.debit_account_id + credit_acc = closing.credit_account_id + debit = credit = new_amount = 0.0 + ctx1 = dict(self._context) + ctx1.update({'date_from': False, 'date_to': date_to}) + accounts = account_obj.browse( + [closing.debit_account_id.id, + closing.credit_account_id.id]) + account_res = self.with_context(ctx1)._get_accounts( + accounts, 'all') + for acc in account_res: + if acc['id'] == closing.debit_account_id.id: + debit = acc['balance'] + if acc['id'] == closing.credit_account_id.id: + credit = acc['balance'] + old_balance = debit - (-1 * credit) + if credit and debit: + if old_balance > 0: + debit_acc = closing.credit_account_id + credit_acc = closing.debit_account_id + elif old_balance < 0: + debit_acc = closing.debit_account_id + credit_acc = closing.credit_account_id + if abs(debit) > abs(credit): + new_amount = -1 * credit + else: + new_amount = debit + diff_line = { + 'name': 'Closing ' + closing.name + + ' ' + str(debit_acc.code), + 'move_id': move.id, + 'account_id': debit_acc.id, + 'credit': 0.0, + 'debit': new_amount, + } + self.env['account.move.line'].with_context( + check_move_validity=False).create(diff_line) + diff_line = { + 'name': 'Closing ' + closing.name + + ' ' + str(credit_acc.code), + 'move_id': move.id, + 'account_id': credit_acc.id, + 'credit': new_amount, + 'debit': 0.0, + } + self.env['account.move.line'].with_context( + check_move_validity=False).create(diff_line) + move.post() diff --git a/l10n_ro_account_period_close/security/account_security.xml b/l10n_ro_account_period_close/security/account_security.xml new file mode 100644 index 000000000..e87d6a629 --- /dev/null +++ b/l10n_ro_account_period_close/security/account_security.xml @@ -0,0 +1,11 @@ + + + + + Account Period Closing + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + diff --git a/l10n_ro_account_period_close/security/ir.model.access.csv b/l10n_ro_account_period_close/security/ir.model.access.csv new file mode 100644 index 000000000..7071f08c2 --- /dev/null +++ b/l10n_ro_account_period_close/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_period_closing_user,account.period.closing,model_account_period_closing,account.group_account_user,1,1,1,0 +access_account_period_closing_manager,account.period.closing,model_account_period_closing,account.group_account_manager,1,1,1,1 diff --git a/l10n_ro_account_period_close/static/description/icon.png b/l10n_ro_account_period_close/static/description/icon.png new file mode 100644 index 000000000..7ba9c1019 Binary files /dev/null and b/l10n_ro_account_period_close/static/description/icon.png differ diff --git a/l10n_ro_account_period_close/views/account_period_close_view.xml b/l10n_ro_account_period_close/views/account_period_close_view.xml new file mode 100644 index 000000000..84bcc45b8 --- /dev/null +++ b/l10n_ro_account_period_close/views/account_period_close_view.xml @@ -0,0 +1,81 @@ + + + + account.account.form + account.account + form + + + + + + + + + + account.period.closing.tree + account.period.closing + form + + + + + + + + + + + + account.period.closing.form + account.period.closing + form + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + Account Period Closing + account.period.closing + form + tree,form + + + + + + +
diff --git a/l10n_ro_account_period_close/wizards/__init__.py b/l10n_ro_account_period_close/wizards/__init__.py new file mode 100644 index 000000000..db467347a --- /dev/null +++ b/l10n_ro_account_period_close/wizards/__init__.py @@ -0,0 +1 @@ +from . import wizard_account_period_closing diff --git a/l10n_ro_account_period_close/wizards/wizard_account_period_closing.py b/l10n_ro_account_period_close/wizards/wizard_account_period_closing.py new file mode 100644 index 000000000..a4a3592e1 --- /dev/null +++ b/l10n_ro_account_period_close/wizards/wizard_account_period_closing.py @@ -0,0 +1,52 @@ +# Copyright 2018 Forest and Biomass Romania +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models + + +class WizardAccountPeriodClosing(models.TransientModel): + _name = "account.period.closing.wizard" + _description = "Wizard for Account Period Closing" + + def _get_default_date_from(self): + today = fields.Date.from_string(fields.Date.today()) + return today + relativedelta(day=1, months=-1) + + def _get_default_date_to(self): + today = fields.Date.from_string(fields.Date.today()) + return today + relativedelta(day=1, days=-1) + + closing_id = fields.Many2one( + "account.period.closing", + "Closing Model", + required=True, + ondelete="cascade" + ) + company_id = fields.Many2one( + comodel_name="res.company", + related="closing_id.company_id") + journal_id = fields.Many2one( + comodel_name="account.journal", + related="closing_id.journal_id") + date_range_id = fields.Many2one( + comodel_name="date.range", + string="Date range") + date_from = fields.Date("Start Date", required=True, + default=_get_default_date_from) + date_to = fields.Date("End Date", required=True, + default=_get_default_date_to) + + @api.onchange('date_range_id') + def onchange_date_range_id(self): + """Handle date range change.""" + if self.date_range_id: + self.date_from = self.date_range_id.date_start + self.date_to = self.date_range_id.date_end + + @api.multi + def do_close(self): + self.ensure_one() + self.closing_id.close(self.date_from, self.date_to) + return {'type': 'ir.actions.act_window_close'} diff --git a/l10n_ro_account_period_close/wizards/wizard_account_period_closing_view.xml b/l10n_ro_account_period_close/wizards/wizard_account_period_closing_view.xml new file mode 100644 index 000000000..26c296932 --- /dev/null +++ b/l10n_ro_account_period_close/wizards/wizard_account_period_closing_view.xml @@ -0,0 +1,28 @@ + + + + + account.period.closing.wizard + account.period.closing.wizard + +
+ + + + + + + + + + + + + +
+
+
+
diff --git a/oca_dependencies.txt b/oca_dependencies.txt new file mode 100644 index 000000000..9b5df92e9 --- /dev/null +++ b/oca_dependencies.txt @@ -0,0 +1 @@ +server-ux