<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _

from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_round, float_compare


class PackOperation(models.Model):
    _name = "stock.pack.operation"
    _description = "Packing Operation"
    _order = "result_package_id desc, id"

    # TDE FIXME: strange, probably to remove
    def _get_default_from_loc(self):
        default_loc = self.env.context.get('default_location_id')
        if default_loc:
            return self.env['stock.location'].browse(default_loc).name

    # TDE FIXME: strange, probably to remove
    def _get_default_to_loc(self):
        default_loc = self.env.context.get('default_location_dest_id')
        if default_loc:
            return self.env['stock.location'].browse(default_loc).name

    picking_id = fields.Many2one(
        'stock.picking', 'Stock Picking',
        required=True,
        help='The stock operation where the packing has been made')
    product_id = fields.Many2one('product.product', 'Product', ondelete="cascade")
    product_uom_id = fields.Many2one('product.uom', 'Unit of Measure')
    product_qty = fields.Float('To Do', default=0.0, digits=dp.get_precision('Product Unit of Measure'), required=True)
    ordered_qty = fields.Float('Ordered Quantity', digits=dp.get_precision('Product Unit of Measure'))
    qty_done = fields.Float('Done', default=0.0, digits=dp.get_precision('Product Unit of Measure'))
    is_done = fields.Boolean(compute='_compute_is_done', string='Done', readonly=False, oldname='processed_boolean')
    package_id = fields.Many2one('stock.quant.package', 'Source Package')
    pack_lot_ids = fields.One2many('stock.pack.operation.lot', 'operation_id', 'Lots/Serial Numbers Used')
    result_package_id = fields.Many2one(
        'stock.quant.package', 'Destination Package',
        ondelete='cascade', required=False,
        help="If set, the operations are packed into this package")
    date = fields.Datetime('Date', default=fields.Date.context_today, required=True)
    owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants")
    linked_move_operation_ids = fields.One2many(
        'stock.move.operation.link', 'operation_id', string='Linked Moves',
        readonly=True,
        help='Moves impacted by this operation for the computation of the remaining quantities')
    remaining_qty = fields.Float(
        compute='_get_remaining_qty', string="Remaining Qty", digits=0,
        help="Remaining quantity in default UoM according to moves matched with this operation.")
    location_id = fields.Many2one('stock.location', 'Source Location', required=True)
    location_dest_id = fields.Many2one('stock.location', 'Destination Location', required=True)
    picking_source_location_id = fields.Many2one('stock.location', related='picking_id.location_id')
    picking_destination_location_id = fields.Many2one('stock.location', related='picking_id.location_dest_id')
    # TDE FIXME: unnecessary fields IMO, to remove
    from_loc = fields.Char(compute='_compute_location_description', default=_get_default_from_loc, string='From')
    to_loc = fields.Char(compute='_compute_location_description', default=_get_default_to_loc, string='To')
    fresh_record = fields.Boolean('Newly created pack operation', default=True)
    lots_visible = fields.Boolean(compute='_compute_lots_visible')
    state = fields.Selection(selection=[
        ('draft', 'Draft'),
        ('cancel', 'Cancelled'),
        ('waiting', 'Waiting Another Operation'),
        ('confirmed', 'Waiting Availability'),
        ('partially_available', 'Partially Available'),
        ('assigned', 'Available'),
        ('done', 'Done')], related='picking_id.state')

    @api.one
    def _compute_is_done(self):
        self.is_done = self.qty_done > 0.0

    @api.onchange('is_done')
    def on_change_is_done(self):
        if not self.product_id:
            if self.is_done and self.qty_done == 0:
                self.qty_done = 1.0
            if not self.is_done and self.qty_done != 0:
                self.qty_done = 0.0

    def _get_remaining_prod_quantities(self):
        '''Get the remaining quantities per product on an operation with a package. This function returns a dictionary'''
        # TDE CLEANME: merge with _get_all_products_quantities in quant to ease code understanding + clean code
        # if the operation doesn't concern a package, it's not relevant to call this function
        if not self.package_id or self.product_id:
            return {self.product_id: self.remaining_qty}
        # get the total of products the package contains
        res = self.package_id._get_all_products_quantities()
        # reduce by the quantities linked to a move
        for record in self.linked_move_operation_ids:
            if record.move_id.product_id.id not in res:
                res[record.move_id.product_id] = 0
            res[record.move_id.product_id] -= record.qty
        return res

    @api.one
    def _get_remaining_qty(self):
        if self.package_id and not self.product_id:
            # dont try to compute the remaining quantity for packages because it's not relevant (a package could include different products).
            # should use _get_remaining_prod_quantities instead
            # TDE FIXME: actually resolve the comment hereabove
            self.remaining_qty = 0
        else:
            qty = self.product_qty
            if self.product_uom_id:
                qty = self.product_uom_id._compute_quantity(self.product_qty, self.product_id.uom_id)
            for record in self.linked_move_operation_ids:
                qty -= record.qty
            self.remaining_qty = float_round(qty, precision_rounding=self.product_id.uom_id.rounding)

    @api.multi
    def _compute_location_description(self):
        for operation, operation_sudo in zip(self, self.sudo()):
            operation.from_loc = '%s%s' % (operation_sudo.location_id.name, operation.product_id and operation_sudo.package_id.name or '')
            operation.to_loc = '%s%s' % (operation_sudo.location_dest_id.name, operation_sudo.result_package_id.name or '')

    @api.one
    def _compute_lots_visible(self):
        if self.pack_lot_ids:
            self.lots_visible = True
        elif self.picking_id.picking_type_id and self.product_id.tracking != 'none':  # TDE FIXME: not sure correctly migrated<commit_msg>[FIX] stock: correct PY3 incompatibility introduced by previous forward-port<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _

