Browse Source

[MIGR] migration of analytic_hours_block to OpenERP version 7.0

pull/2/head
Joel Grand-Guillaume 12 years ago
committed by Guewen Baconnier
parent
commit
caabefc691
  1. 32
      analytic_hours_block/__init__.py
  2. 82
      analytic_hours_block/__openerp__.py
  3. 368
      analytic_hours_block/hours_block.py
  4. 10
      analytic_hours_block/hours_block_menu.xml
  5. 113
      analytic_hours_block/hours_block_view.xml
  6. 34
      analytic_hours_block/report/__init__.py
  7. 60
      analytic_hours_block/report/hours_block.py
  8. 42
      analytic_hours_block/report/hours_block.rml
  9. 2
      analytic_hours_block/security/hours_block_security.xml

32
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 <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import hours_block import hours_block

82
analytic_hours_block/__openerp__.py

@ -1,60 +1,54 @@
# -*- 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 <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
{ {
"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. 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", "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, "active": False,
"installable": False
"installable": True
} }

368
analytic_hours_block/hours_block.py

@ -1,120 +1,93 @@
# -*- 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 <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import time
import string
from osv import osv, fields
import netsvc
from openerp.osv import orm, fields
############################################################################
## Add hours blocks on invoice
############################################################################
class AccountHoursBlock(osv.osv):
class AccountHoursBlock(orm.Model):
_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"""
context = context or {}
""" Return the last analytic line date for an invoice"""
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:
date = fetch_res[0]
else:
date = False
res[block.id] = date
res[block.id] = fetch_res[0] if fetch_res else False
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 not isinstance(ids, list):
ids=[ids]
if isinstance(ids, (int, long)):
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):
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 # 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"
" 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 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
hours_used += ((line.unit_amount / factor) * factor_invoicing) hours_used += ((line.unit_amount / factor) * factor_invoicing)
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):
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 amount bought # Compute amount bought
for line in block.invoice_id.invoice_line: for line in block.invoice_id.invoice_line:
@ -135,95 +108,218 @@ 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()
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 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
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 total_amount += amount * line.unit_amount * factor_invoicing
result[block.id]['amount_hours_block_done'] += total_amount result[block.id]['amount_hours_block_done'] += total_amount
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[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: for block_type in block_per_types:
if block_type: 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: 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,
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=_recompute_triggers,
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=_recompute_triggers,
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=_recompute_triggers,
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'), '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 # 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",
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',
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' _inherit = 'account.invoice'
_columns = { _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()

10
analytic_hours_block/hours_block_menu.xml

@ -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>

113
analytic_hours_block/hours_block_view.xml

@ -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,67 +27,77 @@
<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">
<field name="type" />
<field name="invoice_id" />
<field name="last_action_date" />
<field name="close_date" />
<group col="6" colspan="4">
<separator colspan="6" string="Hours Quantity / Amount"/>
<form string="Hours Blocks" version="7.0">
<sheet>
<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="close_date" />
</group>
<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>
<separator colspan="4" string="Invoice's related information"/>
<field name="date_invoice"/>
<field name="name"/>
<field name="number"/>
<field name="partner_id" groups="base.group_user"/>
<field name="user_id"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="journal_id" invisible="1"/>
<field name="period_id" invisible="1" groups="account.group_account_user"/>
<field name="currency_id"/>
<newline/>
<field name="residual" sum="Residual Amount"/>
<field name="amount_total" sum="Total Amount"/>
<field name="state"/>
<group>
<separator colspan="4" string="Invoice's related information"/>
<field name="date_invoice"/>
<field name="name"/>
<field name="number"/>
<field name="partner_id" groups="base.group_user"/>
<field name="user_id"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="journal_id" invisible="1"/>
<field name="period_id" invisible="1" groups="account.group_account_user"/>
<field name="currency_id"/>
<newline/>
<field name="residual" sum="Residual Amount"/>
<field name="amount_total" sum="Total Amount"/>
<field name="state"/>
</group>
</sheet>
</form> </form>
</field> </field>
</record> </record>
<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"/>
<field name="partner_id" groups="base.group_user"/> <field name="partner_id" groups="base.group_user"/>
<field name="name"/> <field name="name"/>
<field name="amount_hours_block" sum="Quantity of hours bought"/> <field name="amount_hours_block" sum="Quantity of hours bought"/>
<field name="amount_hours_block_done" sum="Quantity of hours used" /> <field name="amount_hours_block_done" sum="Quantity of hours used" />
<field name="amount_hours_block_delta" sum="Quantity of hours difference"/> <field name="amount_hours_block_delta" sum="Quantity of hours difference"/>
<field name="last_action_date" /> <field name="last_action_date" />
<field name="close_date" /> <field name="close_date" />
<field name="journal_id" invisible="1"/> <field name="journal_id" invisible="1"/>
<field name="period_id" invisible="1" groups="account.group_account_user"/> <field name="period_id" invisible="1" groups="account.group_account_user"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/> <field name="company_id" groups="base.group_multi_company" widget="selection"/>
@ -100,21 +109,20 @@
</tree> </tree>
</field> </field>
</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>

34
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 <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import hours_block
import hours_block

60
analytic_hours_block/report/hours_block.py

@ -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
# 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 <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import time import time
from report import report_sxw
#import xml.dom.minidom
#import re
from openerp.report import report_sxw
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
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,
[['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) report_sxw.report_sxw('report.account.hours.block', 'account.hours.block', 'addons/analytic_hours_block/report/hours_block.rml', parser=account_hours_block)

42
analytic_hours_block/report/hours_block.rml

@ -10,13 +10,13 @@
<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"/>
<lines>0.6cm 27.9cm 20.3cm 27.9cm</lines> <lines>0.6cm 27.9cm 20.3cm 27.9cm</lines>
<setFont name="Helvetica" size="8"/> <setFont name="Helvetica" size="8"/>
</pageGraphics>
</pageGraphics>
</pageTemplate> </pageTemplate>
</template> </template>
@ -35,7 +35,7 @@
<blockBackground colorName="#e6e6e6" start="0,1" stop="0,-1"/> <blockBackground colorName="#e6e6e6" start="0,1" stop="0,-1"/>
<blockBackground colorName="#e6e6e6" start="1,1" stop="1,-1"/> <blockBackground colorName="#e6e6e6" start="1,1" stop="1,-1"/>
<blockBackground colorName="#e6e6e6" start="2,1" stop="2,-1"/> <blockBackground colorName="#e6e6e6" start="2,1" stop="2,-1"/>
</blockTableStyle> </blockTableStyle>
<blockTableStyle id="Table6"> <blockTableStyle id="Table6">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
@ -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">Quantity of hours bought : [[ (o.type == 'hours' 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>
</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">Quantity of hours used : [[ (o.type == 'hours' 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>
</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,10 +207,9 @@
</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 hours : [[ (o.type == 'hours' 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>
</td> </td>
<td> <td>
<para style="P2">[[ o.amount_hours_block and round(o.amount_hours_block_delta, 2) or '' ]]</para> <para style="P2">[[ o.amount_hours_block and round(o.amount_hours_block_delta, 2) or '' ]]</para>
@ -239,23 +237,23 @@
<para style="P12a">Deduced</para> <para style="P12a">Deduced</para>
</td> </td>
</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>

2
analytic_hours_block/security/hours_block_security.xml

@ -7,5 +7,5 @@
<field eval="True" name="global"/> <field eval="True" name="global"/>
<field name="domain_force">[]</field> <field name="domain_force">[]</field>
</record> </record>
</data></openerp> </data></openerp>
Loading…
Cancel
Save