From 107f5141896319d9c3ae351c7942f1f5e0ad2e55 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Feb 2014 14:41:42 +0100 Subject: [PATCH 01/22] Add account_analytic_analysis_recurring --- contract/__init__.py | 22 ++ contract/__openerp__.py | 46 ++++ .../account_analytic_analysis_recurring.py | 209 ++++++++++++++++++ ...count_analytic_analysis_recurring_cron.xml | 16 ++ ...count_analytic_analysis_recurring_view.xml | 44 ++++ 5 files changed, 337 insertions(+) create mode 100644 contract/__init__.py create mode 100644 contract/__openerp__.py create mode 100644 contract/account_analytic_analysis_recurring.py create mode 100644 contract/account_analytic_analysis_recurring_cron.xml create mode 100644 contract/account_analytic_analysis_recurring_view.xml diff --git a/contract/__init__.py b/contract/__init__.py new file mode 100644 index 000000000..7088b5aa8 --- /dev/null +++ b/contract/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL () +# +# 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 . +# +############################################################################## + +import account_analytic_analysis_recurring diff --git a/contract/__openerp__.py b/contract/__openerp__.py new file mode 100644 index 000000000..0c417361a --- /dev/null +++ b/contract/__openerp__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 . +# +############################################################################## + + +{ + 'name': 'Contracts Management recurring', + 'version': '0.1', + 'category': 'Other', + 'description': """ +This module add a new feature in contracts to manage recurring invoice +======================================================================================= + +This is a backport of the new V8 feature available in trunk and saas. With the V8 release this module will be deprecated. +It also add a little feature, you can use #START# and #END# in the contract line to automatically insert the dates of the invoiced period. +""", + 'author': 'Yannick Buron', + 'website': 'http://openerp.com', + 'depends': ['base', 'account_analytic_analysis'], + 'data': [ + 'account_analytic_analysis_recurring_cron.xml', + 'account_analytic_analysis_recurring_view.xml', + ], + 'demo': [''], + 'test':[], + 'installable': True, + 'images': [], +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py new file mode 100644 index 000000000..2b85cce6d --- /dev/null +++ b/contract/account_analytic_analysis_recurring.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 . +# +############################################################################## +from dateutil.relativedelta import relativedelta +import datetime +import logging +import time + +from openerp.osv import osv, fields +from openerp.osv.orm import intersect, except_orm +import openerp.tools +from openerp.tools.translate import _ + +from openerp.addons.decimal_precision import decimal_precision as dp + +_logger = logging.getLogger(__name__) + +class account_analytic_invoice_line(osv.osv): + _name = "account.analytic.invoice.line" + + def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None): + res = {} + for line in self.browse(cr, uid, ids, context=context): + res[line.id] = line.quantity * line.price_unit + if line.analytic_account_id.pricelist_id: + cur = line.analytic_account_id.pricelist_id.currency_id + res[line.id] = self.pool.get('res.currency').round(cr, uid, cur, res[line.id]) + return res + + _columns = { + 'product_id': fields.many2one('product.product','Product',required=True), + 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account'), + 'name': fields.text('Description', required=True), + 'quantity': fields.float('Quantity', required=True), + 'uom_id': fields.many2one('product.uom', 'Unit of Measure',required=True), + 'price_unit': fields.float('Unit Price', required=True), + 'price_subtotal': fields.function(_amount_line, string='Sub Total', type="float",digits_compute= dp.get_precision('Account')), + } + _defaults = { + 'quantity' : 1, + } + + def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', partner_id=False, price_unit=False, pricelist_id=False, company_id=None, context=None): + context = context or {} + uom_obj = self.pool.get('product.uom') + company_id = company_id or False + context.update({'company_id': company_id, 'force_company': company_id, 'pricelist_id': pricelist_id}) + + if not product: + return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} + if partner_id: + part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) + if part.lang: + context.update({'lang': part.lang}) + + result = {} + res = self.pool.get('product.product').browse(cr, uid, product, context=context) + result.update({'name':res.partner_ref or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0}) + if res.description: + result['name'] += '\n'+res.description + + res_final = {'value':result} + if result['uom_id'] != res.uom_id.id: + selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=context) + new_price = uom_obj._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uom_id']) + res_final['value']['price_unit'] = new_price + return res_final + + +class account_analytic_account(osv.osv): + _name = "account.analytic.account" + _inherit = "account.analytic.account" + + _columns = { + 'recurring_invoice_line_ids': fields.one2many('account.analytic.invoice.line', 'analytic_account_id', 'Invoice Lines'), + 'recurring_invoices' : fields.boolean('Generate recurring invoices automatically'), + 'recurring_rule_type': fields.selection([ + ('daily', 'Day(s)'), + ('weekly', 'Week(s)'), + ('monthly', 'Month(s)'), + ('yearly', 'Year(s)'), + ], 'Recurrency', help="Invoice automatically repeat at specified interval"), + 'recurring_interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"), + 'recurring_next_date': fields.date('Date of Next Invoice'), + } + + _defaults = { + 'recurring_interval': 1, + 'recurring_next_date': lambda *a: time.strftime('%Y-%m-%d'), + 'recurring_rule_type':'monthly' + } + + def onchange_recurring_invoices(self, cr, uid, ids, recurring_invoices, date_start=False, context=None): + value = {} + if date_start and recurring_invoices: + value = {'value': {'recurring_next_date': date_start}} + return value + + def _prepare_invoice(self, cr, uid, contract, context=None): + context = context or {} + + inv_obj = self.pool.get('account.invoice') + journal_obj = self.pool.get('account.journal') + fpos_obj = self.pool.get('account.fiscal.position') + lang_obj = self.pool.get('res.lang') + + if not contract.partner_id: + raise osv.except_osv(_('No Customer Defined!'),_("You must first select a Customer for Contract %s!") % contract.name ) + + fpos = contract.partner_id.property_account_position or False + journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id or False)], limit=1) + if not journal_ids: + raise osv.except_osv(_('Error!'), + _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', )) + + partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False + + + inv_data = { + 'reference': contract.code or False, + 'account_id': contract.partner_id.property_account_receivable.id, + 'type': 'out_invoice', + 'partner_id': contract.partner_id.id, + 'currency_id': contract.partner_id.property_product_pricelist.id or False, + 'journal_id': len(journal_ids) and journal_ids[0] or False, + 'date_invoice': contract.recurring_next_date, + 'origin': contract.name, + 'fiscal_position': fpos and fpos.id, + 'payment_term': partner_payment_term, + 'company_id': contract.company_id.id or False, + } + invoice_id = inv_obj.create(cr, uid, inv_data, context=context) + + for line in contract.recurring_invoice_line_ids: + + res = line.product_id + account_id = res.property_account_income.id + if not account_id: + account_id = res.categ_id.property_account_income_categ.id + account_id = fpos_obj.map_account(cr, uid, fpos, account_id) + + taxes = res.taxes_id or False + tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) + + if 'old_date' in context: + lang_ids = lang_obj.search(cr, uid, [('code', '=', contract.partner_id.lang)], context=context) + format = lang_obj.browse(cr, uid, lang_ids, context=context)[0].date_format + line.name = line.name.replace('#START#', context['old_date'].strftime(format)) + line.name = line.name.replace('#END#', context['next_date'].strftime(format)) + + invoice_line_vals = { + 'name': line.name, + 'account_id': account_id, + 'account_analytic_id': contract.id, + 'price_unit': line.price_unit or 0.0, + 'quantity': line.quantity, + 'uos_id': line.uom_id.id or False, + 'product_id': line.product_id.id or False, + 'invoice_id' : invoice_id, + 'invoice_line_tax_id': [(6, 0, tax_id)], + } + self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context) + + inv_obj.button_compute(cr, uid, [invoice_id], context=context) + return invoice_id + + def recurring_create_invoice(self, cr, uid, automatic=False, context=None): + context = context or {} + current_date = time.strftime('%Y-%m-%d') + + contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True)]) + for contract in self.browse(cr, uid, contract_ids, context=context): + + next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d") + interval = contract.recurring_interval + if contract.recurring_rule_type == 'daily': + old_date = next_date-relativedelta(days=+interval) + new_date = next_date+relativedelta(days=+interval) + elif contract.recurring_rule_type == 'weekly': + old_date = next_date-relativedelta(weeks=+interval) + new_date = next_date+relativedelta(weeks=+interval) + else: + old_date = next_date+relativedelta(months=+interval) + new_date = next_date+relativedelta(months=+interval) + + context['old_date'] = old_date + context['next_date'] = datetime.datetime.strptime(contract.recurring_next_date or current_date,"%Y-%m-%d") + invoice_id = self._prepare_invoice(cr, uid, contract, context=context) + + self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context) + return True + diff --git a/contract/account_analytic_analysis_recurring_cron.xml b/contract/account_analytic_analysis_recurring_cron.xml new file mode 100644 index 000000000..95ae54de8 --- /dev/null +++ b/contract/account_analytic_analysis_recurring_cron.xml @@ -0,0 +1,16 @@ + + + + + + Generate Recurring Invoices from Contracts + 1 + days + -1 + + + + + + + diff --git a/contract/account_analytic_analysis_recurring_view.xml b/contract/account_analytic_analysis_recurring_view.xml new file mode 100644 index 000000000..e28934767 --- /dev/null +++ b/contract/account_analytic_analysis_recurring_view.xml @@ -0,0 +1,44 @@ + + + + + + account.analytic.account.invoice.recurring.form.inherit + account.analytic.account + + + + + +
+ +
+ + +
+
+
+ +
+
From fa624403414bd1ca72a932f1302a8bbabc83ea04 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 21 Feb 2014 12:43:57 +0100 Subject: [PATCH 02/22] Corrections following the feedback of MP --- contract/__openerp__.py | 4 +- .../account_analytic_analysis_recurring.pot | 129 ++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 contract/account_analytic_analysis_recurring.pot diff --git a/contract/__openerp__.py b/contract/__openerp__.py index 0c417361a..cfdd15fc8 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -30,8 +30,10 @@ This module add a new feature in contracts to manage recurring invoice This is a backport of the new V8 feature available in trunk and saas. With the V8 release this module will be deprecated. It also add a little feature, you can use #START# and #END# in the contract line to automatically insert the dates of the invoiced period. + +Backport done By Yannick Buron. """, - 'author': 'Yannick Buron', + 'author': 'OpenERP SA', 'website': 'http://openerp.com', 'depends': ['base', 'account_analytic_analysis'], 'data': [ diff --git a/contract/account_analytic_analysis_recurring.pot b/contract/account_analytic_analysis_recurring.pot new file mode 100644 index 000000000..7d2f21f1f --- /dev/null +++ b/contract/account_analytic_analysis_recurring.pot @@ -0,0 +1,129 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_analytic_analysis_recurring +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-21 11:41+0000\n" +"PO-Revision-Date: 2014-02-21 11:41+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_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_subtotal:0 +msgid "Sub Total" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_rule_type:0 +msgid "Recurrency" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_unit:0 +msgid "Unit Price" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid ". create invoices" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Account Analytic Lines" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoice_line_ids:0 +msgid "Invoice Lines" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,uom_id:0 +msgid "Unit of Measure" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Day(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_rule_type:0 +msgid "Invoice automatically repeat at specified interval" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,product_id:0 +msgid "Product" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,name:0 +msgid "Description" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_interval:0 +msgid "Repeat Every" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Recurring Invoices" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoices:0 +msgid "Generate recurring invoices automatically" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Year(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Week(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,quantity:0 +msgid "Quantity" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line +msgid "account.analytic.invoice.line" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_next_date:0 +msgid "Date of Next Invoice" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,analytic_account_id:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Month(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_interval:0 +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "" + + From d49952ea703b9dc3e6d17a7ca0c6d5345d3b9ed9 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Fri, 11 Jul 2014 16:18:08 +0200 Subject: [PATCH 03/22] [ADD] Pot file and Dutch translation --- .../account_analytic_analysis_recurring.pot | 145 ++++++++++++++++ contract/i18n/nl.po | 156 ++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 contract/i18n/account_analytic_analysis_recurring.pot create mode 100644 contract/i18n/nl.po diff --git a/contract/i18n/account_analytic_analysis_recurring.pot b/contract/i18n/account_analytic_analysis_recurring.pot new file mode 100644 index 000000000..ce2499c51 --- /dev/null +++ b/contract/i18n/account_analytic_analysis_recurring.pot @@ -0,0 +1,145 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_analytic_analysis_recurring +# +msgid "" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid ". create invoices" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Account Analytic Lines" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: field:account.analytic.invoice.line,analytic_account_id:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account +#, python-format +msgid "Analytic Account" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_next_date:0 +msgid "Date of Next Invoice" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Day(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,name:0 +msgid "Description" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:130 +#, python-format +msgid "Error!" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoices:0 +msgid "Generate recurring invoices automatically" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoice_line_ids:0 +msgid "Invoice Lines" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_rule_type:0 +msgid "Invoice automatically repeat at specified interval" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Month(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 +#, python-format +msgid "No Customer Defined!" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:131 +#, python-format +msgid "Please define a sale journal for the company \"%s\"." +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,product_id:0 +msgid "Product" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,quantity:0 +msgid "Quantity" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_rule_type:0 +msgid "Recurrency" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Recurring Invoices" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_interval:0 +msgid "Repeat Every" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_interval:0 +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_subtotal:0 +msgid "Sub Total" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_unit:0 +msgid "Unit Price" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,uom_id:0 +msgid "Unit of Measure" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Week(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Year(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 +#, python-format +msgid "You must first select a Customer for Contract %s!" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line +#, python-format +msgid "account.analytic.invoice.line" +msgstr "" diff --git a/contract/i18n/nl.po b/contract/i18n/nl.po new file mode 100644 index 000000000..b04b804dd --- /dev/null +++ b/contract/i18n/nl.po @@ -0,0 +1,156 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_analytic_analysis_recurring +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-11 13:24+0000\n" +"PO-Revision-Date: 2014-07-11 13:24+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_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid ". create invoices" +msgstr ". create invoices" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Account Analytic Lines" +msgstr "Kostenplaatsenboekingen" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: field:account.analytic.invoice.line,analytic_account_id:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account +#, python-format +msgid "Analytic Account" +msgstr "Kostenplaats" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_next_date:0 +msgid "Date of Next Invoice" +msgstr "Datum volgende factuur" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Day(s)" +msgstr "Dag(en)" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,name:0 +msgid "Description" +msgstr "Omschrijving" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:130 +#, python-format +msgid "Error!" +msgstr "Fout" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoices:0 +msgid "Generate recurring invoices automatically" +msgstr "Periodieke facturering" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoice_line_ids:0 +msgid "Invoice Lines" +msgstr "Sjablonen factuurregels" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_rule_type:0 +msgid "Invoice automatically repeat at specified interval" +msgstr "Factureer automatisch met dit interval" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Month(s)" +msgstr "Maand(en)" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 +#, python-format +msgid "No Customer Defined!" +msgstr "Er is geen klant ingesteld." + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:131 +#, python-format +msgid "Please define a sale journal for the company \"%s\"." +msgstr "Er moet een inkoopdagboek worden ingesteld voor bedrijf \"%s\"." + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,product_id:0 +msgid "Product" +msgstr "Product" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,quantity:0 +msgid "Quantity" +msgstr "Hoeveelheid" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_rule_type:0 +msgid "Recurrency" +msgstr "Herhaling" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Recurring Invoices" +msgstr "Periodieke facturen" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_interval:0 +msgid "Repeat Every" +msgstr "Herhaal elke:" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_interval:0 +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "Herhaal elke (dag/week/maand/jaar)" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_subtotal:0 +msgid "Sub Total" +msgstr "Subtotaal" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_unit:0 +msgid "Unit Price" +msgstr "Prijs per eenheid" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,uom_id:0 +msgid "Unit of Measure" +msgstr "Maateenheid" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Week(s)" +msgstr "Week/weken" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Year(s)" +msgstr "Jaar" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 +#, python-format +msgid "You must first select a Customer for Contract %s!" +msgstr " Er moet eerst een klant worden ingesteld op contract %s!" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line +#, python-format +msgid "account.analytic.invoice.line" +msgstr "account.analytic.invoice.line" + From 7b1b64bf05364fff43789a901c417554c1461e57 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Fri, 11 Jul 2014 16:18:31 +0200 Subject: [PATCH 04/22] [RFR] PEP8 --- contract/__openerp__.py | 11 +- .../account_analytic_analysis_recurring.py | 168 ++++++++++++------ 2 files changed, 116 insertions(+), 63 deletions(-) diff --git a/contract/__openerp__.py b/contract/__openerp__.py index cfdd15fc8..3e323eb29 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -26,10 +26,13 @@ 'category': 'Other', 'description': """ This module add a new feature in contracts to manage recurring invoice -======================================================================================= +====================================================================== -This is a backport of the new V8 feature available in trunk and saas. With the V8 release this module will be deprecated. -It also add a little feature, you can use #START# and #END# in the contract line to automatically insert the dates of the invoiced period. +This is a backport of the new V8 feature available in trunk and saas. With +the V8 release this module will be deprecated. + +It also add a little feature, you can use #START# and #END# in the contract +line to automatically insert the dates of the invoiced period. Backport done By Yannick Buron. """, @@ -41,7 +44,7 @@ Backport done By Yannick Buron. 'account_analytic_analysis_recurring_view.xml', ], 'demo': [''], - 'test':[], + 'test': [], 'installable': True, 'images': [], } diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index 2b85cce6d..f6f0d71ec 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -24,62 +24,82 @@ import logging import time from openerp.osv import osv, fields -from openerp.osv.orm import intersect, except_orm -import openerp.tools from openerp.tools.translate import _ from openerp.addons.decimal_precision import decimal_precision as dp _logger = logging.getLogger(__name__) + class account_analytic_invoice_line(osv.osv): _name = "account.analytic.invoice.line" - def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None): + def _amount_line( + self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None): res = {} for line in self.browse(cr, uid, ids, context=context): res[line.id] = line.quantity * line.price_unit if line.analytic_account_id.pricelist_id: cur = line.analytic_account_id.pricelist_id.currency_id - res[line.id] = self.pool.get('res.currency').round(cr, uid, cur, res[line.id]) + res[line.id] = self.pool.get('res.currency').round( + cr, uid, cur, res[line.id]) return res _columns = { - 'product_id': fields.many2one('product.product','Product',required=True), - 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account'), + 'product_id': fields.many2one( + 'product.product', 'Product', required=True), + 'analytic_account_id': fields.many2one( + 'account.analytic.account', 'Analytic Account'), 'name': fields.text('Description', required=True), 'quantity': fields.float('Quantity', required=True), - 'uom_id': fields.many2one('product.uom', 'Unit of Measure',required=True), + 'uom_id': fields.many2one( + 'product.uom', 'Unit of Measure', required=True), 'price_unit': fields.float('Unit Price', required=True), - 'price_subtotal': fields.function(_amount_line, string='Sub Total', type="float",digits_compute= dp.get_precision('Account')), + 'price_subtotal': fields.function( + _amount_line, string='Sub Total', + type="float", digits_compute=dp.get_precision('Account')), } _defaults = { - 'quantity' : 1, + 'quantity': 1, } - def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', partner_id=False, price_unit=False, pricelist_id=False, company_id=None, context=None): + def product_id_change( + self, cr, uid, ids, product, uom_id, qty=0, name='', + partner_id=False, price_unit=False, pricelist_id=False, + company_id=None, context=None): context = context or {} uom_obj = self.pool.get('product.uom') company_id = company_id or False - context.update({'company_id': company_id, 'force_company': company_id, 'pricelist_id': pricelist_id}) + context.update( + {'company_id': company_id, + 'force_company': company_id, + 'pricelist_id': pricelist_id}) if not product: - return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} + return { + 'value': {'price_unit': 0.0}, + 'domain': {'product_uom': []}} if partner_id: - part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) + part = self.pool.get('res.partner').browse( + cr, uid, partner_id, context=context) if part.lang: context.update({'lang': part.lang}) result = {} - res = self.pool.get('product.product').browse(cr, uid, product, context=context) - result.update({'name':res.partner_ref or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0}) + res = self.pool.get('product.product').browse( + cr, uid, product, context=context) + result.update( + {'name': res.partner_ref or False, + 'uom_id': uom_id or res.uom_id.id or False, + 'price_unit': res.list_price or 0.0}) if res.description: result['name'] += '\n'+res.description - res_final = {'value':result} + res_final = {'value': result} if result['uom_id'] != res.uom_id.id: - selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=context) - new_price = uom_obj._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uom_id']) + new_price = uom_obj._compute_price( + cr, uid, res.uom_id.id, + res_final['value']['price_unit'], result['uom_id']) res_final['value']['price_unit'] = new_price return res_final @@ -89,25 +109,32 @@ class account_analytic_account(osv.osv): _inherit = "account.analytic.account" _columns = { - 'recurring_invoice_line_ids': fields.one2many('account.analytic.invoice.line', 'analytic_account_id', 'Invoice Lines'), - 'recurring_invoices' : fields.boolean('Generate recurring invoices automatically'), - 'recurring_rule_type': fields.selection([ - ('daily', 'Day(s)'), - ('weekly', 'Week(s)'), - ('monthly', 'Month(s)'), - ('yearly', 'Year(s)'), - ], 'Recurrency', help="Invoice automatically repeat at specified interval"), - 'recurring_interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"), + 'recurring_invoice_line_ids': fields.one2many( + 'account.analytic.invoice.line', 'analytic_account_id', + 'Invoice Lines'), + 'recurring_invoices': fields.boolean( + 'Generate recurring invoices automatically'), + 'recurring_rule_type': fields.selection( + [('daily', 'Day(s)'), + ('weekly', 'Week(s)'), + ('monthly', 'Month(s)'), + ('yearly', 'Year(s)'), + ], 'Recurrency', + help="Invoice automatically repeat at specified interval"), + 'recurring_interval': fields.integer( + 'Repeat Every', help="Repeat every (Days/Week/Month/Year)"), 'recurring_next_date': fields.date('Date of Next Invoice'), } _defaults = { 'recurring_interval': 1, 'recurring_next_date': lambda *a: time.strftime('%Y-%m-%d'), - 'recurring_rule_type':'monthly' + 'recurring_rule_type': 'monthly' } - def onchange_recurring_invoices(self, cr, uid, ids, recurring_invoices, date_start=False, context=None): + def onchange_recurring_invoices( + self, cr, uid, ids, recurring_invoices, + date_start=False, context=None): value = {} if date_start and recurring_invoices: value = {'value': {'recurring_next_date': date_start}} @@ -122,29 +149,37 @@ class account_analytic_account(osv.osv): lang_obj = self.pool.get('res.lang') if not contract.partner_id: - raise osv.except_osv(_('No Customer Defined!'),_("You must first select a Customer for Contract %s!") % contract.name ) + raise osv.except_osv( + _('No Customer Defined!'), + _("You must first select a Customer for Contract %s!") % + contract.name) fpos = contract.partner_id.property_account_position or False - journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id or False)], limit=1) + journal_ids = journal_obj.search( + cr, uid, + [('type', '=', 'sale'), + ('company_id', '=', contract.company_id.id or False)], + limit=1) if not journal_ids: - raise osv.except_osv(_('Error!'), - _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', )) - - partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False + raise osv.except_osv( + _('Error!'), + _('Please define a sale journal for the company "%s".') % + (contract.company_id.name or '', )) + partner_payment_term = contract.partner_id.property_payment_term.id inv_data = { - 'reference': contract.code or False, - 'account_id': contract.partner_id.property_account_receivable.id, - 'type': 'out_invoice', - 'partner_id': contract.partner_id.id, - 'currency_id': contract.partner_id.property_product_pricelist.id or False, - 'journal_id': len(journal_ids) and journal_ids[0] or False, - 'date_invoice': contract.recurring_next_date, - 'origin': contract.name, - 'fiscal_position': fpos and fpos.id, - 'payment_term': partner_payment_term, - 'company_id': contract.company_id.id or False, + 'reference': contract.code or False, + 'account_id': contract.partner_id.property_account_receivable.id, + 'type': 'out_invoice', + 'partner_id': contract.partner_id.id, + 'currency_id': contract.partner_id.property_product_pricelist.id, + 'journal_id': len(journal_ids) and journal_ids[0] or False, + 'date_invoice': contract.recurring_next_date, + 'origin': contract.name, + 'fiscal_position': fpos and fpos.id, + 'payment_term': partner_payment_term, + 'company_id': contract.company_id.id or False, } invoice_id = inv_obj.create(cr, uid, inv_data, context=context) @@ -160,10 +195,15 @@ class account_analytic_account(osv.osv): tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) if 'old_date' in context: - lang_ids = lang_obj.search(cr, uid, [('code', '=', contract.partner_id.lang)], context=context) - format = lang_obj.browse(cr, uid, lang_ids, context=context)[0].date_format - line.name = line.name.replace('#START#', context['old_date'].strftime(format)) - line.name = line.name.replace('#END#', context['next_date'].strftime(format)) + lang_ids = lang_obj.search( + cr, uid, [('code', '=', contract.partner_id.lang)], + context=context) + format = lang_obj.browse( + cr, uid, lang_ids, context=context)[0].date_format + line.name = line.name.replace( + '#START#', context['old_date'].strftime(format)) + line.name = line.name.replace( + '#END#', context['next_date'].strftime(format)) invoice_line_vals = { 'name': line.name, @@ -173,22 +213,28 @@ class account_analytic_account(osv.osv): 'quantity': line.quantity, 'uos_id': line.uom_id.id or False, 'product_id': line.product_id.id or False, - 'invoice_id' : invoice_id, + 'invoice_id': invoice_id, 'invoice_line_tax_id': [(6, 0, tax_id)], } - self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context) + self.pool.get('account.invoice.line').create( + cr, uid, invoice_line_vals, context=context) inv_obj.button_compute(cr, uid, [invoice_id], context=context) return invoice_id def recurring_create_invoice(self, cr, uid, automatic=False, context=None): context = context or {} - current_date = time.strftime('%Y-%m-%d') + current_date = time.strftime('%Y-%m-%d') - contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True)]) + contract_ids = self.search( + cr, uid, + [('recurring_next_date', '<=', current_date), + ('state', '=', 'open'), + ('recurring_invoices', '=', True)]) for contract in self.browse(cr, uid, contract_ids, context=context): - next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d") + next_date = datetime.datetime.strptime( + contract.recurring_next_date or current_date, "%Y-%m-%d") interval = contract.recurring_interval if contract.recurring_rule_type == 'daily': old_date = next_date-relativedelta(days=+interval) @@ -201,9 +247,13 @@ class account_analytic_account(osv.osv): new_date = next_date+relativedelta(months=+interval) context['old_date'] = old_date - context['next_date'] = datetime.datetime.strptime(contract.recurring_next_date or current_date,"%Y-%m-%d") - invoice_id = self._prepare_invoice(cr, uid, contract, context=context) + context['next_date'] = datetime.datetime.strptime( + contract.recurring_next_date or current_date, "%Y-%m-%d") + self._prepare_invoice( + cr, uid, contract, context=context) - self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context) + self.write( + cr, uid, [contract.id], + {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, + context=context) return True - From c9ea8bde2224269f138b8a374f47ec175abcf6a9 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 25 Aug 2014 20:32:38 +0200 Subject: [PATCH 05/22] [IMP] account_analytic_analysis_recurring: Spanish translation --- contract/i18n/es.po | 156 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 contract/i18n/es.po diff --git a/contract/i18n/es.po b/contract/i18n/es.po new file mode 100644 index 000000000..8ea63a970 --- /dev/null +++ b/contract/i18n/es.po @@ -0,0 +1,156 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_analytic_analysis_recurring +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-08-18 23:13+0000\n" +"PO-Revision-Date: 2014-08-19 01:14+0100\n" +"Last-Translator: Joaquin Gutierrez \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid ". create invoices" +msgstr ". crear facturas" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Account Analytic Lines" +msgstr "Ver líneas contables analíticas" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: field:account.analytic.invoice.line,analytic_account_id:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account +#, python-format +msgid "Analytic Account" +msgstr "Cuenta analítica" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_next_date:0 +msgid "Date of Next Invoice" +msgstr "Próximo fecha de factura" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Day(s)" +msgstr "Día(s)" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,name:0 +msgid "Description" +msgstr "Descripción" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:165 +#, python-format +msgid "Error!" +msgstr "¡Error!" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoices:0 +msgid "Generate recurring invoices automatically" +msgstr "Generar facturas recurrentes automáticamente." + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoice_line_ids:0 +msgid "Invoice Lines" +msgstr "Líneas de factura" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_rule_type:0 +msgid "Invoice automatically repeat at specified interval" +msgstr "Repetir factura automáticamente en ese intervalo" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Month(s)" +msgstr "Mes(es)" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:153 +#, python-format +msgid "No Customer Defined!" +msgstr "¡No se ha definido un cliente!" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:166 +#, python-format +msgid "Please define a sale journal for the company \"%s\"." +msgstr "Defina por favor un diario de ventas para esta compañía \"%s\"." + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,product_id:0 +msgid "Product" +msgstr "Producto" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,quantity:0 +msgid "Quantity" +msgstr "Cantidad" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_rule_type:0 +msgid "Recurrency" +msgstr "Recurrencia" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Recurring Invoices" +msgstr "Facturas recurrentes" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_interval:0 +msgid "Repeat Every" +msgstr "Repetir cada" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_interval:0 +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "Repetir cada (días/semana/mes/año)" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_subtotal:0 +msgid "Sub Total" +msgstr "Subtotal" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_unit:0 +msgid "Unit Price" +msgstr "Precio unidad" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,uom_id:0 +msgid "Unit of Measure" +msgstr "Unidad de medida" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Week(s)" +msgstr "Semana(s)" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Year(s)" +msgstr "Año(s)" + +#. module: account_analytic_analysis_recurring +#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:154 +#, python-format +msgid "You must first select a Customer for Contract %s!" +msgstr "¡Seleccione un cliente para este contrato %s!" + +#. module: account_analytic_analysis_recurring +#: code:_description:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line +#, python-format +msgid "account.analytic.invoice.line" +msgstr "account.analytic.invoice.line" + From 7e68d75184dda256698963134403de42729c97fb Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 26 Aug 2014 02:07:20 +0200 Subject: [PATCH 06/22] [FIX] POT file [FIX] Access rules file --- contract/__openerp__.py | 10 +- .../account_analytic_analysis_recurring.pot | 129 ------------ .../account_analytic_analysis_recurring.pot | 190 ++++++++---------- contract/security/ir.model.access.csv | 4 + 4 files changed, 96 insertions(+), 237 deletions(-) delete mode 100644 contract/account_analytic_analysis_recurring.pot create mode 100644 contract/security/ir.model.access.csv diff --git a/contract/__openerp__.py b/contract/__openerp__.py index 3e323eb29..09a3cee2d 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -25,14 +25,14 @@ 'version': '0.1', 'category': 'Other', 'description': """ -This module add a new feature in contracts to manage recurring invoice -====================================================================== +This module adds a new feature in contracts to manage recurring invoicing +========================================================================= This is a backport of the new V8 feature available in trunk and saas. With the V8 release this module will be deprecated. -It also add a little feature, you can use #START# and #END# in the contract -line to automatically insert the dates of the invoiced period. +It also adds a little feature, you can use #START# and #END# in the contract +line description to automatically insert the dates of the invoiced period. Backport done By Yannick Buron. """, @@ -40,6 +40,7 @@ Backport done By Yannick Buron. 'website': 'http://openerp.com', 'depends': ['base', 'account_analytic_analysis'], 'data': [ + 'security/ir.model.access.csv', 'account_analytic_analysis_recurring_cron.xml', 'account_analytic_analysis_recurring_view.xml', ], @@ -48,4 +49,3 @@ Backport done By Yannick Buron. 'installable': True, 'images': [], } -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/contract/account_analytic_analysis_recurring.pot b/contract/account_analytic_analysis_recurring.pot deleted file mode 100644 index 7d2f21f1f..000000000 --- a/contract/account_analytic_analysis_recurring.pot +++ /dev/null @@ -1,129 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * account_analytic_analysis_recurring -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 7.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-21 11:41+0000\n" -"PO-Revision-Date: 2014-02-21 11:41+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_analytic_analysis_recurring -#: field:account.analytic.invoice.line,price_subtotal:0 -msgid "Sub Total" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_rule_type:0 -msgid "Recurrency" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,price_unit:0 -msgid "Unit Price" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: view:account.analytic.account:0 -msgid ". create invoices" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: view:account.analytic.account:0 -msgid "Account Analytic Lines" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_invoice_line_ids:0 -msgid "Invoice Lines" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,uom_id:0 -msgid "Unit of Measure" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Day(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: help:account.analytic.account,recurring_rule_type:0 -msgid "Invoice automatically repeat at specified interval" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,product_id:0 -msgid "Product" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,name:0 -msgid "Description" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_interval:0 -msgid "Repeat Every" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: view:account.analytic.account:0 -msgid "Recurring Invoices" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_invoices:0 -msgid "Generate recurring invoices automatically" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Year(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Week(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,quantity:0 -msgid "Quantity" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line -msgid "account.analytic.invoice.line" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_next_date:0 -msgid "Date of Next Invoice" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,analytic_account_id:0 -#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account -msgid "Analytic Account" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Month(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: help:account.analytic.account,recurring_interval:0 -msgid "Repeat every (Days/Week/Month/Year)" -msgstr "" - - diff --git a/contract/i18n/account_analytic_analysis_recurring.pot b/contract/i18n/account_analytic_analysis_recurring.pot index ce2499c51..7d2f21f1f 100644 --- a/contract/i18n/account_analytic_analysis_recurring.pot +++ b/contract/i18n/account_analytic_analysis_recurring.pot @@ -1,9 +1,34 @@ # Translation of OpenERP Server. # This file contains the translation of the following modules: -# * account_analytic_analysis_recurring +# * account_analytic_analysis_recurring # msgid "" msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-21 11:41+0000\n" +"PO-Revision-Date: 2014-02-21 11:41+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_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_subtotal:0 +msgid "Sub Total" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_rule_type:0 +msgid "Recurrency" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,price_unit:0 +msgid "Unit Price" +msgstr "" #. module: account_analytic_analysis_recurring #: view:account.analytic.account:0 @@ -15,107 +40,11 @@ msgstr "" msgid "Account Analytic Lines" msgstr "" -#. module: account_analytic_analysis_recurring -#: code:_description:0 -#: field:account.analytic.invoice.line,analytic_account_id:0 -#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account -#, python-format -msgid "Analytic Account" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_next_date:0 -msgid "Date of Next Invoice" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Day(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,name:0 -msgid "Description" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:130 -#, python-format -msgid "Error!" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_invoices:0 -msgid "Generate recurring invoices automatically" -msgstr "" - #. module: account_analytic_analysis_recurring #: field:account.analytic.account,recurring_invoice_line_ids:0 msgid "Invoice Lines" msgstr "" -#. module: account_analytic_analysis_recurring -#: help:account.analytic.account,recurring_rule_type:0 -msgid "Invoice automatically repeat at specified interval" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: selection:account.analytic.account,recurring_rule_type:0 -msgid "Month(s)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 -#, python-format -msgid "No Customer Defined!" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:131 -#, python-format -msgid "Please define a sale journal for the company \"%s\"." -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,product_id:0 -msgid "Product" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,quantity:0 -msgid "Quantity" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_rule_type:0 -msgid "Recurrency" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: view:account.analytic.account:0 -msgid "Recurring Invoices" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.account,recurring_interval:0 -msgid "Repeat Every" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: help:account.analytic.account,recurring_interval:0 -msgid "Repeat every (Days/Week/Month/Year)" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,price_subtotal:0 -msgid "Sub Total" -msgstr "" - -#. module: account_analytic_analysis_recurring -#: field:account.analytic.invoice.line,price_unit:0 -msgid "Unit Price" -msgstr "" - #. module: account_analytic_analysis_recurring #: field:account.analytic.invoice.line,uom_id:0 msgid "Unit of Measure" @@ -123,7 +52,37 @@ msgstr "" #. module: account_analytic_analysis_recurring #: selection:account.analytic.account,recurring_rule_type:0 -msgid "Week(s)" +msgid "Day(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_rule_type:0 +msgid "Invoice automatically repeat at specified interval" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,product_id:0 +msgid "Product" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,name:0 +msgid "Description" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_interval:0 +msgid "Repeat Every" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: view:account.analytic.account:0 +msgid "Recurring Invoices" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_invoices:0 +msgid "Generate recurring invoices automatically" msgstr "" #. module: account_analytic_analysis_recurring @@ -132,14 +91,39 @@ msgid "Year(s)" msgstr "" #. module: account_analytic_analysis_recurring -#: code:addons/account_analytic_analysis_recurring/account_analytic_analysis_recurring.py:125 -#, python-format -msgid "You must first select a Customer for Contract %s!" +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Week(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,quantity:0 +msgid "Quantity" msgstr "" #. module: account_analytic_analysis_recurring -#: code:_description:0 #: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_invoice_line -#, python-format msgid "account.analytic.invoice.line" msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.account,recurring_next_date:0 +msgid "Date of Next Invoice" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: field:account.analytic.invoice.line,analytic_account_id:0 +#: model:ir.model,name:account_analytic_analysis_recurring.model_account_analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: selection:account.analytic.account,recurring_rule_type:0 +msgid "Month(s)" +msgstr "" + +#. module: account_analytic_analysis_recurring +#: help:account.analytic.account,recurring_interval:0 +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "" + + diff --git a/contract/security/ir.model.access.csv b/contract/security/ir.model.access.csv new file mode 100644 index 000000000..fb584e025 --- /dev/null +++ b/contract/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"account_analytic_invoice_line_manager","Recurring manager","model_account_analytic_invoice_line","project.group_project_manager",1,1,1,1 +"account_analytic_invoice_line_user","Recurring user","model_account_analytic_invoice_line","project.group_project_user",1,0,0,0 + From b6cb18df08a7dfa23a9d61e0f29ae48e7b6ca22c Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 3 Sep 2014 02:57:24 +0200 Subject: [PATCH 07/22] [FIX] account_analytic_analysis_recurring: Currency for created invoices. --- .../account_analytic_analysis_recurring.py | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index f6f0d71ec..dfc25e8ed 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -141,8 +141,8 @@ class account_analytic_account(osv.osv): return value def _prepare_invoice(self, cr, uid, contract, context=None): - context = context or {} - + if context is None: + context = {} inv_obj = self.pool.get('account.invoice') journal_obj = self.pool.get('account.journal') fpos_obj = self.pool.get('account.fiscal.position') @@ -153,8 +153,8 @@ class account_analytic_account(osv.osv): _('No Customer Defined!'), _("You must first select a Customer for Contract %s!") % contract.name) - - fpos = contract.partner_id.property_account_position or False + partner = contract.partner_id + fpos = partner.property_account_position or False journal_ids = journal_obj.search( cr, uid, [('type', '=', 'sale'), @@ -165,15 +165,13 @@ class account_analytic_account(osv.osv): _('Error!'), _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', )) - partner_payment_term = contract.partner_id.property_payment_term.id - inv_data = { 'reference': contract.code or False, - 'account_id': contract.partner_id.property_account_receivable.id, + 'account_id': partner.property_account_receivable.id, 'type': 'out_invoice', - 'partner_id': contract.partner_id.id, - 'currency_id': contract.partner_id.property_product_pricelist.id, + 'partner_id': partner.id, + 'currency_id': partner.property_product_pricelist.currency_id.id, 'journal_id': len(journal_ids) and journal_ids[0] or False, 'date_invoice': contract.recurring_next_date, 'origin': contract.name, @@ -182,18 +180,14 @@ class account_analytic_account(osv.osv): 'company_id': contract.company_id.id or False, } invoice_id = inv_obj.create(cr, uid, inv_data, context=context) - for line in contract.recurring_invoice_line_ids: - res = line.product_id account_id = res.property_account_income.id if not account_id: account_id = res.categ_id.property_account_income_categ.id account_id = fpos_obj.map_account(cr, uid, fpos, account_id) - taxes = res.taxes_id or False tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) - if 'old_date' in context: lang_ids = lang_obj.search( cr, uid, [('code', '=', contract.partner_id.lang)], @@ -204,7 +198,6 @@ class account_analytic_account(osv.osv): '#START#', context['old_date'].strftime(format)) line.name = line.name.replace( '#END#', context['next_date'].strftime(format)) - invoice_line_vals = { 'name': line.name, 'account_id': account_id, From fc8afd563c6746637c444a5f380022268e4c9715 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 15 Sep 2014 18:20:53 +0200 Subject: [PATCH 08/22] [FIX] account_analytic_analysis_recurring: #START# and #END# set correctly [IMP] Some conventions --- .../account_analytic_analysis_recurring.py | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index dfc25e8ed..59d3ba824 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -23,15 +23,14 @@ import datetime import logging import time -from openerp.osv import osv, fields +from openerp.osv import orm, fields from openerp.tools.translate import _ - from openerp.addons.decimal_precision import decimal_precision as dp _logger = logging.getLogger(__name__) -class account_analytic_invoice_line(osv.osv): +class AccountAnalyticInvoiceLine(orm.Model): _name = "account.analytic.invoice.line" def _amount_line( @@ -104,7 +103,7 @@ class account_analytic_invoice_line(osv.osv): return res_final -class account_analytic_account(osv.osv): +class AccountAnalyticAccount(orm.Model): _name = "account.analytic.account" _inherit = "account.analytic.account" @@ -132,6 +131,13 @@ class account_analytic_account(osv.osv): 'recurring_rule_type': 'monthly' } + def copy(self, cr, uid, id, default=None, context=None): + # Reset next invoice date + default['recurring_next_date'] = \ + self._defaults['recurring_next_date']() + return super(AccountAnalyticAccount, self).copy( + cr, uid, id, default=default, context=context) + def onchange_recurring_invoices( self, cr, uid, ids, recurring_invoices, date_start=False, context=None): @@ -147,9 +153,8 @@ class account_analytic_account(osv.osv): journal_obj = self.pool.get('account.journal') fpos_obj = self.pool.get('account.fiscal.position') lang_obj = self.pool.get('res.lang') - if not contract.partner_id: - raise osv.except_osv( + raise orm.except_orm( _('No Customer Defined!'), _("You must first select a Customer for Contract %s!") % contract.name) @@ -161,7 +166,7 @@ class account_analytic_account(osv.osv): ('company_id', '=', contract.company_id.id or False)], limit=1) if not journal_ids: - raise osv.except_osv( + raise orm.except_orm( _('Error!'), _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', )) @@ -209,42 +214,35 @@ class account_analytic_account(osv.osv): 'invoice_id': invoice_id, 'invoice_line_tax_id': [(6, 0, tax_id)], } - self.pool.get('account.invoice.line').create( + self.pool['account.invoice.line'].create( cr, uid, invoice_line_vals, context=context) - inv_obj.button_compute(cr, uid, [invoice_id], context=context) return invoice_id def recurring_create_invoice(self, cr, uid, automatic=False, context=None): - context = context or {} + if context is None: + context = {} current_date = time.strftime('%Y-%m-%d') - contract_ids = self.search( cr, uid, [('recurring_next_date', '<=', current_date), ('state', '=', 'open'), ('recurring_invoices', '=', True)]) for contract in self.browse(cr, uid, contract_ids, context=context): - next_date = datetime.datetime.strptime( contract.recurring_next_date or current_date, "%Y-%m-%d") interval = contract.recurring_interval + old_date = next_date if contract.recurring_rule_type == 'daily': - old_date = next_date-relativedelta(days=+interval) - new_date = next_date+relativedelta(days=+interval) + new_date = next_date + relativedelta(days=+interval) elif contract.recurring_rule_type == 'weekly': - old_date = next_date-relativedelta(weeks=+interval) - new_date = next_date+relativedelta(weeks=+interval) + new_date = next_date + relativedelta(weeks=+interval) else: - old_date = next_date+relativedelta(months=+interval) - new_date = next_date+relativedelta(months=+interval) - + new_date = next_date + relativedelta(months=+interval) context['old_date'] = old_date - context['next_date'] = datetime.datetime.strptime( - contract.recurring_next_date or current_date, "%Y-%m-%d") + context['next_date'] = new_date self._prepare_invoice( cr, uid, contract, context=context) - self.write( cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, From 4b73c6f5f5ac05f16064ff6b631d57c656cf6d06 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sat, 27 Sep 2014 13:02:32 +0200 Subject: [PATCH 09/22] [FIX] account_analytic_analysis_recurring: Fix ACL --- contract/security/ir.model.access.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/security/ir.model.access.csv b/contract/security/ir.model.access.csv index fb584e025..902c5d592 100644 --- a/contract/security/ir.model.access.csv +++ b/contract/security/ir.model.access.csv @@ -1,4 +1,4 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"account_analytic_invoice_line_manager","Recurring manager","model_account_analytic_invoice_line","project.group_project_manager",1,1,1,1 -"account_analytic_invoice_line_user","Recurring user","model_account_analytic_invoice_line","project.group_project_user",1,0,0,0 +"account_analytic_invoice_line_manager","Recurring manager","model_account_analytic_invoice_line","base.group_sale_manager",1,1,1,1 +"account_analytic_invoice_line_user","Recurring user","model_account_analytic_invoice_line","base.group_sale_salesman",1,0,0,0 From 57791c2d4ea2bd10d8aa8c97f4428bb554978695 Mon Sep 17 00:00:00 2001 From: Federico Cruz Date: Thu, 23 Oct 2014 19:59:12 -0500 Subject: [PATCH 10/22] Set correct company on invoices when working on multicompany environment Now when working on multy company environmets the server action creates invioices with accounts belong to correct company based on contract company. --- .../account_analytic_analysis_recurring.py | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index dfc25e8ed..e05c8f7f4 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -93,7 +93,7 @@ class account_analytic_invoice_line(osv.osv): 'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0}) if res.description: - result['name'] += '\n'+res.description + result['name'] += '\n' + res.description res_final = {'value': result} if result['uom_id'] != res.uom_id.id: @@ -164,7 +164,7 @@ class account_analytic_account(osv.osv): raise osv.except_osv( _('Error!'), _('Please define a sale journal for the company "%s".') % - (contract.company_id.name or '', )) + (contract.company_id.name or '',)) partner_payment_term = contract.partner_id.property_payment_term.id inv_data = { 'reference': contract.code or False, @@ -230,20 +230,41 @@ class account_analytic_account(osv.osv): contract.recurring_next_date or current_date, "%Y-%m-%d") interval = contract.recurring_interval if contract.recurring_rule_type == 'daily': - old_date = next_date-relativedelta(days=+interval) - new_date = next_date+relativedelta(days=+interval) + old_date = next_date - relativedelta(days=+interval) + new_date = next_date + relativedelta(days=+interval) elif contract.recurring_rule_type == 'weekly': - old_date = next_date-relativedelta(weeks=+interval) - new_date = next_date+relativedelta(weeks=+interval) + old_date = next_date - relativedelta(weeks=+interval) + new_date = next_date + relativedelta(weeks=+interval) else: - old_date = next_date+relativedelta(months=+interval) - new_date = next_date+relativedelta(months=+interval) + old_date = next_date + relativedelta(months=+interval) + new_date = next_date + relativedelta(months=+interval) context['old_date'] = old_date context['next_date'] = datetime.datetime.strptime( contract.recurring_next_date or current_date, "%Y-%m-%d") - self._prepare_invoice( - cr, uid, contract, context=context) + # Force company for correct evaluate domain access rules + context['force_company'] = contract.company_id.id + + # Create new cursor for handle multi company environments + from openerp import pooler + db, pool = pooler.get_db_and_pool(cr.dbname) + cursor = db.cursor() + try: + this = pool.get('account.analytic.account') + # Need to reload contract on new cursor for prevent + # ORM optimizations use same company for load all + # partner properties + contract = this.browse( + cursor, uid, contract.id, context=context + ) + this._prepare_invoice( + cursor, uid, contract, context=context + ) + cursor.commit() # commit results + except Exception: + cursor.rollback() # error, rollback everything + finally: + cursor.close() # always close cursor self.write( cr, uid, [contract.id], From f25bd906bf57c2df59d094c5bc918fcf2555b1ff Mon Sep 17 00:00:00 2001 From: Federico Cruz Date: Tue, 28 Oct 2014 19:10:40 -0600 Subject: [PATCH 11/22] Set correct company on invoices when working on multicompany environment In the last commit we used a new cursor for this problem, but in this time we make a browse for each contract like Pedro Baeza proposed --- .../account_analytic_analysis_recurring.py | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index 23e528335..fc1958038 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -243,30 +243,14 @@ class AccountAnalyticAccount(orm.Model): context['next_date'] = new_date # Force company for correct evaluate domain access rules context['force_company'] = contract.company_id.id - - # Create new cursor for handle multi company environments - from openerp import pooler - db, pool = pooler.get_db_and_pool(cr.dbname) - cursor = db.cursor() - try: - this = pool.get('account.analytic.account') - # Need to reload contract on new cursor for prevent - # ORM optimizations use same company for load all - # partner properties - contract = this.browse( - cursor, uid, contract.id, context=context - ) - this._prepare_invoice( - cursor, uid, contract, context=context - ) - cursor.commit() # commit results - except Exception: - cursor.rollback() # error, rollback everything - finally: - cursor.close() # always close cursor - + # Re-read contract with correct company + contract = self.browse(cr, uid, contract.id, context=context) + self._prepare_invoice( + cr, uid, contract, context=context + ) self.write( cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, - context=context) + context=context + ) return True From 3b0ed9dd2156a23010232d7495b278bb31f2d28d Mon Sep 17 00:00:00 2001 From: moylop260 Date: Thu, 13 Nov 2014 21:42:35 -0600 Subject: [PATCH 12/22] [REF] auto-fix conventions --- contract/__init__.py | 2 +- contract/account_analytic_analysis_recurring.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/__init__.py b/contract/__init__.py index 7088b5aa8..41ecc2add 100644 --- a/contract/__init__.py +++ b/contract/__init__.py @@ -19,4 +19,4 @@ # ############################################################################## -import account_analytic_analysis_recurring +from . import account_analytic_analysis_recurring diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index 59d3ba824..6d87916ba 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -92,7 +92,7 @@ class AccountAnalyticInvoiceLine(orm.Model): 'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0}) if res.description: - result['name'] += '\n'+res.description + result['name'] += '\n' + res.description res_final = {'value': result} if result['uom_id'] != res.uom_id.id: From f9bc249b7a77744a0102cb4915a28c9094a149a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=BE=20=D0=9A=D0=B0=D1=82?= =?UTF-8?q?=D1=8E=D1=85=D0=B0?= Date: Wed, 14 Jan 2015 18:23:21 +0200 Subject: [PATCH 13/22] Partch for #55 (missing dependency hr-timesheet) Also fixed bug on loading of 'account_analytic_analysis_recurring' module and pylint fixes in project_sla.m2m --- contract/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/__openerp__.py b/contract/__openerp__.py index 09a3cee2d..cbea33c28 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -44,7 +44,7 @@ Backport done By Yannick Buron. 'account_analytic_analysis_recurring_cron.xml', 'account_analytic_analysis_recurring_view.xml', ], - 'demo': [''], + 'demo': [], 'test': [], 'installable': True, 'images': [], From 1afffca5f22bb0f582ad4dcef1709c6c4000ad70 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Thu, 12 Feb 2015 16:30:41 +0100 Subject: [PATCH 14/22] [IMP] account_analytic_analysis_recurring: Hook method for preparing invoice lines --- .../account_analytic_analysis_recurring.py | 72 ++++++++++--------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py index fc1958038..e5f2a99ca 100644 --- a/contract/account_analytic_analysis_recurring.py +++ b/contract/account_analytic_analysis_recurring.py @@ -146,13 +146,45 @@ class AccountAnalyticAccount(orm.Model): value = {'value': {'recurring_next_date': date_start}} return value + def _prepare_invoice_line(self, cr, uid, line, invoice_id, context=None): + fpos_obj = self.pool['account.fiscal.position'] + lang_obj = self.pool['res.lang'] + product = line.product_id + account_id = product.property_account_income.id + if not account_id: + account_id = product.categ_id.property_account_income_categ.id + contract = line.analytic_account_id + fpos = contract.partner_id.property_account_position or False + account_id = fpos_obj.map_account(cr, uid, fpos, account_id) + taxes = product.taxes_id or False + tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) + if 'old_date' in context: + lang_ids = lang_obj.search( + cr, uid, [('code', '=', contract.partner_id.lang)], + context=context) + format = lang_obj.browse( + cr, uid, lang_ids, context=context)[0].date_format + line.name = line.name.replace( + '#START#', context['old_date'].strftime(format)) + line.name = line.name.replace( + '#END#', context['next_date'].strftime(format)) + return { + 'name': line.name, + 'account_id': account_id, + 'account_analytic_id': contract.id, + 'price_unit': line.price_unit or 0.0, + 'quantity': line.quantity, + 'uos_id': line.uom_id.id or False, + 'product_id': line.product_id.id or False, + 'invoice_id': invoice_id, + 'invoice_line_tax_id': [(6, 0, tax_id)], + } + def _prepare_invoice(self, cr, uid, contract, context=None): if context is None: context = {} - inv_obj = self.pool.get('account.invoice') - journal_obj = self.pool.get('account.journal') - fpos_obj = self.pool.get('account.fiscal.position') - lang_obj = self.pool.get('res.lang') + inv_obj = self.pool['account.invoice'] + journal_obj = self.pool['account.journal'] if not contract.partner_id: raise orm.except_orm( _('No Customer Defined!'), @@ -170,7 +202,7 @@ class AccountAnalyticAccount(orm.Model): _('Error!'), _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '',)) - partner_payment_term = contract.partner_id.property_payment_term.id + partner_payment_term = partner.property_payment_term.id inv_data = { 'reference': contract.code or False, 'account_id': partner.property_account_receivable.id, @@ -186,34 +218,8 @@ class AccountAnalyticAccount(orm.Model): } invoice_id = inv_obj.create(cr, uid, inv_data, context=context) for line in contract.recurring_invoice_line_ids: - res = line.product_id - account_id = res.property_account_income.id - if not account_id: - account_id = res.categ_id.property_account_income_categ.id - account_id = fpos_obj.map_account(cr, uid, fpos, account_id) - taxes = res.taxes_id or False - tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) - if 'old_date' in context: - lang_ids = lang_obj.search( - cr, uid, [('code', '=', contract.partner_id.lang)], - context=context) - format = lang_obj.browse( - cr, uid, lang_ids, context=context)[0].date_format - line.name = line.name.replace( - '#START#', context['old_date'].strftime(format)) - line.name = line.name.replace( - '#END#', context['next_date'].strftime(format)) - invoice_line_vals = { - 'name': line.name, - 'account_id': account_id, - 'account_analytic_id': contract.id, - 'price_unit': line.price_unit or 0.0, - 'quantity': line.quantity, - 'uos_id': line.uom_id.id or False, - 'product_id': line.product_id.id or False, - 'invoice_id': invoice_id, - 'invoice_line_tax_id': [(6, 0, tax_id)], - } + invoice_line_vals = self._prepare_invoice_line( + cr, uid, line, invoice_id, context=context) self.pool['account.invoice.line'].create( cr, uid, invoice_line_vals, context=context) inv_obj.button_compute(cr, uid, [invoice_id], context=context) From cb7a9f902e41ce662759b9d718097f48264f57af Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 2 Mar 2015 17:27:05 +0100 Subject: [PATCH 15/22] Add OCA as author of OCA addons In order to get visibility on https://www.odoo.com/apps the OCA board has decided to add the OCA as author of all the addons maintained as part of the association. --- contract/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/__openerp__.py b/contract/__openerp__.py index cbea33c28..bd6c976b6 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -36,7 +36,7 @@ line description to automatically insert the dates of the invoiced period. Backport done By Yannick Buron. """, - 'author': 'OpenERP SA', + 'author': "OpenERP SA,Odoo Community Association (OCA)", 'website': 'http://openerp.com', 'depends': ['base', 'account_analytic_analysis'], 'data': [ From 84874d3d76c87b4f774e318d06dd75605994ec31 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Tue, 22 Mar 2016 20:34:18 +0100 Subject: [PATCH 16/22] Migration from v7 to v9 improving features --- contract/README.rst | 56 ++++ contract/__init__.py | 22 +- contract/__openerp__.py | 44 +-- .../account_analytic_analysis_recurring.py | 262 ------------------ ...count_analytic_analysis_recurring_view.xml | 44 --- .../contract_cron.xml} | 0 contract/models/__init__.py | 3 + contract/models/contract.py | 256 +++++++++++++++++ contract/models/invoice.py | 26 ++ contract/views/contract.xml | 103 +++++++ 10 files changed, 451 insertions(+), 365 deletions(-) create mode 100644 contract/README.rst delete mode 100644 contract/account_analytic_analysis_recurring.py delete mode 100644 contract/account_analytic_analysis_recurring_view.xml rename contract/{account_analytic_analysis_recurring_cron.xml => data/contract_cron.xml} (100%) create mode 100644 contract/models/__init__.py create mode 100644 contract/models/contract.py create mode 100644 contract/models/invoice.py create mode 100644 contract/views/contract.xml diff --git a/contract/README.rst b/contract/README.rst new file mode 100644 index 000000000..0abdc88f3 --- /dev/null +++ b/contract/README.rst @@ -0,0 +1,56 @@ +.. 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 + +======== +Contract +======== + +This module helps you to manage contracts with recurring invoices. + +Usage +===== + +To use this module, you need 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/167/8.0 + +For further information, please visit: + +* https://www.odoo.com/forum/help-1 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 +`here `_. + +Credits +======= + +Contributors +------------ + +* Pedro M. Baeza +* Carlos Dauden +* Angel Moya + +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. diff --git a/contract/__init__.py b/contract/__init__.py index 41ecc2add..a0fdc10fe 100644 --- a/contract/__init__.py +++ b/contract/__init__.py @@ -1,22 +1,2 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL () -# -# 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 . -# -############################################################################## - -from . import account_analytic_analysis_recurring +from . import models diff --git a/contract/__openerp__.py b/contract/__openerp__.py index bd6c976b6..4ec334ca4 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -1,51 +1,19 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - +# © 2016 Incaser Informatica S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { 'name': 'Contracts Management recurring', - 'version': '0.1', + 'version': '9.0.1.0.0', 'category': 'Other', - 'description': """ -This module adds a new feature in contracts to manage recurring invoicing -========================================================================= - -This is a backport of the new V8 feature available in trunk and saas. With -the V8 release this module will be deprecated. - -It also adds a little feature, you can use #START# and #END# in the contract -line description to automatically insert the dates of the invoiced period. - -Backport done By Yannick Buron. -""", 'author': "OpenERP SA,Odoo Community Association (OCA)", 'website': 'http://openerp.com', - 'depends': ['base', 'account_analytic_analysis'], + 'depends': ['base', 'account', 'analytic'], 'data': [ 'security/ir.model.access.csv', - 'account_analytic_analysis_recurring_cron.xml', - 'account_analytic_analysis_recurring_view.xml', + 'data/contract_cron.xml', + 'views/contract.xml', ], - 'demo': [], - 'test': [], 'installable': True, 'images': [], } diff --git a/contract/account_analytic_analysis_recurring.py b/contract/account_analytic_analysis_recurring.py deleted file mode 100644 index e5f2a99ca..000000000 --- a/contract/account_analytic_analysis_recurring.py +++ /dev/null @@ -1,262 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## -from dateutil.relativedelta import relativedelta -import datetime -import logging -import time - -from openerp.osv import orm, fields -from openerp.tools.translate import _ -from openerp.addons.decimal_precision import decimal_precision as dp - -_logger = logging.getLogger(__name__) - - -class AccountAnalyticInvoiceLine(orm.Model): - _name = "account.analytic.invoice.line" - - def _amount_line( - self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None): - res = {} - for line in self.browse(cr, uid, ids, context=context): - res[line.id] = line.quantity * line.price_unit - if line.analytic_account_id.pricelist_id: - cur = line.analytic_account_id.pricelist_id.currency_id - res[line.id] = self.pool.get('res.currency').round( - cr, uid, cur, res[line.id]) - return res - - _columns = { - 'product_id': fields.many2one( - 'product.product', 'Product', required=True), - 'analytic_account_id': fields.many2one( - 'account.analytic.account', 'Analytic Account'), - 'name': fields.text('Description', required=True), - 'quantity': fields.float('Quantity', required=True), - 'uom_id': fields.many2one( - 'product.uom', 'Unit of Measure', required=True), - 'price_unit': fields.float('Unit Price', required=True), - 'price_subtotal': fields.function( - _amount_line, string='Sub Total', - type="float", digits_compute=dp.get_precision('Account')), - } - _defaults = { - 'quantity': 1, - } - - def product_id_change( - self, cr, uid, ids, product, uom_id, qty=0, name='', - partner_id=False, price_unit=False, pricelist_id=False, - company_id=None, context=None): - context = context or {} - uom_obj = self.pool.get('product.uom') - company_id = company_id or False - context.update( - {'company_id': company_id, - 'force_company': company_id, - 'pricelist_id': pricelist_id}) - - if not product: - return { - 'value': {'price_unit': 0.0}, - 'domain': {'product_uom': []}} - if partner_id: - part = self.pool.get('res.partner').browse( - cr, uid, partner_id, context=context) - if part.lang: - context.update({'lang': part.lang}) - - result = {} - res = self.pool.get('product.product').browse( - cr, uid, product, context=context) - result.update( - {'name': res.partner_ref or False, - 'uom_id': uom_id or res.uom_id.id or False, - 'price_unit': res.list_price or 0.0}) - if res.description: - result['name'] += '\n' + res.description - - res_final = {'value': result} - if result['uom_id'] != res.uom_id.id: - new_price = uom_obj._compute_price( - cr, uid, res.uom_id.id, - res_final['value']['price_unit'], result['uom_id']) - res_final['value']['price_unit'] = new_price - return res_final - - -class AccountAnalyticAccount(orm.Model): - _name = "account.analytic.account" - _inherit = "account.analytic.account" - - _columns = { - 'recurring_invoice_line_ids': fields.one2many( - 'account.analytic.invoice.line', 'analytic_account_id', - 'Invoice Lines'), - 'recurring_invoices': fields.boolean( - 'Generate recurring invoices automatically'), - 'recurring_rule_type': fields.selection( - [('daily', 'Day(s)'), - ('weekly', 'Week(s)'), - ('monthly', 'Month(s)'), - ('yearly', 'Year(s)'), - ], 'Recurrency', - help="Invoice automatically repeat at specified interval"), - 'recurring_interval': fields.integer( - 'Repeat Every', help="Repeat every (Days/Week/Month/Year)"), - 'recurring_next_date': fields.date('Date of Next Invoice'), - } - - _defaults = { - 'recurring_interval': 1, - 'recurring_next_date': lambda *a: time.strftime('%Y-%m-%d'), - 'recurring_rule_type': 'monthly' - } - - def copy(self, cr, uid, id, default=None, context=None): - # Reset next invoice date - default['recurring_next_date'] = \ - self._defaults['recurring_next_date']() - return super(AccountAnalyticAccount, self).copy( - cr, uid, id, default=default, context=context) - - def onchange_recurring_invoices( - self, cr, uid, ids, recurring_invoices, - date_start=False, context=None): - value = {} - if date_start and recurring_invoices: - value = {'value': {'recurring_next_date': date_start}} - return value - - def _prepare_invoice_line(self, cr, uid, line, invoice_id, context=None): - fpos_obj = self.pool['account.fiscal.position'] - lang_obj = self.pool['res.lang'] - product = line.product_id - account_id = product.property_account_income.id - if not account_id: - account_id = product.categ_id.property_account_income_categ.id - contract = line.analytic_account_id - fpos = contract.partner_id.property_account_position or False - account_id = fpos_obj.map_account(cr, uid, fpos, account_id) - taxes = product.taxes_id or False - tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) - if 'old_date' in context: - lang_ids = lang_obj.search( - cr, uid, [('code', '=', contract.partner_id.lang)], - context=context) - format = lang_obj.browse( - cr, uid, lang_ids, context=context)[0].date_format - line.name = line.name.replace( - '#START#', context['old_date'].strftime(format)) - line.name = line.name.replace( - '#END#', context['next_date'].strftime(format)) - return { - 'name': line.name, - 'account_id': account_id, - 'account_analytic_id': contract.id, - 'price_unit': line.price_unit or 0.0, - 'quantity': line.quantity, - 'uos_id': line.uom_id.id or False, - 'product_id': line.product_id.id or False, - 'invoice_id': invoice_id, - 'invoice_line_tax_id': [(6, 0, tax_id)], - } - - def _prepare_invoice(self, cr, uid, contract, context=None): - if context is None: - context = {} - inv_obj = self.pool['account.invoice'] - journal_obj = self.pool['account.journal'] - if not contract.partner_id: - raise orm.except_orm( - _('No Customer Defined!'), - _("You must first select a Customer for Contract %s!") % - contract.name) - partner = contract.partner_id - fpos = partner.property_account_position or False - journal_ids = journal_obj.search( - cr, uid, - [('type', '=', 'sale'), - ('company_id', '=', contract.company_id.id or False)], - limit=1) - if not journal_ids: - raise orm.except_orm( - _('Error!'), - _('Please define a sale journal for the company "%s".') % - (contract.company_id.name or '',)) - partner_payment_term = partner.property_payment_term.id - inv_data = { - 'reference': contract.code or False, - 'account_id': partner.property_account_receivable.id, - 'type': 'out_invoice', - 'partner_id': partner.id, - 'currency_id': partner.property_product_pricelist.currency_id.id, - 'journal_id': len(journal_ids) and journal_ids[0] or False, - 'date_invoice': contract.recurring_next_date, - 'origin': contract.name, - 'fiscal_position': fpos and fpos.id, - 'payment_term': partner_payment_term, - 'company_id': contract.company_id.id or False, - } - invoice_id = inv_obj.create(cr, uid, inv_data, context=context) - for line in contract.recurring_invoice_line_ids: - invoice_line_vals = self._prepare_invoice_line( - cr, uid, line, invoice_id, context=context) - self.pool['account.invoice.line'].create( - cr, uid, invoice_line_vals, context=context) - inv_obj.button_compute(cr, uid, [invoice_id], context=context) - return invoice_id - - def recurring_create_invoice(self, cr, uid, automatic=False, context=None): - if context is None: - context = {} - current_date = time.strftime('%Y-%m-%d') - contract_ids = self.search( - cr, uid, - [('recurring_next_date', '<=', current_date), - ('state', '=', 'open'), - ('recurring_invoices', '=', True)]) - for contract in self.browse(cr, uid, contract_ids, context=context): - next_date = datetime.datetime.strptime( - contract.recurring_next_date or current_date, "%Y-%m-%d") - interval = contract.recurring_interval - old_date = next_date - if contract.recurring_rule_type == 'daily': - new_date = next_date + relativedelta(days=+interval) - elif contract.recurring_rule_type == 'weekly': - new_date = next_date + relativedelta(weeks=+interval) - else: - new_date = next_date + relativedelta(months=+interval) - context['old_date'] = old_date - context['next_date'] = new_date - # Force company for correct evaluate domain access rules - context['force_company'] = contract.company_id.id - # Re-read contract with correct company - contract = self.browse(cr, uid, contract.id, context=context) - self._prepare_invoice( - cr, uid, contract, context=context - ) - self.write( - cr, uid, [contract.id], - {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, - context=context - ) - return True diff --git a/contract/account_analytic_analysis_recurring_view.xml b/contract/account_analytic_analysis_recurring_view.xml deleted file mode 100644 index e28934767..000000000 --- a/contract/account_analytic_analysis_recurring_view.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - account.analytic.account.invoice.recurring.form.inherit - account.analytic.account - - - - - -
- -
- - -
-
-
- -
-
diff --git a/contract/account_analytic_analysis_recurring_cron.xml b/contract/data/contract_cron.xml similarity index 100% rename from contract/account_analytic_analysis_recurring_cron.xml rename to contract/data/contract_cron.xml diff --git a/contract/models/__init__.py b/contract/models/__init__.py new file mode 100644 index 000000000..27ee63072 --- /dev/null +++ b/contract/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import contract +from . import invoice diff --git a/contract/models/contract.py b/contract/models/contract.py new file mode 100644 index 000000000..ebd253c53 --- /dev/null +++ b/contract/models/contract.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +# © 2016 Incaser Informatica S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from dateutil.relativedelta import relativedelta +import logging +import time + +from openerp import api, exceptions, fields, models +from openerp.addons.decimal_precision import decimal_precision as dp +from openerp.exceptions import ValidationError +from openerp.tools.translate import _ + +_logger = logging.getLogger(__name__) + + +class AccountAnalyticInvoiceLine(models.Model): + _name = "account.analytic.invoice.line" + + product_id = fields.Many2one( + 'product.product', string='Product', required=True) + analytic_account_id = fields.Many2one( + 'account.analytic.account', string='Analytic Account') + name = fields.Text(string='Description', required=True) + quantity = fields.Float(default=1.0, required=True) + uom_id = fields.Many2one( + 'product.uom', string='Unit of Measure', required=True) + price_unit = fields.Float('Unit Price', required=True) + price_subtotal = fields.Float( + compute='_compute_price_subtotal', + digits_compute=dp.get_precision('Account'), + string='Sub Total') + discount = fields.Float( + string='Discount (%)', + digits=dp.get_precision('Discount'), + copy=True, + help='Discount that is applied in generated invoices.' + ' It should be less or equal to 100') + + @api.multi + @api.depends('quantity', 'price_unit', 'discount') + def _compute_price_subtotal(self): + for line in self: + subtotal = line.quantity * line.price_unit + discount = line.discount / 100 + subtotal *= 1 - discount + if line.analytic_account_id.pricelist_id: + cur = line.analytic_account_id.pricelist_id.currency_id + line.price_subtotal = cur.round(subtotal) + else: + line.price_subtotal = subtotal + + @api.one + @api.constrains('discount') + def _check_discount(self): + if self.discount > 100: + raise ValidationError(_("Discount should be less or equal to 100")) + + @api.multi + @api.onchange('product_id') + def product_id_change(self): + if not self.product_id: + return {'domain': {'uom_id': []}} + + vals = {} + domain = {'uom_id': [ + ('category_id', '=', self.product_id.uom_id.category_id.id)]} + if not self.uom_id or (self.product_id.uom_id.category_id.id != self.uom_id.category_id.id): + vals['uom_id'] = self.product_id.uom_id + + product = self.product_id.with_context( + lang=self.analytic_account_id.partner_id.lang, + partner=self.analytic_account_id.partner_id.id, + quantity=self.quantity, + date=self.analytic_account_id.recurring_next_date, + pricelist=self.analytic_account_id.pricelist_id.id, + uom=self.uom_id.id + ) + + name = product.name_get()[0][1] + if product.description_sale: + name += '\n' + product.description_sale + vals['name'] = name + + vals['price_unit'] = product.price + self.update(vals) + return {'domain': domain} + + +class AccountAnalyticAccount(models.Model): + _inherit = 'account.analytic.account' + + @api.model + def _default_journal(self): + company_id = self.env.context.get( + 'company_id', self.env.user.company_id.id) + domain = [ + ('type', '=', 'sale'), + ('company_id', '=', company_id)] + return self.env['account.journal'].search(domain, limit=1) + + pricelist_id = fields.Many2one( + comodel_name='product.pricelist', + string='Pricelist') + date_start = fields.Date(default=fields.Date.context_today) + recurring_invoice_line_ids = fields.One2many( + comodel_name='account.analytic.invoice.line', + inverse_name='analytic_account_id', + string='Invoice Lines') + recurring_invoices = fields.Boolean( + string='Generate recurring invoices automatically') + recurring_rule_type = fields.Selection( + [('daily', 'Day(s)'), + ('weekly', 'Week(s)'), + ('monthly', 'Month(s)'), + ('yearly', 'Year(s)'), + ], + default='monthly', + string='Recurrency', + help="Invoice automatically repeat at specified interval") + recurring_interval = fields.Integer( + default=1, + string='Repeat Every', + help="Repeat every (Days/Week/Month/Year)") + recurring_next_date = fields.Date( + default=fields.Date.context_today, + string='Date of Next Invoice') + journal_id = fields.Many2one( + 'account.journal', + string='Journal', + default=_default_journal, + domain="[('type', '=', 'sale'),('company_id', '=', company_id)]") + + def copy(self, default=None): + # Reset next invoice date + default['recurring_next_date'] = \ + self._defaults['recurring_next_date']() + return super(AccountAnalyticAccount, self).copy(default=default) + + @api.onchange('recurring_invoices') + def _onchange_recurring_invoices(self): + if self.date_start and self.recurring_invoices: + self.recurring_next_date = self.date_start + + @api.model + def _insert_markers(self, line, date_start, date_end, date_format): + line = line.replace('#START#', date_start.strftime(date_format)) + line = line.replace('#END#', date_end.strftime(date_format)) + return line + + @api.model + def _prepare_invoice_line(self, line, invoice_id): + product = line.product_id + account_id = product.property_account_income_id.id or \ + product.categ_id.property_account_income_categ_id.id + contract = line.analytic_account_id + fpos = contract.partner_id.property_account_position_id + account_id = fpos.map_account(account_id) + tax_id = fpos.map_tax(product.taxes_id) + name = line.name + if 'old_date' in self.env.context and 'next_date' in self.env.context: + lang_obj = self.env['res.lang'] + contract = line.analytic_account_id + lang = lang_obj.search( + [('code', '=', contract.partner_id.lang)]) + date_format = lang.date_format or '%m/%d/%Y' + name = self._insert_markers( + name, self.env.context['old_date'], + self.env.context['next_date'], date_format) + return { + 'name': name, + 'account_id': account_id, + 'account_analytic_id': contract.id, + 'price_unit': line.price_unit, + 'quantity': line.quantity, + 'uos_id': line.uom_id.id, + 'product_id': line.product_id.id, + 'invoice_id': invoice_id, + 'invoice_line_tax_id': [(6, 0, tax_id)], + 'discount': line.discount, + } + + @api.model + def _prepare_invoice(self, contract): + if not contract.partner_id: + raise ValidationError( + _('No Customer Defined!'), + _("You must first select a Customer for Contract %s!") % + contract.name) + partner = contract.partner_id + fpos = partner.property_account_position_id + journal_ids = self.env['account.journal'].search( + [('type', '=', 'sale'), + ('company_id', '=', contract.company_id.id)], + limit=1) + if not journal_ids: + raise ValidationError( + _('Error!'), + _('Please define a sale journal for the company "%s".') % + (contract.company_id.name or '',)) + inv_data = { + 'reference': contract.code, + 'account_id': partner.property_account_receivable_id.id, + 'type': 'out_invoice', + 'partner_id': partner.id, + 'currency_id': partner.property_product_pricelist.currency_id.id, + 'journal_id': journal_ids.id, + 'date_invoice': contract.recurring_next_date, + 'origin': contract.name, + 'fiscal_position': fpos and fpos.id, + 'payment_term': partner.property_payment_term_id.id, + 'company_id': contract.company_id.id, + 'journal_id': contract.journal_id.id, + 'contract_id': contract.id, + } + # if contract.journal_id: + # inv_data['journal_id'] = contract.journal_id.id + invoice = self.env['account.invoice'].create(inv_data) + for line in contract.recurring_invoice_line_ids: + invoice_line_vals = self._prepare_invoice_line(line, invoice.id) + self.env['account.invoice.line'].create(invoice_line_vals) + # invoice.button_compute() + return invoice + + @api.model + def recurring_create_invoice(self, automatic=False): + current_date = time.strftime('%Y-%m-%d') + contracts = self.search( + [('recurring_next_date', '<=', current_date), + ('account_type', '=', 'normal'), + ('recurring_invoices', '=', True)]) + for contract in contracts: + next_date = fields.Date.from_string( + contract.recurring_next_date or fields.Date.today()) + interval = contract.recurring_interval + old_date = next_date + if contract.recurring_rule_type == 'daily': + new_date = next_date + relativedelta(days=interval - 1) + elif contract.recurring_rule_type == 'weekly': + new_date = next_date + relativedelta(weeks=interval, days=-1) + else: + new_date = next_date + relativedelta(months=interval, days=-1) + ctx = self.env.context.copy() + ctx.update({ + 'old_date': old_date, + 'next_date': new_date, + # Force company for correct evaluate domain access rules + 'force_company': contract.company_id.id, + }) + # Re-read contract with correct company + contract = self.with_context(ctx).browse(contract.id) + self.with_context(ctx)._prepare_invoice(contract) + contract.write({ + 'recurring_next_date': new_date.strftime('%Y-%m-%d') + }) + return True diff --git a/contract/models/invoice.py b/contract/models/invoice.py new file mode 100644 index 000000000..9d2dd050e --- /dev/null +++ b/contract/models/invoice.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# © 2016 Incaser Informatica S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import api, fields, models + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + contract_id = fields.Many2one( + 'account.analytic.account', + string='Contract') + analytic_account_ids = fields.Many2many( + comodel_name='account.analytic.account', + compute='_compute_analytic_account_ids', + store=True, + string='Contracts') + + @api.multi + @api.depends('invoice_line_ids.account_analytic_id') + def _compute_analytic_account_ids(self): + for invoice in self: + invoice.analytic_account_ids = invoice.mapped( + 'invoice_line_ids.account_analytic_id' + ) diff --git a/contract/views/contract.xml b/contract/views/contract.xml new file mode 100644 index 000000000..216828a22 --- /dev/null +++ b/contract/views/contract.xml @@ -0,0 +1,103 @@ + + + + + + {'search_default_contract_id': + [active_id], + 'default_contract_id': active_id} + + Invoices + account.invoice + + + + + + {'search_default_analytic_account_ids': + [active_id], 'default_analytic_account_ids': active_id} + Invoices + account.invoice + [('analytic_account_ids','=',active_id)] + + + + + + account.analytic.account.invoice.recurring.form.inherit + account.analytic.account + + + + + + + + +
+ +
+ + + + +
+
+
+ + + + account.analytic.account.journal.list + account.analytic.account + + + + + + + + + + + account.analytic.account.journal.search + account.analytic.account + + + + + + + + +
+
From d994fb64117f5f71e09b628fd5d9b5a96a0b3a83 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Wed, 23 Mar 2016 13:01:15 +0100 Subject: [PATCH 17/22] [IMP] Tests and filters added. Add _id/_ids in old property fields --- contract/README.rst | 19 ++++++++-- contract/__openerp__.py | 2 + contract/models/contract.py | 50 +++++++++++++------------ contract/tests/__init__.py | 6 +++ contract/tests/test_contract.py | 41 ++++++++++++++++++++ contract/views/account_invoice_view.xml | 19 ++++++++++ contract/views/contract.xml | 47 ++++++++++++++++++----- 7 files changed, 147 insertions(+), 37 deletions(-) create mode 100644 contract/tests/__init__.py create mode 100644 contract/tests/test_contract.py create mode 100644 contract/views/account_invoice_view.xml diff --git a/contract/README.rst b/contract/README.rst index 0abdc88f3..2f105c93c 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -6,29 +6,42 @@ Contract ======== -This module helps you to manage contracts with recurring invoices. + * This module recover contracts management with recurring invoicing functions. Usage ===== To use this module, you need to: +#. Go to Sales -> Contracts and select or create a new contract. +#. Check *Generate recurring invoices automatically*. +#. Fill fields and add new lines. +#. To view discount field set *Discount on lines* in user access rights. +#. A cron is created with daily interval, but if you are in debug mode can + click on *Create invoices* to force this action. + + .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/167/8.0 + :target: https://runbot.odoo-community.org/runbot/110/9.0 For further information, please visit: * https://www.odoo.com/forum/help-1 +Known issues / Roadmap +====================== + +* Recovery states and others functional fields in Contracts. + Bug Tracker =========== Bugs are tracked on `GitHub 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 -`here `_. +`here `_. Credits ======= diff --git a/contract/__openerp__.py b/contract/__openerp__.py index 4ec334ca4..efb884580 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -6,6 +6,7 @@ 'name': 'Contracts Management recurring', 'version': '9.0.1.0.0', 'category': 'Other', + 'license': 'AGPL-3', 'author': "OpenERP SA,Odoo Community Association (OCA)", 'website': 'http://openerp.com', 'depends': ['base', 'account', 'analytic'], @@ -13,6 +14,7 @@ 'security/ir.model.access.csv', 'data/contract_cron.xml', 'views/contract.xml', + 'views/account_invoice_view.xml', ], 'installable': True, 'images': [], diff --git a/contract/models/contract.py b/contract/models/contract.py index ebd253c53..4b7ed069b 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -6,7 +6,7 @@ from dateutil.relativedelta import relativedelta import logging import time -from openerp import api, exceptions, fields, models +from openerp import api, fields, models from openerp.addons.decimal_precision import decimal_precision as dp from openerp.exceptions import ValidationError from openerp.tools.translate import _ @@ -15,7 +15,7 @@ _logger = logging.getLogger(__name__) class AccountAnalyticInvoiceLine(models.Model): - _name = "account.analytic.invoice.line" + _name = 'account.analytic.invoice.line' product_id = fields.Many2one( 'product.product', string='Product', required=True) @@ -65,7 +65,8 @@ class AccountAnalyticInvoiceLine(models.Model): vals = {} domain = {'uom_id': [ ('category_id', '=', self.product_id.uom_id.category_id.id)]} - if not self.uom_id or (self.product_id.uom_id.category_id.id != self.uom_id.category_id.id): + if not self.uom_id or (self.product_id.uom_id.category_id.id != + self.uom_id.category_id.id): vals['uom_id'] = self.product_id.uom_id product = self.product_id.with_context( @@ -131,32 +132,38 @@ class AccountAnalyticAccount(models.Model): default=_default_journal, domain="[('type', '=', 'sale'),('company_id', '=', company_id)]") + @api.multi def copy(self, default=None): # Reset next invoice date default['recurring_next_date'] = \ self._defaults['recurring_next_date']() return super(AccountAnalyticAccount, self).copy(default=default) + @api.onchange('partner_id') + def _onchange_partner_id(self): + self.pricelist_id = self.partner_id.property_product_pricelist.id + @api.onchange('recurring_invoices') def _onchange_recurring_invoices(self): if self.date_start and self.recurring_invoices: self.recurring_next_date = self.date_start @api.model - def _insert_markers(self, line, date_start, date_end, date_format): + def _insert_markers(self, line, date_start, next_date, date_format): line = line.replace('#START#', date_start.strftime(date_format)) + date_end = next_date - relativedelta(days=1) line = line.replace('#END#', date_end.strftime(date_format)) return line @api.model def _prepare_invoice_line(self, line, invoice_id): product = line.product_id - account_id = product.property_account_income_id.id or \ - product.categ_id.property_account_income_categ_id.id + account_id = (product.property_account_income_id.id or + product.categ_id.property_account_income_categ_id.id) contract = line.analytic_account_id fpos = contract.partner_id.property_account_position_id account_id = fpos.map_account(account_id) - tax_id = fpos.map_tax(product.taxes_id) + taxes = fpos.map_tax(product.taxes_id) name = line.name if 'old_date' in self.env.context and 'next_date' in self.env.context: lang_obj = self.env['res.lang'] @@ -173,10 +180,10 @@ class AccountAnalyticAccount(models.Model): 'account_analytic_id': contract.id, 'price_unit': line.price_unit, 'quantity': line.quantity, - 'uos_id': line.uom_id.id, + 'uom_id': line.uom_id.id, 'product_id': line.product_id.id, 'invoice_id': invoice_id, - 'invoice_line_tax_id': [(6, 0, tax_id)], + 'invoice_line_tax_ids': [(6, 0, taxes.ids)], 'discount': line.discount, } @@ -184,19 +191,17 @@ class AccountAnalyticAccount(models.Model): def _prepare_invoice(self, contract): if not contract.partner_id: raise ValidationError( - _('No Customer Defined!'), _("You must first select a Customer for Contract %s!") % contract.name) partner = contract.partner_id fpos = partner.property_account_position_id - journal_ids = self.env['account.journal'].search( + journal = contract.journal_id or self.env['account.journal'].search( [('type', '=', 'sale'), ('company_id', '=', contract.company_id.id)], limit=1) - if not journal_ids: + if not journal: raise ValidationError( - _('Error!'), - _('Please define a sale journal for the company "%s".') % + _("Please define a sale journal for the company '%s'.") % (contract.company_id.name or '',)) inv_data = { 'reference': contract.code, @@ -204,22 +209,19 @@ class AccountAnalyticAccount(models.Model): 'type': 'out_invoice', 'partner_id': partner.id, 'currency_id': partner.property_product_pricelist.currency_id.id, - 'journal_id': journal_ids.id, + 'journal_id': journal.id, 'date_invoice': contract.recurring_next_date, 'origin': contract.name, - 'fiscal_position': fpos and fpos.id, - 'payment_term': partner.property_payment_term_id.id, + 'fiscal_position_id': fpos.id, + 'payment_term_id': partner.property_payment_term_id.id, 'company_id': contract.company_id.id, - 'journal_id': contract.journal_id.id, 'contract_id': contract.id, } - # if contract.journal_id: - # inv_data['journal_id'] = contract.journal_id.id invoice = self.env['account.invoice'].create(inv_data) for line in contract.recurring_invoice_line_ids: invoice_line_vals = self._prepare_invoice_line(line, invoice.id) self.env['account.invoice.line'].create(invoice_line_vals) - # invoice.button_compute() + invoice.compute_taxes() return invoice @api.model @@ -235,11 +237,11 @@ class AccountAnalyticAccount(models.Model): interval = contract.recurring_interval old_date = next_date if contract.recurring_rule_type == 'daily': - new_date = next_date + relativedelta(days=interval - 1) + new_date = next_date + relativedelta(days=interval) elif contract.recurring_rule_type == 'weekly': - new_date = next_date + relativedelta(weeks=interval, days=-1) + new_date = next_date + relativedelta(weeks=interval) else: - new_date = next_date + relativedelta(months=interval, days=-1) + new_date = next_date + relativedelta(months=interval) ctx = self.env.context.copy() ctx.update({ 'old_date': old_date, diff --git a/contract/tests/__init__.py b/contract/tests/__init__.py new file mode 100644 index 000000000..2b43f40bb --- /dev/null +++ b/contract/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel +# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import test_contract diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py new file mode 100644 index 000000000..f492d943c --- /dev/null +++ b/contract/tests/test_contract.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# © 2016 Incaser Informatica S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from openerp.exceptions import ValidationError +from openerp.tests.common import TransactionCase + + +class TestContract(TransactionCase): + # Use case : Prepare some data for current test case + def setUp(self): + super(TestContract, self).setUp() + self.partner = self.env.ref('base.res_partner_2') + self.product = self.env.ref('product.product_product_2') + self.contract = self.env['account.analytic.account'].create({ + 'name': 'Test Contract', + 'partner_id': self.partner.id, + 'pricelist_id': self.partner.property_product_pricelist.id, + 'recurring_invoices': True, + }) + self.contract_line = self.env['account.analytic.invoice.line'].create({ + 'analytic_account_id': self.contract.id, + 'product_id': self.product.id, + 'name': 'Services from #START# to #END#', + 'quantity': 1, + 'uom_id': self.product.uom_id.id, + 'price_unit': 100, + 'discount': 50, + }) + + def test_check_discount(self): + with self.assertRaises(ValidationError): + self.contract_line.write({'discount': 120}) + + def test_create_invoice(self): + self.contract.recurring_create_invoice() + self.invoice = self.env['account.invoice'].search( + [('contract_id', '=', self.contract.id)]) + self.assertTrue(self.invoice) + + self.inv_line = self.invoice.invoice_line_ids[0] + self.assertAlmostEqual(self.inv_line.price_subtotal, 50.0) diff --git a/contract/views/account_invoice_view.xml b/contract/views/account_invoice_view.xml new file mode 100644 index 000000000..09752e9f7 --- /dev/null +++ b/contract/views/account_invoice_view.xml @@ -0,0 +1,19 @@ + + + + + + + account.invoice.select.contract + account.invoice + + + + + + + + + + + diff --git a/contract/views/contract.xml b/contract/views/contract.xml index 216828a22..3f9e9bf1e 100644 --- a/contract/views/contract.xml +++ b/contract/views/contract.xml @@ -40,13 +40,16 @@
- + @@ -98,7 +103,7 @@ - + @@ -112,15 +117,11 @@ account.analytic.account form tree,form - {'search_default_active':1, 'search_default_recurring_invoice':1} + {'search_default_active':1, 'search_default_recurring_invoices':1}

