diff --git a/analytic_hours_block/__openerp__.py b/analytic_hours_block/__openerp__.py
index 4a0b8f45..09c78ad7 100644
--- a/analytic_hours_block/__openerp__.py
+++ b/analytic_hours_block/__openerp__.py
@@ -20,31 +20,35 @@
##############################################################################
{
- "name" : "Project Hours Blocks Management",
- "description" : """
+ "name": "Project Hours Blocks Management",
+ "description": """
+Project Hours Blocks Management
+===============================
-This module allows you to handle hours blocks, to follow for example the user support contracts.
-This means, you sell a product of type "hours block" then you input the spent hours on the hours block and
+This module allows you to handle hours blocks,
+to follow for example the user support contracts.
+This means, you sell a product of type "hours block"
+then you input the spent hours on the hours block and
you can track and follow how much has been used.
""",
- "version" : "1.2",
- "author" : "Camptocamp",
- "category" : "Generic Modules/Projects & Services",
+ "version": "1.3",
+ "author": "Camptocamp",
+ "license": 'AGPL-3',
+ "category": "Generic Modules/Projects & Services",
"website": "http://www.camptocamp.com",
- "depends" : [
- "account",
- "hr_timesheet_invoice",
- "analytic"
- ],
- "init_xml" : [],
- "update_xml" : [
- "hours_block_view.xml",
- "hours_block_menu.xml",
- "report.xml",
- "security/hours_block_security.xml",
- "security/ir.model.access.csv",
- ],
+ "depends": [
+ "account",
+ "hr_timesheet_invoice",
+ "analytic"
+ ],
+ "data": [
+ "hours_block_view.xml",
+ "hours_block_menu.xml",
+ "report.xml",
+ "security/hours_block_security.xml",
+ "security/ir.model.access.csv",
+ ],
"active": False,
"installable": True
}
diff --git a/analytic_hours_block/hours_block.py b/analytic_hours_block/hours_block.py
index e191f3ab..256c2baf 100644
--- a/analytic_hours_block/hours_block.py
+++ b/analytic_hours_block/hours_block.py
@@ -19,92 +19,75 @@
#
##############################################################################
-import time
-import string
+from openerp.osv import orm, fields
-from osv import osv, fields
-import netsvc
-
-############################################################################
-## Add hours blocks on invoice
-############################################################################
-
-class AccountHoursBlock(osv.osv):
+class AccountHoursBlock(orm.Model):
_name = "account.hours.block"
def _get_last_action(self, cr, uid, ids, name, arg, context=None):
- """TODO"""
- context = context or {}
+ """ Return the last analytic line date for an invoice"""
res = {}
- for block in self.browse(cr, uid, ids):
+ for block in self.browse(cr, uid, ids, context=context):
cr.execute("SELECT max(al.date) FROM account_analytic_line AS al"
" WHERE al.invoice_id = %s", (block.invoice_id.id,))
fetch_res = cr.fetchone()
- if fetch_res:
- date = fetch_res[0]
- else:
- date = False
- res[block.id] = date
+ res[block.id] = fetch_res[0] if fetch_res else False
return res
def _compute_hours(self, cr, uid, ids, fields, args, context=None):
"""Return a dict of [id][fields]"""
- context = context or {}
- if not isinstance(ids, list):
- ids=[ids]
+ if isinstance(ids, (int, long)):
+ ids = [ids]
result = {}
aal_obj = self.pool.get('account.analytic.line')
- for block in self.browse(cr,uid,ids):
- result[block.id] = {'amount_hours_block' : 0.0,
- 'amount_hours_block_done' : 0.0,
- 'amount_hours_block_delta' : 0.0}
+ for block in self.browse(cr, uid, ids, context=context):
+ result[block.id] = {'amount_hours_block': 0.0,
+ 'amount_hours_block_done': 0.0}
# Compute hours bought
for line in block.invoice_id.invoice_line:
hours_bought = 0.0
if line.product_id:
- ## We will now calculate the product_quantity
+ # We will now calculate the product_quantity
factor = line.uos_id.factor
if factor == 0.0:
factor = 1.0
amount = line.quantity
hours_bought += (amount / factor)
result[block.id]['amount_hours_block'] += hours_bought
+
# Compute hours spent
hours_used = 0.0
- # Get ids of analytic line generated from timesheet associated to current block
+ # Get ids of analytic line generated from
+ # timesheet associated to the current block
cr.execute("SELECT al.id "
- " FROM account_analytic_line AS al,account_analytic_journal AS aj"
- " WHERE aj.id = al.journal_id AND"
- " aj.type='general' AND"
- " al.invoice_id = %s", (block.invoice_id.id,))
- res2 = cr.fetchall()
- if res2:
- ids2 = [x[0] for x in res2]
- else:
- ids2 = []
- for line in aal_obj.browse(cr, uid, ids2, context):
- if line.product_uom_id:
+ "FROM account_analytic_line AS al, "
+ " account_analytic_journal AS aj "
+ "WHERE aj.id = al.journal_id "
+ "AND aj.type = 'general' "
+ "AND al.invoice_id = %s", (block.invoice_id.id,))
+ res_line_ids = cr.fetchall()
+ line_ids = [l[0] for l in res_line_ids] if res_line_ids else []
+ for line in aal_obj.browse(cr, uid, line_ids, context=context):
+ factor = 1.0
+ if line.product_uom_id and line.product_uom_id.factor != 0.0:
factor = line.product_uom_id.factor
- if factor == 0.0:
- factor = 1.0
- else:
- factor = 1.0
factor_invoicing = 1.0
if line.to_invoice and line.to_invoice.factor != 0.0:
- factor_invoicing = 1.0 - line.to_invoice.factor / 100
+ factor_invoicing = 1.0 - line.to_invoice.factor / 100
hours_used += ((line.unit_amount / factor) * factor_invoicing)
result[block.id]['amount_hours_block_done'] = hours_used
return result
- def _compute_amount(self, cr, uid, ids, fields, args, context):
+ def _compute_amount(self, cr, uid, ids, fields, args, context=None):
+ if context is None:
+ context = {}
result = {}
aal_obj = self.pool.get('account.analytic.line')
pricelist_obj = self.pool.get('product.pricelist')
- for block in self.browse(cr,uid,ids):
+ for block in self.browse(cr, uid, ids, context=context):
result[block.id] = {'amount_hours_block' : 0.0,
- 'amount_hours_block_done' : 0.0,
- 'amount_hours_block_delta' : 0.0}
+ 'amount_hours_block_done' : 0.0}
# Compute amount bought
for line in block.invoice_id.invoice_line:
@@ -125,95 +108,193 @@ class AccountHoursBlock(osv.osv):
" WHERE aj.id = al.journal_id"
" AND aj.type='general'"
" AND al.invoice_id = %s", (block.invoice_id.id,))
- res2 = cr.fetchall()
- if res2:
- ids2 = [x[0] for x in res2]
- else:
- ids2 = []
+ res_line_ids = cr.fetchall()
+ line_ids = [l[0] for l in res_line_ids] if res_line_ids else []
total_amount = 0.0
- for line in aal_obj.browse(cr, uid, ids2, context):
+ for line in aal_obj.browse(cr, uid, line_ids, context=context):
factor_invoicing = 1.0
if line.to_invoice and line.to_invoice.factor != 0.0:
- factor_invoicing = 1.0 - line.to_invoice.factor / 100
-
- ctx = {'uom': line.product_uom_id.id}
- amount = pricelist_obj.price_get(cr, uid,
- [line.account_id.pricelist_id.id],
- line.product_id.id,
- line.unit_amount or 1.0,
- line.account_id.partner_id.id or False,
- ctx)[line.account_id.pricelist_id.id]
+ factor_invoicing = 1.0 - line.to_invoice.factor / 100
+
+ ctx = dict(context, uom=line.product_uom_id.id)
+ amount = pricelist_obj.price_get(
+ cr, uid,
+ [line.account_id.pricelist_id.id],
+ line.product_id.id,
+ line.unit_amount or 1.0,
+ line.account_id.partner_id.id or False,
+ ctx)[line.account_id.pricelist_id.id]
total_amount += amount * line.unit_amount * factor_invoicing
result[block.id]['amount_hours_block_done'] += total_amount
return result
- def _compute(self, cr, uid, ids, fields, args, context):
+ def _compute(self, cr, uid, ids, fields, args, context=None):
result = {}
block_per_types = {}
for block in self.browse(cr, uid, ids, context=context):
- if not block.type in block_per_types.keys():
- block_per_types[block.type] = []
- block_per_types[block.type].append(block.id)
-
+ block_per_types.setdefault(block.type, []).append(block.id)
+
for block_type in block_per_types:
if block_type:
- func = getattr(self, "_compute_%s" % (block_type,))
- result.update(func(cr, uid, ids, fields, args, context))
+ func = getattr(self, "_compute_%s" % block_type)
+ result.update(func(cr, uid, ids, fields, args, context=context))
for block in result:
result[block]['amount_hours_block_delta'] = \
- result[block]['amount_hours_block'] - result[block]['amount_hours_block_done']
+ result[block]['amount_hours_block'] - \
+ result[block]['amount_hours_block_done']
return result
_columns = {
- 'amount_hours_block': fields.function(_compute, method=True, type='float', string='Quantity /Amount bought', store=True,
- multi='amount_hours_block_delta',
- help="Amount bought by the customer. This amount is expressed in the base UoM (factor=1.0)"),
- 'amount_hours_block_done': fields.function(_compute, method=True, type='float', string='Quantity / Amount used', store=True,
- multi='amount_hours_block_delta',
- help="Amount done by the staff. This amount is expressed in the base UoM (factor=1.0)"),
- 'amount_hours_block_delta': fields.function(_compute, method=True, type='float', string='Difference', store=True,
- multi='amount_hours_block_delta',
- help="Difference between bought and used. This amount is expressed in the base UoM (factor=1.0)"),
- 'last_action_date': fields.function(_get_last_action, method=True, type='date', string='Last action date',
- help="Date of the last analytic line linked to the invoice related to this block hours."),
+ 'amount_hours_block': fields.function(
+ _compute,
+ type='float',
+ string='Quantity / Amount bought',
+ store=True,
+ multi='amount_hours_block_delta',
+ help="Amount bought by the customer. "
+ "This amount is expressed in the base Unit of Measure "
+ "(factor=1.0)"),
+ 'amount_hours_block_done': fields.function(
+ _compute,
+ type='float',
+ string='Quantity / Amount used',
+ store=True,
+ multi='amount_hours_block_delta',
+ help="Amount done by the staff. "
+ "This amount is expressed in the base Unit of Measure "
+ "(factor=1.0)"),
+ 'amount_hours_block_delta': fields.function(
+ _compute,
+ type='float',
+ string='Difference',
+ store=True,
+ multi='amount_hours_block_delta',
+ help="Difference between bought and used. "
+ "This amount is expressed in the base Unit of Measure "
+ "(factor=1.0)"),
+ 'last_action_date': fields.function(
+ _get_last_action,
+ type='date',
+ string='Last action date',
+ help="Date of the last analytic line linked to the invoice "
+ "related to this block hours."),
'close_date': fields.date('Closed Date'),
- 'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete='cascade', required=True),
- 'type': fields.selection([('hours','Hours'), ('amount','Amount')], 'Type of Block',
- required=True, help="Choose if you want a time or amount base block."),
+ 'invoice_id': fields.many2one(
+ 'account.invoice',
+ 'Invoice',
+ ondelete='cascade',
+ required=True),
+ 'type': fields.selection(
+ [('hours','Hours'),
+ ('amount','Amount')],
+ string='Type of Block',
+ required=True,
+ help="The block is based on the quantity of hours "
+ "or on the amount."),
+
# Invoices related infos
- 'date_invoice': fields.related('invoice_id', 'date_invoice', type="date", string="Invoice Date", store=True, readonly=True),
- 'user_id': fields.related('invoice_id', 'user_id', type="many2one", relation="res.users", string="Salesman", store=True, readonly=True),
- 'partner_id': fields.related('invoice_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True, readonly=True),
- 'name': fields.related('invoice_id', 'name', type="char",size=64, string="Description", store=True,readonly=True),
- 'number': fields.related('invoice_id', 'number', type="char",size=64, string="Number", store=True,readonly=True),
- 'journal_id': fields.related('invoice_id', 'journal_id', type="many2one", relation="account.journal", string="Journal", store=True,readonly=True),
- 'period_id': fields.related('invoice_id', 'period_id', type="many2one", relation="account.period", string="Period", store=True,readonly=True),
- 'company_id': fields.related('invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", store=True,readonly=True),
- 'currency_id': fields.related('invoice_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", store=True,readonly=True),
- 'residual': fields.related('invoice_id', 'residual', type="float", string="Residual", store=True,readonly=True),
- 'amount_total': fields.related('invoice_id', 'amount_total', type="float", string="Total", store=True,readonly=True),
- 'state':fields.related('invoice_id','state',
- type='selection',
- selection=[
- ('draft','Draft'),
- ('proforma','Pro-forma'),
- ('proforma2','Pro-forma'),
- ('open','Open'),
- ('paid','Paid'),
- ('cancel','Cancelled')
- ],
- string='State', readonly=True, store=True),
+ 'date_invoice': fields.related(
+ 'invoice_id', 'date_invoice',
+ type="date",
+ string="Invoice Date",
+ store=True,
+ readonly=True),
+ 'user_id': fields.related(
+ 'invoice_id', 'user_id',
+ type="many2one",
+ relation="res.users",
+ string="Salesman",
+ store=True,
+ readonly=True),
+ 'partner_id': fields.related(
+ 'invoice_id', 'partner_id',
+ type="many2one",
+ relation="res.partner",
+ string="Partner",
+ store=True,
+ readonly=True),
+ 'name': fields.related(
+ 'invoice_id', 'name',
+ type="char",
+ size=64,
+ string="Description",
+ store=True,
+ readonly=True),
+ 'number': fields.related(
+ 'invoice_id', 'number',
+ type="char",
+ size=64,
+ string="Number",
+ store=True,
+ readonly=True),
+ 'journal_id': fields.related(
+ 'invoice_id', 'journal_id',
+ type="many2one",
+ relation="account.journal",
+ string="Journal",
+ store=True,
+ readonly=True),
+ 'period_id': fields.related(
+ 'invoice_id', 'period_id',
+ type="many2one",
+ relation="account.period",
+ string="Period",
+ store=True,
+ readonly=True),
+ 'company_id': fields.related(
+ 'invoice_id', 'company_id',
+ type="many2one",
+ relation="res.company",
+ string="Company",
+ store=True,
+ readonly=True),
+ 'currency_id': fields.related(
+ 'invoice_id', 'currency_id',
+ type="many2one",
+ relation="res.currency",
+ string="Currency",
+ store=True,
+ readonly=True),
+ 'residual': fields.related(
+ 'invoice_id', 'residual',
+ type="float",
+ string="Residual",
+ store=True,
+ readonly=True),
+ 'amount_total': fields.related(
+ 'invoice_id', 'amount_total',
+ type="float",
+ string="Total",
+ store=True,
+ readonly=True),
+ 'state':fields.related(
+ 'invoice_id','state',
+ type='selection',
+ selection=[
+ ('draft','Draft'),
+ ('proforma','Pro-forma'),
+ ('proforma2','Pro-forma'),
+ ('open','Open'),
+ ('paid','Paid'),
+ ('cancel','Cancelled'),
+ ],
+ string='State',
+ readonly=True,
+ store=True),
}
-AccountHoursBlock()
-
-class AccountInvoice(osv.osv):
+############################################################################
+## Add hours blocks on invoice
+############################################################################
+class AccountInvoice(orm.Model):
_inherit = 'account.invoice'
+
_columns = {
- 'account_hours_block_ids': fields.one2many('account.hours.block', 'invoice_id', 'Hours Block')
+ 'account_hours_block_ids': fields.one2many(
+ 'account.hours.block',
+ 'invoice_id',
+ string='Hours Block')
}
-
-AccountInvoice()
diff --git a/analytic_hours_block/report/hours_block.py b/analytic_hours_block/report/hours_block.py
index ad459fd2..906c694b 100644
--- a/analytic_hours_block/report/hours_block.py
+++ b/analytic_hours_block/report/hours_block.py
@@ -20,31 +20,26 @@
##############################################################################
import time
-from report import report_sxw
+from openerp.report import report_sxw
class account_hours_block(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context=None):
- super(account_hours_block, self).__init__(cr, uid, name, context)
+ super(account_hours_block, self).__init__(cr, uid, name, context=context)
self.localcontext.update({
'time': time,
- 'format_date': self._get_and_change_date_format_for_swiss,
'analytic_lines': self._get_analytic_lines,
})
self.context = context
def _get_analytic_lines(self, hours_block):
al_pool = self.pool.get('account.analytic.line')
- al_ids = al_pool.search(self.cr, self.uid,
- [['invoice_id', '=', hours_block.invoice_id.id]],
- order='date desc')
- res = al_pool.browse(self.cr, self.uid, al_ids)
- return res
-
- def _get_and_change_date_format_for_swiss(self, date_to_format):
- date_formatted = ''
- if date_to_format:
- date_formatted = strptime(date_to_format, '%Y-%m-%d').strftime('%d.%m.%Y')
- return date_formatted
+ al_ids = al_pool.search(
+ self.cr,
+ self.uid,
+ [('invoice_id', '=', hours_block.invoice_id.id)],
+ order='date desc',
+ context=self.context)
+ return al_pool.browse(self.cr, self.uid, al_ids, context=self.context)
report_sxw.report_sxw('report.account.hours.block', 'account.hours.block', 'addons/analytic_hours_block/report/hours_block.rml', parser=account_hours_block)
diff --git a/analytic_hours_block/report/hours_block.rml b/analytic_hours_block/report/hours_block.rml
index 8fe879a8..1b66efa9 100644
--- a/analytic_hours_block/report/hours_block.rml
+++ b/analytic_hours_block/report/hours_block.rml
@@ -184,7 +184,7 @@