[MIGR] migration of analytic_hours_block to OpenERP version 7.0

This commit is contained in:
Joel Grand-Guillaume
2013-01-14 11:28:03 +01:00
9 changed files with 406 additions and 337 deletions

View File

@@ -1,27 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# @author Renaville Vincent, ported by Joel Grand-Guillaume # Author: Vincent Renaville, ported by Joel Grand-Guillaume
# WARNING: This program as such is intended to be used by professional # Copyright 2010-2012 Camptocamp SA
# 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 # This program is free software: you can redistribute it and/or modify
# modify it under the terms of the GNU General Public License # it under the terms of the GNU Affero General Public License as
# as published by the Free Software Foundation; either version 2 # published by the Free Software Foundation, either version 3 of the
# of the License, or (at your option) any later version. # License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import hours_block import hours_block

View File

@@ -1,54 +1,48 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) # Author: Vincent Renaville, ported by Joel Grand-Guillaume
# All Right Reserved # 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
# WARNING: This program as such is intended to be used by professional # published by the Free Software Foundation, either version 3 of the
# programmers who take the whole responsability of assessing all potential # License, or (at your option) any later version.
# 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, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
{ {
"name" : "Project Hours Blocks Management", "name": "Project Hours Blocks Management",
"description" : """ "description": """
Project Hours Blocks Management
===============================
This module allows you to handle hours blocks, to follow for example the user support contracts. This module allows you to handle hours blocks,
This means, you sell a product of type "hours block" then you input the spent hours on the hours block and 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. you can track and follow how much has been used.
""", """,
"version" : "1.2", "version": "1.3",
"author" : "Camptocamp", "author": "Camptocamp",
"category" : "Generic Modules/Projects & Services", "license": 'AGPL-3',
"category": "Generic Modules/Projects & Services",
"website": "http://www.camptocamp.com", "website": "http://www.camptocamp.com",
"depends" : [ "depends": [
"account", "account",
"hr_timesheet_invoice", "hr_timesheet_invoice",
"analytic" "analytic"
], ],
"init_xml" : [], "data": [
"update_xml" : [
"hours_block_view.xml", "hours_block_view.xml",
"hours_block_menu.xml", "hours_block_menu.xml",
"report.xml", "report.xml",

View File

@@ -1,105 +1,77 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Camptocamp SA # Author: Vincent Renaville, ported by Joel Grand-Guillaume
# All Rights Reserved # 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
# WARNING: This program as such is intended to be used by professional # published by the Free Software Foundation, either version 3 of the
# programmers who take the whole responsability of assessing all potential # License, or (at your option) any later version.
# 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, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import time from openerp.osv import orm, fields
import string
from osv import osv, fields
import netsvc
############################################################################ class AccountHoursBlock(orm.Model):
## Add hours blocks on invoice
############################################################################
class AccountHoursBlock(osv.osv):
_name = "account.hours.block" _name = "account.hours.block"
def _get_last_action(self, cr, uid, ids, name, arg, context=None): def _get_last_action(self, cr, uid, ids, name, arg, context=None):
"""TODO""" """ Return the last analytic line date for an invoice"""
context = context or {}
res = {} 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" cr.execute("SELECT max(al.date) FROM account_analytic_line AS al"
" WHERE al.invoice_id = %s", (block.invoice_id.id,)) " WHERE al.invoice_id = %s", (block.invoice_id.id,))
fetch_res = cr.fetchone() fetch_res = cr.fetchone()
if fetch_res: res[block.id] = fetch_res[0] if fetch_res else False
date = fetch_res[0]
else:
date = False
res[block.id] = date
return res return res
def _compute_hours(self, cr, uid, ids, fields, args, context=None): def _compute_hours(self, cr, uid, ids, fields, args, context=None):
"""Return a dict of [id][fields]""" """Return a dict of [id][fields]"""
context = context or {} if isinstance(ids, (int, long)):
if not isinstance(ids, list): ids = [ids]
ids=[ids]
result = {} result = {}
aal_obj = self.pool.get('account.analytic.line') aal_obj = self.pool.get('account.analytic.line')
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, result[block.id] = {'amount_hours_block': 0.0,
'amount_hours_block_done' : 0.0, 'amount_hours_block_done': 0.0}
'amount_hours_block_delta' : 0.0}
# Compute hours bought # Compute hours bought
for line in block.invoice_id.invoice_line: for line in block.invoice_id.invoice_line:
hours_bought = 0.0 hours_bought = 0.0
if line.product_id: if line.product_id:
## We will now calculate the product_quantity # We will now calculate the product_quantity
factor = line.uos_id.factor factor = line.uos_id.factor
if factor == 0.0: if factor == 0.0:
factor = 1.0 factor = 1.0
amount = line.quantity amount = line.quantity
hours_bought += (amount / factor) hours_bought += (amount / factor)
result[block.id]['amount_hours_block'] += hours_bought result[block.id]['amount_hours_block'] += hours_bought
# Compute hours spent # Compute hours spent
hours_used = 0.0 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 " cr.execute("SELECT al.id "
" FROM account_analytic_line AS al,account_analytic_journal AS aj" "FROM account_analytic_line AS al, "
" WHERE aj.id = al.journal_id AND" " account_analytic_journal AS aj "
" aj.type='general' AND" "WHERE aj.id = al.journal_id "
" al.invoice_id = %s", (block.invoice_id.id,)) "AND aj.type = 'general' "
res2 = cr.fetchall() "AND al.invoice_id = %s", (block.invoice_id.id,))
if res2: res_line_ids = cr.fetchall()
ids2 = [x[0] for x in res2] line_ids = [l[0] for l in res_line_ids] if res_line_ids else []
else: for line in aal_obj.browse(cr, uid, line_ids, context=context):
ids2 = [] factor = 1.0
for line in aal_obj.browse(cr, uid, ids2, context): if line.product_uom_id and line.product_uom_id.factor != 0.0:
if line.product_uom_id:
factor = line.product_uom_id.factor factor = line.product_uom_id.factor
if factor == 0.0:
factor = 1.0
else:
factor = 1.0
factor_invoicing = 1.0 factor_invoicing = 1.0
if line.to_invoice and line.to_invoice.factor != 0.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
@@ -107,14 +79,15 @@ class AccountHoursBlock(osv.osv):
result[block.id]['amount_hours_block_done'] = hours_used result[block.id]['amount_hours_block_done'] = hours_used
return result 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 = {} result = {}
aal_obj = self.pool.get('account.analytic.line') aal_obj = self.pool.get('account.analytic.line')
pricelist_obj = self.pool.get('product.pricelist') 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, result[block.id] = {'amount_hours_block': 0.0,
'amount_hours_block_done' : 0.0, 'amount_hours_block_done': 0.0}
'amount_hours_block_delta' : 0.0}
# Compute amount bought # Compute amount bought
for line in block.invoice_id.invoice_line: for line in block.invoice_id.invoice_line:
@@ -135,19 +108,17 @@ class AccountHoursBlock(osv.osv):
" WHERE aj.id = al.journal_id" " WHERE aj.id = al.journal_id"
" AND aj.type='general'" " AND aj.type='general'"
" AND al.invoice_id = %s", (block.invoice_id.id,)) " AND al.invoice_id = %s", (block.invoice_id.id,))
res2 = cr.fetchall() res_line_ids = cr.fetchall()
if res2: line_ids = [l[0] for l in res_line_ids] if res_line_ids else []
ids2 = [x[0] for x in res2]
else:
ids2 = []
total_amount = 0.0 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 factor_invoicing = 1.0
if line.to_invoice and line.to_invoice.factor != 0.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
ctx = {'uom': line.product_uom_id.id} ctx = dict(context, uom=line.product_uom_id.id)
amount = pricelist_obj.price_get(cr, uid, amount = pricelist_obj.price_get(
cr, uid,
[line.account_id.pricelist_id.id], [line.account_id.pricelist_id.id],
line.product_id.id, line.product_id.id,
line.unit_amount or 1.0, line.unit_amount or 1.0,
@@ -158,72 +129,197 @@ class AccountHoursBlock(osv.osv):
return result return result
def _compute(self, cr, uid, ids, fields, args, context): def _compute(self, cr, uid, ids, fields, args, context=None):
result = {} result = {}
block_per_types = {} block_per_types = {}
for block in self.browse(cr, uid, ids, context=context): for block in self.browse(cr, uid, ids, context=context):
if not block.type in block_per_types.keys(): block_per_types.setdefault(block.type, []).append(block.id)
block_per_types[block.type] = []
block_per_types[block.type].append(block.id)
for block_type in block_per_types: for block_type in block_per_types:
if block_type: if block_type:
func = getattr(self, "_compute_%s" % (block_type,)) func = getattr(self, "_compute_%s" % block_type)
result.update(func(cr, uid, ids, fields, args, context)) result.update(func(cr, uid, ids, fields, args, context=context))
for block in result: for block in result:
result[block]['amount_hours_block_delta'] = \ 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 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 = { _columns = {
'amount_hours_block': fields.function(_compute, method=True, type='float', string='Quantity /Amount bought', store=True, 'amount_hours_block': fields.function(
_compute,
type='float',
string='Quantity / Amount bought',
store=_recompute_triggers,
multi='amount_hours_block_delta', multi='amount_hours_block_delta',
help="Amount bought by the customer. This amount is expressed in the base UoM (factor=1.0)"), help="Amount bought by the customer. "
'amount_hours_block_done': fields.function(_compute, method=True, type='float', string='Quantity / Amount used', store=True, "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=_recompute_triggers,
multi='amount_hours_block_delta', multi='amount_hours_block_delta',
help="Amount done by the staff. This amount is expressed in the base UoM (factor=1.0)"), help="Amount done by the staff. "
'amount_hours_block_delta': fields.function(_compute, method=True, type='float', string='Difference', store=True, "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=_recompute_triggers,
multi='amount_hours_block_delta', multi='amount_hours_block_delta',
help="Difference between bought and used. This amount is expressed in the base UoM (factor=1.0)"), help="Difference between bought and used. "
'last_action_date': fields.function(_get_last_action, method=True, type='date', string='Last action date', "This amount is expressed in the base Unit of Measure "
help="Date of the last analytic line linked to the invoice related to this block hours."), "(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'), 'close_date': fields.date('Closed Date'),
'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete='cascade', required=True), 'invoice_id': fields.many2one(
'type': fields.selection([('hours','Hours'), ('amount','Amount')], 'Type of Block', 'account.invoice',
required=True, help="Choose if you want a time or amount base block."), '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 # Invoices related infos
'date_invoice': fields.related('invoice_id', 'date_invoice', type="date", string="Invoice Date", store=True, readonly=True), 'date_invoice': fields.related(
'user_id': fields.related('invoice_id', 'user_id', type="many2one", relation="res.users", string="Salesman", store=True, readonly=True), 'invoice_id', 'date_invoice',
'partner_id': fields.related('invoice_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True, readonly=True), type="date",
'name': fields.related('invoice_id', 'name', type="char",size=64, string="Description", store=True,readonly=True), string="Invoice Date",
'number': fields.related('invoice_id', 'number', type="char",size=64, string="Number", store=True,readonly=True), store=True,
'journal_id': fields.related('invoice_id', 'journal_id', type="many2one", relation="account.journal", string="Journal", store=True,readonly=True), readonly=True),
'period_id': fields.related('invoice_id', 'period_id', type="many2one", relation="account.period", string="Period", store=True,readonly=True), 'user_id': fields.related(
'company_id': fields.related('invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", store=True,readonly=True), 'invoice_id', 'user_id',
'currency_id': fields.related('invoice_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", store=True,readonly=True), type="many2one",
'residual': fields.related('invoice_id', 'residual', type="float", string="Residual", store=True,readonly=True), relation="res.users",
'amount_total': fields.related('invoice_id', 'amount_total', type="float", string="Total", store=True,readonly=True), string="Salesman",
'state':fields.related('invoice_id','state', 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",
string="Description",
store=True,
readonly=True),
'number': fields.related(
'invoice_id', 'number',
type="char",
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', type='selection',
selection=[ selection=[
('draft','Draft'), ('draft', 'Draft'),
('proforma','Pro-forma'), ('proforma', 'Pro-forma'),
('proforma2','Pro-forma'), ('proforma2', 'Pro-forma'),
('open','Open'), ('open', 'Open'),
('paid','Paid'), ('paid', 'Paid'),
('cancel','Cancelled') ('cancel', 'Cancelled'),
], ],
string='State', readonly=True, store=True), string='State',
readonly=True,
store=True),
} }
AccountHoursBlock()
############################################################################
class AccountInvoice(osv.osv): ## Add hours blocks on invoice
############################################################################
class AccountInvoice(orm.Model):
_inherit = 'account.invoice' _inherit = 'account.invoice'
_columns = {
'account_hours_block_ids': fields.one2many('account.hours.block', 'invoice_id', 'Hours Block')
}
AccountInvoice() _columns = {
'account_hours_block_ids': fields.one2many(
'account.hours.block',
'invoice_id',
string='Hours Block')
}

View File

@@ -2,13 +2,11 @@
<openerp> <openerp>
<data> <data>
<!--
#--------------------------------------------------------------------------------------------------------- Hours block menu
# Hours block menu -->
#---------------------------------------------------------------------------------------------------------
<record model="ir.actions.act_window" id="action_all_block_hour"> <record model="ir.actions.act_window" id="action_all_block_hour">
<field name="name">Hours Block</field> <field name="name">Hours Blocks</field>
<field name="res_model">account.hours.block</field> <field name="res_model">account.hours.block</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>

View File

@@ -2,13 +2,12 @@
<openerp> <openerp>
<data> <data>
#--------------------------------------------------------------------- <!--
# Hours block search form Hours block search form
#--------------------------------------------------------------------- -->
<record id="view_account_invoice_filter" model="ir.ui.view"> <record id="view_account_invoice_filter" model="ir.ui.view">
<field name="name">account.hours.block.select</field> <field name="name">account.hours.block.select</field>
<field name="model">account.hours.block</field> <field name="model">account.hours.block</field>
<field name="type">search</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Search Invoice"> <search string="Search Invoice">
<group col="7" colspan="4"> <group col="7" colspan="4">
@@ -28,31 +27,40 @@
<group expand="0" string="Group By..."> <group expand="0" string="Group By...">
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> <filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Invoice State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
</group> </group>
</search> </search>
</field> </field>
</record> </record>
#------------------------------------------------------------------------ <!--
# Hours Block View Hours Block View
#------------------------------------------------------------------------ -->
<record id="hours_block_invoice_form" model="ir.ui.view"> <record id="hours_block_invoice_form" model="ir.ui.view">
<field name="name">account.hours.block.form</field> <field name="name">account.hours.block.form</field>
<field name="model">account.hours.block</field> <field name="model">account.hours.block</field>
<field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Invoice"> <form string="Hours Blocks" version="7.0">
<field name="type" /> <sheet>
<field name="invoice_id" /> <h1>
<field name="invoice_id" placeholder="Choose an invoice..."/>
<label for="type" string="Based on:" class="oe_inline"/>
<field name="type" class="oe_inline"/>
</h1>
<group>
<field name="last_action_date" /> <field name="last_action_date" />
<field name="close_date" /> <field name="close_date" />
<group col="6" colspan="4"> </group>
<separator colspan="6" string="Hours Quantity / Amount"/>
<group>
<separator colspan="4" string="Hours Quantity / Amount"/>
<field name="amount_hours_block" string="Bought"/> <field name="amount_hours_block" string="Bought"/>
<field name="amount_hours_block_done" string="Used"/> <field name="amount_hours_block_done" string="Used"/>
<field name="amount_hours_block_delta" string="Difference"/> <field name="amount_hours_block_delta" string="Difference"/>
</group> </group>
<group>
<separator colspan="4" string="Invoice's related information"/> <separator colspan="4" string="Invoice's related information"/>
<field name="date_invoice"/> <field name="date_invoice"/>
<field name="name"/> <field name="name"/>
@@ -69,6 +77,8 @@
<field name="residual" sum="Residual Amount"/> <field name="residual" sum="Residual Amount"/>
<field name="amount_total" sum="Total Amount"/> <field name="amount_total" sum="Total Amount"/>
<field name="state"/> <field name="state"/>
</group>
</sheet>
</form> </form>
</field> </field>
</record> </record>
@@ -76,7 +86,6 @@
<record model="ir.ui.view" id="invoice_tree_hour_block"> <record model="ir.ui.view" id="invoice_tree_hour_block">
<field name="name">account.hours.block.tree</field> <field name="name">account.hours.block.tree</field>
<field name="model">account.hours.block</field> <field name="model">account.hours.block</field>
<field name="type">tree</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree colors="blue:state in ('draft');black:state in ('proforma','proforma2','open');gray:state in ('cancel')" string="Invoice"> <tree colors="blue:state in ('draft');black:state in ('proforma','proforma2','open');gray:state in ('cancel')" string="Invoice">
<field name="date_invoice"/> <field name="date_invoice"/>
@@ -102,19 +111,18 @@
</record> </record>
#--------------------------------------------------------------------------------------------------------- <!--
# Add related act_window from partner and Hours Block Add related act_window from partner and Hours Block
#--------------------------------------------------------------------------------------------------------- -->
<act_window name="All blocks hours" <act_window name="All blocks hours"
domain="[('partner_id', '=', active_id)]" domain="[('partner_id', '=', active_id)]"
res_model="account.hours.block" res_model="account.hours.block"
src_model="res.partner" src_model="res.partner"
id="act_block_hour_from_partner"/> id="act_block_hour_from_partner"/>
<!--
#--------------------------------------------------------------------------------------------------------- Link to invoice on hours block view
# Link to invoice on hours block view -->
#---------------------------------------------------------------------------------------------------------
<act_window <act_window
domain="[('account_hours_block_ids', '=', active_id)]" domain="[('account_hours_block_ids', '=', active_id)]"
id="act_invoice_from_hours_block" id="act_invoice_from_hours_block"
@@ -124,9 +132,9 @@
view_mode="tree,form" view_mode="tree,form"
view_type="form"/> view_type="form"/>
#--------------------------------------------------------------------------------------------------------- <!--
# Link to analytic lines on hours block view Link to analytic lines on hours block view
#--------------------------------------------------------------------------------------------------------- -->
<act_window <act_window
domain="[('invoice_id.account_hours_block_ids', '=', active_id)]" domain="[('invoice_id.account_hours_block_ids', '=', active_id)]"
id="act_analytic_lines_from_hours_block" id="act_analytic_lines_from_hours_block"
@@ -136,9 +144,9 @@
view_mode="tree,form" view_mode="tree,form"
view_type="form"/> view_type="form"/>
#--------------------------------------------------------------------------------------------------------- <!--
# Link to hours block on invoice view Link to hours block on invoice view
#--------------------------------------------------------------------------------------------------------- -->
<act_window <act_window
domain="[('invoice_id', '=', active_id)]" domain="[('invoice_id', '=', active_id)]"
id="act_hours_block_from_invoice" id="act_hours_block_from_invoice"
@@ -148,6 +156,5 @@
view_mode="tree,form,calendar,graph" view_mode="tree,form,calendar,graph"
view_type="form"/> view_type="form"/>
</data> </data>
</openerp> </openerp>

View File

@@ -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 # This program is free software: you can redistribute it and/or modify
# programmers who take the whole responsability of assessing all potential # it under the terms of the GNU Affero General Public License as
# consequences resulting from its eventual inadequacies and bugs # published by the Free Software Foundation, either version 3 of the
# End users who are looking for a ready-to-use solution with commercial # License, or (at your option) any later version.
# 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, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import hours_block import hours_block

View File

@@ -1,59 +1,47 @@
# -*- 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 # This program is free software: you can redistribute it and/or modify
# programmers who take the whole responsability of assessing all potential # it under the terms of the GNU Affero General Public License as
# consequences resulting from its eventual inadequacies and bugs # published by the Free Software Foundation, either version 3 of the
# End users who are looking for a ready-to-use solution with commercial # License, or (at your option) any later version.
# 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, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software # along with this program. If not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import time import time
from report import report_sxw from openerp.report import report_sxw
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
#import xml.dom.minidom
#import re
class account_hours_block(report_sxw.rml_parse): class account_hours_block(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context=None): 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({ self.localcontext.update({
'time': time, 'time': time,
'format_date': self._get_and_change_date_format_for_swiss, 'date_format': DEFAULT_SERVER_DATE_FORMAT,
'analytic_lines': self._get_analytic_lines, 'analytic_lines': self._get_analytic_lines,
}) })
self.context = context self.context = context
def _get_analytic_lines(self, hours_block): def _get_analytic_lines(self, hours_block):
al_pool = self.pool.get('account.analytic.line') al_pool = self.pool.get('account.analytic.line')
al_ids = al_pool.search(self.cr, self.uid, al_ids = al_pool.search(
[['invoice_id', '=', hours_block.invoice_id.id]], self.cr,
order='date desc') self.uid,
res = al_pool.browse(self.cr, self.uid, al_ids) [('invoice_id', '=', hours_block.invoice_id.id)],
return res order='date desc',
context=self.context)
def _get_and_change_date_format_for_swiss(self, date_to_format): return al_pool.browse(self.cr, self.uid, al_ids, context=self.context)
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) report_sxw.report_sxw('report.account.hours.block', 'account.hours.block', 'addons/analytic_hours_block/report/hours_block.rml', parser=account_hours_block)

View File

@@ -10,7 +10,7 @@
<drawString x="17.7cm" y="28.1cm">Maintenance And Support Summary</drawString> <drawString x="17.7cm" y="28.1cm">Maintenance And Support Summary</drawString>
<setFont name="Helvetica" size="9"/> <setFont name="Helvetica" size="9"/>
<drawString x="1.0cm" y="2cm"> [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]]</drawString> <drawString x="1.0cm" y="2cm"> [[ formatLang(time.strftime(date_format), date=True) ]]</drawString>
<drawString x="17.7cm" y="2cm">Page <pageNumber/></drawString> <drawString x="17.7cm" y="2cm">Page <pageNumber/></drawString>
<lineMode width="0.7"/> <lineMode width="0.7"/>
@@ -165,7 +165,7 @@
<blockTable colWidths="258.0,259.0" style="Table1" repeatRows="1"> <blockTable colWidths="258.0,259.0" style="Table1" repeatRows="1">
<tr> <tr>
<td> <td>
<para style="P12a">Description :</para> <para style="P12a">Description: </para>
</td> </td>
<td> <td>
<para style="P2">[[ o.name ]]</para> <para style="P2">[[ o.name ]]</para>
@@ -173,24 +173,24 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<para style="P12a">Report Date : </para> <para style="P12a">Report Date: </para>
</td> </td>
<td> <td>
<para style="P2">[[ time.strftime("%d.%m.%Y", time.localtime()) ]]</para> <para style="P2">[[ formatLang(time.strftime(date_format), date=True) ]]</para>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<para style="P12a">Invoice Date : </para> <para style="P12a">Invoice Date: </para>
</td> </td>
<td> <td>
<para style="P2">[[ o.date_invoice and format_date(o.date_invoice) or '' ]]</para> <para style="P2">[[ o.date_invoice and formatLang(o.date_invoice, date=True) or '' ]]</para>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<para style="P12a">Amount bought : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para> <para style="P12a">Amount bought: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para>
<para style="P12a">Quantity of hours bought : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para> <para style="P12a">Quantity of hours bought: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para>
</td> </td>
<td> <td>
<para style="P2">[[ o.amount_hours_block ]]</para> <para style="P2">[[ o.amount_hours_block ]]</para>
@@ -198,9 +198,8 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<!-- <para style="P12a">[[ o.type == 'hours' and "Quantity of hours" or "Amount"]] used : </para> --> <para style="P12a">Amount used: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para>
<para style="P12a">Amount used : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para> <para style="P12a">Quantity of hours used: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para>
<para style="P12a">Quantity of hours used : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para>
</td> </td>
<td> <td>
<para style="P2">[[ round(o.amount_hours_block_done, 2) ]]</para> <para style="P2">[[ round(o.amount_hours_block_done, 2) ]]</para>
@@ -208,9 +207,8 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<!-- <para style="P12a">Remaining [[ o.type == 'hours' and "hours" or "amount"]] : </para> --> <para style="P12a">Remaining amount: [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para>
<para style="P12a">Remaining amount : [[ (o.type == 'amount' or removeParentNode('para')) and '' ]]</para> <para style="P12a">Remaining hours: [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para>
<para style="P12a">Remaining hours : [[ (o.type == 'hours' or removeParentNode('para')) and '' ]]</para>
</td> </td>
<td> <td>
@@ -241,21 +239,21 @@
</tr> </tr>
<tr> <tr>
[[ repeatIn(analytic_lines(o),'l') ]] [[ repeatIn(analytic_lines(o), 'l') ]]
<td> <td>
<para style="P2">[[ l.date and format_date(l.date) or '' ]]</para> <para style="P2">[[ l.date if formatLang(l.date, date=True) else '' ]]</para>
</td> </td>
<td> <td>
<para style="P2">[[ l.name or '' ]]</para> <para style="P2">[[ l.name or '' ]]</para>
</td> </td>
<td> <td>
<para style="P2c">[[ round(l.unit_amount or '0.0', 2) ]]</para> <para style="P2c">[[ round(l.unit_amount, 2) or '0.0' ]]</para>
</td> </td>
<td> <td>
<para style="P2c">[[ l.to_invoice.customer_name ]]</para> <para style="P2c">[[ l.to_invoice.customer_name ]]</para>
</td> </td>
<td> <td>
<para style="P2c">[[ round((l.unit_amount and l.to_invoice) and (l.unit_amount - (l.unit_amount * l.to_invoice.factor) / 100 ) or '0.0', 2) ]]</para> <para style="P2c">[[ round((l.unit_amount and l.to_invoice) and (l.unit_amount - (l.unit_amount * l.to_invoice.factor) / 100 ), 2) or '0.0' ]]</para>
</td> </td>
</tr> </tr>
</blockTable> </blockTable>