1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/industry_fsm_sale/models/product_product.py
2024-12-10 09:04:09 +07:00

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)