forked from Mapan/odoo17e
162 lines
7.6 KiB
Python
162 lines
7.6 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.tools import float_round
|
|
|
|
|
|
class ProductProduct(models.Model):
|
|
_inherit = 'product.product'
|
|
|
|
fsm_quantity = fields.Float('Material Quantity', compute="_compute_fsm_quantity", inverse="_inverse_fsm_quantity", search="_search_fsm_quantity")
|
|
fsm_partner_price = fields.Float('Partner Price', compute="_compute_fsm_partner_price")
|
|
fsm_partner_price_currency_id = fields.Many2one('res.currency', compute="_compute_fsm_partner_price")
|
|
|
|
@api.depends_context('fsm_task_id')
|
|
@api.depends('fsm_quantity')
|
|
def _compute_fsm_partner_price(self):
|
|
task = self._get_contextual_fsm_task()
|
|
price_per_product = {}
|
|
product_per_quantity = defaultdict(lambda: self.env['product.product'])
|
|
for product in self:
|
|
product_per_quantity[product.fsm_quantity] |= product
|
|
for quantity, products in product_per_quantity.items():
|
|
price_per_product = {**price_per_product, **task.pricelist_id._get_products_price(products, quantity)}
|
|
for product in self:
|
|
product.fsm_partner_price = price_per_product.get(product.id, product.list_price)
|
|
product.fsm_partner_price_currency_id = task.currency_id or product.currency_id
|
|
|
|
@api.depends_context('fsm_task_id')
|
|
def _compute_fsm_quantity(self):
|
|
task = self._get_contextual_fsm_task()
|
|
if task:
|
|
|
|
SaleOrderLine = self.env['sale.order.line']
|
|
if self.user_has_groups('project.group_project_user'):
|
|
task = task.sudo()
|
|
SaleOrderLine = SaleOrderLine.sudo()
|
|
|
|
products_qties = SaleOrderLine._read_group(
|
|
[('id', 'in', task.sale_order_id.order_line.ids), ('task_id', '=', task.id), ('product_id', '!=', False)],
|
|
['product_id'], ['product_uom_qty:sum'])
|
|
qty_dict = {product.id: product_uom_qty_sum for product, product_uom_qty_sum in products_qties}
|
|
for product in self:
|
|
product.fsm_quantity = qty_dict.get(product.id, 0)
|
|
else:
|
|
self.fsm_quantity = False
|
|
|
|
def _inverse_fsm_quantity(self):
|
|
task = self._get_contextual_fsm_task()
|
|
if task:
|
|
SaleOrderLine_sudo = self.env['sale.order.line'].sudo()
|
|
sale_lines_read_group = SaleOrderLine_sudo._read_group([
|
|
('order_id', '=', task.sale_order_id.id),
|
|
('product_id', 'in', self.ids),
|
|
('task_id', '=', task.id)],
|
|
['product_id', 'sequence'],
|
|
['id:array_agg'])
|
|
sale_lines_per_product = defaultdict(lambda: self.env['sale.order.line'])
|
|
for product, __, ids in sale_lines_read_group:
|
|
sale_lines_per_product[product.id] |= SaleOrderLine_sudo.browse(ids)
|
|
for product in self:
|
|
sale_lines = sale_lines_per_product.get(product.id, self.env['sale.order.line'])
|
|
all_editable_lines = sale_lines.filtered(lambda l: l.qty_delivered == 0 or l.qty_delivered_method == 'manual' or not l.order_id.locked)
|
|
diff_qty = product.fsm_quantity - sum(sale_lines.mapped('product_uom_qty'))
|
|
if all_editable_lines: # existing line: change ordered qty (and delivered, if delivered method)
|
|
if diff_qty > 0:
|
|
vals = {
|
|
'product_uom_qty': all_editable_lines[0].product_uom_qty + diff_qty,
|
|
}
|
|
if product.service_type == 'manual':
|
|
vals['qty_delivered'] = all_editable_lines[0].product_uom_qty + diff_qty
|
|
all_editable_lines[0].with_context(fsm_no_message_post=True).write(vals)
|
|
continue
|
|
# diff_qty is negative, we remove the quantities from existing editable lines:
|
|
for line in all_editable_lines:
|
|
new_line_qty = max(0, line.product_uom_qty + diff_qty)
|
|
diff_qty += line.product_uom_qty - new_line_qty
|
|
if product.service_type == 'manual':
|
|
line.with_context(fsm_no_message_post=True).qty_delivered = new_line_qty
|
|
line.with_context(fsm_no_message_post=True).product_uom_qty = new_line_qty
|
|
if diff_qty == 0:
|
|
break
|
|
elif diff_qty > 0: # create new SOL
|
|
vals = {
|
|
'order_id': task.sale_order_id.id,
|
|
'product_id': product.id,
|
|
'product_uom_qty': diff_qty,
|
|
'product_uom': product.uom_id.id,
|
|
'task_id': task.id
|
|
}
|
|
if product.service_type == 'manual':
|
|
vals['qty_delivered'] = diff_qty
|
|
|
|
sol_sudo = SaleOrderLine_sudo.create(vals)
|
|
if task.sale_order_id.pricelist_id.discount_policy != 'without_discount':
|
|
sol_sudo.discount = 0.0
|
|
|
|
@api.model
|
|
def _search_fsm_quantity(self, operator, value):
|
|
if not (isinstance(value, int) or (isinstance(value, bool) and value is False)):
|
|
raise ValueError(_('Invalid value: %s', value))
|
|
if operator not in ('=', '!=', '<=', '<', '>', '>=') or (operator == '!=' and value is False):
|
|
raise ValueError(_('Invalid operator: %s', operator))
|
|
|
|
task = self._get_contextual_fsm_task()
|
|
if not task:
|
|
return []
|
|
op = 'inselect'
|
|
if value is False:
|
|
value = 0
|
|
operator = '>='
|
|
op = 'not inselect'
|
|
query = """
|
|
SELECT sol.product_id
|
|
FROM sale_order_line sol
|
|
LEFT JOIN sale_order so
|
|
ON sol.order_id = so.id
|
|
LEFT JOIN project_task task
|
|
ON so.id = task.sale_order_id
|
|
WHERE task.id = %s
|
|
AND sol.product_uom_qty {} %s
|
|
""".format(operator)
|
|
return [('id', op, (query, (task.id, value)))]
|
|
|
|
@api.model
|
|
def _get_contextual_fsm_task(self):
|
|
task_id = self.env.context.get('fsm_task_id')
|
|
if task_id:
|
|
return self.env['project.task'].browse(task_id)
|
|
return self.env['project.task']
|
|
|
|
def set_fsm_quantity(self, quantity):
|
|
task = self._get_contextual_fsm_task()
|
|
# project user with no sale rights should be able to change material quantities
|
|
if not task or quantity and quantity < 0 or not self.user_has_groups('project.group_project_user'):
|
|
return
|
|
self = self.sudo()
|
|
|
|
# don't add material on locked SO
|
|
if task.sale_order_id.sudo().locked:
|
|
return False
|
|
# ensure that the task is linked to a sale order
|
|
task._fsm_ensure_sale_order()
|
|
wizard_product_lot = self.action_assign_serial(from_onchange=True)
|
|
if wizard_product_lot:
|
|
return wizard_product_lot
|
|
self.fsm_quantity = float_round(quantity or 0, precision_rounding=self.uom_id.rounding)
|
|
return True
|
|
|
|
# Is override by fsm_stock to manage lot
|
|
def action_assign_serial(self, from_onchange=False):
|
|
return False
|
|
|
|
def fsm_add_quantity(self):
|
|
return self.set_fsm_quantity(self.sudo().fsm_quantity + 1)
|
|
|
|
def fsm_remove_quantity(self):
|
|
fsm_product_qty = self.sudo().fsm_quantity
|
|
fsm_product_qty = fsm_product_qty - 1 if fsm_product_qty > 1 else 0
|
|
return self.set_fsm_quantity(fsm_product_qty)
|