From 8c176802d971eeba6cb28d4bdd2c7840c3ae48e5 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 12:58:04 +0100 Subject: [PATCH 01/11] [MIGR] set flag 'installable' to False on modules --- analytic_hours_block/__openerp__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py index b9cc878c..d13fdc16 100644 --- a/analytic_hours_block/__openerp__.py +++ b/analytic_hours_block/__openerp__.py @@ -56,5 +56,5 @@ you can track and follow how much has been used. "security/ir.model.access.csv", ], "active": False, - "installable": True -} \ No newline at end of file + "installable": False +} From 6a56d820f87c1f6eb244a418eb74c12ec224ba84 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 13:18:53 +0100 Subject: [PATCH 02/11] [FIX] set module as installable --- analytic_hours_block/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py index d13fdc16..33d92826 100644 --- a/analytic_hours_block/__openerp__.py +++ b/analytic_hours_block/__openerp__.py @@ -56,5 +56,5 @@ you can track and follow how much has been used. "security/ir.model.access.csv", ], "active": False, - "installable": False + "installable": True } From ef7732c7047b47348d89bd4ed7154a8fe25cae5e Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 13:31:50 +0100 Subject: [PATCH 03/11] [CHG] change license from GPLv2 to AGPLv3, by the way, fixed a wrong copyright attribution to TINY SPRL (this heedless copy-paste seems common in our report files...) --- analytic_hours_block/__init__.py | 32 ++++++++----------- analytic_hours_block/__openerp__.py | 36 ++++++++-------------- analytic_hours_block/hours_block.py | 36 ++++++++-------------- analytic_hours_block/report/__init__.py | 34 +++++++++----------- analytic_hours_block/report/hours_block.py | 35 ++++++++------------- 5 files changed, 66 insertions(+), 107 deletions(-) diff --git a/analytic_hours_block/__init__.py b/analytic_hours_block/__init__.py index 7c87d3d2..93f4db71 100644 --- a/analytic_hours_block/__init__.py +++ b/analytic_hours_block/__init__.py @@ -1,27 +1,21 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # -# @author Renaville Vincent, ported by Joel Grand-Guillaume -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company +# Author: Vincent Renaville, ported by Joel Grand-Guillaume +# Copyright 2010-2012 Camptocamp SA # -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# 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 General Public License for more details. +# 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 General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . # ############################################################################## import hours_block diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py index 33d92826..4a0b8f45 100644 --- a/analytic_hours_block/__openerp__.py +++ b/analytic_hours_block/__openerp__.py @@ -1,31 +1,21 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) -# All Right Reserved +# Author: Vincent Renaville, ported by Joel Grand-Guillaume +# Copyright 2010-2012 Camptocamp SA # -# Author : Joël Grand-Guillaume (Camptocamp) +# 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. # -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company +# 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. # -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . # ############################################################################## diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index 86a8cab3..e191f3ab 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -1,31 +1,21 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2010 Camptocamp SA -# All Rights Reserved +# Author: Vincent Renaville, ported by Joel Grand-Guillaume +# Copyright 2010-2012 Camptocamp SA # -# Author : Vincent Renaville, ported by Joel Grand-Guillaume +# 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. # -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company +# 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. # -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . # ############################################################################## diff --git a/analytic_hours_block/report/__init__.py b/analytic_hours_block/report/__init__.py index fb482f03..111d0eb4 100644 --- a/analytic_hours_block/report/__init__.py +++ b/analytic_hours_block/report/__init__.py @@ -1,27 +1,21 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # +# Author: Vincent Renaville, ported by Joel Grand-Guillaume +# Copyright 2010-2012 Camptocamp SA # -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# 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. # -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . # ############################################################################## -import hours_block \ No newline at end of file +import hours_block diff --git a/analytic_hours_block/report/hours_block.py b/analytic_hours_block/report/hours_block.py index 10226fbb..ad459fd2 100644 --- a/analytic_hours_block/report/hours_block.py +++ b/analytic_hours_block/report/hours_block.py @@ -1,36 +1,27 @@ +# -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Author: Vincent Renaville, ported by Joel Grand-Guillaume +# Copyright 2010-2012 Camptocamp SA # -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# 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. # -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . # ############################################################################## import time from report import report_sxw -#import xml.dom.minidom -#import re - class account_hours_block(report_sxw.rml_parse): def __init__(self, cr, uid, name, context=None): From c60a8d0d46d3c23f4b718d6274283feed6ee8266 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 15:42:58 +0100 Subject: [PATCH 04/11] [FIX] python formatting and cleanings --- analytic_hours_block/__openerp__.py | 44 +-- analytic_hours_block/hours_block.py | 305 +++++++++++++------- analytic_hours_block/report/hours_block.py | 23 +- analytic_hours_block/report/hours_block.rml | 4 +- 4 files changed, 228 insertions(+), 148 deletions(-) diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py index 4a0b8f45..09c78ad7 100644 --- a/analytic_hours_block/__openerp__.py +++ b/analytic_hours_block/__openerp__.py @@ -20,31 +20,35 @@ ############################################################################## { - "name" : "Project Hours Blocks Management", - "description" : """ + "name": "Project Hours Blocks Management", + "description": """ +Project Hours Blocks Management +=============================== -This module allows you to handle hours blocks, to follow for example the user support contracts. -This means, you sell a product of type "hours block" then you input the spent hours on the hours block and +This module allows you to handle hours blocks, +to follow for example the user support contracts. +This means, you sell a product of type "hours block" +then you input the spent hours on the hours block and you can track and follow how much has been used. """, - "version" : "1.2", - "author" : "Camptocamp", - "category" : "Generic Modules/Projects & Services", + "version": "1.3", + "author": "Camptocamp", + "license": 'AGPL-3', + "category": "Generic Modules/Projects & Services", "website": "http://www.camptocamp.com", - "depends" : [ - "account", - "hr_timesheet_invoice", - "analytic" - ], - "init_xml" : [], - "update_xml" : [ - "hours_block_view.xml", - "hours_block_menu.xml", - "report.xml", - "security/hours_block_security.xml", - "security/ir.model.access.csv", - ], + "depends": [ + "account", + "hr_timesheet_invoice", + "analytic" + ], + "data": [ + "hours_block_view.xml", + "hours_block_menu.xml", + "report.xml", + "security/hours_block_security.xml", + "security/ir.model.access.csv", + ], "active": False, "installable": True } diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index e191f3ab..256c2baf 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -19,92 +19,75 @@ # ############################################################################## -import time -import string +from openerp.osv import orm, fields -from osv import osv, fields -import netsvc - -############################################################################ -## Add hours blocks on invoice -############################################################################ - -class AccountHoursBlock(osv.osv): +class AccountHoursBlock(orm.Model): _name = "account.hours.block" def _get_last_action(self, cr, uid, ids, name, arg, context=None): - """TODO""" - context = context or {} + """ Return the last analytic line date for an invoice""" res = {} - for block in self.browse(cr, uid, ids): + for block in self.browse(cr, uid, ids, context=context): cr.execute("SELECT max(al.date) FROM account_analytic_line AS al" " WHERE al.invoice_id = %s", (block.invoice_id.id,)) fetch_res = cr.fetchone() - if fetch_res: - date = fetch_res[0] - else: - date = False - res[block.id] = date + res[block.id] = fetch_res[0] if fetch_res else False return res def _compute_hours(self, cr, uid, ids, fields, args, context=None): """Return a dict of [id][fields]""" - context = context or {} - if not isinstance(ids, list): - ids=[ids] + if isinstance(ids, (int, long)): + ids = [ids] result = {} aal_obj = self.pool.get('account.analytic.line') - for block in self.browse(cr,uid,ids): - result[block.id] = {'amount_hours_block' : 0.0, - 'amount_hours_block_done' : 0.0, - 'amount_hours_block_delta' : 0.0} + for block in self.browse(cr, uid, ids, context=context): + result[block.id] = {'amount_hours_block': 0.0, + 'amount_hours_block_done': 0.0} # Compute hours bought for line in block.invoice_id.invoice_line: hours_bought = 0.0 if line.product_id: - ## We will now calculate the product_quantity + # We will now calculate the product_quantity factor = line.uos_id.factor if factor == 0.0: factor = 1.0 amount = line.quantity hours_bought += (amount / factor) result[block.id]['amount_hours_block'] += hours_bought + # Compute hours spent hours_used = 0.0 - # Get ids of analytic line generated from timesheet associated to current block + # Get ids of analytic line generated from + # timesheet associated to the current block cr.execute("SELECT al.id " - " FROM account_analytic_line AS al,account_analytic_journal AS aj" - " WHERE aj.id = al.journal_id AND" - " aj.type='general' AND" - " al.invoice_id = %s", (block.invoice_id.id,)) - res2 = cr.fetchall() - if res2: - ids2 = [x[0] for x in res2] - else: - ids2 = [] - for line in aal_obj.browse(cr, uid, ids2, context): - if line.product_uom_id: + "FROM account_analytic_line AS al, " + " account_analytic_journal AS aj " + "WHERE aj.id = al.journal_id " + "AND aj.type = 'general' " + "AND al.invoice_id = %s", (block.invoice_id.id,)) + res_line_ids = cr.fetchall() + line_ids = [l[0] for l in res_line_ids] if res_line_ids else [] + for line in aal_obj.browse(cr, uid, line_ids, context=context): + factor = 1.0 + if line.product_uom_id and line.product_uom_id.factor != 0.0: factor = line.product_uom_id.factor - if factor == 0.0: - factor = 1.0 - else: - factor = 1.0 factor_invoicing = 1.0 if line.to_invoice and line.to_invoice.factor != 0.0: - factor_invoicing = 1.0 - line.to_invoice.factor / 100 + factor_invoicing = 1.0 - line.to_invoice.factor / 100 hours_used += ((line.unit_amount / factor) * factor_invoicing) result[block.id]['amount_hours_block_done'] = hours_used return result - def _compute_amount(self, cr, uid, ids, fields, args, context): + def _compute_amount(self, cr, uid, ids, fields, args, context=None): + if context is None: + context = {} result = {} aal_obj = self.pool.get('account.analytic.line') pricelist_obj = self.pool.get('product.pricelist') - for block in self.browse(cr,uid,ids): + for block in self.browse(cr, uid, ids, context=context): result[block.id] = {'amount_hours_block' : 0.0, - 'amount_hours_block_done' : 0.0, - 'amount_hours_block_delta' : 0.0} + 'amount_hours_block_done' : 0.0} # Compute amount bought for line in block.invoice_id.invoice_line: @@ -125,95 +108,193 @@ class AccountHoursBlock(osv.osv): " WHERE aj.id = al.journal_id" " AND aj.type='general'" " AND al.invoice_id = %s", (block.invoice_id.id,)) - res2 = cr.fetchall() - if res2: - ids2 = [x[0] for x in res2] - else: - ids2 = [] + res_line_ids = cr.fetchall() + line_ids = [l[0] for l in res_line_ids] if res_line_ids else [] total_amount = 0.0 - for line in aal_obj.browse(cr, uid, ids2, context): + for line in aal_obj.browse(cr, uid, line_ids, context=context): factor_invoicing = 1.0 if line.to_invoice and line.to_invoice.factor != 0.0: - factor_invoicing = 1.0 - line.to_invoice.factor / 100 - - ctx = {'uom': line.product_uom_id.id} - amount = pricelist_obj.price_get(cr, uid, - [line.account_id.pricelist_id.id], - line.product_id.id, - line.unit_amount or 1.0, - line.account_id.partner_id.id or False, - ctx)[line.account_id.pricelist_id.id] + factor_invoicing = 1.0 - line.to_invoice.factor / 100 + + ctx = dict(context, uom=line.product_uom_id.id) + amount = pricelist_obj.price_get( + cr, uid, + [line.account_id.pricelist_id.id], + line.product_id.id, + line.unit_amount or 1.0, + line.account_id.partner_id.id or False, + ctx)[line.account_id.pricelist_id.id] total_amount += amount * line.unit_amount * factor_invoicing result[block.id]['amount_hours_block_done'] += total_amount return result - def _compute(self, cr, uid, ids, fields, args, context): + def _compute(self, cr, uid, ids, fields, args, context=None): result = {} block_per_types = {} for block in self.browse(cr, uid, ids, context=context): - if not block.type in block_per_types.keys(): - block_per_types[block.type] = [] - block_per_types[block.type].append(block.id) - + block_per_types.setdefault(block.type, []).append(block.id) + for block_type in block_per_types: if block_type: - func = getattr(self, "_compute_%s" % (block_type,)) - result.update(func(cr, uid, ids, fields, args, context)) + func = getattr(self, "_compute_%s" % block_type) + result.update(func(cr, uid, ids, fields, args, context=context)) for block in result: result[block]['amount_hours_block_delta'] = \ - result[block]['amount_hours_block'] - result[block]['amount_hours_block_done'] + result[block]['amount_hours_block'] - \ + result[block]['amount_hours_block_done'] return result _columns = { - 'amount_hours_block': fields.function(_compute, method=True, type='float', string='Quantity /Amount bought', store=True, - multi='amount_hours_block_delta', - help="Amount bought by the customer. This amount is expressed in the base UoM (factor=1.0)"), - 'amount_hours_block_done': fields.function(_compute, method=True, type='float', string='Quantity / Amount used', store=True, - multi='amount_hours_block_delta', - help="Amount done by the staff. This amount is expressed in the base UoM (factor=1.0)"), - 'amount_hours_block_delta': fields.function(_compute, method=True, type='float', string='Difference', store=True, - multi='amount_hours_block_delta', - help="Difference between bought and used. This amount is expressed in the base UoM (factor=1.0)"), - 'last_action_date': fields.function(_get_last_action, method=True, type='date', string='Last action date', - help="Date of the last analytic line linked to the invoice related to this block hours."), + 'amount_hours_block': fields.function( + _compute, + type='float', + string='Quantity / Amount bought', + store=True, + multi='amount_hours_block_delta', + help="Amount bought by the customer. " + "This amount is expressed in the base Unit of Measure " + "(factor=1.0)"), + 'amount_hours_block_done': fields.function( + _compute, + type='float', + string='Quantity / Amount used', + store=True, + multi='amount_hours_block_delta', + help="Amount done by the staff. " + "This amount is expressed in the base Unit of Measure " + "(factor=1.0)"), + 'amount_hours_block_delta': fields.function( + _compute, + type='float', + string='Difference', + store=True, + multi='amount_hours_block_delta', + help="Difference between bought and used. " + "This amount is expressed in the base Unit of Measure " + "(factor=1.0)"), + 'last_action_date': fields.function( + _get_last_action, + type='date', + string='Last action date', + help="Date of the last analytic line linked to the invoice " + "related to this block hours."), 'close_date': fields.date('Closed Date'), - 'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete='cascade', required=True), - 'type': fields.selection([('hours','Hours'), ('amount','Amount')], 'Type of Block', - required=True, help="Choose if you want a time or amount base block."), + 'invoice_id': fields.many2one( + 'account.invoice', + 'Invoice', + ondelete='cascade', + required=True), + 'type': fields.selection( + [('hours','Hours'), + ('amount','Amount')], + string='Type of Block', + required=True, + help="The block is based on the quantity of hours " + "or on the amount."), + # Invoices related infos - 'date_invoice': fields.related('invoice_id', 'date_invoice', type="date", string="Invoice Date", store=True, readonly=True), - 'user_id': fields.related('invoice_id', 'user_id', type="many2one", relation="res.users", string="Salesman", store=True, readonly=True), - 'partner_id': fields.related('invoice_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True, readonly=True), - 'name': fields.related('invoice_id', 'name', type="char",size=64, string="Description", store=True,readonly=True), - 'number': fields.related('invoice_id', 'number', type="char",size=64, string="Number", store=True,readonly=True), - 'journal_id': fields.related('invoice_id', 'journal_id', type="many2one", relation="account.journal", string="Journal", store=True,readonly=True), - 'period_id': fields.related('invoice_id', 'period_id', type="many2one", relation="account.period", string="Period", store=True,readonly=True), - 'company_id': fields.related('invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", store=True,readonly=True), - 'currency_id': fields.related('invoice_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", store=True,readonly=True), - 'residual': fields.related('invoice_id', 'residual', type="float", string="Residual", store=True,readonly=True), - 'amount_total': fields.related('invoice_id', 'amount_total', type="float", string="Total", store=True,readonly=True), - 'state':fields.related('invoice_id','state', - type='selection', - selection=[ - ('draft','Draft'), - ('proforma','Pro-forma'), - ('proforma2','Pro-forma'), - ('open','Open'), - ('paid','Paid'), - ('cancel','Cancelled') - ], - string='State', readonly=True, store=True), + 'date_invoice': fields.related( + 'invoice_id', 'date_invoice', + type="date", + string="Invoice Date", + store=True, + readonly=True), + 'user_id': fields.related( + 'invoice_id', 'user_id', + type="many2one", + relation="res.users", + string="Salesman", + store=True, + readonly=True), + 'partner_id': fields.related( + 'invoice_id', 'partner_id', + type="many2one", + relation="res.partner", + string="Partner", + store=True, + readonly=True), + 'name': fields.related( + 'invoice_id', 'name', + type="char", + size=64, + string="Description", + store=True, + readonly=True), + 'number': fields.related( + 'invoice_id', 'number', + type="char", + size=64, + string="Number", + store=True, + readonly=True), + 'journal_id': fields.related( + 'invoice_id', 'journal_id', + type="many2one", + relation="account.journal", + string="Journal", + store=True, + readonly=True), + 'period_id': fields.related( + 'invoice_id', 'period_id', + type="many2one", + relation="account.period", + string="Period", + store=True, + readonly=True), + 'company_id': fields.related( + 'invoice_id', 'company_id', + type="many2one", + relation="res.company", + string="Company", + store=True, + readonly=True), + 'currency_id': fields.related( + 'invoice_id', 'currency_id', + type="many2one", + relation="res.currency", + string="Currency", + store=True, + readonly=True), + 'residual': fields.related( + 'invoice_id', 'residual', + type="float", + string="Residual", + store=True, + readonly=True), + 'amount_total': fields.related( + 'invoice_id', 'amount_total', + type="float", + string="Total", + store=True, + readonly=True), + 'state':fields.related( + 'invoice_id','state', + type='selection', + selection=[ + ('draft','Draft'), + ('proforma','Pro-forma'), + ('proforma2','Pro-forma'), + ('open','Open'), + ('paid','Paid'), + ('cancel','Cancelled'), + ], + string='State', + readonly=True, + store=True), } -AccountHoursBlock() - -class AccountInvoice(osv.osv): +############################################################################ +## Add hours blocks on invoice +############################################################################ +class AccountInvoice(orm.Model): _inherit = 'account.invoice' + _columns = { - 'account_hours_block_ids': fields.one2many('account.hours.block', 'invoice_id', 'Hours Block') + 'account_hours_block_ids': fields.one2many( + 'account.hours.block', + 'invoice_id', + string='Hours Block') } - -AccountInvoice() diff --git a/analytic_hours_block/report/hours_block.py b/analytic_hours_block/report/hours_block.py index ad459fd2..906c694b 100644 --- a/analytic_hours_block/report/hours_block.py +++ b/analytic_hours_block/report/hours_block.py @@ -20,31 +20,26 @@ ############################################################################## import time -from report import report_sxw +from openerp.report import report_sxw class account_hours_block(report_sxw.rml_parse): def __init__(self, cr, uid, name, context=None): - super(account_hours_block, self).__init__(cr, uid, name, context) + super(account_hours_block, self).__init__(cr, uid, name, context=context) self.localcontext.update({ 'time': time, - 'format_date': self._get_and_change_date_format_for_swiss, 'analytic_lines': self._get_analytic_lines, }) self.context = context def _get_analytic_lines(self, hours_block): al_pool = self.pool.get('account.analytic.line') - al_ids = al_pool.search(self.cr, self.uid, - [['invoice_id', '=', hours_block.invoice_id.id]], - order='date desc') - res = al_pool.browse(self.cr, self.uid, al_ids) - return res - - def _get_and_change_date_format_for_swiss(self, date_to_format): - date_formatted = '' - if date_to_format: - date_formatted = strptime(date_to_format, '%Y-%m-%d').strftime('%d.%m.%Y') - return date_formatted + al_ids = al_pool.search( + self.cr, + self.uid, + [('invoice_id', '=', hours_block.invoice_id.id)], + order='date desc', + context=self.context) + return al_pool.browse(self.cr, self.uid, al_ids, context=self.context) report_sxw.report_sxw('report.account.hours.block', 'account.hours.block', 'addons/analytic_hours_block/report/hours_block.rml', parser=account_hours_block) diff --git a/analytic_hours_block/report/hours_block.rml b/analytic_hours_block/report/hours_block.rml index 8fe879a8..1b66efa9 100644 --- a/analytic_hours_block/report/hours_block.rml +++ b/analytic_hours_block/report/hours_block.rml @@ -184,7 +184,7 @@ Invoice Date : - [[ o.date_invoice and format_date(o.date_invoice) or '' ]] + [[ o.date_invoice and formatLang(o.date_invoice, date=True) or '' ]] @@ -243,7 +243,7 @@ [[ repeatIn(analytic_lines(o),'l') ]] - [[ l.date and format_date(l.date) or '' ]] + [[ l.date and formatLang(l.date, date=True) or '' ]] [[ l.name or '' ]] From 6a41f6ee302a0e5f5c77d70f5b0dd688bd6f257d Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 15:43:18 +0100 Subject: [PATCH 05/11] [FIX] xml formatting and remove useless type on views --- analytic_hours_block/hours_block_view.xml | 57 +++++++++++------------ 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/analytic_hours_block/hours_block_view.xml b/analytic_hours_block/hours_block_view.xml index 81fd52be..b28d3f77 100644 --- a/analytic_hours_block/hours_block_view.xml +++ b/analytic_hours_block/hours_block_view.xml @@ -2,13 +2,12 @@ - #--------------------------------------------------------------------- - # Hours block search form - #--------------------------------------------------------------------- + account.hours.block.select account.hours.block - search @@ -33,14 +32,13 @@ - - #------------------------------------------------------------------------ - # Hours Block View - #------------------------------------------------------------------------ + + account.hours.block.form account.hours.block - form
@@ -60,10 +58,10 @@ - + - + @@ -72,23 +70,22 @@
- + account.hours.block.tree account.hours.block - tree - + - + @@ -100,21 +97,20 @@ - - - #--------------------------------------------------------------------------------------------------------- - # Add related act_window from partner and Hours Block - #--------------------------------------------------------------------------------------------------------- + + + - - #--------------------------------------------------------------------------------------------------------- - # Link to invoice on hours block view - #--------------------------------------------------------------------------------------------------------- + - #--------------------------------------------------------------------------------------------------------- - # Link to analytic lines on hours block view - #--------------------------------------------------------------------------------------------------------- + - #--------------------------------------------------------------------------------------------------------- - # Link to hours block on invoice view - #--------------------------------------------------------------------------------------------------------- + -
From 23755acecf25fd5aa563836d8d12841d25f660a6 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 16:37:32 +0100 Subject: [PATCH 06/11] [IMP] v7 view style --- analytic_hours_block/hours_block_view.xml | 54 ++++++++++++++--------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/analytic_hours_block/hours_block_view.xml b/analytic_hours_block/hours_block_view.xml index b28d3f77..50ac6ac3 100644 --- a/analytic_hours_block/hours_block_view.xml +++ b/analytic_hours_block/hours_block_view.xml @@ -40,33 +40,45 @@ account.hours.block.form account.hours.block -
- - - - - - + + +

