forked from Mapan/odoo17e
147 lines
6.8 KiB
Python
147 lines
6.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import defaultdict
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError, AccessError
|
|
|
|
class ProductProduct(models.Model):
|
|
_inherit = 'product.product'
|
|
|
|
serial_missing = fields.Boolean(compute='_compute_serial_missing')
|
|
quantity_decreasable = fields.Boolean(compute='_compute_quantity_decreasable')
|
|
quantity_decreasable_sum = fields.Integer(compute='_compute_quantity_decreasable')
|
|
|
|
@api.depends('fsm_quantity')
|
|
@api.depends_context('fsm_task_id')
|
|
def _compute_serial_missing(self):
|
|
task_id = self.env.context.get('fsm_task_id')
|
|
if not task_id:
|
|
self.serial_missing = False
|
|
return
|
|
|
|
task = self.env['project.task'].browse(task_id)
|
|
sale_lines = self.env['sale.order.line'].sudo().search([('order_id', '=', task.sale_order_id.id), ('task_id', '=', task.id)])
|
|
for product in self:
|
|
if product.tracking != 'none':
|
|
sale_product = sale_lines.filtered(lambda sale: sale.product_id == product)
|
|
product.serial_missing = sale_product.filtered(lambda p: not p.fsm_lot_id and p.product_uom_qty > 0 and not p.qty_delivered)
|
|
else:
|
|
product.serial_missing = False
|
|
|
|
@api.depends('fsm_quantity')
|
|
@api.depends_context('fsm_task_id', 'uid')
|
|
def _compute_quantity_decreasable(self):
|
|
# Compute if a product is already delivered. If a quantity is not yet delivered,
|
|
# we can decrease the quantity
|
|
task_id = self.env.context.get('fsm_task_id')
|
|
if not task_id:
|
|
self.quantity_decreasable = True
|
|
self.quantity_decreasable_sum = 0
|
|
return
|
|
|
|
task = self.env['project.task'].browse(task_id)
|
|
if not task:
|
|
self.quantity_decreasable = False
|
|
self.quantity_decreasable_sum = 0
|
|
return
|
|
elif task.sale_order_id.sudo().state in ['draft', 'sent']:
|
|
self.quantity_decreasable = True
|
|
self.quantity_decreasable_sum = 0
|
|
return
|
|
|
|
moves_read_group = self.env['stock.move'].sudo()._read_group(
|
|
[
|
|
('sale_line_id.order_id', '=', task.sale_order_id.id),
|
|
('sale_line_id.task_id', '=', task.id),
|
|
('product_id', 'in', self.ids),
|
|
('warehouse_id', '=', self.env.user._get_default_warehouse_id().id),
|
|
('state', 'not in', ['done', 'cancel']),
|
|
],
|
|
['product_id'],
|
|
['product_uom_qty:sum'],
|
|
)
|
|
move_per_product = {product.id: product_uom_qty for product, product_uom_qty in moves_read_group}
|
|
|
|
# If no move line can be found, look into the SOL in case one line has no move and could be used to decrease the qty
|
|
sale_lines_read_group = self.env['sale.order.line'].sudo()._read_group(
|
|
[
|
|
('order_id', '=', task.sale_order_id.id),
|
|
('task_id', '=', task.id),
|
|
('product_id', 'in', self.ids),
|
|
('move_ids', '=', False),
|
|
],
|
|
['product_id'],
|
|
['product_uom_qty:sum', 'qty_delivered:sum'],
|
|
)
|
|
product_uom_qty_per_product = {
|
|
product.id: product_uom_qty - qty_delivered if product.service_policy != 'delivered_manual' else product_uom_qty
|
|
for product, product_uom_qty, qty_delivered in sale_lines_read_group
|
|
if product_uom_qty > qty_delivered or product.service_policy == 'delivered_manual'
|
|
}
|
|
|
|
for product in self:
|
|
product.quantity_decreasable_sum = move_per_product.get(product.id, product_uom_qty_per_product.get(product.id, 0))
|
|
product.quantity_decreasable = product.quantity_decreasable_sum > 0
|
|
|
|
def _inverse_fsm_quantity(self):
|
|
super(ProductProduct, self.with_context(industry_fsm_stock_set_quantity=True))._inverse_fsm_quantity()
|
|
|
|
def write(self, vals):
|
|
if 'fsm_quantity' in vals and any(product.fsm_quantity - vals['fsm_quantity'] > product.quantity_decreasable_sum for product in self):
|
|
raise UserError(_('The ordered quantity cannot be decreased below the amount already delivered. Instead, create a return in your inventory.'))
|
|
return super().write(vals)
|
|
|
|
def action_assign_serial(self, from_onchange=False):
|
|
""" Opens a wizard to assign SN's name on each move lines.
|
|
"""
|
|
self.ensure_one()
|
|
if self.tracking == 'none':
|
|
return False
|
|
# If the wizard is triggered from the menu, an error should not be raised, the wizard will be in readonly instead.
|
|
if from_onchange and not self.env.user.has_group('stock.group_stock_user'):
|
|
raise AccessError(_("Adding or updating this product is restricted due to its tracked status. Your current access rights do not allow you to perform these actions. "
|
|
"Please contact your administrator to request the necessary permissions."))
|
|
|
|
task_id = self.env.context.get('fsm_task_id')
|
|
task = self.env['project.task'].browse(task_id)
|
|
# project user with no sale rights should be able to change material quantities
|
|
sale_lines = self.env['sale.order.line'].sudo().search([
|
|
('order_id', '=', task.sale_order_id.id), ('task_id', '=', task.id), ('product_id', '=', self.id), ('product_uom_qty', '>', 0)])
|
|
tracking_line_ids = [(0, 0, {
|
|
'lot_id': line.fsm_lot_id.id,
|
|
'quantity': line.product_uom_qty - line.qty_delivered,
|
|
'product_id': self.id,
|
|
'sale_order_line_id': line.id,
|
|
'company_id': task.sale_order_id.company_id.id,
|
|
}) for line in sale_lines.filtered(lambda sl: sl.product_uom_qty - sl.qty_delivered)]
|
|
|
|
lot_done_dict = defaultdict(int)
|
|
for move_line in sale_lines.move_ids.filtered(lambda m: m.state == 'done').move_line_ids:
|
|
lot_done_dict[move_line.lot_id.id] += move_line.quantity
|
|
|
|
tracking_validated_line_ids = [(0, 0, {
|
|
'lot_id': vals,
|
|
'quantity': lot_done_dict[vals],
|
|
'product_id': self.id,
|
|
'company_id': task.sale_order_id.company_id.id,
|
|
}) for vals in lot_done_dict]
|
|
|
|
validation = self.env['fsm.stock.tracking'].create({
|
|
'task_id': task_id,
|
|
'product_id': self.id,
|
|
'tracking_line_ids': tracking_line_ids,
|
|
'tracking_validated_line_ids': tracking_validated_line_ids,
|
|
'company_id': task.sale_order_id.company_id.id,
|
|
})
|
|
|
|
return {
|
|
'name': _('Validate Lot/Serial Number'),
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'new',
|
|
'res_model': 'fsm.stock.tracking',
|
|
'res_id': validation.id,
|
|
'views': [(False, 'form')]
|
|
}
|