from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_round, float_compare
from odoo.tools.compat import izip


class PackOperation(models.Model):
    _name = "stock.pack.operation"
    _description = "Packing Operation"
    _order = "result_package_id desc, id"

    # TDE FIXME: strange, probably to remove
    def _get_default_from_loc(self):
        default_loc = self.env.context.get('default_location_id')
        if default_loc:
            return self.env['stock.location'].browse(default_loc).name

    # TDE FIXME: strange, probably to remove
    def _get_default_to_loc(self):
        default_loc = self.env.context.get('default_location_dest_id')
        if default_loc:
            return self.env['stock.location'].browse(default_loc).name

    picking_id = fields.Many2one(
        'stock.picking', 'Stock Picking',
        required=True,
        help='The stock operation where the packing has been made')
    product_id = fields.Many2one('product.product', 'Product', ondelete="cascade")
    product_uom_id = fields.Many2one('product.uom', 'Unit of Measure')
    product_qty = fields.Float('To Do', default=0.0, digits=dp.get_precision('Product Unit of Measure'), required=True)
    ordered_qty = fields.Float('Ordered Quantity', digits=dp.get_precision('Product Unit of Measure'))
    qty_done = fields.Float('Done', default=0.0, digits=dp.get_precision('Product Unit of Measure'))
    is_done = fields.Boolean(compute='_compute_is_done', string='Done', readonly=False, oldname='processed_boolean')
    package_id = fields.Many2one('stock.quant.package', 'Source Package')
    pack_lot_ids = fields.One2many('stock.pack.operation.lot', 'operation_id', 'Lots/Serial Numbers Used')
    result_package_id = fields.Many2one(
        'stock.quant.package', 'Destination Package',
        ondelete='cascade', required=False,
        help="If set, the operations are packed into this package")
    date = fields.Datetime('Date', default=fields.Date.context_today, required=True)
    owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants")
    linked_move_operation_ids = fields.One2many(
        'stock.move.operation.link', 'operation_id', string='Linked Moves',
        readonly=True,
        help='Moves impacted by this operation for the computation of the remaining quantities')
    remaining_qty = fields.Float(
        compute='_get_remaining_qty', string="Remaining Qty", digits=0,
        help="Remaining quantity in default UoM according to moves matched with this operation.")
    location_id = fields.Many2one('stock.location', 'Source Location', required=True)
    location_dest_id = fields.Many2one('stock.location', 'Destination Location', required=True)
    picking_source_location_id = fields.Many2one('stock.location', related='picking_id.location_id')
    picking_destination_location_id = fields.Many2one('stock.location', related='picking_id.location_dest_id')
    # TDE FIXME: unnecessary fields IMO, to remove
    from_loc = fields.Char(compute='_compute_location_description', default=_get_default_from_loc, string='From')
    to_loc = fields.Char(compute='_compute_location_description', default=_get_default_to_loc, string='To')
    fresh_record = fields.Boolean('Newly created pack operation', default=True)
    lots_visible = fields.Boolean(compute='_compute_lots_visible')
    state = fields.Selection(selection=[
        ('draft', 'Draft'),
        ('cancel', 'Cancelled'),
        ('waiting', 'Waiting Another Operation'),
        ('confirmed', 'Waiting Availability'),
        ('partially_available', 'Partially Available'),
        ('assigned', 'Available'),
        ('done', 'Done')], related='picking_id.state')

    @api.one
    def _compute_is_done(self):
        self.is_done = self.qty_done > 0.0

    @api.onchange('is_done')
    def on_change_is_done(self):
        if not self.product_id:
            if self.is_done and self.qty_done == 0:
                self.qty_done = 1.0
            if not self.is_done and self.qty_done != 0:
                self.qty_done = 0.0

    def _get_remaining_prod_quantities(self):
        '''Get the remaining quantities per product on an operation with a package. This function returns a dictionary'''
        # TDE CLEANME: merge with _get_all_products_quantities in quant to ease code understanding + clean code
        # if the operation doesn't concern a package, it's not relevant to call this function
        if not self.package_id or self.product_id:
            return {self.product_id: self.remaining_qty}
        # get the total of products the package contains
        res = self.package_id._get_all_products_quantities()
        # reduce by the quantities linked to a move
        for record in self.linked_move_operation_ids:
            if record.move_id.product_id.id not in res:
                res[record.move_id.product_id] = 0
            res[record.move_id.product_id] -= record.qty
        return res

    @api.one
    def _get_remaining_qty(self):
        if self.package_id and not self.product_id:
            # dont try to compute the remaining quantity for packages because it's not relevant (a package could include different products).
            # should use _get_remaining_prod_quantities instead
            # TDE FIXME: actually resolve the comment hereabove
            self.remaining_qty = 0
        else:
            qty = self.product_qty
            if self.product_uom_id:
                qty = self.product_uom_id._compute_quantity(self.product_qty, self.product_id.uom_id)
            for record in self.linked_move_operation_ids:
                qty -= record.qty
            self.remaining_qty = float_round(qty, precision_rounding=self.product_id.uom_id.rounding)

    @api.multi
    def _compute_location_description(self):
        for operation, operation_sudo in izip(self, self.sudo()):
            operation.from_loc = '%s%s' % (operation_sudo.location_id.name, operation.product_id and operation_sudo.package_id.name or '')
            operation.to_loc = '%s%s' % (operation_sudo.location_dest_id.name, operation_sudo.result_package_id.name or '')

    @api.one
    def _compute_lots_visible(self):
        if self.pack_lot_ids:
            self.lots_visible = True
        elif self.picking_id.picking_type_id and self.product_id.tracking != 'none':  # TDE FIXME: not sure correctly migrated
{'fraction_non_alphanumeric': 0.10110918282652984, 'fraction_numerical': 0.004300913000830001, 'mean_word_length': 3.483761840324763, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1448667', 'n_tokens_mistral': 3963, 'n_tokens_neox': 3881, 'n_words': 955}