From 394d2a4105041804510b51b63f10cb25a306219e Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Wed, 6 Sep 2017 13:18:00 +0200 Subject: [PATCH 1/2] [IMP] Add type to analytic account / contract --- contract/models/account_analytic_account.py | 21 ++-- contract/models/account_analytic_contract.py | 17 ++- contract/tests/test_contract.py | 6 + .../views/account_analytic_account_view.xml | 103 ++++++++++++++++-- 4 files changed, 128 insertions(+), 19 deletions(-) diff --git a/contract/models/account_analytic_account.py b/contract/models/account_analytic_account.py index 31ccf503..01f39037 100644 --- a/contract/models/account_analytic_account.py +++ b/contract/models/account_analytic_account.py @@ -199,28 +199,33 @@ class AccountAnalyticAccount(models.Model): return invoice_line_vals @api.multi - def _prepare_invoice(self): + def _prepare_invoice(self, journal=None): self.ensure_one() if not self.partner_id: raise ValidationError( _("You must first select a Customer for Contract %s!") % self.name) - journal = self.journal_id or self.env['account.journal'].search( - [('type', '=', 'sale'), - ('company_id', '=', self.company_id.id)], - limit=1) + if not journal: + journal = self.journal_id or self.env['account.journal'].search([ + ('type', '=', self.contract_type), + ('company_id', '=', self.company_id.id) + ], limit=1) if not journal: raise ValidationError( - _("Please define a sale journal for the company '%s'.") % - (self.company_id.name or '',)) + _("Please define a %s journal for the company '%s'.") % + (self.contract_type, self.company_id.name or '') + ) currency = ( self.pricelist_id.currency_id or self.partner_id.property_product_pricelist.currency_id or self.company_id.currency_id ) + invoice_type = 'out_invoice' + if self.contract_type == 'purchase': + invoice_type = 'in_invoice' invoice = self.env['account.invoice'].new({ 'reference': self.code, - 'type': 'out_invoice', + 'type': invoice_type, 'partner_id': self.partner_id.address_get( ['invoice'])['invoice'], 'currency_id': currency.id, diff --git a/contract/models/account_analytic_contract.py b/contract/models/account_analytic_contract.py index 6f04a9e5..1d67fb3b 100644 --- a/contract/models/account_analytic_contract.py +++ b/contract/models/account_analytic_contract.py @@ -6,7 +6,7 @@ # Copyright 2015-2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import api, fields, models, _ class AccountAnalyticContract(models.Model): @@ -26,6 +26,12 @@ class AccountAnalyticContract(models.Model): comodel_name="res.partner", string="Partner (always False)", ) + contract_type = fields.Selection( + selection=[ + ('sale', _('Sale')), + ('purchase', _('Purchase')), + ], default='sale' + ) pricelist_id = fields.Many2one( comodel_name='product.pricelist', string='Pricelist', @@ -64,7 +70,7 @@ class AccountAnalyticContract(models.Model): 'account.journal', string='Journal', default=lambda s: s._default_journal(), - domain="[('type', '=', 'sale'),('company_id', '=', company_id)]", + domain="[('company_id', '=', company_id)]", ) company_id = fields.Many2one( 'res.company', @@ -73,6 +79,13 @@ class AccountAnalyticContract(models.Model): default=lambda self: self.env.user.company_id, ) + @api.onchange('contract_type') + def _onchange_contract_type(self): + self.journal_id = self.env['account.journal'].search([ + ('type', '=', self.contract_type), + ('company_id', '=', self.company_id.id) + ], limit=1) + @api.model def _default_journal(self): company_id = self.env.context.get( diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index eaef5f5f..df9c0525 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -233,6 +233,12 @@ class TestContract(TestContractBase): result = self.contract.action_contract_send() self.assertEqual(result['res_model'], 'mail.compose.message') + def test_onchange_contract_type(self): + self.contract._onchange_contract_type() + self.assertEqual(self.contract.journal_id.type, 'sale') + self.assertEqual( + self.contract.journal_id.company_id, self.contract.company_id) + def test_contract_onchange_product_id_domain_blank(self): """It should return a blank UoM domain when no product.""" line = self.env['account.analytic.contract.line'].new() diff --git a/contract/views/account_analytic_account_view.xml b/contract/views/account_analytic_account_view.xml index c85a7d5c..bf9ef8d5 100644 --- a/contract/views/account_analytic_account_view.xml +++ b/contract/views/account_analytic_account_view.xml @@ -95,6 +95,54 @@ + + account.analytic.account.sale.form + account.analytic.account + + primary + + + + Customer + [('customer', '=', True)] + { + 'default_customer': True, + 'default_supplier': False + } + + + [('sale_ok', '=', True)] + + + sale + + + + + + account.analytic.account.purchase.form + account.analytic.account + + primary + + + + Supplier + [('supplier', '=', True)] + { + 'default_customer': False, + 'default_supplier': True + } + + + [('purchase_ok', '=', True)] + + + purchase + + + + Contract list @@ -154,12 +202,13 @@ - + Contracts account.analytic.account form tree,form - {'is_contract':1, 'search_default_not_finished':1, 'search_default_recurring_invoices':1, 'default_recurring_invoices': 1} + [('contract_type', '=', 'sale')] + {'is_contract':1, 'search_default_not_finished':1, 'search_default_recurring_invoices':1, 'default_recurring_invoices': 1, 'default_type': 'sale'}

@@ -168,24 +217,60 @@ - + tree - + - + form - - + + - + + + + Contracts + account.analytic.account + form + tree,form + [('contract_type', '=', 'purchase')] + {'is_contract':1, 'search_default_not_finished':1, 'search_default_recurring_invoices':1, 'default_recurring_invoices': 1, 'default_type': 'purchase'} + + +

+ Click to create a new contract. +

+
+
+ + + + tree + + + + + + + form + + + + + From 402370f1cbe5cde6b484200f87596e9d803a36af Mon Sep 17 00:00:00 2001 From: mreficent Date: Wed, 4 Jul 2018 19:07:35 +0200 Subject: [PATCH 2/2] [IMP] Make sure it works for sale and purchase contracts --- contract/README.rst | 7 +-- contract/__manifest__.py | 2 +- contract/models/account_analytic_account.py | 17 ++++--- contract/models/account_analytic_contract.py | 17 ++++--- .../models/account_analytic_contract_line.py | 1 - .../models/account_analytic_invoice_line.py | 2 +- contract/models/res_partner.py | 47 ++++++++++++++----- contract/readme/CONTRIBUTORS.rst | 1 + contract/readme/DESCRIPTION.rst | 6 +-- contract/tests/test_contract.py | 45 ++++++++++++++---- .../views/account_analytic_account_view.xml | 44 ++++++++--------- .../views/account_analytic_contract_view.xml | 16 +++++-- contract/views/res_partner_view.xml | 11 +++-- 13 files changed, 144 insertions(+), 72 deletions(-) diff --git a/contract/README.rst b/contract/README.rst index af3ce01d..57ffab1b 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -25,10 +25,10 @@ Contracts Management - Recurring |badge1| |badge2| |badge3| |badge4| |badge5| -This module brings back the contracts management with recurring invoicing -features. Also you can print and send by email contract report. +This module enables contracts management with recurring +invoicing functions. Also you can print and send by email contract report. -In upstream Odoo, this functionality was moved into the Enterprise edition. +It works for customer contract and supplier contracts. **Table of contents** @@ -106,6 +106,7 @@ Contributors * Angel Moya * Dave Lasley * Vicent Cubells +* Miquel Raïch Maintainers ~~~~~~~~~~~ diff --git a/contract/__manifest__.py b/contract/__manifest__.py index 37155810..f065ff52 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -8,7 +8,7 @@ { 'name': 'Contracts Management - Recurring', - 'version': '11.0.3.0.0', + 'version': '11.0.4.0.0', 'category': 'Contract Management', 'license': 'AGPL-3', 'author': "OpenERP SA, " diff --git a/contract/models/account_analytic_account.py b/contract/models/account_analytic_account.py index 01f39037..bc268f79 100644 --- a/contract/models/account_analytic_account.py +++ b/contract/models/account_analytic_account.py @@ -202,14 +202,19 @@ class AccountAnalyticAccount(models.Model): def _prepare_invoice(self, journal=None): self.ensure_one() if not self.partner_id: - raise ValidationError( - _("You must first select a Customer for Contract %s!") % - self.name) + if self.contract_type == 'purchase': + raise ValidationError( + _("You must first select a Supplier for Contract %s!") % + self.name) + else: + raise ValidationError( + _("You must first select a Customer for Contract %s!") % + self.name) if not journal: journal = self.journal_id or self.env['account.journal'].search([ - ('type', '=', self.contract_type), - ('company_id', '=', self.company_id.id) - ], limit=1) + ('type', '=', self.contract_type), + ('company_id', '=', self.company_id.id) + ], limit=1) if not journal: raise ValidationError( _("Please define a %s journal for the company '%s'.") % diff --git a/contract/models/account_analytic_contract.py b/contract/models/account_analytic_contract.py index 1d67fb3b..162206cb 100644 --- a/contract/models/account_analytic_contract.py +++ b/contract/models/account_analytic_contract.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2004-2010 OpenERP SA # Copyright 2014 Angel Moya # Copyright 2016 Carlos Dauden @@ -6,7 +5,7 @@ # Copyright 2015-2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import api, fields, models class AccountAnalyticContract(models.Model): @@ -28,9 +27,9 @@ class AccountAnalyticContract(models.Model): ) contract_type = fields.Selection( selection=[ - ('sale', _('Sale')), - ('purchase', _('Purchase')), - ], default='sale' + ('sale', 'Customer'), + ('purchase', 'Supplier'), + ], default='sale', ) pricelist_id = fields.Many2one( comodel_name='product.pricelist', @@ -70,7 +69,8 @@ class AccountAnalyticContract(models.Model): 'account.journal', string='Journal', default=lambda s: s._default_journal(), - domain="[('company_id', '=', company_id)]", + domain="[('type', '=', contract_type)," + "('company_id', '=', company_id)]", ) company_id = fields.Many2one( 'res.company', @@ -81,6 +81,9 @@ class AccountAnalyticContract(models.Model): @api.onchange('contract_type') def _onchange_contract_type(self): + if self.contract_type == 'purchase': + self.recurring_invoice_line_ids.filtered('automatic_price').update( + {'automatic_price': False}) self.journal_id = self.env['account.journal'].search([ ('type', '=', self.contract_type), ('company_id', '=', self.company_id.id) @@ -91,6 +94,6 @@ class AccountAnalyticContract(models.Model): company_id = self.env.context.get( 'company_id', self.env.user.company_id.id) domain = [ - ('type', '=', 'sale'), + ('type', '=', self.contract_type), ('company_id', '=', company_id)] return self.env['account.journal'].search(domain, limit=1) diff --git a/contract/models/account_analytic_contract_line.py b/contract/models/account_analytic_contract_line.py index 5eff76aa..9dbb0f05 100644 --- a/contract/models/account_analytic_contract_line.py +++ b/contract/models/account_analytic_contract_line.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2004-2010 OpenERP SA # Copyright 2014 Angel Moya # Copyright 2016 Carlos Dauden diff --git a/contract/models/account_analytic_invoice_line.py b/contract/models/account_analytic_invoice_line.py index 9328178c..a82c8ee6 100644 --- a/contract/models/account_analytic_invoice_line.py +++ b/contract/models/account_analytic_invoice_line.py @@ -9,7 +9,7 @@ class AccountAnalyticInvoiceLine(models.Model): _inherit = 'account.analytic.contract.line' analytic_account_id = fields.Many2one( - 'account.analytic.account', + comodel_name='account.analytic.account', string='Analytic Account', required=True, ondelete='cascade', diff --git a/contract/models/res_partner.py b/contract/models/res_partner.py index f175912a..ead5e4c7 100644 --- a/contract/models/res_partner.py +++ b/contract/models/res_partner.py @@ -7,30 +7,45 @@ from odoo import fields, models class ResPartner(models.Model): _inherit = 'res.partner' - contract_count = fields.Integer( - string='Contracts', + sale_contract_count = fields.Integer( + string='Sale Contracts', + compute='_compute_contract_count', + ) + purchase_contract_count = fields.Integer( + string='Purchase Contracts', compute='_compute_contract_count', ) def _compute_contract_count(self): - Contract = self.env['account.analytic.account'] + contract_model = self.env['account.analytic.account'] today = fields.Date.today() + fetch_data = contract_model.read_group([ + ('recurring_invoices', '=', True), + ('partner_id', 'child_of', self.ids), + '|', + ('date_end', '=', False), + ('date_end', '>=', today)], + ['partner_id', 'contract_type'], ['partner_id', 'contract_type'], + lazy=False) + result = [[data['partner_id'][0], data['contract_type'], + data['__count']] for data in fetch_data] for partner in self: - partner.contract_count = Contract.search_count([ - ('recurring_invoices', '=', True), - ('partner_id', 'child_of', partner.ids), - '|', - ('date_end', '=', False), - ('date_end', '>=', today), - ]) + partner_child_ids = partner.child_ids.ids + partner.ids + partner.sale_contract_count = sum([ + r[2] for r in result + if r[0] in partner_child_ids and r[1] == 'sale']) + partner.purchase_contract_count = sum([ + r[2] for r in result + if r[0] in partner_child_ids and r[1] == 'purchase']) def act_show_contract(self): """ This opens contract view @return: the contract view """ self.ensure_one() - res = self.env['ir.actions.act_window'].for_xml_id( - 'contract', 'action_account_analytic_overdue_all') + contract_type = self._context.get('contract_type') + + res = self._get_act_window_contract_xml(contract_type) res.update( context=dict( self.env.context, @@ -43,3 +58,11 @@ class ResPartner(models.Model): ), ) return res + + def _get_act_window_contract_xml(self, contract_type): + if contract_type == 'purchase': + return self.env['ir.actions.act_window'].for_xml_id( + 'contract', 'action_account_analytic_purchase_overdue_all') + else: + return self.env['ir.actions.act_window'].for_xml_id( + 'contract', 'action_account_analytic_sale_overdue_all') diff --git a/contract/readme/CONTRIBUTORS.rst b/contract/readme/CONTRIBUTORS.rst index c749c1ef..ed395a7d 100644 --- a/contract/readme/CONTRIBUTORS.rst +++ b/contract/readme/CONTRIBUTORS.rst @@ -3,3 +3,4 @@ * Angel Moya * Dave Lasley * Vicent Cubells +* Miquel Raïch diff --git a/contract/readme/DESCRIPTION.rst b/contract/readme/DESCRIPTION.rst index ab6e258d..7498c830 100644 --- a/contract/readme/DESCRIPTION.rst +++ b/contract/readme/DESCRIPTION.rst @@ -1,4 +1,4 @@ -This module brings back the contracts management with recurring invoicing -features. Also you can print and send by email contract report. +This module enables contracts management with recurring +invoicing functions. Also you can print and send by email contract report. -In upstream Odoo, this functionality was moved into the Enterprise edition. +It works for customer contract and supplier contracts. diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index df9c0525..cc676bbb 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -32,6 +32,15 @@ class TestContractBase(common.SavepointCase): 'date_start': '2016-02-15', 'recurring_next_date': '2016-02-29', }) + cls.contract2 = cls.env['account.analytic.account'].create({ + 'name': 'Test Contract 2', + 'partner_id': cls.partner.id, + 'pricelist_id': cls.partner.property_product_pricelist.id, + 'recurring_invoices': True, + 'date_start': '2016-02-15', + 'recurring_next_date': '2016-02-29', + 'contract_type': 'purchase', + }) cls.line_vals = { 'analytic_account_id': cls.contract.id, 'product_id': cls.product.id, @@ -275,24 +284,27 @@ class TestContract(TestContractBase): ])) def test_contract_count(self): - """It should return contract count.""" - count = self.partner.contract_count + 2 + """It should return sale contract count.""" + count = self.partner.sale_contract_count + 2 self.contract.copy() self.contract.copy() - self.assertEqual(self.partner.contract_count, count) + self.assertEqual(self.partner.sale_contract_count, count) + count = self.partner.purchase_contract_count + 1 + self.contract2.copy() + self.assertEqual(self.partner.purchase_contract_count, count) def test_same_date_start_and_date_end(self): """It should create one invoice with same start and end date.""" - AccountInvoice = self.env['account.invoice'] + account_invoice_model = self.env['account.invoice'] self.contract.write({ 'date_start': fields.Date.today(), 'date_end': fields.Date.today(), 'recurring_next_date': fields.Date.today(), }) - init_count = AccountInvoice.search_count( + init_count = account_invoice_model.search_count( [('contract_id', '=', self.contract.id)]) self.contract.cron_recurring_create_invoice() - last_count = AccountInvoice.search_count( + last_count = account_invoice_model.search_count( [('contract_id', '=', self.contract.id)]) self.assertEqual(last_count, init_count + 1) with self.assertRaises(ValidationError): @@ -311,14 +323,29 @@ class TestContract(TestContractBase): self.assertFalse(self.contract.create_invoice_visibility) def test_extend_invoice(self): - AccountInvoice = self.env['account.invoice'] + account_invoice_model = self.env['account.invoice'] self.contract.recurring_create_invoice() - invoice = AccountInvoice.search( + invoice = account_invoice_model.search( [('contract_id', '=', self.contract.id)]) invoice.origin = 'Orig Invoice' self.contract._create_invoice(invoice) self.assertEqual(invoice.origin, 'Orig Invoice Test Contract') - invoice_count = AccountInvoice.search_count( + invoice_count = account_invoice_model.search_count( [('contract_id', '=', self.contract.id)]) self.assertEqual(invoice_count, 1) self.assertEqual(len(invoice.invoice_line_ids), 2) + + def test_act_show_contract(self): + show_contract = self.partner.\ + with_context(contract_type='sale').act_show_contract() + self.assertDictContainsSubset( + { + 'name': 'Customer Contracts', + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'res_model': 'account.analytic.account', + 'xml_id': 'contract.action_account_analytic_sale_overdue_all', + }, + show_contract, + 'There was an error and the view couldn\'t be opened.' + ) diff --git a/contract/views/account_analytic_account_view.xml b/contract/views/account_analytic_account_view.xml index bf9ef8d5..1db6df28 100644 --- a/contract/views/account_analytic_account_view.xml +++ b/contract/views/account_analytic_account_view.xml @@ -16,6 +16,9 @@ +