Files
tryton/work.py
T

201 lines
7.3 KiB
Python
Raw Normal View History

2014-12-02 12:55:45 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2010-08-19 22:17:42 +02:00
from decimal import Decimal
2015-09-24 16:08:08 +02:00
from collections import defaultdict
2015-09-22 19:27:26 +02:00
from sql.aggregate import Sum
2015-09-24 16:08:08 +02:00
from sql.operators import Concat
2013-09-20 17:34:12 +02:00
2012-09-24 22:18:08 +02:00
from trytond.model import fields
2013-05-27 17:55:27 +02:00
from trytond.pyson import Eval, Id
2010-08-19 22:17:42 +02:00
from trytond.transaction import Transaction
2012-09-24 22:18:08 +02:00
from trytond.pool import Pool, PoolMeta
2014-07-17 18:59:36 +02:00
from trytond.tools import reduce_ids, grouped_slice
2012-09-24 22:18:08 +02:00
2015-10-01 22:27:18 +02:00
from trytond.modules.product import price_digits
__all__ = ['Work']
2010-08-19 22:17:42 +02:00
2009-06-04 11:19:01 +02:00
2012-09-24 22:18:08 +02:00
class Work:
2016-03-01 21:49:37 +01:00
__metaclass__ = PoolMeta
2012-09-24 22:18:08 +02:00
__name__ = 'project.work'
2009-06-04 11:19:01 +02:00
product = fields.Many2One('product.product', 'Product',
2013-05-27 17:55:27 +02:00
domain=[
('type', '=', 'service'),
('default_uom_category', '=', Id('product', 'uom_cat_time')),
])
2015-10-01 22:27:18 +02:00
list_price = fields.Numeric('List Price', digits=price_digits)
2011-08-21 11:20:49 +02:00
revenue = fields.Function(fields.Numeric('Revenue',
digits=(16, Eval('currency_digits', 2)),
2015-09-17 11:19:59 +02:00
depends=['currency_digits']), 'get_total')
2010-03-11 16:52:56 +01:00
cost = fields.Function(fields.Numeric('Cost',
digits=(16, Eval('currency_digits', 2)),
2015-09-17 11:19:59 +02:00
depends=['currency_digits']), 'get_total')
2014-01-26 14:29:27 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'on_change_with_currency_digits')
2009-06-04 11:19:01 +02:00
2015-09-24 16:08:08 +02:00
@classmethod
def __setup__(cls):
pool = Pool()
super(Work, cls).__setup__()
try:
pool.get('purchase.line')
except KeyError:
pass
else:
# Add purchase lines if purchase is installed
cls.purchase_lines = fields.One2Many('purchase.line', 'work',
'Purchase Lines', domain=[
('purchase.company', '=', Eval('company', -1)),
],
depends=['company'])
2012-09-24 22:18:08 +02:00
@classmethod
2015-09-17 11:19:59 +02:00
def _get_cost(cls, works):
2013-09-20 17:34:12 +02:00
pool = Pool()
Line = pool.get('timesheet.line')
Work = pool.get('timesheet.work')
transaction = Transaction()
cursor = transaction.connection.cursor()
2013-09-20 17:34:12 +02:00
costs = dict.fromkeys([w.id for w in works], 0)
2012-10-17 16:30:06 +02:00
2015-09-17 09:31:09 +02:00
table = cls.__table__()
work = Work.__table__()
2013-09-20 17:34:12 +02:00
line = Line.__table__()
2015-09-24 16:08:08 +02:00
# Timesheet cost
2015-09-17 09:31:09 +02:00
work_ids = [w.id for w in works]
for sub_ids in grouped_slice(work_ids):
red_sql = reduce_ids(table.id, sub_ids)
2015-09-17 11:19:59 +02:00
cursor.execute(*table.join(work,
2016-06-24 09:31:08 +02:00
condition=(
Concat(cls.__name__ + ',', table.id) == work.origin)
2015-09-17 09:31:09 +02:00
).join(line, condition=line.work == work.id
2015-10-01 22:27:18 +02:00
).select(table.id, Sum(line.cost_price * line.duration),
2013-09-20 17:34:12 +02:00
where=red_sql,
2015-10-01 22:27:18 +02:00
group_by=[table.id]))
for work_id, cost in cursor.fetchall():
2015-09-22 19:27:26 +02:00
# SQLite stores timedelta as float
2015-10-01 22:27:18 +02:00
if not isinstance(cost, float):
cost = cost.total_seconds()
# Convert from seconds
cost /= 60 * 60
costs[work_id] += Decimal(str(cost))
2015-09-24 16:08:08 +02:00
# Purchase cost
if hasattr(cls, 'purchase_lines'):
for work_id, cost in cls._purchase_cost(works).iteritems():
costs[work_id] += cost
2014-12-28 16:12:40 +01:00
for work in works:
costs[work.id] = work.company.currency.round(costs[work.id])
2013-09-20 17:34:12 +02:00
return costs
2009-06-04 11:19:01 +02:00
2015-09-24 16:08:08 +02:00
@classmethod
def _purchase_cost(cls, works):
'Compute direct purchase cost'
pool = Pool()
Currency = pool.get('currency.currency')
PurchaseLine = pool.get('purchase.line')
InvoiceLine = pool.get('account.invoice.line')
Invoice = pool.get('account.invoice')
Company = pool.get('company.company')
cursor = Transaction().connection.cursor()
2015-09-24 16:08:08 +02:00
table = cls.__table__()
purchase_line = PurchaseLine.__table__()
invoice_line = InvoiceLine.__table__()
invoice = Invoice.__table__()
company = Company.__table__()
amounts = defaultdict(Decimal)
work_ids = [w.id for w in works]
work2currency = {}
iline2work = {}
for sub_ids in grouped_slice(work_ids):
where = reduce_ids(table.id, sub_ids)
cursor.execute(*table.join(purchase_line,
condition=purchase_line.work == table.id
).join(invoice_line,
condition=invoice_line.origin == Concat(
'purchase.line,', purchase_line.id)
).join(invoice,
condition=invoice_line.invoice == invoice.id
).select(invoice_line.id, table.id,
where=where & ~invoice.state.in_(['draft', 'cancel'])))
iline2work.update(cursor.fetchall())
cursor.execute(*table.join(company,
condition=table.company == company.id
).select(table.id, company.currency,
where=where))
work2currency.update(cursor.fetchall())
currencies = Currency.browse(set(work2currency.itervalues()))
id2currency = {c.id: c for c in currencies}
invoice_lines = InvoiceLine.browse(iline2work.keys())
for invoice_line in invoice_lines:
invoice = invoice_line.invoice
work_id = iline2work[invoice_line.id]
currency_id = work2currency[work_id]
currency = id2currency[currency_id]
if currency != invoice.currency:
with Transaction().set_context(date=invoice.currency_date):
amount = Currency.compute(invoice.currency,
invoice_line.amount, currency)
else:
amount = invoice_line.amount
amounts[work_id] += amount
return amounts
2012-09-24 22:18:08 +02:00
@classmethod
2015-09-17 11:19:59 +02:00
def _get_revenue(cls, works):
return {w.id: (w.list_price * Decimal(str(w.effort_hours))
if w.list_price else Decimal(0))
for w in works}
2009-06-04 11:19:01 +02:00
2014-01-26 14:29:27 +01:00
@fields.depends('company')
2012-09-24 22:18:08 +02:00
def on_change_with_currency_digits(self, name=None):
if self.company:
return self.company.currency.digits
2009-06-04 13:29:31 +02:00
return 2
2012-09-24 22:18:08 +02:00
@staticmethod
def default_currency_digits():
Company = Pool().get('company.company')
2010-08-19 22:17:42 +02:00
company = Transaction().context.get('company')
if company:
2012-09-24 22:18:08 +02:00
company = Company(company)
2009-06-04 13:29:31 +02:00
return company.currency.digits
return 2
2015-10-01 22:27:18 +02:00
@fields.depends('product', 'company')
2012-09-24 22:18:08 +02:00
def on_change_product(self):
2011-07-03 13:19:08 +02:00
pool = Pool()
2012-09-24 22:18:08 +02:00
User = pool.get('res.user')
ModelData = pool.get('ir.model.data')
Uom = pool.get('product.uom')
Currency = pool.get('currency.currency')
2009-06-04 11:19:01 +02:00
2012-09-24 22:18:08 +02:00
if not self.product:
2014-10-29 20:40:07 +01:00
return
2009-06-04 11:19:01 +02:00
2012-09-24 22:18:08 +02:00
hour_uom = Uom(ModelData.get_id('product', 'uom_hour'))
2015-10-01 22:27:18 +02:00
self.list_price = Uom.compute_price(self.product.default_uom,
self.product.list_price, hour_uom)
2009-06-04 11:19:01 +02:00
2012-09-24 22:18:08 +02:00
if self.company:
user = User(Transaction().user)
if user.company != self.company:
if user.company.currency != self.company.currency:
2014-10-29 20:40:07 +01:00
self.list_price = Currency.compute(user.company.currency,
2015-10-01 22:27:18 +02:00
self.list_price, self.company.currency, round=False)
digits = self.__class__.list_price.digits
self.list_price = self.list_price.quantize(
Decimal(str(10.0 ** -digits[1])))