Click to create a new contract. -

- Use contracts to follow tasks, issues, timesheets or invoicing based on - work done, expenses and/or sales orders. Odoo will automatically manage - the alerts for the renewal of the contracts to the right salesperson.

From a27a749c0909113745af431735b1722db095d928 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Sat, 26 Mar 2016 01:24:40 +0100 Subject: [PATCH 20/22] Improve tests --- contract/tests/test_contract.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index e2974bae7..ea68192f9 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -16,6 +16,7 @@ class TestContract(TransactionCase): self.product = self.env.ref('product.product_product_2') self.tax = self.env.ref('l10n_generic_coa.sale_tax_template') self.product.taxes_id = self.tax.ids + self.product.description_sale = 'Test description sale' self.contract = self.env['account.analytic.account'].create({ 'name': 'Test Contract', 'partner_id': self.partner.id, @@ -66,6 +67,7 @@ class TestContract(TransactionCase): self.assertTrue(self.inv_line.invoice_line_tax_ids) def test_contract_daily(self): + self.contract_daily.pricelist_id = False self.contract_daily.recurring_create_invoice() invoice_daily = self.env['account.invoice'].search( [('contract_id', '=', self.contract_daily.id)]) @@ -86,7 +88,6 @@ class TestContract(TransactionCase): new_date.strftime('%Y-%m-%d')) def test_onchange_partner_id(self): - self.contract.pricelist_id = False self.contract._onchange_partner_id() self.assertEqual(self.contract.pricelist_id, self.contract.partner_id.property_product_pricelist) @@ -97,6 +98,23 @@ class TestContract(TransactionCase): self.assertEqual(self.contract.recurring_next_date, self.contract.date_start) + def test_uom(self): + uom_litre = self.env.ref('product.product_uom_litre') + self.contract_line.uom_id = uom_litre.id + self.contract_line._onchange_product_id() + self.assertEqual(self.contract_line.uom_id, + self.contract_line.product_id.uom_id) + + def test_onchange_product_id(self): + line = self.env['account.analytic.invoice.line'].new() + res = line._onchange_product_id() + self.assertFalse(res['domain']['uom_id']) + + def test_no_pricelist(self): + self.contract.pricelist_id = False + self.contract_line.quantity = 2 + self.assertAlmostEqual(self.contract_line.price_subtotal, 100.0) + def test_check_journal(self): contract_no_journal = self.contract.copy() contract_no_journal.journal_id = False @@ -104,4 +122,3 @@ class TestContract(TransactionCase): journal.write({'type': 'general'}) with self.assertRaises(ValidationError): contract_no_journal.recurring_create_invoice() - journal.write({'type': 'sale'}) From 0233b31aa0121610132574b155495175c844b262 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Mon, 28 Mar 2016 21:32:18 +0200 Subject: [PATCH 21/22] Changes suggested by @pedrobaeza --- contract/README.rst | 16 ++++++++++------ contract/i18n/es.po | 29 ++++++++++------------------- contract/models/contract.py | 3 +++ contract/models/invoice.py | 15 +-------------- contract/views/contract.xml | 18 +----------------- 5 files changed, 25 insertions(+), 56 deletions(-) diff --git a/contract/README.rst b/contract/README.rst index 878fc31d0..1c10286e4 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -6,8 +6,13 @@ Contracts for recurrent invoicing ================================= - * This module forward-port to v9 the contracts management with recurring - invoicing functions. +This module forward-port to v9 the contracts management with recurring +invoicing functions. + +Configuration +============= + +To view discount field set *Discount on lines* in user access rights. Usage ===== @@ -17,12 +22,11 @@ To use this module, you need to: #. Go to Sales -> Contracts and select or create a new contract. #. Check *Generate recurring invoices automatically*. #. Fill fields and add new lines. You have the possibility to use markers in - in description field to show init date and end of invoiced period. -#. To view discount field set *Discount on lines* in user access rights. + the description field to show the start and end date of the invoiced period. #. A cron is created with daily interval, but if you are in debug mode can click on *Create invoices* to force this action. -#. Click *Show invoices* link to show all invoices created by the contract. -#. Press *Invoices* button to show all invoices related with the contract. +#. Click *Show recurring invoices* link to show all invoices created by the + contract. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot diff --git a/contract/i18n/es.po b/contract/i18n/es.po index 72b6c85d2..5557f41c6 100644 --- a/contract/i18n/es.po +++ b/contract/i18n/es.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo 9.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-25 23:49+0000\n" -"PO-Revision-Date: 2016-03-26 00:49+0100\n" +"POT-Creation-Date: 2016-03-28 19:26+0000\n" +"PO-Revision-Date: 2016-03-28 21:28+0100\n" "Last-Translator: Carlos Incaser \n" "Language-Team: \n" "Language: es_ES\n" @@ -49,7 +49,6 @@ msgstr "Contrato" #. module: contract #: model:ir.actions.act_window,name:contract.action_account_analytic_overdue_all -#: model:ir.model.fields,field_description:contract.field_account_invoice_analytic_account_ids #: model:ir.ui.menu,name:contract.menu_action_account_analytic_overdue_all msgid "Contracts" msgstr "Contratos" @@ -72,7 +71,7 @@ msgstr "Creado en" #. module: contract #: model:ir.model.fields,field_description:contract.field_account_analytic_account_recurring_next_date msgid "Date of Next Invoice" -msgstr "Próximo fecha de factura" +msgstr "Próxima fecha de factura" #. module: contract #: model:ir.model.fields,field_description:contract.field_account_analytic_account_date_start @@ -95,7 +94,7 @@ msgid "Discount (%)" msgstr "Descuento (%)" #. module: contract -#: code:addons/contract/models/contract.py:56 +#: code:addons/contract/models/contract.py:59 #, python-format msgid "Discount should be less or equal to 100" msgstr "El descuento debería ser menor o igual a 100" @@ -140,17 +139,10 @@ msgid "Invoice Lines" msgstr "Líneas de factura" #. module: contract -#: model:ir.actions.act_window,name:contract.act_analytic_invoices #: model:ir.actions.act_window,name:contract.act_recurring_invoices -#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form msgid "Invoices" msgstr "Facturas" -#. module: contract -#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form -msgid "Invoices related with this contract" -msgstr "Facturas relacionadas con este contrato" - #. module: contract #: model:ir.model.fields,field_description:contract.field_account_analytic_account_journal_id msgid "Journal" @@ -188,7 +180,7 @@ msgid "Next Invoice" msgstr "Próxima factura" #. module: contract -#: code:addons/contract/models/contract.py:194 +#: code:addons/contract/models/contract.py:197 #, python-format msgid "Please define a sale journal for the company '%s'." msgstr "Por favor define un diario de ventas para la compañía '%s'." @@ -260,7 +252,7 @@ msgid "Year(s)" msgstr "Año(s)" #. module: contract -#: code:addons/contract/models/contract.py:186 +#: code:addons/contract/models/contract.py:189 #, python-format msgid "You must first select a Customer for Contract %s!" msgstr "¡Seleccione un cliente para este contrato %s!" @@ -272,12 +264,11 @@ msgstr "account.analytic.invoice.line" #. module: contract #: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form -msgid "⇒ Show invoices" -msgstr "⇒ Mostrar facturas" +msgid "⇒ Show recurring invoices" +msgstr "⇒ Mostrar facturas recurrentes" -#, fuzzy -#~ msgid "Recurring Invoice" -#~ msgstr "Recurring Invoice" +#~ msgid "Invoices related with this contract" +#~ msgstr "Facturas relacionadas con este contrato" #~ msgid "" #~ "Use contracts to follow tasks, issues, timesheets or invoicing based on\n" diff --git a/contract/models/contract.py b/contract/models/contract.py index 4de2182ac..add718947 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- # © 2016 Incaser Informatica S.L. - Carlos Dauden +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# © 2014 Domatix Technologies S.L. - Angel Moya +# © 2004-2010 OpenERP SA () # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from dateutil.relativedelta import relativedelta diff --git a/contract/models/invoice.py b/contract/models/invoice.py index 9d2dd050e..f8365f062 100644 --- a/contract/models/invoice.py +++ b/contract/models/invoice.py @@ -2,7 +2,7 @@ # © 2016 Incaser Informatica S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from openerp import api, fields, models +from openerp import fields, models class AccountInvoice(models.Model): @@ -11,16 +11,3 @@ class AccountInvoice(models.Model): contract_id = fields.Many2one( 'account.analytic.account', string='Contract') - analytic_account_ids = fields.Many2many( - comodel_name='account.analytic.account', - compute='_compute_analytic_account_ids', - store=True, - string='Contracts') - - @api.multi - @api.depends('invoice_line_ids.account_analytic_id') - def _compute_analytic_account_ids(self): - for invoice in self: - invoice.analytic_account_ids = invoice.mapped( - 'invoice_line_ids.account_analytic_id' - ) diff --git a/contract/views/contract.xml b/contract/views/contract.xml index 277d929e3..ca5d34ff4 100644 --- a/contract/views/contract.xml +++ b/contract/views/contract.xml @@ -13,28 +13,12 @@ - - {'search_default_analytic_account_ids': - [active_id], 'default_analytic_account_ids': active_id} - Invoices - account.invoice - [('analytic_account_ids','=',active_id)] - - - - account.analytic.account.invoice.recurring.form.inherit account.analytic.account - - -
@@ -46,7 +30,7 @@ groups="base.group_no_one"/>
From 28f944d1fe821f6014fcfb417698c337f8c7efb1 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Fri, 1 Apr 2016 11:45:34 +0200 Subject: [PATCH 22/22] [FIX] contract: Update author info. --- contract/README.rst | 4 ++-- contract/__openerp__.py | 8 ++++---- contract/models/__init__.py | 3 +++ contract/models/contract.py | 10 +++++----- contract/models/invoice.py | 4 ++-- contract/tests/__init__.py | 5 ++--- contract/tests/test_contract.py | 5 +++-- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/contract/README.rst b/contract/README.rst index 1c10286e4..e369c2dce 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -51,8 +51,8 @@ Credits Contributors ------------ -* Pedro M. Baeza -* Carlos Dauden +* Pedro M. Baeza +* Carlos Dauden * Angel Moya Maintainer diff --git a/contract/__openerp__.py b/contract/__openerp__.py index a01d51ed6..76011715c 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# © 2004-2010 OpenERP SA () -# © 2016 Incaser Informatica S.L. - Carlos Dauden -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +# © 2004-2010 OpenERP SA +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Contracts Management recurring', @@ -9,7 +9,7 @@ 'category': 'Other', 'license': 'AGPL-3', 'author': "OpenERP SA," - "Incaser Informatica S.L.," + "Tecnativa," "Odoo Community Association (OCA)", 'website': 'http://openerp.com', 'depends': ['base', 'account', 'analytic'], diff --git a/contract/models/__init__.py b/contract/models/__init__.py index 27ee63072..8deef4105 100644 --- a/contract/models/__init__.py +++ b/contract/models/__init__.py @@ -1,3 +1,6 @@ # -*- coding: utf-8 -*- +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from . import contract from . import invoice diff --git a/contract/models/contract.py b/contract/models/contract.py index add718947..618bf8e55 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# © 2016 Incaser Informatica S.L. - Carlos Dauden -# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2014 Domatix Technologies S.L. - Angel Moya -# © 2004-2010 OpenERP SA () -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +# © 2004-2010 OpenERP SA +# © 2014 Angel Moya +# © 2015 Pedro M. Baeza +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from dateutil.relativedelta import relativedelta import logging diff --git a/contract/models/invoice.py b/contract/models/invoice.py index f8365f062..8761dfa39 100644 --- a/contract/models/invoice.py +++ b/contract/models/invoice.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Incaser Informatica S.L. - Carlos Dauden -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from openerp import fields, models diff --git a/contract/tests/__init__.py b/contract/tests/__init__.py index 2b43f40bb..2002a1d82 100644 --- a/contract/tests/__init__.py +++ b/contract/tests/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel -# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import test_contract diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index ea68192f9..7adb7335e 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# © 2016 Incaser Informatica S.L. - Carlos Dauden -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from dateutil.relativedelta import relativedelta import datetime