commit 4db3e2556cf1a30e365d91e6e5c9a68765de06a4 Author: Joël Grand-Guillaume Date: Fri Aug 12 14:33:12 2011 +0200 [ADD] First commit of the first generic modules to move in our new public branch (lp:c2c-addons/6.1 rev 1) diff --git a/analytic_hours_block/__init__.py b/analytic_hours_block/__init__.py new file mode 100644 index 00000000..7c87d3d2 --- /dev/null +++ b/analytic_hours_block/__init__.py @@ -0,0 +1,28 @@ +# -*- 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 +# +# 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. +# +############################################################################## +import hours_block +import report diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py new file mode 100644 index 00000000..fcebbb70 --- /dev/null +++ b/analytic_hours_block/__openerp__.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################## +## @author Bessi Nicolas, 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 +# +# 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. +# +############################################################################## + +{ + "name" : "Project Hours Blocks Management", + "description" : """ +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", + "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", + ], + "active": False, + "installable": True +} diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py new file mode 100644 index 00000000..1dcafaf1 --- /dev/null +++ b/analytic_hours_block/hours_block.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# @author Bessi Nicolas +# 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 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. +# +############################################################################## + +from osv import osv, fields +import time +from mx import DateTime +import netsvc +import string + +############################################################################ +## Add hours blocks on invoice +############################################################################ + +class AccountHoursBlock(osv.osv): + _name = "account.hours.block" + + def _get_last_action(self, cr, uid, ids, name, arg, context={}): + res = {} + for block in self.browse(cr, uid, ids): + cr.execute('SELECT max(al.date) FROM account_analytic_line AS al WHERE al.invoice_id = ' + str(block.invoice_id.id)) + date = map(lambda x: x[0], cr.fetchall() or []) + res[block.id] = date[0] + return res + + def _compute_hours(self, cr, uid, ids, fields, args, context): + # Return a dict of [id][fields] + 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 + } + # 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 + 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 + 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 = " + str(block.invoice_id.id) + ) + ids2 = map(lambda x: x[0], cr.fetchall() or []) + for line in aal_obj.browse(cr, uid, ids2, context): + if line.product_uom_id: + 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 + 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): + result = {} + aal_obj = self.pool.get('account.analytic.line') + pricelist_obj = self.pool.get('product.pricelist') + 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 + } + + # Compute amount bought + for line in block.invoice_id.invoice_line: + amount_bought = 0.0 + if line.product_id: + ## We will now calculate the product_quantity + factor = line.uos_id.factor + if factor == 0.0: + factor = 1.0 + amount = line.quantity * line.price_unit + amount_bought += (amount / factor) + result[block.id]['amount_hours_block'] += amount_bought + + # Compute total amount + # Get ids of analytic line generated from timesheet associated to 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 = " + str(block.invoice_id.id) + ) + ids2 = map(lambda x: x[0], cr.fetchall() or []) + total_amount = 0.0 + for line in aal_obj.browse(cr, uid, ids2, 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] + 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): + 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) + + for block_type in block_per_types.keys(): + if block_type: + result.update(eval("self._compute_%s" % (block_type,))(cr, uid, ids, fields, args, context)) + + for block in result: + result[block]['amount_hours_block_delta'] = \ + 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."), + '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."), + # 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), + } + +AccountHoursBlock() + + +class AccountInvoice(osv.osv): + _inherit = 'account.invoice' + _columns = { + 'account_hours_block_ids': fields.one2many('account.hours.block', 'invoice_id', 'Hours Block') + } + +AccountInvoice() diff --git a/analytic_hours_block/hours_block_menu.xml b/analytic_hours_block/hours_block_menu.xml new file mode 100644 index 00000000..0dff97be --- /dev/null +++ b/analytic_hours_block/hours_block_menu.xml @@ -0,0 +1,26 @@ + + + + + + #--------------------------------------------------------------------------------------------------------- + # Hours block menu + #--------------------------------------------------------------------------------------------------------- + + + Hours Block + account.hours.block + form + tree,form + + + + + + + + diff --git a/analytic_hours_block/hours_block_view.xml b/analytic_hours_block/hours_block_view.xml new file mode 100644 index 00000000..81fd52be --- /dev/null +++ b/analytic_hours_block/hours_block_view.xml @@ -0,0 +1,153 @@ + + + + + #--------------------------------------------------------------------- + # Hours block search form + #--------------------------------------------------------------------- + + account.hours.block.select + account.hours.block + search + + + + + + + + + + + + + + + + + + + + + + + + + + #------------------------------------------------------------------------ + # Hours Block View + #------------------------------------------------------------------------ + + account.hours.block.form + account.hours.block + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + account.hours.block.tree + account.hours.block + tree + + + + + + + + + + + + + + + + + + + + + + + + + + #--------------------------------------------------------------------------------------------------------- + # 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 + #--------------------------------------------------------------------------------------------------------- + + + +
+
diff --git a/analytic_hours_block/report.xml b/analytic_hours_block/report.xml new file mode 100644 index 00000000..9d60be43 --- /dev/null +++ b/analytic_hours_block/report.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/analytic_hours_block/report/__init__.py b/analytic_hours_block/report/__init__.py new file mode 100644 index 00000000..fb482f03 --- /dev/null +++ b/analytic_hours_block/report/__init__.py @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# +# 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 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. +# +############################################################################## +import hours_block \ No newline at end of file diff --git a/analytic_hours_block/report/hours_block.py b/analytic_hours_block/report/hours_block.py new file mode 100644 index 00000000..54d83319 --- /dev/null +++ b/analytic_hours_block/report/hours_block.py @@ -0,0 +1,60 @@ +############################################################################## +# +# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# 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 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. +# +############################################################################## + +import time +from mx.DateTime import * +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): + super(account_hours_block, self).__init__(cr, uid, name, 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 + +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 new file mode 100644 index 00000000..8fe879a8 --- /dev/null +++ b/analytic_hours_block/report/hours_block.rml @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [[ repeatIn(objects,'o') ]] + [[ setLang(o.partner_id.lang) ]] + Maintenance And Support Summary + + + + + + + Description : + + + [[ o.name ]] + + + + + Report Date : + + + [[ time.strftime("%d.%m.%Y", time.localtime()) ]] + + + + + Invoice Date : + + + [[ o.date_invoice and format_date(o.date_invoice) or '' ]] + + + + + Amount bought : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]] + Quantity of hours bought : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]] + + + [[ o.amount_hours_block ]] + + + + + + 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) ]] + + + + + + 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 '' ]] + + + + + + + + + + Date + + + Description + + + Quantity + + + Invoicing + + + Deduced + + + + + [[ repeatIn(analytic_lines(o),'l') ]] + + [[ l.date and format_date(l.date) or '' ]] + + + [[ l.name or '' ]] + + + [[ round(l.unit_amount or '0.0', 2) ]] + + + [[ 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) ]] + + + + + + + diff --git a/analytic_hours_block/security/hours_block_security.xml b/analytic_hours_block/security/hours_block_security.xml new file mode 100644 index 00000000..3f9ff31e --- /dev/null +++ b/analytic_hours_block/security/hours_block_security.xml @@ -0,0 +1,11 @@ + + + + + Hours Block multi company rule + + + [] + + + diff --git a/analytic_hours_block/security/ir.model.access.csv b/analytic_hours_block/security/ir.model.access.csv new file mode 100644 index 00000000..57d8a5c9 --- /dev/null +++ b/analytic_hours_block/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_hours_block_user","account.hours.block","model_account_hours_block","base.group_user",1,0,0,0 +"access_hours_block_invoice_manager","account.hours.block","model_account_hours_block","account.group_account_invoice",1,1,1,1