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

150 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.osv import expression
class ProductProduct(models.Model):
_inherit = 'product.product'
def _compute_show_qty_status_button(self):
super()._compute_show_qty_status_button()
for product in self:
if product.rent_ok and not product.sale_ok:
product.show_forecasted_qty_status_button = False
@api.depends('type', 'rent_ok', 'qty_available', 'qty_in_rent')
@api.depends_context('sale_stock_renting_show_total_qty', 'allowed_company_ids')
def _compute_display_name(self):
super()._compute_display_name()
if self.env.context.get('sale_stock_renting_show_total_qty'):
storable_rental_products = self.filtered(
lambda product: product.rent_ok and product.type == 'product'
)
if not storable_rental_products:
return
# Rental/Stock qties only have to be computed on the current date to know the current
# total qty (in stock + in rental)
ctxt = {
'from_date': self.env.cr.now(),
'to_date': self.env.cr.now(),
}
# Generate new display_name results
for product in storable_rental_products.with_context(**ctxt):
total_qty = product.qty_available + product.qty_in_rent
if int(total_qty) == total_qty:
# Display as integer if float has no decimal value
total_qty = int(total_qty)
if total_qty in (0, 1):
product.display_name = _(
"%(product)s (%(qty)s item)",
product=product.display_name,
qty=total_qty,
)
else:
product.display_name = _(
"%(product)s (%(qty)s items)",
product=product.display_name,
qty=total_qty,
)
def _get_domain_locations_new(self, location_ids):
domain_quant, domain_move_in_loc, domain_move_out_loc = super()._get_domain_locations_new(location_ids)
if self.env.context.get('ignore_rental_returns'):
rental_loc_ids = self.env.companies.rental_loc_id.ids
domain_move_in_loc = expression.AND([domain_move_in_loc, [('location_id', 'not in', rental_loc_ids)]])
return domain_quant, domain_move_in_loc, domain_move_out_loc
def _get_qty_in_rent_domain(self):
"""Allow precising the warehouse_id to get qty currently in rent."""
if self.env.context.get('warehouse_id', False):
return super()._get_qty_in_rent_domain() + [
('order_id.warehouse_id', '=', int(self.env.context.get('warehouse_id')))
]
else:
return super()._get_qty_in_rent_domain()
def _get_unavailable_qty(self, fro, to=None, **kwargs):
"""Return max qty of self (unique) unavailable between fro and to.
Doesn't count already returned quantities.
:param datetime fro:
:param datetime to:
:param dict kwargs: search domain restrictions (ignored_soline_id, warehouse_id)
"""
def unavailable_qty(so_line):
return so_line.product_uom_qty - so_line.qty_returned
begins_during_period, ends_during_period, covers_period = self._get_active_rental_lines(fro, to, **kwargs)
active_lines_in_period = begins_during_period + ends_during_period
max_qty_rented = 0
# TODO is it more efficient to filter the records active in period
# or to make another search on all the sale order lines???
if active_lines_in_period:
for date in (begins_during_period.mapped('reservation_begin') + [fro]):
# If no soline in begins_during_period, we need to check at period beginning
# how much products are rented.
active_lines_at_date = active_lines_in_period.filtered(
lambda line: line.reservation_begin and line.reservation_begin <= date and line.return_date and line.return_date >= date)
qty_rented_at_date = sum(active_lines_at_date.mapped(unavailable_qty))
if qty_rented_at_date > max_qty_rented:
max_qty_rented = qty_rented_at_date
qty_always_in_rent_during_period = sum(covers_period.mapped(unavailable_qty)) if covers_period else 0
return max_qty_rented + qty_always_in_rent_during_period
def _get_active_rental_lines(self, fro, to, **kwargs):
self.ensure_one()
Reservation = self.env['sale.order.line']
domain = [
('is_rental', '=', True),
('product_id', '=', self.id),
('state', '=', 'sale'),
]
ignored_soline_id = kwargs.get('ignored_soline_id', False)
if ignored_soline_id:
domain += [('id', '!=', ignored_soline_id)]
warehouse_id = kwargs.get('warehouse_id', False)
if warehouse_id:
domain += [('order_id.warehouse_id', '=', warehouse_id)]
if not to or fro == to:
active_lines_at_time_fro = Reservation.search(domain + [
('reservation_begin', '<=', fro),
('return_date', '>=', fro)
])
return Reservation, Reservation, active_lines_at_time_fro
else:
begins_during_period = Reservation.search(domain + [
('reservation_begin', '>', fro),
('reservation_begin', '<', to)])
ends_during_period = Reservation.search(domain + [
('return_date', '>', fro),
('return_date', '<', to),
('id', 'not in', begins_during_period.ids)])
covers_period = Reservation.search(domain + [
('reservation_begin', '<=', fro),
('return_date', '>=', to)])
return begins_during_period, ends_during_period, covers_period
"""
Products with tracking (by serial number)
"""
def _get_unavailable_lots(self, fro=fields.Datetime.now(), to=None, **kwargs):
begins_during_period, ends_during_period, covers_period = self._get_active_rental_lines(fro, to, **kwargs)
return (begins_during_period + ends_during_period + covers_period).mapped('unavailable_lot_ids')
def action_view_rentals(self):
result = super().action_view_rentals()
result['context'].update({'sale_stock_renting_show_total_qty': 1})
return result