Files
tradon/modules/sale_rental/stock.py
2026-03-14 09:42:12 +00:00

132 lines
4.7 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.i18n import gettext
from trytond.model import ModelView, Workflow, fields
from trytond.modules.stock.exceptions import MoveValidationError
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
class Location(metaclass=PoolMeta):
__name__ = 'stock.location'
rental_location = fields.Many2One(
'stock.location', "Rental",
states={
'invisible': Eval('type') != 'warehouse',
'required': Eval('type') == 'warehouse',
},
domain=[
('type', '=', 'rental'),
],
help="The destination location for stock rent.")
rental_picking_location = fields.Many2One(
'stock.location', "Rental Picking",
states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'storage'),
('parent', 'child_of', [Eval('id', -1)]),
],
help="Where the rented assets are picked from.\n"
"Leave empty to use the warehouse storage location.")
rental_return_location = fields.Many2One(
'stock.location', "Rental Return",
states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'storage'),
('parent', 'child_of', [Eval('id', -1)]),
],
help="Where the rented assets are returned to.\n"
"Leave empty to use the warehouse storage location.")
class Move(metaclass=PoolMeta):
__name__ = 'stock.move'
sale_rental_lines_outgoing = fields.Many2Many(
'sale.rental.line-outgoing-stock.move', 'move', 'line',
"Outgoing Rental Lines", readonly=True,
states={
'invisible': ~Eval('sale_rental_lines_outgoing'),
})
sale_rental_lines_incoming = fields.Many2Many(
'sale.rental.line-incoming-stock.move', 'move', 'line',
"Incoming Rental Lines", readonly=True,
states={
'invisible': ~Eval('sale_rental_lines_incoming'),
})
@classmethod
def _get_origin(cls):
return super()._get_origin() + ['sale.rental']
@classmethod
def view_attributes(cls):
return super().view_attributes() + [
('//page[@id="sale_rental"]', 'states', {
'invisible': (
~Eval('sale_rental_lines_outgoing')
& ~Eval('sale_rental_lines_incoming')),
}),
]
@classmethod
@ModelView.button
@Workflow.transition('done')
def do(cls, moves):
pool = Pool()
SaleRental = pool.get('sale.rental')
super().do(moves)
sale_rentals = {
m.origin for m in moves if isinstance(m.origin, SaleRental)}
if sale_rentals:
sale_rentals = SaleRental.browse(list(sale_rentals))
SaleRental.try_picked_up(sale_rentals)
SaleRental.try_done(sale_rentals)
@classmethod
def validate_fields(cls, moves, field_names):
super().validate_fields(moves, field_names)
cls.check_rental_quantities(moves, field_names)
@classmethod
def check_rental_quantities(cls, moves, field_names):
pool = Pool()
Lang = pool.get('ir.lang')
SaleRental = pool.get('sale.rental')
SaleRentalLine = pool.get('sale.rental.line')
UoM = pool.get('product.uom')
if field_names and not (field_names & {
'state', 'origin', 'quantity', 'unit'}):
return
sale_rental_lines = set()
for move in moves:
if move.state != 'done' or not isinstance(move.origin, SaleRental):
continue
for lines in filter(None, [
move.sale_rental_lines_outgoing,
move.sale_rental_lines_incoming]):
sale_rental_lines.update(lines)
quantity = move.unit.round(sum(
UoM.compute_qty(
l.unit, l.quantity, move.unit, round=False)
for l in lines))
if quantity != move.quantity:
lang = Lang.get()
raise MoveValidationError(gettext(
'sale_rental.msg_stock_move_rental_lines_quantity',
move=move.rec_name,
quantity=lang.format_number_symbol(
quantity, move.unit)))
if sale_rental_lines:
sale_rental_lines = SaleRentalLine.browse(list(sale_rental_lines))
SaleRentalLine._validate(
sale_rental_lines, ['actual_start', 'actual_end'])