160 lines
5.9 KiB
Python
160 lines
5.9 KiB
Python
from lxml.builder import E
|
|
|
|
from odoo import fields, models, api, Command
|
|
|
|
class Product(models.Model):
|
|
_name = _description = 'ttu.product'
|
|
|
|
class Root(models.Model):
|
|
_name = _description = 'ttu.root'
|
|
|
|
product_id = fields.Many2one('ttu.product')
|
|
product_qty = fields.Integer()
|
|
qty_producing = fields.Integer()
|
|
qty_produced = fields.Integer(compute='_get_produced_qty')
|
|
|
|
move_raw_ids = fields.One2many('ttu.child', 'root_raw_id')
|
|
move_finished_ids = fields.One2many('ttu.child', 'root_id')
|
|
|
|
@api.depends('move_finished_ids.move_line_ids.qty_done')
|
|
def _get_produced_qty(self):
|
|
for r in self:
|
|
r.qty_produced = sum(r.mapped('move_finished_ids.move_line_ids.qty_done'))
|
|
@api.onchange('qty_producing')
|
|
def _onchange_producing(self):
|
|
production_move = self.move_finished_ids.filtered(
|
|
lambda move: move.product_id == self.product_id
|
|
)
|
|
if not production_move:
|
|
# Happens when opening the mo?
|
|
return
|
|
for line in production_move.move_line_ids:
|
|
line.qty_done = 0
|
|
qty_producing = self.qty_producing - self.qty_produced
|
|
vals = production_move._set_quantity_done_prepare_vals(qty_producing)
|
|
if vals['to_create']:
|
|
for res in vals['to_create']:
|
|
production_move.move_line_ids.new(res)
|
|
if vals['to_write']:
|
|
for move_line, res in vals['to_write']:
|
|
move_line.update(res)
|
|
|
|
for move in (self.move_raw_ids | self.move_finished_ids.filtered(lambda m: m.product_id != self.product_id)):
|
|
new_qty = qty_producing * move.unit_factor
|
|
for line in move.move_line_ids:
|
|
line.qty_done = 0
|
|
vals = move._set_quantity_done_prepare_vals(new_qty)
|
|
if vals['to_create']:
|
|
for res in vals['to_create']:
|
|
move.move_line_ids.new(res)
|
|
if vals['to_write']:
|
|
for move_line, res in vals['to_write']:
|
|
move_line.update(res)
|
|
|
|
def _get_default_form_view(self):
|
|
move_subview = E.list(
|
|
{'editable': 'bottom'},
|
|
E.field(name='product_id'),
|
|
E.field(name='unit_factor'),
|
|
E.field(name='quantity_done'),
|
|
E.field(
|
|
{'name': 'move_line_ids', 'column_invisible': '1'},
|
|
E.list(
|
|
E.field(name='qty_done', invisible='1'),
|
|
E.field(name='product_id', invisible='1'),
|
|
E.field(name='move_id', invisible='1'),
|
|
E.field(name='id', invisible='1'),
|
|
)
|
|
)
|
|
)
|
|
|
|
t = E.form(
|
|
E.field(name='product_id'),
|
|
E.field(name='product_qty'),
|
|
E.field(name='qty_producing'),
|
|
E.field({'name': 'move_raw_ids', 'on_change': '1'}, move_subview),
|
|
E.field({'name': 'move_finished_ids', 'on_change': '1'}, move_subview),
|
|
)
|
|
# deoptimise to ensure we call onchange most of the time, as im the real
|
|
# case this is done as a result of the metric fuckton of computes, but
|
|
# here the near complete lack of computes causes most of the onchange
|
|
# triggers to get disabled
|
|
for f in t.iter('field'):
|
|
f.set('on_change', '1')
|
|
return t
|
|
|
|
|
|
class Child(models.Model):
|
|
_name = _description = 'ttu.child'
|
|
|
|
product_id = fields.Many2one('ttu.product')
|
|
unit_factor = fields.Integer(default=1, required=True) # should be computed but we can ignore that
|
|
quantity_done = fields.Integer(
|
|
compute='_quantity_done_compute',
|
|
inverse='_quantity_done_set'
|
|
)
|
|
|
|
root_raw_id = fields.Many2one('ttu.root')
|
|
root_id = fields.Many2one('ttu.root')
|
|
move_line_ids = fields.One2many('ttu.grandchild', 'move_id')
|
|
|
|
def _set_quantity_done_prepare_vals(self, qty):
|
|
res = {'to_write': [], 'to_create': []}
|
|
for ml in self.move_line_ids:
|
|
ml_qty = ml.product_uom_qty - ml.qty_done
|
|
if ml_qty <= 0:
|
|
continue
|
|
|
|
taken_qty = min(qty, ml_qty)
|
|
|
|
res['to_write'].append((ml, {'qty_done': ml.qty_done + taken_qty}))
|
|
qty -= taken_qty
|
|
|
|
if qty <= 0:
|
|
break
|
|
|
|
if qty > 0:
|
|
res['to_create'].append({
|
|
'move_id': self.id,
|
|
'product_id': self.product_id.id,
|
|
'product_uom_qty': 0,
|
|
'qty_done': qty,
|
|
})
|
|
return res
|
|
|
|
@api.depends('move_line_ids.qty_done')
|
|
def _quantity_done_compute(self):
|
|
for move in self:
|
|
move.quantity_done = sum(move.mapped('move_line_ids.qty_done'))
|
|
|
|
def _quantity_done_set(self):
|
|
quantity_done = self[0].quantity_done # any call to create will invalidate `move.quantity_done`
|
|
for move in self:
|
|
move_lines = move.move_line_ids
|
|
if not move_lines:
|
|
if quantity_done:
|
|
# do not impact reservation here
|
|
move_line = self.env['ttu.grandchild'].create({
|
|
'move_id': move.id,
|
|
'product_id': move.product_id.id,
|
|
'product_uom_qty': 0,
|
|
'qty_done': quantity_done,
|
|
})
|
|
move.write({'move_line_ids': [Command.link(move_line.id)]})
|
|
elif len(move_lines) == 1:
|
|
move_lines[0].qty_done = quantity_done
|
|
else:
|
|
# Bypass the error if we're trying to write the same value.
|
|
ml_quantity_done = sum(l.qty_done for l in move_lines)
|
|
assert quantity_done == ml_quantity_done, "Cannot set the done quantity from this stock move, work directly with the move lines."
|
|
|
|
|
|
class Grandchild(models.Model):
|
|
_name = _description = 'ttu.grandchild'
|
|
|
|
product_id = fields.Many2one('ttu.product')
|
|
product_uom_qty = fields.Integer()
|
|
qty_done = fields.Integer()
|
|
|
|
move_id = fields.Many2one('ttu.child')
|