2
0

[MIG] account_invoice_constraint_chronology

This commit is contained in:
gilles 2017-03-13 14:18:36 +01:00 committed by Zina Rasoamanana
parent daf9cd6a73
commit cda5bc8b9c
9 changed files with 158 additions and 299 deletions

View File

@ -0,0 +1,58 @@
.. 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
=====================================
Account Invoice Constraint Chronology
=====================================
This module helps ensuring the chronology of invoice numbers.
It prevents the validation of invoices when:
* there are draft invoices with an anterior date
* there are validated invoices with a posterior date
Configuration
=============
To configure this module, go to the menu *Accounting > Configuration > Journals > Journals* and activate the option *Check Chronology* on the relevant journals. After the installation of the module, this option will be active on *sale* and *sale refund* journals.
Usage
=====
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/92/8.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/account-financial-tools/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
=======
Contributors
------------
* Adrien Peiffer (`Acsone SA/NV <http://www.acsone.eu>`_)
* Gilles Gilles <meyomesse.gilles@gmail.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.

View File

@ -1,31 +1,2 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
from . import model from . import model
from . import tests from . import tests

View File

@ -1,60 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# # Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
{ {
"name": "Account Invoice Constraint Chronology", "name": "Account Invoice Constraint Chronology",
"version": "8.0.1.0.0", "version": "10.0.1.0.0",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)", "author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"maintainer": "ACSONE SA/NV", "maintainer": "ACSONE SA/NV",
"website": "http://www.acsone.eu", "website": "http://www.acsone.eu",
"license": "AGPL-3", "license": "AGPL-3",
"images": [],
"category": "Accounting", "category": "Accounting",
"depends": ["account"], "depends": ["account"],
"description": """ "description": """
Account Invoice Constraint Chronology Account Invoice Constraint Chronology
===================================== This module helps ensuring the chronology of invoice numbers.
It prevents the validation of invoices when:
This module helps ensuring the chronology of invoice numbers. * there are draft invoices with an anterior date
* there are validated invoices with a posterior date
It prevents the validation of invoices when: """,
* there are draft invoices with an anterior date "test": ["../account/test/account_minimal_test.xml"],
* there are validated invoices with a posterior date
The check can be activated on a per-journal basis
(for sale and purchase journals).
""",
"data": ["view/account_view.xml"], "data": ["view/account_view.xml"],
"demo": [], 'installable': True,
"test": [],
"licence": "AGPL-3",
'installable': False,
"auto_install": False,
"application": True,
} }

View File

@ -1,31 +1,2 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
from . import account_invoice from . import account_invoice
from . import account from . import account

View File

@ -1,33 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# # Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
from openerp import models, fields, api from odoo import models, fields, api
class account_journal(models.Model): class account_journal(models.Model):

View File

@ -1,42 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# # Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
from openerp import models, api, fields from odoo import models, api, fields, _
from openerp.tools.translate import _ from odoo.exceptions import UserError
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from datetime import datetime
from openerp import exceptions
class account_invoice(models.Model): class account_invoice(models.Model):
_inherit = "account.invoice" _inherit = "account.invoice"
already_validated = fields.Boolean(readonly=True, copy=False)
@api.multi @api.multi
def action_move_create(self): def action_move_create(self):
res = super(account_invoice, self).action_move_create() res = super(account_invoice, self).action_move_create()
@ -51,33 +25,31 @@ class account_invoice(models.Model):
('journal_id', '=', inv.journal_id.id)], ('journal_id', '=', inv.journal_id.id)],
limit=1) limit=1)
if len(invoices) > 0: if len(invoices) > 0:
date_invoice_format = datetime\ date_invoice_format = fields.Date.\
.strptime(inv.date_invoice, from_string(inv.date_invoice)
DEFAULT_SERVER_DATE_FORMAT)
date_invoice_tz = fields\ date_invoice_tz = fields\
.Date.context_today(self, date_invoice_format) .Date.context_today(self, date_invoice_format)
raise exceptions.Warning(_("Chronology Error." raise UserError(_("Chronology Error. "
" Please confirm older draft" "Please confirm older draft "
" invoices before %s and" "invoices before %s and try again.")
" try again.") % % date_invoice_tz)
date_invoice_tz) if not inv.already_validated:
if inv.internal_number is False:
invoices = self.search([('state', 'in', ['open', 'paid']), invoices = self.search([('state', 'in', ['open', 'paid']),
('date_invoice', '>', ('date_invoice', '>',
inv.date_invoice), inv.date_invoice),
('journal_id', '=', ('journal_id', '=',
inv.journal_id.id)], inv.journal_id.id)],
limit=1) limit=1)
if len(invoices) > 0: if len(invoices) > 0:
date_invoice_format = datetime\ date_invoice_format = fields.Date.\
.strptime(inv.date_invoice, from_string(inv.date_invoice)
DEFAULT_SERVER_DATE_FORMAT)
date_invoice_tz = fields\ date_invoice_tz = fields\
.Date.context_today(self, date_invoice_format) .Date.context_today(self, date_invoice_format)
raise exceptions.Warning(_("Chronology Error. There" raise UserError(_("Chronology Error. "
" exist at least one" "There exist at least one invoice "
" invoice with a date" "with a date posterior to %s.") %
" posterior to %s.") % date_invoice_tz)
date_invoice_tz) if not inv.already_validated:
inv.already_validated = True
return res return res

