Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] Added functionality to create, reserve, confirm, unreserve, ca… #1

Open
wants to merge 1 commit into
base: 12.0-mig-project_task_material_stock
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 196 additions & 27 deletions project_task_material_stock/models/project_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,51 @@
# Copyright 2019 Valentin Vinagre <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import _, api, exceptions, fields, models
from odoo.exceptions import UserError


class ProjectTaskType(models.Model):
_inherit = 'project.task.type'

consume_material = fields.Boolean(
pick_material = fields.Boolean(
help="If you mark this check, when a task goes to this state, "
"it will consume the associated materials",
"it will create a stock picking for the associated materials",
)
reserve_material = fields.Boolean(
help="If you mark this check, when a task goes to this state, "
"it will reserve materials in associated stock.pickings",
)
unreserve_material = fields.Boolean(
help="If you mark this check, when a task goes to this state, "
"it will unreserve materials in associated stock.pickings",
)
done_material = fields.Boolean(
help="If you mark this check, when a task goes to this state, "
"it will validate the associated stock.pickings if possible",
)
abort_material = fields.Boolean(
help="If you mark this check, when a task goes to this state, "
"it will try to unreserve and cancel stock.pickings, if picking is already done, then it will return goods",
)
pick_type_id = fields.Many2one(
comodel_name='stock.picking.type',
string='Picktype',
default=lambda self: self.env.ref('project_task_material_stock.project_task_material_picking_type').id
)
location_id = fields.Many2one(
comodel_name='stock.location',
string='Source Location',
)
location_dest_id = fields.Many2one(
comodel_name='stock.location',
string='Destination Location',
)


class StockPicking(models.Model):
_inherit = "stock.picking"

task_id = fields.Many2one('project.task', string="Task")


class Task(models.Model):
Expand All @@ -22,7 +58,7 @@ class Task(models.Model):
@api.depends('material_ids.stock_move_id')
def _compute_stock_move(self):
for task in self:
task.stock_move_ids = task.mapped('material_ids.stock_move_id')
task.stock_move_ids = task.mapped('material_ids.stock_move_ids')

@api.multi
@api.depends('material_ids.analytic_line_id')
Expand All @@ -44,9 +80,24 @@ def _compute_stock_state(self):
task.stock_state = state
break

@api.multi
def _compute_matching_picking(self):
default_pick_type = self.env.ref(
'project_task_material_stock.project_task_material_picking_type')
for task in self:
pick_type = task.pick_type_id or default_pick_type
pickings = task.picking_ids.filtered(lambda p: p.picking_type_id.id == pick_type.id)
task.picking_id = pickings[0].id if pickings else None

picking_ids = fields.One2many(
"stock.picking",
"task_id",
string="Stock Pickings"
)
picking_id = fields.Many2one(
"stock.picking",
related="stock_move_ids.picking_id",
compute="_compute_matching_picking",
string="Current Picking"
)
stock_move_ids = fields.Many2many(
comodel_name='stock.move',
Expand All @@ -63,8 +114,29 @@ def _compute_stock_state(self):
compute='_compute_analytic_line',
string='Analytic Lines',
)
consume_material = fields.Boolean(
related='stage_id.consume_material',
pick_material = fields.Boolean(
related='stage_id.pick_material',
)
reserve_material = fields.Boolean(
related='stage_id.reserve_material',
)
unreserve_material = fields.Boolean(
related='stage_id.unreserve_material',
)
done_material = fields.Boolean(
related='stage_id.done_material',
)
abort_material = fields.Boolean(
related='stage_id.abort_material',
)
pick_type_id = fields.Many2one(
related='stage_id.pick_type_id',
)
stage_location_id = fields.Many2one(
related='stage_id.location_id',
)
stage_location_dest_id = fields.Many2one(
related='stage_id.location_dest_id',
)
stock_state = fields.Selection(
selection=[
Expand Down Expand Up @@ -102,26 +174,83 @@ def unlink_stock_move(self):
res = moves.unlink()
return res

@api.multi
def _pick_material(self):
self.ensure_one()
if not self.picking_id or (self.picking_id and self.picking_id.state == 'draft'):
todo_lines = self.material_ids.filtered(
lambda m: not m.stock_move_id
)
if todo_lines:
todo_lines.create_stock_move()
todo_lines.create_analytic_line()

@api.multi
def _reserve_material(self):
self.ensure_one()
if not self.picking_id or self.picking_id.state not in ['waiting','confirmed']:
return
self.picking_id.action_assign()

@api.multi
def _unreserve_material(self):
self.ensure_one()
if not self.picking_id or self.picking_id.state not in ['assigned']:
return
self.picking_id.do_unreserve()

@api.multi
def _abort_material(self):
self.ensure_one()
for picking in self.picking_ids:
if picking.state == 'done':
# Return the picking
stock_return_picking = self.env['stock.return.picking'] \
.with_context(active_id=picking.id) \
.create({})
stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign()
return_pick.move_lines.quantity_done = 1
return_pick.action_done()
else:
picking.action_cancel()

@api.multi
def _validate_material(self):
self.ensure_one()
if not self.picking_id:
return
if self.picking_id.state == 'done':
return
res = self.picking_id.button_validate()
if res and res['res_model'] == 'stock.immediate.transfer':
# if we get back the immediate transfer wizard - then process it
wizard = self.env['stock.immediate.transfer'].browse(res['res_id'])
wizard.process()
elif res and res['res_model'] == 'stock.overprocessed.transfer':
raise UserError(_('There are overprocessed stock moves. You have to manually resolv this !'))

@api.multi
def write(self, vals):
res = super(Task, self).write(vals)
for task in self:
if 'stage_id' in vals or 'material_ids' in vals:
if task.consume_material:
todo_lines = task.material_ids.filtered(
lambda m: not m.stock_move_id
)
if todo_lines:
todo_lines.create_stock_move()
todo_lines.create_analytic_line()
else:
if task.unlink_stock_move() and task.material_ids.mapped(
'analytic_line_id'):
raise exceptions.Warning(
_("You can't move to a not consume stage if "
"there are already analytic lines")
)
task.material_ids.mapped('analytic_line_id').unlink()
if task.done_material:
task._pick_material()
task.refresh()
task._reserve_material()
task._validate_material()
elif task.reserve_material:
task._pick_material()
task.refresh()
task._reserve_material()
elif task.pick_material:
task._pick_material()
elif task.abort_material:
task._abort_material()
elif task.unreserve_material:
task._unreserve_material()
return res

@api.multi
Expand All @@ -141,13 +270,39 @@ def action_done(self):
self.mapped('stock_move_ids')._action_done()


class StockMove(models.Model):
_inherit = "stock.move"

project_task_material_id = fields.Many2one(
comodel_name='project.task.material',
string="Task Material"
)


class ProjectTaskMaterial(models.Model):
_inherit = "project.task.material"

@api.multi
@api.depends('stock_move_ids')
def _compute_stage_move(self):
default_pick_type = self.env.ref(
'project_task_material_stock.project_task_material_picking_type')
for material in self:
pick_type = material.task_id.pick_type_id or default_pick_type
moves = material.stock_move_ids.filtered(
lambda m: m.picking_type_id.id == pick_type.id)
material.stock_move_id = moves[0].id if moves else None

stock_move_id = fields.Many2one(
comodel_name='stock.move',
compute='_compute_stage_move',
string='Stock Move',
)
stock_move_ids = fields.One2many(
comodel_name='stock.move',
inverse_name='project_task_material_id',
string='Stock Moves',
)
analytic_line_id = fields.Many2one(
comodel_name='account.analytic.line',
string='Analytic Line',
Expand All @@ -173,38 +328,52 @@ def _prepare_stock_move(self):
'product_id': product.id,
'name': product.partner_ref,
'state': 'confirmed',
'project_task_material_id': self.id,
'product_uom': self.product_uom_id.id or product.uom_id.id,
'product_uom_qty': self.quantity,
'origin': self.task_id.name,
'location_id':
self.task_id.location_source_id.id or
self.task_id.stage_id.location_id.id or
self.task_id.project_id.location_source_id.id or
self.env.ref('stock.stock_location_stock').id,
'location_dest_id':
self.task_id.location_dest_id.id or
self.task_id.stage_id.location_dest_id.id or
self.task_id.project_id.location_dest_id.id or
self.env.ref('stock.stock_location_customers').id,
}
return res

@api.multi
def create_stock_move(self):
pick_type = self.env.ref(
'project_task_material_stock.project_task_material_picking_type')
task = self[0].task_id
pick_type = task.pick_type_id
if not pick_type:
pick_type = self.env.ref(
'project_task_material_stock.project_task_material_picking_type')
# Do search for matching picking
picking_id = task.picking_id or self.env['stock.picking'].create({
'origin': "{}/{}".format(task.project_id.name, task.name),
'partner_id': task.partner_id.id,
'task_id': task.id,
'picking_type_id': pick_type.id,
'location_id': pick_type.default_location_src_id.id,
'location_dest_id': pick_type.default_location_dest_id.id,
'location_id':
task.location_source_id.id or
task.stage_id.location_id.id or
task.project_id.location_source_id.id or
self.env.ref('stock.stock_location_stock').id,
'location_dest_id':
task.location_dest_id.id or
task.stage_id.location_dest_id.id or
task.project_id.location_dest_id.id or
self.env.ref('stock.stock_location_customers').id,
})
for line in self:
if not line.stock_move_id:
move_vals = line._prepare_stock_move()
move_vals.update({'picking_id': picking_id.id or False})
move_id = self.env['stock.move'].create(move_vals)
line.stock_move_id = move_id.id
self.env['stock.move'].create(move_vals)

def _prepare_analytic_line(self):
product = self.product_id
Expand Down
10 changes: 8 additions & 2 deletions project_task_material_stock/views/project_task_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
<field name="inherit_id" ref="project.task_type_edit"/>
<field name="arch" type="xml">
<field name="fold" position="after">
<field name="consume_material"/>
<field name="pick_material"/>
<field name="reserve_material"/>
<field name="unreserve_material"/>
<field name="done_material"/>
<field name="abort_material"/>
<field name="pick_type_id"/>
<field name="location_id"/>
<field name="location_dest_id"/>
</field>
</field>
</record>
Expand All @@ -38,7 +45,6 @@
</field>
<field name="material_ids" position="replace">
<field name="stock_state" invisible="1"/>
<field name="consume_material" invisible="1"/>
<group string="Locations to consume" name="materials" groups="stock.group_stock_multi_locations">
<field name="location_source_id"/>
<field name="location_dest_id"/>
Expand Down