mirror of
https://github.com/OCA/account-financial-tools.git
synced 2025-02-02 12:47:26 +02:00
[ADD] new module account_subsequence_fiscal_year
Co-authored-by: Saran @ Ecosoft <saranl@ecosoft.co.th>
This commit is contained in:
8
account_subsequence_fiscal_year/README.rst
Normal file
8
account_subsequence_fiscal_year/README.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
========================================
|
||||
Accounting Subsequences per Fiscal Years
|
||||
========================================
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
1
account_subsequence_fiscal_year/__init__.py
Normal file
1
account_subsequence_fiscal_year/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
27
account_subsequence_fiscal_year/__manifest__.py
Normal file
27
account_subsequence_fiscal_year/__manifest__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
"name": "Accounting Subsequences per Fiscal Years",
|
||||
"summary": "Allow to create sub sequences for account moves number, based"
|
||||
" on the fiscal years settings",
|
||||
"version": "12.0.1.0.0",
|
||||
"category": "Accounting",
|
||||
"author": "GRAP,Odoo Community Association (OCA)",
|
||||
"maintainers": ["legalsylvain"],
|
||||
"website": "http://www.github.com/OCA/account-financial-tools",
|
||||
"license": "AGPL-3",
|
||||
"depends": [
|
||||
"account",
|
||||
],
|
||||
"data": [
|
||||
"views/view_res_config_settings.xml",
|
||||
],
|
||||
"demo": [
|
||||
"demo/res_groups.xml",
|
||||
],
|
||||
"images": [
|
||||
"static/description/res_config_setting.png",
|
||||
],
|
||||
}
|
||||
14
account_subsequence_fiscal_year/demo/res_groups.xml
Normal file
14
account_subsequence_fiscal_year/demo/res_groups.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="account.group_account_user" model="res.groups">
|
||||
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
79
account_subsequence_fiscal_year/i18n/fr.po
Normal file
79
account_subsequence_fiscal_year/i18n/fr.po
Normal file
@@ -0,0 +1,79 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * account_subsequence_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 12.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-05-30 17:11+0000\n"
|
||||
"PO-Revision-Date: 2020-05-30 17:11+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:account_subsequence_fiscal_year.view_res_config_settings_form
|
||||
msgid "<span class=\"o_form_label\">Accounting Subsequences Method</span>"
|
||||
msgstr "<span class=\"o_form_label\">Méthode pour les sous-séquences comptables</span>"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model.fields,field_description:account_subsequence_fiscal_year.field_res_company__account_subsequence_method
|
||||
#: model:ir.model.fields,field_description:account_subsequence_fiscal_year.field_res_config_settings__account_subsequence_method
|
||||
msgid "Accounting Subsequences Method"
|
||||
msgstr "Méthode pour les sous-séquences comptables"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model,name:account_subsequence_fiscal_year.model_account_bank_statement
|
||||
msgid "Bank Statement"
|
||||
msgstr "Relevé bancaire"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: selection:res.company,account_subsequence_method:0
|
||||
msgid "Based on Company Settings"
|
||||
msgstr "Basé sur les paramètres de la société"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: selection:res.company,account_subsequence_method:0
|
||||
msgid "Based on Fiscal Years Settings"
|
||||
msgstr "Basés sur les exercices comptables"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model,name:account_subsequence_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Sociétés"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model,name:account_subsequence_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Paramètres de config"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:account_subsequence_fiscal_year.view_res_config_settings_form
|
||||
msgid "Define how accounting subsequences are generated"
|
||||
msgstr "Définit comment les sous-séquences comptables sont générées"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:account_subsequence_fiscal_year.view_res_config_settings_form
|
||||
msgid "Generation Method"
|
||||
msgstr "Méthode de génération"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model,name:account_subsequence_fiscal_year.model_account_move
|
||||
msgid "Journal Entries"
|
||||
msgstr "Pièces comptables"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: model:ir.model,name:account_subsequence_fiscal_year.model_ir_sequence
|
||||
msgid "Sequence"
|
||||
msgstr "Séquence"
|
||||
|
||||
#. module: account_subsequence_fiscal_year
|
||||
#: code:addons/account_subsequence_fiscal_year/models/ir_sequence.py:45
|
||||
#, python-format
|
||||
msgid "You can not post an accounting entry for the date %s because there is no fiscal year defined at this date."
|
||||
msgstr "Vous ne pouvez pas valider une pièce comptable pour la date du %s car aucun exercice comptable n'est défini à cette date."
|
||||
|
||||
5
account_subsequence_fiscal_year/models/__init__.py
Normal file
5
account_subsequence_fiscal_year/models/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from . import account_bank_statement
|
||||
from . import account_move
|
||||
from . import ir_sequence
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountBankStatement(models.Model):
|
||||
_inherit = "account.bank.statement"
|
||||
|
||||
@api.multi
|
||||
def button_open(self):
|
||||
return super(
|
||||
AccountBankStatement, self.with_context(account_sequence=True)
|
||||
).button_open()
|
||||
14
account_subsequence_fiscal_year/models/account_move.py
Normal file
14
account_subsequence_fiscal_year/models/account_move.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
@api.multi
|
||||
def post(self, invoice=False):
|
||||
return super(
|
||||
AccountMove, self.with_context(account_sequence=True)
|
||||
).post(invoice=invoice)
|
||||
59
account_subsequence_fiscal_year/models/ir_sequence.py
Normal file
59
account_subsequence_fiscal_year/models/ir_sequence.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import _, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class IrSequence(models.Model):
|
||||
_inherit = "ir.sequence"
|
||||
|
||||
def _create_date_range_seq(self, date):
|
||||
AccountFiscalYear = self.env["account.fiscal.year"]
|
||||
IrSequenceDateRange = self.env["ir.sequence.date_range"]
|
||||
if self._context.get("account_sequence", False)\
|
||||
and self.company_id\
|
||||
and self.company_id.account_subsequence_method:
|
||||
|
||||
method = self.company_id.account_subsequence_method
|
||||
object_date = fields.Date.from_string(date)
|
||||
|
||||
if method == "company_setting":
|
||||
base_date = datetime.date(
|
||||
object_date.year,
|
||||
int(self.company_id.fiscalyear_last_month),
|
||||
int(self.company_id.fiscalyear_last_day),
|
||||
)
|
||||
if object_date <= base_date:
|
||||
date_from = base_date + relativedelta(years=-1, days=1)
|
||||
date_to = base_date
|
||||
else:
|
||||
date_from = base_date + relativedelta(days=1)
|
||||
date_to = base_date + relativedelta(years=1)
|
||||
|
||||
elif method == "fiscal_year_setting":
|
||||
fiscal_years = AccountFiscalYear.search(
|
||||
[("date_to", ">=", object_date.strftime("%Y-%m-%d"))],
|
||||
order="date_from desc",
|
||||
limit=1
|
||||
)
|
||||
if not fiscal_years or fiscal_years[0].date_from > object_date:
|
||||
raise ValidationError(_(
|
||||
"You can not post an accounting entry for the"
|
||||
" date %s because there is no fiscal year defined at"
|
||||
" this date.") % (date))
|
||||
date_from = fiscal_years[0].date_from
|
||||
date_to = fiscal_years[0].date_to
|
||||
|
||||
# Create and return new sequence
|
||||
return IrSequenceDateRange.sudo().create({
|
||||
'sequence_id': self.id,
|
||||
'date_from': date_from,
|
||||
'date_to': date_to,
|
||||
})
|
||||
|
||||
return super()._create_date_range_seq(date)
|
||||
16
account_subsequence_fiscal_year/models/res_company.py
Normal file
16
account_subsequence_fiscal_year/models/res_company.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
account_subsequence_method = fields.Selection(
|
||||
string="Accounting Subsequences Method",
|
||||
selection=[
|
||||
("company_setting", "Based on Company Settings"),
|
||||
("fiscal_year_setting", "Based on Fiscal Years Settings"),
|
||||
])
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
account_subsequence_method = fields.Selection(
|
||||
related="company_id.account_subsequence_method",
|
||||
readonly=False,
|
||||
)
|
||||
27
account_subsequence_fiscal_year/readme/CONFIGURE.rst
Normal file
27
account_subsequence_fiscal_year/readme/CONFIGURE.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
To configure this module, you need to:
|
||||
|
||||
* Add your users to the group 'Technical Settings / Show Full Accounting Features'
|
||||
|
||||
* Go to Invoicing > Configuration / Settings
|
||||
|
||||
* configure your accounting settings
|
||||
|
||||
.. figure:: ../static/description/res_config_setting.png
|
||||
|
||||
Depending on what you want, 3 options are available.
|
||||
|
||||
Given the following example:
|
||||
|
||||
- an account move with a date set to 2030-06-01
|
||||
- a company set with ``fiscalyear_last_day = 1`` and ``fiscalyear_last_month = 10``
|
||||
|
||||
* 'empty' (default value):
|
||||
the subsequence will be created "normaly", with ``date_from = 2030-01-01`` and ``date_to = 2030-12-31``
|
||||
|
||||
* 'Based on Company Settings':
|
||||
the subsequence will be created depending on the values of ``fiscalyear_last_day``
|
||||
and ``fiscalyear_last_month``, so ``date_from = 2029-10-01`` and ``date_to = 2030-09-30``
|
||||
|
||||
* 'Based on Fiscal Years Settings':
|
||||
the subsequence will be created with the values defined in the according fiscal year.
|
||||
Note that if no fiscal year is found, the generation will fail.
|
||||
1
account_subsequence_fiscal_year/readme/CONTRIBUTORS.rst
Normal file
1
account_subsequence_fiscal_year/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1 @@
|
||||
* Sylvain LE GAL <https://twitter.com/legalsylvain>
|
||||
9
account_subsequence_fiscal_year/readme/DESCRIPTION.rst
Normal file
9
account_subsequence_fiscal_year/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
This module extends the functionality of Accounting Odoo module, to
|
||||
generate custom sub sequences for accounting entries, if fiscal years are not "classic".
|
||||
(ie: last day = 31 / last month = 12)
|
||||
|
||||
without this module, in such cases (for example, for fiscal year from 01 April 2020 to 31 May 2021)
|
||||
accounting moves of the same fiscal year will not have the same numbering: Some entries will
|
||||
have ``BILL/2020/`` prefix and other will have ``BILL/2021/``, that is not allowed in many countries.
|
||||
|
||||
That modules fixes this problem.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
1
account_subsequence_fiscal_year/tests/__init__.py
Normal file
1
account_subsequence_fiscal_year/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_module
|
||||
142
account_subsequence_fiscal_year/tests/test_module.py
Normal file
142
account_subsequence_fiscal_year/tests/test_module.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# Copyright 2020 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from datetime import date
|
||||
|
||||
|
||||
class TestModule(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.AccountFiscalYear = self.env["account.fiscal.year"]
|
||||
self.company = self.env.ref("base.main_company")
|
||||
self.account_type_receivable = self.env["account.account.type"].create(
|
||||
{"name": "Test Receivable", "type": "receivable"}
|
||||
)
|
||||
self.account_receivable = self.env["account.account"].create(
|
||||
{
|
||||
"name": "Test Receivable",
|
||||
"code": "TEST_AR",
|
||||
"user_type_id": self.account_type_receivable.id,
|
||||
"reconcile": True,
|
||||
}
|
||||
)
|
||||
self.sale_journal = self.env["account.journal"].search(
|
||||
[("type", "=", "sale"), ("company_id", "=", self.company.id)]
|
||||
)[0]
|
||||
self.sequence = self.sale_journal.sequence_id
|
||||
self.sequence.use_date_range = True
|
||||
|
||||
self.account_move = self.env["account.move"].create({
|
||||
"journal_id": self.sale_journal.id,
|
||||
"company_id": self.company.id,
|
||||
"line_ids": [(0, 0, {
|
||||
"account_id": self.account_receivable.id,
|
||||
"debit": 100,
|
||||
}), (0, 0, {
|
||||
"account_id": self.account_receivable.id,
|
||||
"credit": 100,
|
||||
})]
|
||||
})
|
||||
|
||||
self.existing_subsequence_ids =\
|
||||
self.env["ir.sequence.date_range"].search([
|
||||
("sequence_id", "=", self.sequence.id)]).ids
|
||||
|
||||
def _get_new_subsequence(self):
|
||||
return self.env["ir.sequence.date_range"].search([
|
||||
("sequence_id", "=", self.sequence.id),
|
||||
("id", "not in", self.existing_subsequence_ids),
|
||||
])
|
||||
|
||||
def test_method_normal(self):
|
||||
"""Non Regression Test"""
|
||||
self.company.account_subsequence_method = False
|
||||
self.account_move.date = "2100-06-01"
|
||||
self.account_move.post()
|
||||
new_subsequences = self._get_new_subsequence()
|
||||
self.assertEqual(len(new_subsequences), 1)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_from,
|
||||
date(2100, 1, 1)
|
||||
)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_to,
|
||||
date(2100, 12, 31)
|
||||
)
|
||||
|
||||
def test_method_company_setting_before(self):
|
||||
self.company.account_subsequence_method = 'company_setting'
|
||||
self.company.fiscalyear_last_day = 31
|
||||
self.company.fiscalyear_last_month = 3
|
||||
self.account_move.date = "2100-03-15"
|
||||
self.account_move.post()
|
||||
|
||||
new_subsequences = self._get_new_subsequence()
|
||||
self.assertEqual(len(new_subsequences), 1)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_from,
|
||||
date(2099, 4, 1)
|
||||
)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_to,
|
||||
date(2100, 3, 31)
|
||||
)
|
||||
|
||||
def test_method_company_setting_after(self):
|
||||
self.company.account_subsequence_method = 'company_setting'
|
||||
self.company.fiscalyear_last_day = 31
|
||||
self.company.fiscalyear_last_month = 3
|
||||
self.account_move.date = "2100-05-15"
|
||||
self.account_move.post()
|
||||
|
||||
new_subsequences = self._get_new_subsequence()
|
||||
self.assertEqual(len(new_subsequences), 1)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_from,
|
||||
date(2100, 4, 1)
|
||||
)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_to,
|
||||
date(2101, 3, 31)
|
||||
)
|
||||
|
||||
def test_method_fiscal_year_setting(self):
|
||||
self.company.account_subsequence_method = 'fiscal_year_setting'
|
||||
with self.assertRaises(ValidationError):
|
||||
self.account_move.date = "2100-06-01"
|
||||
self.account_move.post()
|
||||
|
||||
# create a Fiscal Year
|
||||
self.AccountFiscalYear.create({
|
||||
"name": "2100 FY April to March",
|
||||
"company_id": self.company.id,
|
||||
"date_from": "2100-04-01",
|
||||
"date_to": "2101-03-31",
|
||||
})
|
||||
|
||||
# Try to post out the date range
|
||||
with self.assertRaises(ValidationError):
|
||||
self.account_move.date = "2100-03-31"
|
||||
self.account_move.post()
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.account_move.date = "2101-04-01"
|
||||
self.account_move.post()
|
||||
|
||||
# Post in the range should succeed
|
||||
self.account_move.date = "2100-06-01"
|
||||
self.account_move.post()
|
||||
new_subsequences = self._get_new_subsequence()
|
||||
self.assertEqual(len(new_subsequences), 1)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_from,
|
||||
date(2100, 4, 1)
|
||||
)
|
||||
self.assertEqual(
|
||||
new_subsequences[0].date_to,
|
||||
date(2101, 3, 31)
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
|
||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="view_res_config_settings_form" model="ir.ui.view">
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='accounting_reports']" position="inside">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Accounting Subsequences Method</span>
|
||||
<div class="text-muted">
|
||||
Define how accounting subsequences are generated
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<label string="Generation Method" for="account_subsequence_method" class="col-lg-3 o_light_label"/>
|
||||
<field name="account_subsequence_method"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user