details: https://code.tryton.org/tryton/commit/230e1cb684b6
branch: default
user: Cédric Krier <[email protected]>
date: Wed May 06 09:04:23 2026 +0200
description:
Add purchase support to project work using depend on register
diffstat:
modules/project_revenue/tryton.cfg | 1 +
modules/project_revenue/work.py | 142 ++++++++++++++++--------------------
2 files changed, 66 insertions(+), 77 deletions(-)
diffs (184 lines):
diff -r 16057e0c1c93 -r 230e1cb684b6 modules/project_revenue/tryton.cfg
--- a/modules/project_revenue/tryton.cfg Thu Jun 18 23:24:06 2026 +0200
+++ b/modules/project_revenue/tryton.cfg Wed May 06 09:04:23 2026 +0200
@@ -20,3 +20,4 @@
[register purchase]
model:
purchase.Line
+ work.Work_Purchase
diff -r 16057e0c1c93 -r 230e1cb684b6 modules/project_revenue/work.py
--- a/modules/project_revenue/work.py Thu Jun 18 23:24:06 2026 +0200
+++ b/modules/project_revenue/work.py Wed May 06 09:04:23 2026 +0200
@@ -47,24 +47,6 @@
'Currency'), 'on_change_with_currency')
@classmethod
- def __setup__(cls):
- pool = Pool()
-
- super().__setup__()
-
- try:
- pool.get('purchase.line')
- except KeyError:
- pass
- else:
- # Add purchase lines if purchase is activated
- cls.purchase_lines = fields.One2Many('purchase.line', 'work',
- 'Purchase Lines', domain=[
- ('purchase.company', '=', Eval('company', -1)),
- ('type', '=', 'line'),
- ])
-
- @classmethod
def get_total(cls, works, names):
result = super().get_total(works, names)
currencies = []
@@ -82,12 +64,8 @@
@classmethod
def _get_cost(cls, works):
costs = defaultdict(Decimal)
-
for work_id, cost in cls._timesheet_cost(works):
costs[work_id] += cost
-
- for work_id, cost in cls._purchase_cost(works):
- costs[work_id] += cost
return costs
@classmethod
@@ -116,11 +94,71 @@
yield from cursor
@classmethod
- def _purchase_cost(cls, works):
- 'Compute direct purchase cost'
- if not hasattr(cls, 'purchase_lines'):
+ def _get_revenue(cls, works):
+ revenues = defaultdict(Decimal)
+ for work in works:
+ if not work.list_price:
+ continue
+ if work.price_list_hour:
+ revenue = work.list_price * Decimal(str(work.effort_hours))
+ else:
+ revenue = work.list_price
+ revenues[work.id] = revenue
+ return revenues
+
+ @fields.depends('company')
+ def on_change_with_currency(self, name=None):
+ return self.company.currency if self.company else None
+
+ @fields.depends('product')
+ def on_change_product(self):
+ pool = Pool()
+ ModelData = pool.get('ir.model.data')
+ Uom = pool.get('product.uom')
+
+ if not self.product:
return
+ list_price = self.product.list_price_used
+ if list_price is not None:
+ if self.price_list_hour:
+ hour_uom = Uom(ModelData.get_id('product', 'uom_hour'))
+ list_price = Uom.compute_price(
+ self.product.default_uom, list_price, hour_uom)
+ list_price = round_price(list_price)
+ self.list_price = list_price
+
+ @property
+ def price_list_hour(self):
+ pool = Pool()
+ ModelData = pool.get('ir.model.data')
+ Category = pool.get('product.uom.category')
+ if not self.product:
+ return
+ time = Category(ModelData.get_id('product', 'uom_cat_time'))
+ return self.product.default_uom_category == time
+
+
+class Work_Purchase(metaclass=PoolMeta):
+ __name__ = 'project.work'
+
+ purchase_lines = fields.One2Many(
+ 'purchase.line', 'work', "Purchase Lines",
+ domain=[
+ ('purchase.company', '=', Eval('company', -1)),
+ ('type', '=', 'line'),
+ ])
+
+ @classmethod
+ def _get_cost(cls, works):
+ costs = super()._get_cost(works)
+ for work_id, cost in cls._purchase_cost(works):
+ costs[work_id] += cost
+ return costs
+
+ @classmethod
+ def _purchase_cost(cls, works):
+ "Compute direct purchase cost"
pool = Pool()
Currency = pool.get('currency.currency')
PurchaseLine = pool.get('purchase.line')
@@ -173,57 +211,7 @@
yield work_id, amount
@classmethod
- def _get_revenue(cls, works):
- revenues = defaultdict(Decimal)
- for work in works:
- if not work.list_price:
- continue
- if work.price_list_hour:
- revenue = work.list_price * Decimal(str(work.effort_hours))
- else:
- revenue = work.list_price
- revenues[work.id] = revenue
- return revenues
-
- @fields.depends('company')
- def on_change_with_currency(self, name=None):
- return self.company.currency if self.company else None
-
- @fields.depends('product')
- def on_change_product(self):
- pool = Pool()
- ModelData = pool.get('ir.model.data')
- Uom = pool.get('product.uom')
-
- if not self.product:
- return
-
- list_price = self.product.list_price_used
- if list_price is not None:
- if self.price_list_hour:
- hour_uom = Uom(ModelData.get_id('product', 'uom_hour'))
- list_price = Uom.compute_price(
- self.product.default_uom, list_price, hour_uom)
- list_price = round_price(list_price)
- self.list_price = list_price
-
- @property
- def price_list_hour(self):
- pool = Pool()
- ModelData = pool.get('ir.model.data')
- Category = pool.get('product.uom.category')
- if not self.product:
- return
- time = Category(ModelData.get_id('product', 'uom_cat_time'))
- return self.product.default_uom_category == time
-
- @classmethod
def copy(cls, records, default=None):
- if default is None:
- default = {}
- else:
- default = default.copy()
- if hasattr(cls, 'purchase_lines'):
- # Do not copy purchase lines if purchase is activated
- default.setdefault('purchase_lines', None)
+ default = default.copy() if default is not None else {}
+ default.setdefault('purchase_lines', None)
return super().copy(records, default=default)