Skip to content

Commit

Permalink
[ADD] account_fiscalyear_closing: Generic engine for closing operations
Browse files Browse the repository at this point in the history
  • Loading branch information
antespi authored and yajo committed Jul 10, 2017
1 parent 52738ad commit dbc8ca9
Show file tree
Hide file tree
Showing 22 changed files with 1,091 additions and 0 deletions.
78 changes: 78 additions & 0 deletions account_fiscal_year_closing/README.rst
@@ -0,0 +1,78 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

===================
Fiscal year closing
===================

This module implements a generic fiscal year closing for those countries
where closing and opening moves are mandatory in accounting books.

Installation
============

To install this module, you need to:

#. Do this ...

Configuration
=============

To configure this module, you need to:

#. Go to ...

Usage
=====

To use this module, you need to:

#. Go to ...

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/89/9.0


Known issues / Roadmap
======================

* ...

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/account-closing/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.

Credits
=======

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Antonio Espinosa <antonio.espinosa@tecnativa.com>
* Pedro M. Baeza <pedro.baeza@tecnativa.com>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
4 changes: 4 additions & 0 deletions account_fiscal_year_closing/__init__.py
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import models
28 changes: 28 additions & 0 deletions account_fiscal_year_closing/__openerp__.py
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Fiscal year closing",
"summary": "Generic fiscal year closing wizard",
"version": "9.0.1.0.0",
"category": "Accounting & Finance",
"website": "https://www.tecnativa.org/",
"author": "Tecnativa, "
"Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"depends": [
"account_reversal",
],
"data": [
"security/account_fiscalyear_closing_security.xml",
"security/ir.model.access.csv",
"views/account_fiscalyear_closing_view.xml",
"views/account_fiscalyear_closing_mapping_view.xml",
"views/account_fiscalyear_closing_type_view.xml",
"views/account_fiscalyear_closing_config_view.xml",
"views/account_move_view.xml",
"views/account_move_line_view.xml",
],
}
Empty file.
8 changes: 8 additions & 0 deletions account_fiscal_year_closing/models/__init__.py
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-

from . import account_fiscalyear_closing
from . import account_fiscalyear_closing_config
from . import account_fiscalyear_closing_mapping
from . import account_fiscalyear_closing_type
from . import account_move
from . import account_move_line
250 changes: 250 additions & 0 deletions account_fiscal_year_closing/models/account_fiscalyear_closing.py
@@ -0,0 +1,250 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
from openerp import api, fields, models, _
from openerp.exceptions import ValidationError
from dateutil.relativedelta import relativedelta

_logger = logging.getLogger(__name__)


class AccountFiscalyearClosing(models.Model):
_name = "account.fiscalyear.closing"
_description = "Fiscal year closing"

def _default_fiscalyear(self):
company = self._default_company()
last_month_day = '%s-%s' % (
company.fiscalyear_last_month or '12',
company.fiscalyear_last_day or '31',
)
lock_date = company.fiscalyear_lock_date or fields.Date.today()
fiscalyear = int(lock_date[:4])
if lock_date[5:] < last_month_day:
fiscalyear = fiscalyear - 1
return str(fiscalyear)

def _default_name(self):
return self._default_fiscalyear()

def _default_company(self):
return self.env['res.company']._company_default_get(
'account.fiscalyear.closing')

def _default_date_start(self):
date_end = fields.Date.from_string(self._default_date_end())
return fields.Date.to_string(
date_end - relativedelta(years=1) + relativedelta(days=1))

def _default_date_end(self):
company = self._default_company()
fiscalyear = self._default_fiscalyear()
return '%s-%s-%s' % (
fiscalyear,
company.fiscalyear_last_month or '12',
company.fiscalyear_last_day or '31',
)

def _default_date_opening(self):
date_end = fields.Date.from_string(self._default_date_end())
return fields.Date.to_string(
date_end + relativedelta(days=1))

def _default_journal(self):
# Used in inherited models
return self.env['account.journal'].search([
('code', '=', 'MISC'),
], limit=1)

def _default_move_config_ids(self):
# To be inherited in localization modules
return []

name = fields.Char(
string="Description", default=_default_name, required=True)
company_id = fields.Many2one(
comodel_name='res.company', string="Company", ondelete='cascade',
readonly=True, required=True, default=_default_company)
state = fields.Selection(selection=[
('draft', 'Draft'),
('calculated', 'Processed'),
('posted', 'Posted'),
('cancelled', 'Cancelled'),
], string="State", readonly=True, default='draft')
calculation_date = fields.Datetime(
string="Calculation date", readonly=True)

