mirror of
https://github.com/bringout/oca-warehouse.git
synced 2026-04-25 03:22:07 +02:00
Initial commit: OCA Warehouse packages (12 packages)
This commit is contained in:
commit
af1eea7692
627 changed files with 55555 additions and 0 deletions
|
|
@ -0,0 +1,8 @@
|
|||
from . import stock_barcodes_action
|
||||
from . import stock_barcodes_option
|
||||
from . import stock_move
|
||||
from . import stock_move_line
|
||||
from . import stock_picking
|
||||
from . import stock_picking_type
|
||||
from . import stock_quant
|
||||
from . import barcode_events_mixin
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class BarcodesEventsMixin(models.AbstractModel):
|
||||
_inherit = "barcodes.barcode_events_mixin"
|
||||
|
||||
def send_bus_done(self, channel, type_channel, data=None):
|
||||
self.env["bus.bus"]._sendone(channel, type_channel, data or {})
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
import base64
|
||||
import re
|
||||
from io import BytesIO
|
||||
|
||||
import barcode
|
||||
from barcode.writer import ImageWriter
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
REGEX = {
|
||||
"context": r"^[^\s].*[^\s]$|^$",
|
||||
"barcode": "^[a-zA-Z0-9-]+$",
|
||||
}
|
||||
FIELDS_NAME = {"barcode_options": "barcode_option_group_id"}
|
||||
|
||||
|
||||
class StockBarcodesAction(models.Model):
|
||||
_name = "stock.barcodes.action"
|
||||
_description = "Actions for barcode interface"
|
||||
_order = "sequence, id"
|
||||
|
||||
name = fields.Char(translate=True)
|
||||
active = fields.Boolean(default=True)
|
||||
sequence = fields.Integer(default=100)
|
||||
action_window_id = fields.Many2one(
|
||||
comodel_name="ir.actions.act_window", string="Action window"
|
||||
)
|
||||
context = fields.Char()
|
||||
key_shortcut = fields.Integer()
|
||||
key_char_shortcut = fields.Char()
|
||||
icon_class = fields.Char()
|
||||
barcode = fields.Char()
|
||||
barcode_image = fields.Image(
|
||||
"Barcode image",
|
||||
readonly=True,
|
||||
compute="_compute_barcode_image",
|
||||
attachment=True,
|
||||
)
|
||||
|
||||
count_elements = fields.Integer(default=0, compute="_compute_count_elements")
|
||||
|
||||
@api.constrains("barcode")
|
||||
def _constrains_barcode(self):
|
||||
for action in self:
|
||||
if not re.match(REGEX.get("barcode", False), action.barcode):
|
||||
raise ValidationError(
|
||||
_(
|
||||
" The barcode {} is not correct."
|
||||
"Use numbers, letters and dashes, without spaces."
|
||||
"E.g. 15753, BC-5789,er-56 "
|
||||
""
|
||||
).format(action.barcode)
|
||||
)
|
||||
all_barcode = [bar for bar in action.mapped("barcode") if bar]
|
||||
domain = [("barcode", "in", all_barcode)]
|
||||
matched_actions = self.sudo().search(domain, order="id")
|
||||
if len(matched_actions) > len(all_barcode):
|
||||
raise ValidationError(
|
||||
_(
|
||||
""" Barcode has already been assigned to the action(s): {}."""
|
||||
).format(", ".join(matched_actions.mapped("name")))
|
||||
)
|
||||
|
||||
def _generate_barcode(self):
|
||||
barcode_type = barcode.get_barcode_class("code128")
|
||||
buffer = BytesIO()
|
||||
barcode_instance = barcode_type(self.barcode, writer=ImageWriter())
|
||||
barcode_instance.write(buffer)
|
||||
buffer.seek(0)
|
||||
image_base64 = base64.b64encode(buffer.getvalue())
|
||||
return image_base64
|
||||
|
||||
@api.depends("barcode")
|
||||
def _compute_barcode_image(self):
|
||||
for action in self:
|
||||
if action.barcode:
|
||||
action.barcode_image = action._generate_barcode()
|
||||
else:
|
||||
action.barcode_image = False
|
||||
|
||||
@api.constrains("context")
|
||||
def _constrains_context(self):
|
||||
if self.context and not bool(
|
||||
re.match(REGEX.get("context", False), self.context)
|
||||
):
|
||||
raise ValidationError(_("There can be no spaces at the beginning or end."))
|
||||
|
||||
def _count_elements(self):
|
||||
domain = []
|
||||
if self.context:
|
||||
context_values = self.context.strip("{}").split(",")
|
||||
|
||||
def _map_context_values(x):
|
||||
field_values = x.split(":")
|
||||
field_name = field_values[0].split("search_default_")
|
||||
if len(field_name) > 1:
|
||||
field_name = field_name[1].strip("'")
|
||||
field_value_format = field_values[1].replace("'", "").strip()
|
||||
field_value = (
|
||||
int(field_value_format)
|
||||
if field_value_format.isdigit()
|
||||
else field_value_format
|
||||
)
|
||||
if hasattr(
|
||||
self.action_window_id.res_model,
|
||||
FIELDS_NAME.get(field_name, field_name),
|
||||
):
|
||||
return (
|
||||
"{}".format(FIELDS_NAME.get(field_name, field_name)),
|
||||
"=",
|
||||
field_value,
|
||||
)
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return ()
|
||||
|
||||
domain = [
|
||||
val_domain
|
||||
for val_domain in list(
|
||||
map(lambda x: _map_context_values(x), context_values)
|
||||
)
|
||||
]
|
||||
search_count = (
|
||||
list(filter(lambda x: x, domain))
|
||||
if all(val_d is True for val_d in domain)
|
||||
else []
|
||||
)
|
||||
return (
|
||||
self.env[self.action_window_id.res_model].search_count(search_count)
|
||||
if self.action_window_id.res_model
|
||||
else 0
|
||||
)
|
||||
return 0
|
||||
|
||||
@api.depends("context")
|
||||
def _compute_count_elements(self):
|
||||
for barcode_action in self:
|
||||
barcode_action.count_elements = (
|
||||
barcode_action._count_elements()
|
||||
if "search_default_" in barcode_action.context
|
||||
else 0
|
||||
)
|
||||
|
||||
def open_action(self):
|
||||
action = self.action_window_id.sudo().read()[0]
|
||||
action_context = safe_eval(action["context"])
|
||||
ctx = self.env.context.copy()
|
||||
if action_context:
|
||||
ctx.update(action_context)
|
||||
if self.context:
|
||||
ctx.update(safe_eval(self.context))
|
||||
if action_context.get("inventory_mode", False):
|
||||
action = self.open_inventory_action(ctx)
|
||||
else:
|
||||
action["context"] = ctx
|
||||
|
||||
return action
|
||||
|
||||
def open_inventory_action(self, ctx):
|
||||
option_group = self.env.ref(
|
||||
"stock_barcodes.stock_barcodes_option_group_inventory"
|
||||
)
|
||||
vals = {
|
||||
"option_group_id": option_group.id,
|
||||
"manual_entry": option_group.manual_entry,
|
||||
"display_read_quant": option_group.display_read_quant,
|
||||
}
|
||||
if option_group.get_option_value("location_id", "filled_default"):
|
||||
vals["location_id"] = (
|
||||
self.env["stock.warehouse"].search([], limit=1).lot_stock_id.id
|
||||
)
|
||||
wiz = self.env["wiz.stock.barcodes.read.inventory"].create(vals)
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
"stock_barcodes.action_stock_barcodes_read_inventory"
|
||||
)
|
||||
action["res_id"] = wiz.id
|
||||
action["context"] = ctx
|
||||
return action
|
||||
|
||||
def print_barcodes(self):
|
||||
report_action = self.env.ref(
|
||||
"stock_barcodes.action_report_barcode_actions"
|
||||
).report_action(None, data={})
|
||||
return report_action
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockBarcodesOptionGroup(models.Model):
|
||||
_name = "stock.barcodes.option.group"
|
||||
_description = "Options group for barcode interface"
|
||||
|
||||
name = fields.Char()
|
||||
code = fields.Char()
|
||||
option_ids = fields.One2many(
|
||||
comodel_name="stock.barcodes.option", inverse_name="option_group_id", copy=True
|
||||
)
|
||||
barcode_guided_mode = fields.Selection(
|
||||
[("guided", "Guided")],
|
||||
string="Mode",
|
||||
help="When guided mode is selected, information will appear with the "
|
||||
"movement to be processed",
|
||||
)
|
||||
manual_entry = fields.Boolean(
|
||||
string="Manual entry",
|
||||
help="Default value when open scan interface",
|
||||
)
|
||||
manual_entry_field_focus = fields.Char(
|
||||
help="Set field to set focus when manual entry mode is enabled",
|
||||
default="location_id",
|
||||
)
|
||||
confirmed_moves = fields.Boolean(
|
||||
string="Confirmed moves",
|
||||
help="It allows to work with movements without reservation "
|
||||
"(Without detailed operations)",
|
||||
)
|
||||
show_pending_moves = fields.Boolean(
|
||||
string="Show pending moves", help="Shows a list of movements to process"
|
||||
)
|
||||
source_pending_moves = fields.Selection(
|
||||
[("move_line_ids", "Detailed operations"), ("move_ids", "Operations")],
|
||||
default="move_line_ids",
|
||||
help="Origin of the data to generate the movements to process",
|
||||
)
|
||||
ignore_filled_fields = fields.Boolean(
|
||||
string="Ignore filled fields",
|
||||
)
|
||||
auto_put_in_pack = fields.Boolean(
|
||||
string="Auto put in pack", help="Auto put in pack before picking validation"
|
||||
)
|
||||
is_manual_qty = fields.Boolean(
|
||||
help="If it is checked, it always shows the product quantity field in edit mode"
|
||||
)
|
||||
is_manual_confirm = fields.Boolean(
|
||||
help="If it is marked, the movement must always be confirmed from a button"
|
||||
)
|
||||
allow_negative_quant = fields.Boolean(
|
||||
help="If it is checked, it will allow the creation of movements that "
|
||||
"generate negative stock"
|
||||
)
|
||||
fill_fields_from_lot = fields.Boolean(
|
||||
help="If checked, the fields in the interface will be filled from "
|
||||
"the scanned lot"
|
||||
)
|
||||
ignore_quant_location = fields.Boolean(
|
||||
help="If it is checked, quant location will be ignored when reading lot/package",
|
||||
)
|
||||
group_key_for_todo_records = fields.Char(
|
||||
help="You can establish a list of fields that will act as a grouping "
|
||||
"key to generate the movements to be process.\n"
|
||||
"The object variable is used to refer to the source record\n"
|
||||
"For example, object.location_id,object.product_id,object.lot_id"
|
||||
)
|
||||
auto_lot = fields.Boolean(
|
||||
string="Get lots automatically",
|
||||
help="If checked the lot will be set automatically with the same "
|
||||
"removal startegy",
|
||||
)
|
||||
create_lot = fields.Boolean(
|
||||
string="Create lots if not match",
|
||||
help="If checked the lot will created automatically with the scanned barcode "
|
||||
"if not exists ",
|
||||
)
|
||||
show_detailed_operations = fields.Boolean(
|
||||
help="If checked the picking detailed operations are displayed",
|
||||
)
|
||||
keep_screen_values = fields.Boolean(
|
||||
help="If checked the wizard values are kept until the pending move is completed",
|
||||
)
|
||||
accumulate_read_quantity = fields.Boolean(
|
||||
help="If checked quantity will be accumulated to the existing record instead of "
|
||||
"overwrite it with the new quantity value",
|
||||
)
|
||||
display_notification = fields.Boolean(
|
||||
string="Display Odoo notifications",
|
||||
)
|
||||
use_location_dest_putaway = fields.Boolean(
|
||||
string="Use location dest. putaway",
|
||||
)
|
||||
location_field_to_sort = fields.Selection(
|
||||
selection=[
|
||||
("location_id", "Origin Location"),
|
||||
("location_dest_id", "Destination Location"),
|
||||
]
|
||||
)
|
||||
display_read_quant = fields.Boolean(string="Read items on inventory mode")
|
||||
|
||||
def get_option_value(self, field_name, attribute):
|
||||
option = self.option_ids.filtered(lambda op: op.field_name == field_name)[:1]
|
||||
return option[attribute]
|
||||
|
||||
|
||||
class StockBarcodesOption(models.Model):
|
||||
_name = "stock.barcodes.option"
|
||||
_description = "Options for barcode interface"
|
||||
_order = "step, sequence, id"
|
||||
|
||||
sequence = fields.Integer(default=100)
|
||||
name = fields.Char()
|
||||
option_group_id = fields.Many2one(
|
||||
comodel_name="stock.barcodes.option.group", ondelete="cascade"
|
||||
)
|
||||
field_name = fields.Char()
|
||||
filled_default = fields.Boolean()
|
||||
forced = fields.Boolean()
|
||||
to_scan = fields.Boolean()
|
||||
required = fields.Boolean()
|
||||
clean_after_done = fields.Boolean()
|
||||
message = fields.Char()
|
||||
step = fields.Integer()
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2024 Tecnativa - Sergio Teruel
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
barcode_backorder_action = fields.Selection(
|
||||
[
|
||||
("pending", "Pending"),
|
||||
("create_backorder", "Create Backorder"),
|
||||
("skip_backorder", "No Backorder"),
|
||||
],
|
||||
string="Backorder action",
|
||||
default="pending",
|
||||
)
|
||||
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
moves_cancel_backorder = self.browse()
|
||||
if not cancel_backorder:
|
||||
moves_cancel_backorder = self.filtered(
|
||||
lambda sm: sm.barcode_backorder_action == "skip_backorder"
|
||||
)
|
||||
super(StockMove, moves_cancel_backorder)._action_done(cancel_backorder=True)
|
||||
moves_backorder = self - moves_cancel_backorder
|
||||
moves_backorder.barcode_backorder_action = "pending"
|
||||
return super(StockMove, moves_backorder)._action_done(
|
||||
cancel_backorder=cancel_backorder
|
||||
)
|
||||
|
||||
def copy_data(self, default=None):
|
||||
vals_list = super().copy_data(default=default)
|
||||
for vals in vals_list:
|
||||
vals.pop("barcode_backorder_action", None)
|
||||
return vals_list
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockMoveLine(models.Model):
|
||||
_inherit = "stock.move.line"
|
||||
|
||||
barcode_scan_state = fields.Selection(
|
||||
[("pending", "Pending"), ("done", "Done"), ("done_forced", "Done forced")],
|
||||
string="Scan State",
|
||||
default="pending",
|
||||
compute="_compute_barcode_scan_state",
|
||||
readonly=False,
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends("qty_done", "reserved_uom_qty")
|
||||
def _compute_barcode_scan_state(self):
|
||||
for line in self:
|
||||
if line.qty_done >= line.reserved_uom_qty:
|
||||
line.barcode_scan_state = "done"
|
||||
else:
|
||||
line.barcode_scan_state = "pending"
|
||||
|
||||
def _barcodes_process_line_to_unlink(self):
|
||||
self.qty_done = 0.0
|
||||
|
||||
def action_barcode_detailed_operation_unlink(self):
|
||||
for sml in self:
|
||||
stock_move = sml.move_id
|
||||
stock_move.barcode_backorder_action = "pending"
|
||||
sml.unlink()
|
||||
# HACK: To force refresh wizard values
|
||||
wiz_barcode = self.env["wiz.stock.barcodes.read.picking"].browse(
|
||||
self.env.context.get("wiz_barcode_id", False)
|
||||
)
|
||||
stock_move._action_assign()
|
||||
wiz_barcode.fill_todo_records()
|
||||
wiz_barcode.determine_todo_action()
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
def _prepare_barcode_wiz_vals(self, option_group):
|
||||
vals = {
|
||||
"picking_id": self.id,
|
||||
"res_model_id": self.env.ref("stock.model_stock_picking").id,
|
||||
"res_id": self.id,
|
||||
"picking_type_code": self.picking_type_code,
|
||||
"option_group_id": option_group.id,
|
||||
"manual_entry": option_group.manual_entry,
|
||||
"picking_mode": "picking",
|
||||
}
|
||||
if self.picking_type_id.code == "outgoing":
|
||||
vals["location_dest_id"] = self.location_dest_id.id
|
||||
elif self.picking_type_id.code == "incoming":
|
||||
vals["location_id"] = self.location_id.id
|
||||
|
||||
if option_group.get_option_value("location_id", "filled_default"):
|
||||
vals["location_id"] = self.location_id.id
|
||||
if option_group.get_option_value("location_dest_id", "filled_default"):
|
||||
vals["location_dest_id"] = self.location_dest_id.id
|
||||
return vals
|
||||
|
||||
def action_barcode_scan(self, option_group=False):
|
||||
option_group = (
|
||||
option_group
|
||||
or self.picking_type_id.barcode_option_group_id
|
||||
or self.env.ref("stock_barcodes.stock_barcodes_option_group_operation")
|
||||
)
|
||||
wiz = self.env["wiz.stock.barcodes.read.picking"].create(
|
||||
self._prepare_barcode_wiz_vals(option_group)
|
||||
)
|
||||
wiz.fill_pending_moves()
|
||||
wiz.determine_todo_action()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
"stock_barcodes.action_stock_barcodes_read_picking"
|
||||
)
|
||||
action["res_id"] = wiz.id
|
||||
return action
|
||||
|
||||
def button_validate(self):
|
||||
put_in_pack_picks = self.filtered(
|
||||
lambda p: p.picking_type_id.barcode_option_group_id.auto_put_in_pack
|
||||
and not p.move_line_ids.result_package_id
|
||||
)
|
||||
if put_in_pack_picks:
|
||||
put_in_pack_picks.action_put_in_pack()
|
||||
if self.env.context.get("stock_barcodes_validate_picking", False):
|
||||
res = super(
|
||||
StockPicking, self.with_context(skip_backorder=True)
|
||||
).button_validate()
|
||||
else:
|
||||
pickings_to_backorder = self._check_backorder()
|
||||
if pickings_to_backorder:
|
||||
return pickings_to_backorder._action_generate_backorder_wizard(
|
||||
show_transfers=self._should_show_transfers()
|
||||
)
|
||||
res = super().button_validate()
|
||||
if res is True and self.env.context.get("show_picking_type_action_tree", False):
|
||||
res = self[:1].picking_type_id.get_action_picking_tree_ready()
|
||||
|
||||
if self.state == "done":
|
||||
self.env["bus.bus"]._sendone(
|
||||
"stock_barcodes_scan", "actions_barcode", {"valid_picking": True}
|
||||
)
|
||||
|
||||
return res
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from ast import literal_eval
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockPickingType(models.Model):
|
||||
_inherit = "stock.picking.type"
|
||||
|
||||
barcode_option_group_id = fields.Many2one(
|
||||
comodel_name="stock.barcodes.option.group"
|
||||
)
|
||||
|
||||
new_picking_barcode_option_group_id = fields.Many2one(
|
||||
comodel_name="stock.barcodes.option.group",
|
||||
help="This Barcode Option Group will be selected when clicking the 'New' button"
|
||||
" in an operation type. It will be used to create a non planned picking.",
|
||||
)
|
||||
|
||||
def action_barcode_scan(self):
|
||||
vals = {
|
||||
"res_model_id": self.env.ref("stock.model_stock_picking_type").id,
|
||||
"res_id": self.id,
|
||||
"picking_type_code": self.code,
|
||||
"option_group_id": self.barcode_option_group_id.id,
|
||||
"manual_entry": self.barcode_option_group_id.manual_entry,
|
||||
"picking_mode": "picking",
|
||||
}
|
||||
if self.code == "outgoing":
|
||||
vals["location_dest_id"] = (
|
||||
self.default_location_dest_id.id
|
||||
or self.env.ref("stock.stock_location_customers").id
|
||||
)
|
||||
elif self.code == "incoming":
|
||||
vals["location_id"] = (
|
||||
self.default_location_src_id.id
|
||||
or self.env.ref("stock.stock_location_suppliers").id
|
||||
)
|
||||
if self.barcode_option_group_id.get_option_value(
|
||||
"location_id", "filled_default"
|
||||
):
|
||||
vals["location_id"] = self.default_location_src_id.id
|
||||
if self.barcode_option_group_id.get_option_value(
|
||||
"location_dest_id", "filled_default"
|
||||
):
|
||||
vals["location_dest_id"] = self.default_location_dest_id.id
|
||||
wiz = self.env["wiz.stock.barcodes.read.picking"].create(vals)
|
||||
wiz.fill_pending_moves()
|
||||
wiz.determine_todo_action()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
"stock_barcodes.action_stock_barcodes_read_picking"
|
||||
)
|
||||
action["res_id"] = wiz.id
|
||||
return action
|
||||
|
||||
def action_barcode_new_picking(self):
|
||||
self.ensure_one()
|
||||
picking = (
|
||||
self.env["stock.picking"]
|
||||
.with_context(default_immediate_transfer=True)
|
||||
.create(
|
||||
{
|
||||
"picking_type_id": self.id,
|
||||
"location_id": self.default_location_src_id.id,
|
||||
"location_dest_id": self.default_location_dest_id.id,
|
||||
}
|
||||
)
|
||||
)
|
||||
option_group = self.new_picking_barcode_option_group_id
|
||||
return picking.action_barcode_scan(option_group=option_group)
|
||||
|
||||
def get_action_picking_tree_ready(self):
|
||||
context = dict(self.env.context)
|
||||
if context.get("operations_mode", False):
|
||||
return self._get_action(
|
||||
"stock_barcodes.stock_barcodes_action_picking_tree_ready"
|
||||
)
|
||||
return super().get_action_picking_tree_ready()
|
||||
|
||||
def _get_action(self, action_xmlid):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(action_xmlid)
|
||||
if self:
|
||||
action["display_name"] = self.display_name
|
||||
|
||||
default_immediate_tranfer = True
|
||||
if (
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("stock.no_default_immediate_tranfer")
|
||||
):
|
||||
default_immediate_tranfer = False
|
||||
|
||||
context = {
|
||||
"search_default_picking_type_id": [self.id],
|
||||
"default_picking_type_id": self.id,
|
||||
"default_immediate_transfer": default_immediate_tranfer,
|
||||
"default_company_id": self.company_id.id,
|
||||
}
|
||||
|
||||
action_context = literal_eval(action["context"].strip())
|
||||
context = {**action_context, **context}
|
||||
action["context"] = context
|
||||
return action
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright 2023 Tecnativa - Sergio Teruel
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
MODEL_UPDATE_INVENTORY = ["wiz.stock.barcodes.read.inventory"]
|
||||
|
||||
|
||||
class StockQuant(models.Model):
|
||||
_name = "stock.quant"
|
||||
_inherit = ["stock.quant", "barcodes.barcode_events_mixin"]
|
||||
|
||||
def action_barcode_inventory_quant_unlink(self):
|
||||
self.with_context(inventory_mode=True).action_set_inventory_quantity_to_zero()
|
||||
context = dict(self.env.context)
|
||||
params = context.get("params", {})
|
||||
res_model = params.get("model", False)
|
||||
res_id = params.get("id", False)
|
||||
if res_id and res_model in MODEL_UPDATE_INVENTORY:
|
||||
wiz_id = self.env[params["model"]].browse(params["id"])
|
||||
wiz_id._compute_count_inventory_quants()
|
||||
wiz_id.send_bus_done(
|
||||
"stock_barcodes_form_update",
|
||||
"count_apply_inventory",
|
||||
{"count": wiz_id.count_inventory_quants},
|
||||
)
|
||||
|
||||
def _get_fields_to_edit(self):
|
||||
return [
|
||||
"location_id",
|
||||
"product_id",
|
||||
"product_uom_id",
|
||||
"lot_id",
|
||||
"package_id",
|
||||
]
|
||||
|
||||
def action_barcode_inventory_quant_edit(self):
|
||||
wiz_barcode_id = self.env.context.get("wiz_barcode_id", False)
|
||||
wiz_barcode = self.env["wiz.stock.barcodes.read.inventory"].browse(
|
||||
wiz_barcode_id
|
||||
)
|
||||
for quant in self:
|
||||
# Try to assign fields with the same name between quant and the scan wizard
|
||||
for fname in self._get_fields_to_edit():
|
||||
wiz_barcode[fname] = quant[fname]
|
||||
wiz_barcode.product_qty = quant.inventory_quantity
|
||||
|
||||
wiz_barcode.manual_entry = True
|
||||
self.send_bus_done(
|
||||
"stock_barcodes_scan",
|
||||
"stock_barcodes_edit_manual",
|
||||
{
|
||||
"manual_entry": True,
|
||||
},
|
||||
)
|
||||
|
||||
def enable_current_operations(self):
|
||||
self.send_bus_done(
|
||||
"stock_barcodes_kanban_update",
|
||||
"enable_operations",
|
||||
{
|
||||
"id": self.id,
|
||||
},
|
||||
)
|
||||
|
||||
def operation_quantities_rest(self):
|
||||
self.write({"inventory_quantity": self.inventory_quantity - 1})
|
||||
self.enable_current_operations()
|
||||
|
||||
def operation_quantities(self):
|
||||
self.write({"inventory_quantity": self.inventory_quantity + 1})
|
||||
self.enable_current_operations()
|
||||
|
||||
def action_apply_inventory(self):
|
||||
res = super().action_apply_inventory()
|
||||
self.send_bus_done(
|
||||
"stock_barcodes_scan",
|
||||
"actions_barcode",
|
||||
{"apply_inventory": True},
|
||||
)
|
||||
return res
|
||||
Loading…
Add table
Add a link
Reference in a new issue