View File

@ -1,30 +1 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
from . import test_account_constraint_chronology from . import test_account_constraint_chronology

View File

@ -1,90 +1,62 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# # Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# Authors: Adrien Peiffer
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import openerp.tests.common as common import odoo.tests.common as common
from openerp import workflow from odoo.exceptions import UserError
from openerp import exceptions
from datetime import datetime, timedelta from datetime import datetime, timedelta
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
def get_simple_product_id(self):
return self.env['product.product'].create({'name': 'product_test_01',
'lst_price': 2000.00,
})
def get_journal_check(self, value): def get_journal_check(self, value):
sale_journal_id = self.ref('account.sales_journal') sale_journal_obj = self.AccountJournal.env['account.journal'].\
sale_journal = self.env['account.journal'].browse([sale_journal_id]) search([('type', '=', 'sale')], limit=1)
journal = sale_journal.copy() journal = sale_journal_obj.copy()
journal.check_chronology = value journal.check_chronology = value
return journal return journal
def get_simple_account_invoice_line_values(self, product_id):
return {'name': 'test',
'account_id': self.ref('account.a_sale'),
'price_unit': 2000.00,
'quantity': 1,
'product_id': product_id,
}
def create_simple_invoice(self, journal_id, date): def create_simple_invoice(self, journal_id, date):
partner_id = self.ref('base.res_partner_2') invoice_account = self.env['account.account'].\
product = get_simple_product_id(self) search([('user_type_id', '=',
return self.env['account.invoice']\ self.env.ref(
.create({'partner_id': partner_id, 'account.data_account_type_receivable'
'account_id': ).id)], limit=1).id
self.ref('account.a_recv'), invoice_line_account = self.env['account.account'].\
'journal_id': search([('user_type_id', '=',
journal_id, self.env.ref(
'date_invoice': date, 'account.data_account_type_expenses'
'invoice_line': [(0, 0, {'name': 'test', ).id)], limit=1).id
'account_id': analytic_account = self.env['account.analytic.account'].\
self.ref('account.a_sale'), create({'name': 'test account'})
'price_unit': 2000.00,
'quantity': 1, invoice = self.env['account.invoice'].create(
'product_id': product.id, {'partner_id': self.env.ref('base.res_partner_2').id,
} 'account_id': invoice_account,
) 'type': 'in_invoice',
], 'journal_id': journal_id,
}) 'date_invoice': date,
'state': 'draft',
})
# invoice.write({'internal_number': invoice.number})
self.env['account.invoice.line'].create(
{'product_id': self.env.ref('product.product_product_4').id,
'quantity': 1.0,
'price_unit': 100.0,
'invoice_id': invoice.id,
'name': 'product that cost 100',
'account_id': invoice_line_account,
'account_analytic_id': analytic_account.id,
})
return invoice
class TestAccountConstraintChronology(common.TransactionCase): class TestAccountConstraintChronology(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestAccountConstraintChronology, self).setUp() super(TestAccountConstraintChronology, self).setUp()
self.context = self.registry("res.users").context_get(self.cr, self.AccountJournal = self.env['account.journal']
self.uid) self.Account = self.env['account.account']
def test_invoice_draft(self): def test_invoice_draft(self):
journal = get_journal_check(self, True) journal = get_journal_check(self, True)
@ -94,23 +66,26 @@ class TestAccountConstraintChronology(common.TransactionCase):
create_simple_invoice(self, journal.id, date) create_simple_invoice(self, journal.id, date)
date = today.strftime(DEFAULT_SERVER_DATE_FORMAT) date = today.strftime(DEFAULT_SERVER_DATE_FORMAT)
invoice_2 = create_simple_invoice(self, journal.id, date) invoice_2 = create_simple_invoice(self, journal.id, date)
self.assertRaises(exceptions.Warning, workflow.trg_validate, self.uid, self.assertTrue((invoice_2.state == 'draft'),
'account.invoice', invoice_2.id, 'invoice_open', "Initial invoice state is not Draft")
self.cr) with self.assertRaises(UserError):
invoice_2.action_invoice_open()
def test_invoice_validate(self): def test_invoice_validate(self):
journal = get_journal_check(self, True) journal = get_journal_check(self, True)
today = datetime.now() today = datetime.now()
tomorrow = today + timedelta(days=1) tomorrow = today + timedelta(days=1)
date = tomorrow.strftime(DEFAULT_SERVER_DATE_FORMAT) date_tomorrow = tomorrow.strftime(DEFAULT_SERVER_DATE_FORMAT)
invoice = create_simple_invoice(self, journal.id, date) invoice_1 = create_simple_invoice(self, journal.id, date_tomorrow)
workflow.trg_validate(self.uid, 'account.invoice', invoice.id, self.assertTrue((invoice_1.state == 'draft'),
'invoice_open', self.cr) "Initial invoice state is not Draft")
invoice_1.action_invoice_open()
date = today.strftime(DEFAULT_SERVER_DATE_FORMAT) date = today.strftime(DEFAULT_SERVER_DATE_FORMAT)
invoice_2 = create_simple_invoice(self, journal.id, date) invoice_2 = create_simple_invoice(self, journal.id, date)
self.assertRaises(exceptions.Warning, workflow.trg_validate, self.uid, self.assertTrue((invoice_2.state == 'draft'),
'account.invoice', invoice_2.id, 'invoice_open', "Initial invoice state is not Draft")
self.cr) with self.assertRaises(UserError):
invoice_2.action_invoice_open()
def test_invoice_without_date(self): def test_invoice_without_date(self):
journal = get_journal_check(self, True) journal = get_journal_check(self, True)
@ -119,6 +94,7 @@ class TestAccountConstraintChronology(common.TransactionCase):
date = yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT) date = yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
create_simple_invoice(self, journal.id, date) create_simple_invoice(self, journal.id, date)
invoice_2 = create_simple_invoice(self, journal.id, False) invoice_2 = create_simple_invoice(self, journal.id, False)
self.assertRaises(exceptions.Warning, workflow.trg_validate, self.uid, self.assertTrue((invoice_2.state == 'draft'),
'account.invoice', invoice_2.id, 'invoice_open', "Initial invoice state is not Draft")
self.cr) with self.assertRaises(UserError):
invoice_2.action_invoice_open()

View File

@ -1,15 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <!-- Copyright 2015-2017 ACSONE SA/NV
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<data> <data>
<record id="view_account_journal_form" model="ir.ui.view"> <record id="view_account_journal_form" model="ir.ui.view">
<field name="name">account.journal.form (account_constraint_chronology)</field> <field name="name">account.journal.form (account_constraint_chronology)</field>
<field name="model">account.journal</field> <field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_form"/> <field name="inherit_id" ref="account.view_account_journal_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="entry_posted" position="after"> <field name="refund_sequence" position="after">
<field name="check_chronology" attrs="{'readonly': [('type', 'not in', ['sale', 'purchase', 'sale_refund', 'purchase_refund'])]}"/> <field name="check_chronology" attrs="{'readonly': [('type', 'not in', ['sale', 'purchase', 'sale_refund', 'purchase_refund'])]}"/>
</field> </field>
</field> </field>
</record> </record>
</data> </data>
</openerp> </odoo>