date_start = fields.Date(
string="From date", default=_default_date_start, required=True)
date_end = fields.Date(
string="To date", default=_default_date_end, required=True)
date_opening = fields.Date(
string="Opening date", default=_default_date_opening, required=True)

check_draft_moves = fields.Boolean(
string="Check draft moves", default=True,
help="Checks that there are no draft moves on the fiscal year "
"that is being closed. Non-confirmed moves won't be taken in "
"account on the closing operations.")

move_config_ids = fields.One2many(
comodel_name='account.fiscalyear.closing.config',
inverse_name='fyc_id', string="Moves configuration",
default=_default_move_config_ids)
move_ids = fields.One2many(
comodel_name='account.move', inverse_name='fyc_id', string="Moves",
readonly=True)
move_line_ids = fields.One2many(
comodel_name='account.move.line', inverse_name='fyc_id',
string="Move lines", readonly=True)

@api.model
def _account_closing_types_get(self, types):
types = types or {}
account_closing_types = []
for xmlid, closing_type in types.iteritems():
account_type = self.env.ref(xmlid)
if account_type:
account_closing_types.append({
'account_type_id': account_type.id,
'closing_type': closing_type,
})
else:
_logger.warning("Account type '%s' not found", xmlid)
return account_closing_types

@api.model
def _account_mappings_get(self, company, mapping):
# Generic account mappings generator, used in inherited models
# 'mapping' is a list of 3-tuples with this format:
# (<source account code>, <dest account code>, <description>)
account_mappings = []
for src, dst, name in mapping:
# Find the source account
src_accounts = self.env['account.account'].search([
('company_id', '=', company.id),
('code', '=ilike', src),
], order="code ASC")
dst_account = False
if src_accounts:
# Find the destination account
if dst:
dst_account = self.env['account.account'].search([
('company_id', '=', company.id),
('code', '=ilike', dst),
], limit=1)
# Use an error name if no destination account found
if not dst_account:
name = _("No destination account '%s' found for "
"account '%s'") % (dst, src)
if not name:
# Use first source account name if not provided
name = src_accounts[0].name
data = {
'name': name,
'src_account_ids': [(6, False, src_accounts.ids)],
}
if dst_account:
data['dst_account_id'] = dst_account.id
account_mappings.append(data)
return account_mappings

@api.multi
def draft_moves_check(self):
for closing in self:
draft_moves = self.env['account.move'].search([
('company_id', '=', closing.company_id.id),
('state', '=', 'draft'),
('date', '>=', closing.date_start),
('date', '<=', closing.date_end),
])
if draft_moves:
msg = _('One or more draft moves found: \n')
for move in draft_moves:
msg += ('ID: %s, Date: %s, Number: %s, Ref: %s\n' %
(move.id, move.date, move.name, move.ref))
raise ValidationError(msg)
return True

@api.multi
def calculate(self):
for closing in self:
# Perform checks, raise exception if check fails
if closing.check_draft_moves:
closing.draft_moves_check()
# Create moves following move_config_ids
for config in closing.move_config_ids.filtered('enable'):
config.moves_create()
return True

@api.multi
def _moves_remove(self):
for closing in self.with_context(search_fyc_moves=True):
closing.move_ids.button_cancel()
closing.move_ids.unlink()
return True

@api.multi
def button_calculate(self):
res = self.with_context(search_fyc_moves=True).calculate()
self.write({
'state': 'calculated',
'calculation_date': fields.Datetime.now(),
})
return res

@api.multi
def button_recalculate(self):
self._moves_remove()
return self.button_calculate()

@api.multi
def button_post(self):
# Post moves
for closing in self.with_context(search_fyc_moves=True):
closing.move_ids.post()
for config in closing.move_config_ids.filtered('reconcile'):
config.move_id.move_reverse_reconcile()
self.write({'state': 'posted'})
return True

@api.multi
def button_open_moves(self):
# Return an action for showing moves
return {
'name': _('Fiscal closing moves'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move',
'domain': [('fyc_id', 'in', self.ids)],
'context': {'search_fyc_moves': True},
}

@api.multi
def button_open_move_lines(self):
return {
'name': _('Fiscal closing move lines'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.move.line',
'domain': [('fyc_id', 'in', self.ids)],
'context': {'search_fyc_moves': True},
}

@api.multi
def button_cancel(self):
self._moves_remove()
self.write({'state': 'cancelled'})
return True

@api.multi
def button_recover(self):
self.write({
'state': 'draft',
'calculation_date': False,
})
return True

0 comments on commit dbc8ca9

Please sign in to comment.