Files
enterprise-addons/stock_barcode/models/stock_picking.py
T

300 lines
15 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, api, _
from odoo.tools import html2plaintext, is_html_empty
from odoo.exceptions import ValidationError
class StockPicking(models.Model):
_inherit = 'stock.picking'
_barcode_field = 'name'
def action_cancel_from_barcode(self):
self.ensure_one()
view = self.env.ref('stock_barcode.stock_barcode_cancel_operation_view')
return {
'name': _('Cancel this operation?'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'stock_barcode.cancel.operation',
'views': [(view.id, 'form')],
'view_id': view.id,
'target': 'new',
'context': dict(self.env.context, default_picking_id=self.id),
}
@api.model
def action_open_new_picking(self):
""" Creates a new picking of the current picking type and open it.
:return: the action used to open the picking, or false
:rtype: dict
"""
context = self.env.context
if context.get('active_model') == 'stock.picking.type':
picking_type = self.env['stock.picking.type'].browse(context.get('active_id'))
if picking_type.exists():
new_picking = self._create_new_picking(picking_type)
return new_picking.action_open_picking_client_action()
return False
def action_open_picking(self):
""" method to open the form view of the current record
from a button on the kanban view
"""
self.ensure_one()
view_id = self.env.ref('stock.view_picking_form').id
return {
'name': _('Open picking form'),
'res_model': 'stock.picking',
'view_mode': 'form',
'view_id': view_id,
'type': 'ir.actions.act_window',
'res_id': self.id,
}
def action_open_picking_client_action(self):
""" method to open the form view of the current record
from a button on the kanban view
"""
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("stock_barcode.stock_barcode_picking_client_action")
context = {'active_id': self.id}
return dict(action, context=context)
def action_create_return_picking(self):
"""
Create a return picking for the current picking and open it in the barcode app
"""
self.ensure_one()
return_picking = self.with_context(active_model='stock.picking', active_id=self.id).env['stock.return.picking'].create({})
for line in return_picking.product_return_moves:
line.quantity = line.move_id.quantity
new_picking = return_picking._create_return()
return new_picking.action_open_picking_client_action()
def action_print_barcode(self):
return self.action_open_label_type()
def action_print_delivery_slip(self):
return self.env.ref('stock.action_report_delivery').report_action(self)
def action_print_packges(self):
return self.env.ref('stock.action_report_picking_packages').report_action(self)
def _get_stock_barcode_data(self):
# Avoid to get the products full name because code and name are separate in the barcode app.
self = self.with_context(display_default_code=False)
move_lines = self.move_line_ids
lots = move_lines.lot_id
partners = move_lines.owner_id | self.partner_id
# Fetch all implied products in `self` and adds last used products to avoid additional rpc.
products = self.move_ids.product_id | move_lines.product_id
uoms = products.uom_id | move_lines.product_uom_id | move_lines.packaging_uom_id | products.uom_ids
# If UoM setting is active, fetch all UoM's data.
if self.env.user.has_group('uom.group_uom'):
uoms |= self.env['uom.uom'].search([])
product_uoms = products.product_uom_ids
# Fetch `stock.location`
source_locations = self.env['stock.location'].search([('id', 'child_of', self.location_id.ids)])
destination_locations = self.env['stock.location'].search([('id', 'child_of', self.location_dest_id.ids)])
package_locations = self.env['stock.location'].search([('id', 'child_of', self.location_dest_id.ids), ('usage', '!=', 'customer')])
locations = self.location_id | self.location_dest_id | move_lines.location_id | move_lines.location_dest_id | source_locations | destination_locations
# Fetch `stock.package` and `stock.package.type` if group_tracking_lot.
packages = self.env['stock.package']
package_types = self.env['stock.package.type']
if self.env.user.has_group('stock.group_tracking_lot'):
packages |= move_lines.package_id
packages |= self.env['stock.package'].search([('id', 'parent_of', move_lines.package_id.ids)])
packages |= self.env['stock.package'].browse(move_lines.result_package_id._get_all_package_dest_ids())
packages |= self.env['stock.package'].with_context(pack_locs=package_locations.ids)._get_usable_packages()
package_types = package_types.search([])
data = {
"records": {
"stock.picking": self.read(self._get_fields_stock_barcode(), load=False),
"stock.picking.type": self.picking_type_id.read(self.picking_type_id._get_fields_stock_barcode(), load=False),
"stock.move": self.move_ids.read(self.move_ids._get_fields_stock_barcode(), load=False),
"stock.move.line": move_lines.read(move_lines._get_fields_stock_barcode(), load=False),
# `self` can be a record set (e.g.: a picking batch), set only the first partner in the context.
"product.product": products.with_context(partner_id=self[:1].partner_id.id).read(products._get_fields_stock_barcode(), load=False),
"res.partner": partners.read(partners._get_fields_stock_barcode(), load=False),
"stock.location": locations.read(locations._get_fields_stock_barcode(), load=False),
"stock.package.type": package_types.read(package_types._get_fields_stock_barcode(), False),
"stock.package": packages.read(packages._get_fields_stock_barcode(), load=False),
"stock.lot": lots.read(lots._get_fields_stock_barcode(), load=False),
"uom.uom": uoms.read(uoms._get_fields_stock_barcode(), load=False),
"product.uom": product_uoms.read(product_uoms._get_fields_stock_barcode(), load=False),
},
"nomenclature_id": [self.env.company.nomenclature_id.id],
"source_location_ids": source_locations.ids,
"destination_locations_ids": destination_locations.ids,
}
# Extracts pickings' note if it's empty HTML.
for picking in data['records']['stock.picking']:
picking['note'] = False if is_html_empty(picking['note']) else html2plaintext(picking['note'])
data['config'] = self.picking_type_id._get_barcode_config()
if self._should_ignore_backorders():
data['config']['create_backorder'] = 'never'
data['line_view_id'] = self.env.ref('stock_barcode.stock_move_line_product_selector').id
data['form_view_id'] = self.env.ref('stock_barcode.stock_picking_barcode').id
data['scrap_view_id'] = self.env.ref('stock_barcode.scrap_product_selector').id
data['package_view_id'] = self.env.ref('stock_barcode.stock_quant_barcode_kanban').id
return data
@api.model
def _create_new_picking(self, picking_type):
""" Create a new picking for the given picking type.
:param picking_type:
:type picking_type: :class:`~odoo.addons.stock.models.stock_picking.StockPickingType`
:return: a new picking
:rtype: :class:`~odoo.addons.stock.models.stock_picking.StockPicking`
"""
# Find source and destination Locations
location_dest_id, location_id = picking_type.warehouse_id._get_partner_locations()
if picking_type.default_location_src_id:
location_id = picking_type.default_location_src_id
if picking_type.default_location_dest_id:
location_dest_id = picking_type.default_location_dest_id
# Create and confirm the picking
return self.env['stock.picking'].create({
'user_id': False,
'picking_type_id': picking_type.id,
'location_id': location_id.id,
'location_dest_id': location_dest_id.id,
})
def _get_fields_stock_barcode(self):
""" List of fields on the stock.picking object that are needed by the
client action. The purpose of this function is to be overridden in order
to inject new fields to the client action.
"""
return [
'company_id',
'location_dest_id',
'location_id',
'move_ids',
'move_line_ids',
'name',
'note',
'partner_id',
'picking_type_code',
'picking_type_entire_packs',
'picking_type_id',
'picking_warning_text',
'return_id',
'signature',
'state',
'use_create_lots',
'use_existing_lots',
'user_id',
]
@api.model
def filter_on_barcode(self, barcode):
""" Searches ready pickings for the scanned product/package/lot/packaging.
"""
barcode_type = None
nomenclature = self.env.company.nomenclature_id
if nomenclature.is_gs1_nomenclature:
try:
parsed_results = nomenclature.parse_barcode(barcode)
except ValidationError:
parsed_results = False
if parsed_results:
# filter with the last feasible rule
for result in parsed_results[::-1]:
if result['type'] in ('product', 'package', 'lot'):
barcode_type = result['type']
break
active_id = self.env.context.get('active_id')
picking_type = self.env['stock.picking.type'].browse(self.env.context.get('active_id'))
base_domain = [
('picking_type_id', '=', picking_type.id),
('state', 'not in', ['cancel', 'done', 'draft'])
]
is_lot_enabled = self.env.user.has_group('stock.group_production_lot')
is_pack_enabled = self.env.user.has_group('stock.group_tracking_lot')
is_uom_enabled = self.env.user.has_group('uom.group_uom')
picking_nums = 0
additional_context = {'active_id': active_id}
if barcode_type == 'product' or not barcode_type:
product = self.env['product.product'].search([('barcode', '=', barcode)], limit=1)
if not product and is_uom_enabled: # Packaging barcode is also of type 'product' (barcodes unique accross product & packaging)
product_packaging = self.env['product.uom'].search([('barcode', '=', barcode)], limit=1)
product = product_packaging.product_id # identify product linked with a packaging barcode
if product:
picking_nums = self.search_count(base_domain + [('product_id', '=', product.id)])
additional_context['search_default_product_id'] = product.id
if is_pack_enabled and (barcode_type == 'package' or (not barcode_type and not picking_nums)):
package = self.env['stock.package'].search([('name', '=', barcode)], limit=1)
if package:
pack_domain = ['|', ('move_line_ids.package_id', '=', package.id), ('move_line_ids.result_package_id', '=', package.id)]
picking_nums = self.search_count(base_domain + pack_domain)
additional_context['search_default_move_line_ids'] = barcode
if is_lot_enabled and (barcode_type == 'lot' or (not barcode_type and not picking_nums)):
lot = self.env['stock.lot'].search([
('name', '=', barcode),
'|', ('company_id', '=', False), ('company_id', '=', picking_type.company_id.id),
], limit=1)
if lot:
lot_domain = [('move_line_ids.lot_id', '=', lot.id)]
picking_nums = self.search_count(base_domain + lot_domain)
additional_context['search_default_lot_id'] = lot.id
if not barcode_type and not picking_nums: # Nothing found yet, try to find picking by name.
picking_nums = self.search_count(base_domain + [('name', '=', barcode)])
additional_context['search_default_name'] = barcode
if not picking_nums:
if barcode_type:
return {
'warning': {
'message': _("No %(picking_type)s ready for this %(barcode_type)s", picking_type=picking_type.name, barcode_type=barcode_type),
}
}
title, message = self._get_barcode_filter_warning(is_lot_enabled, is_pack_enabled, is_uom_enabled, barcode)
return {
'warning': {
'title': title,
'message': message,
}
}
action = picking_type._get_action('stock_barcode.stock_picking_action_kanban')
action['context'].update(additional_context)
return {'action': action}
def _get_barcode_filter_warning(self, is_lot_enabled, is_pack_enabled, is_uom_enabled, barcode):
if is_lot_enabled:
if is_pack_enabled:
if is_uom_enabled:
return (_("No product, lot, packaging, or package found for barcode %s", barcode),
_("Scan a product, a lot, a packaging, or a package to filter the transfers."))
return (_("No product, lot, or package found for barcode %s", barcode),
_("Scan a product, a lot, or a package to filter the transfers."))
elif is_uom_enabled:
return (_("No product, lot, or packaging found for barcode %s", barcode),
_("Scan a product, a lot, or a packaging to filter the transfers."))
return (_("No product or lot found for barcode %s", barcode),
_("Scan a product or a lot to filter the transfers."))
elif is_pack_enabled:
if is_uom_enabled:
return (_("No product, package, or packaging found for barcode %s", barcode),
_("Scan a product, a package, or a packaging to filter the transfers."))
return (_("No product or package found for barcode %s", barcode),
_("Scan a product or a package to filter the transfers."))
elif is_uom_enabled:
return (_("No product or packaging found for barcode %s", barcode),
_("Scan a product or a packaging to filter the transfers."))
return (_("No product found for barcode %s", barcode),
_("Scan a product to filter the transfers."))