trytond-project_invoice_exp.../work.py

214 lines
6.9 KiB
Python

from decimal import Decimal
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Bool
from trytond.modules.product import price_digits
class Expense(ModelSQL, ModelView):
'Projecte Expense'
__name__ = 'project.expense'
_states = {
'readonly': Bool(Eval('invoice_line')),
}
_depends = ['invoice_line']
work = fields.Many2One('project.work', 'Work', required=True,
states=_states, depends=_depends)
origin = fields.Reference('Origin', selection='get_origin', readonly=True)
product = fields.Many2One('product.product', 'Product', required=True,
states=_states, depends=_depends)
uom = fields.Many2One('product.uom', 'UoM', required=True, states=_states,
depends=_depends)
uom_category = fields.Function(fields.Many2One('product.uom.category',
'UoM Category'), 'on_change_with_uom_category')
uom_digits = fields.Function(fields.Integer('UoM Digits'),
'on_change_with_uom_digits')
cost_price = fields.Numeric('Cost Price', digits=price_digits,
states=_states, depends=_depends)
# TODO: Show field and apply price list
unit_price = fields.Numeric('Unit Price', digits=price_digits,
states=_states, depends=_depends)
# TODO: Add currency digits
#amount = fields.Function(fields.Numeric('Amount'), '')
quantity = fields.Float('Quantity', digits=(16, Eval('uom_digits', 2)),
required=True, states=_states, depends=_depends + ['uom_digits'])
description = fields.Char('Description', states=_states, depends=_depends)
invoiceable = fields.Selection([
(None, ''),
('yes', 'Yes'),
('no', 'No'),
], 'Invoiceable', states=_states, depends=_depends)
invoice_line = fields.Many2One('account.invoice.line', 'Invoice Line',
readonly=True)
del _states
# TODO: Add expense cost to the task/project
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
pool = Pool()
res = []
for model in ('purchase.line', 'stock.move'):
try:
pool.get(model)
res.append(model)
except KeyError:
pass
return res
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
models = cls._get_origin()
models = Model.search([
('model', 'in', models),
])
return [(None, '')] + [(m.model, m.name) for m in models]
@fields.depends('product')
def on_change_product(self):
if not self.product:
return
self.uom = self.product.default_uom
@fields.depends('product')
def on_change_with_uom_category(self, name=None):
if not self.product:
return
return self.product.uom.category.id
@fields.depends('uom')
def on_change_with_uom_digits(self, name=None):
if self.uom:
return self.uom.digits
return 2
def _get_invoice_lines(self):
pool = Pool()
try:
AnalyticAccountEntry = pool.get('analytic.account.entry')
except KeyError:
AnalyticAccountEntry = None
if not self.invoiceable == 'yes' or not self.quantity:
# TODO: Raise a UserError if invoiceable is empty
return []
line = {
'product': self.product,
'quantity': self.quantity,
'unit': self.uom,
'unit_price': self.unit_price or Decimal(0),
'origins': [self],
'description': self.description,
}
if AnalyticAccountEntry:
if self.work.analytic_accounts:
new_entries = AnalyticAccountEntry.copy(
self.work.analytic_accounts,
default={
'origin': None,
})
line['analytic_accounts'] = new_entries
return [line]
class Project(metaclass=PoolMeta):
__name__ = 'project.work'
expenses = fields.One2Many('project.expense', 'work', 'Expenses')
@classmethod
def __setup__(cls):
super().__setup__()
cls._buttons.update({
'sync_expenses': {
}
})
@classmethod
@ModelView.button
def sync_expenses(cls, works):
pool = Pool()
Expense = pool.get('project.expense')
Uom = pool.get('product.uom')
to_save = []
to_delete = []
for work in works:
expenses = work._get_expenses()
expenses = {x.origin: x for x in expenses}
for existing in work.expenses:
if not existing.origin:
continue
expense = expenses.get(existing.origin)
if not expense:
to_delete.append(existing)
continue
existing_qty = Uom.compute_qty(existing.uom, existing.quantity,
expense.uom)
expense.quantity -= existing_qty
for expense in expenses.values():
if not expense.quantity:
continue
to_save.append(expense)
Expense.save(to_save)
Expense.delete(to_delete)
def _get_expenses(self):
expenses = []
if hasattr(self, 'moves'):
for move in self.moves:
expense = self._get_expense_move(move)
if expense:
expenses.append(expense)
if hasattr(self, 'purchase_lines'):
for line in self.purchase_lines:
expense = self._get_expense_purchase_line(line)
if expense:
expenses.append(expense)
return expenses
def _get_expense_move(self, move):
Expense = Pool().get('project.expense')
if move.state != 'done':
return
expense = Expense()
expense.work = self
expense.origin = move
expense.product = move.product
expense.uom = move.uom
expense.quantity = move.quantity
expense.cost_price = move.cost_price
return expense
def _get_expense_purchase_line(self, purchase_line):
Expense = Pool().get('project.expense')
expense = Expense()
expense.work = self
expense.origin = purchase_line
expense.product = purchase_line.product
expense.uom = purchase_line.unit
expense.quantity = purchase_line.quantity
expense.cost_price = purchase_line.unit_price
return expense
def _get_lines_to_invoice(self):
lines = super()._get_lines_to_invoice()
lines += self._get_expense_lines_to_invoice()
return lines
def _get_expense_lines_to_invoice(self):
lines = []
for expense in self.expenses:
lines += expense._get_invoice_lines()
return lines