+ +

+ + + + + + + + - - - - - - - - - + + + + + + + + - - - - - + + + + + + + + + +
From 55c3fdb4743178a989779eb6a246ce968dad15fd Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 17 Dec 2012 16:37:44 +0100 Subject: [PATCH 07/11] [FIX] formatting, rml fixes --- analytic_hours_block/hours_block.py | 6 +-- analytic_hours_block/hours_block_menu.xml | 10 ++--- analytic_hours_block/report/hours_block.py | 2 + analytic_hours_block/report/hours_block.rml | 40 +++++++++---------- .../security/hours_block_security.xml | 2 +- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index 256c2baf..df3c2476 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -86,8 +86,8 @@ class AccountHoursBlock(orm.Model): aal_obj = self.pool.get('account.analytic.line') pricelist_obj = self.pool.get('product.pricelist') for block in self.browse(cr, uid, ids, context=context): - result[block.id] = {'amount_hours_block' : 0.0, - 'amount_hours_block_done' : 0.0} + result[block.id] = {'amount_hours_block': 0.0, + 'amount_hours_block_done': 0.0} # Compute amount bought for line in block.invoice_id.invoice_line: @@ -269,7 +269,7 @@ class AccountHoursBlock(orm.Model): string="Total", store=True, readonly=True), - 'state':fields.related( + 'state': fields.related( 'invoice_id','state', type='selection', selection=[ diff --git a/analytic_hours_block/hours_block_menu.xml b/analytic_hours_block/hours_block_menu.xml index 0dff97be..c4969041 100644 --- a/analytic_hours_block/hours_block_menu.xml +++ b/analytic_hours_block/hours_block_menu.xml @@ -2,13 +2,11 @@ - - #--------------------------------------------------------------------------------------------------------- - # Hours block menu - #--------------------------------------------------------------------------------------------------------- - + - Hours Block + Hours Blocks account.hours.block form tree,form diff --git a/analytic_hours_block/report/hours_block.py b/analytic_hours_block/report/hours_block.py index 906c694b..8807ad51 100644 --- a/analytic_hours_block/report/hours_block.py +++ b/analytic_hours_block/report/hours_block.py @@ -21,6 +21,7 @@ import time from openerp.report import report_sxw +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT class account_hours_block(report_sxw.rml_parse): @@ -28,6 +29,7 @@ class account_hours_block(report_sxw.rml_parse): super(account_hours_block, self).__init__(cr, uid, name, context=context) self.localcontext.update({ 'time': time, + 'date_format': DEFAULT_SERVER_DATE_FORMAT, 'analytic_lines': self._get_analytic_lines, }) self.context = context diff --git a/analytic_hours_block/report/hours_block.rml b/analytic_hours_block/report/hours_block.rml index 1b66efa9..7f9344e4 100644 --- a/analytic_hours_block/report/hours_block.rml +++ b/analytic_hours_block/report/hours_block.rml @@ -10,13 +10,13 @@ Maintenance And Support Summary - [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]] + [[ formatLang(time.strftime(date_format), date=True) ]] Page 0.6cm 27.9cm 20.3cm 27.9cm - + @@ -35,7 +35,7 @@ - + @@ -165,7 +165,7 @@ - Description : + Description: [[ o.name ]] @@ -173,15 +173,15 @@ - Report Date : + Report Date: - [[ time.strftime("%d.%m.%Y", time.localtime()) ]] + [[ formatLang(time.strftime(date_format), date=True) ]] - Invoice Date : + Invoice Date: [[ o.date_invoice and formatLang(o.date_invoice, date=True) or '' ]] @@ -189,8 +189,8 @@ - Amount bought : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] - Quantity of hours bought : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] + Amount bought: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] + Quantity of hours bought: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] [[ o.amount_hours_block ]] @@ -198,9 +198,8 @@ - - Amount used : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] - Quantity of hours used : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] + Amount used: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] + Quantity of hours used: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] [[ round(o.amount_hours_block_done, 2) ]] @@ -208,10 +207,9 @@ - - Remaining amount : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] - Remaining hours : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] - + Remaining amount: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] + Remaining hours: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] + [[ o.amount_hours_block and round(o.amount_hours_block_delta, 2) or '' ]] @@ -239,23 +237,23 @@ Deduced - + - [[ repeatIn(analytic_lines(o),'l') ]] + [[ repeatIn(analytic_lines(o), 'l') ]] - [[ l.date and formatLang(l.date, date=True) or '' ]] + [[ l.date if formatLang(l.date, date=True) else '' ]] [[ l.name or '' ]] - [[ round(l.unit_amount or '0.0', 2) ]] + [[ round(l.unit_amount, 2) or '0.0' ]] [[ l.to_invoice.customer_name ]] - [[ round((l.unit_amount and l.to_invoice) and (l.unit_amount - (l.unit_amount * l.to_invoice.factor) / 100 ) or '0.0', 2) ]] + [[ round((l.unit_amount and l.to_invoice) and (l.unit_amount - (l.unit_amount * l.to_invoice.factor) / 100 ), 2) or '0.0' ]] diff --git a/analytic_hours_block/security/hours_block_security.xml b/analytic_hours_block/security/hours_block_security.xml index 3f9ff31e..81c50c4d 100644 --- a/analytic_hours_block/security/hours_block_security.xml +++ b/analytic_hours_block/security/hours_block_security.xml @@ -7,5 +7,5 @@ [] - + From ebf077d35ce7f6f3e8752544fcd95772e0decf39 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Tue, 18 Dec 2012 09:46:36 +0100 Subject: [PATCH 08/11] [FIX] wording --- analytic_hours_block/hours_block_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analytic_hours_block/hours_block_view.xml b/analytic_hours_block/hours_block_view.xml index 50ac6ac3..a81bc136 100644 --- a/analytic_hours_block/hours_block_view.xml +++ b/analytic_hours_block/hours_block_view.xml @@ -27,7 +27,7 @@ - +
From 449cbaafcff6ac61258ee9b639b1fdec3a432bdf Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Tue, 18 Dec 2012 09:58:22 +0100 Subject: [PATCH 09/11] [FIX] space after commas --- analytic_hours_block/hours_block.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index df3c2476..d280a289 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -187,8 +187,8 @@ class AccountHoursBlock(orm.Model): ondelete='cascade', required=True), 'type': fields.selection( - [('hours','Hours'), - ('amount','Amount')], + [('hours', 'Hours'), + ('amount', 'Amount')], string='Type of Block', required=True, help="The block is based on the quantity of hours " @@ -270,15 +270,15 @@ class AccountHoursBlock(orm.Model): store=True, readonly=True), 'state': fields.related( - 'invoice_id','state', + 'invoice_id', 'state', type='selection', selection=[ - ('draft','Draft'), - ('proforma','Pro-forma'), - ('proforma2','Pro-forma'), - ('open','Open'), - ('paid','Paid'), - ('cancel','Cancelled'), + ('draft', 'Draft'), + ('proforma', 'Pro-forma'), + ('proforma2', 'Pro-forma'), + ('open', 'Open'), + ('paid', 'Paid'), + ('cancel', 'Cancelled'), ], string='State', readonly=True, From 82cf132d109309fc99d87e92242695d1cd1854f0 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Tue, 18 Dec 2012 10:57:45 +0100 Subject: [PATCH 10/11] [FIX] remove size on fields.char --- analytic_hours_block/hours_block.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index d280a289..2b048ca4 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -218,14 +218,12 @@ class AccountHoursBlock(orm.Model): 'name': fields.related( 'invoice_id', 'name', type="char", - size=64, string="Description", store=True, readonly=True), 'number': fields.related( 'invoice_id', 'number', type="char", - size=64, string="Number", store=True, readonly=True), From efcdc073bcc85a7716d24f122eab74211ecb9300 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Tue, 18 Dec 2012 12:05:03 +0100 Subject: [PATCH 11/11] [FIX] add triggers to recompute the amounts / hours on invoice or analytic lines changes --- analytic_hours_block/hours_block.py | 33 ++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py index 2b048ca4..b341702a 100644 --- a/analytic_hours_block/hours_block.py +++ b/analytic_hours_block/hours_block.py @@ -146,12 +146,39 @@ class AccountHoursBlock(orm.Model): result[block]['amount_hours_block_done'] return result + def _get_analytic_line(self, cr, uid, ids, context=None): + invoice_ids = [] + an_lines_obj = self.pool.get('account.analytic.line') + block_obj = self.pool.get('account.hours.block') + for line in an_lines_obj.browse(cr, uid, ids, context=context): + if line.invoice_id: + invoice_ids.append(line.invoice_id.id) + return block_obj.search( + cr, uid, [('invoice_id', 'in', invoice_ids)], context=context) + + def _get_invoice(self, cr, uid, ids, context=None): + block_ids = set() + inv_obj = self.pool.get('account.invoice') + for invoice in inv_obj.browse(cr, uid, ids, context=context): + block_ids.update([inv.id for inv in invoice.account_hours_block_ids]) + return list(block_ids) + + _recompute_triggers = { + 'account.hours.block': (lambda self, cr, uid, ids, c=None: + ids, ['invoice_id', 'type'], 10), + 'account.invoice': (_get_invoice, ['analytic_line_ids'], 10), + 'account.analytic.line': ( + _get_analytic_line, + ['product_uom_id', 'unit_amount', 'to_invoice', 'invoice_id'], + 10), + } + _columns = { 'amount_hours_block': fields.function( _compute, type='float', string='Quantity / Amount bought', - store=True, + store=_recompute_triggers, multi='amount_hours_block_delta', help="Amount bought by the customer. " "This amount is expressed in the base Unit of Measure " @@ -160,7 +187,7 @@ class AccountHoursBlock(orm.Model): _compute, type='float', string='Quantity / Amount used', - store=True, + store=_recompute_triggers, multi='amount_hours_block_delta', help="Amount done by the staff. " "This amount is expressed in the base Unit of Measure " @@ -169,7 +196,7 @@ class AccountHoursBlock(orm.Model): _compute, type='float', string='Difference', - store=True, + store=_recompute_triggers, multi='amount_hours_block_delta', help="Difference between bought and used. " "This amount is expressed in the base Unit of Measure "