347 lines
11 KiB
Python
347 lines
11 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 datetime import datetime
|
|
from trytond.model import ModelView, Workflow, ModelSQL, fields
|
|
from trytond.pool import Pool
|
|
from trytond.pyson import Eval
|
|
from trytond.modules.company import CompanyReport
|
|
from trytond.wizard import Wizard, StateView, Button, StateTransition
|
|
from trytond.transaction import Transaction
|
|
from trytond.model.exceptions import AccessError
|
|
from trytond.i18n import gettext
|
|
|
|
STATES = {
|
|
'readonly': Eval('state') == 'checked'
|
|
}
|
|
STATES_LINE = {
|
|
'readonly': Eval('state') == 'loaded'
|
|
}
|
|
|
|
|
|
class ServiceKind(ModelSQL, ModelView):
|
|
'Service Kind'
|
|
__name__ = 'hotel.service.kind'
|
|
name = fields.Char('Name', select=True, required=True)
|
|
product = fields.Many2One('product.product', 'Product')
|
|
salable = fields.Boolean('Salable')
|
|
|
|
|
|
class Service(Workflow, ModelSQL, ModelView):
|
|
'Service'
|
|
__name__ = 'hotel.service'
|
|
_rec_name = 'kind.name'
|
|
number = fields.Char('Number', select=True, required=False)
|
|
kind = fields.Many2One('hotel.service.kind', 'Kind', required=True,
|
|
states={
|
|
'readonly': Eval('state').in_(['confirmed', 'checked'])
|
|
})
|
|
service_date = fields.Date('Date', required=True, states={
|
|
'readonly': Eval('state').in_(['confirmed', 'checked'])
|
|
})
|
|
lines = fields.One2Many('hotel.service.line', 'service', 'Lines',
|
|
states={'readonly': Eval('state') != 'open'}
|
|
)
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('open', 'Open'),
|
|
('confirmed', 'Confirmed'),
|
|
('checked', 'Checked'),
|
|
], 'State', readonly=True, states=STATES)
|
|
state_string = state.translated('state')
|
|
description = fields.Char('Description', select=True, states=STATES)
|
|
count_services = fields.Function(fields.Integer('Count Services',
|
|
select=True), 'get_count_services')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Service, cls).__setup__()
|
|
cls._order.insert(0, ('service_date', 'DESC'))
|
|
cls._transitions |= set((
|
|
('draft', 'open'),
|
|
('open', 'confirmed'),
|
|
('confirmed', 'open'),
|
|
('confirmed', 'checked'),
|
|
))
|
|
cls._buttons.update({
|
|
'draft': {
|
|
'invisible': Eval('state') != 'open',
|
|
},
|
|
'open': {
|
|
'invisible': Eval('state') != 'draft',
|
|
},
|
|
'confirm': {
|
|
'invisible': Eval('state') != 'open',
|
|
},
|
|
'checked': {
|
|
'invisible': Eval('state') != 'confirmed',
|
|
},
|
|
})
|
|
|
|
@staticmethod
|
|
def default_state():
|
|
return 'draft'
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
def draft(cls, records):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('open')
|
|
def open(cls, records):
|
|
cls.set_number(records)
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('confirmed')
|
|
def confirm(cls, records):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('checked')
|
|
def checked(cls, records):
|
|
pass
|
|
|
|
def get_count_services(self, name=None):
|
|
return len(self.lines)
|
|
|
|
@classmethod
|
|
def set_number(cls, services):
|
|
"""
|
|
Fill the number field with the service sequence
|
|
"""
|
|
pool = Pool()
|
|
Sequence = pool.get('ir.sequence')
|
|
Config = pool.get('hotel.configuration')
|
|
config = Config.get_configuration()
|
|
|
|
for service in services:
|
|
if service.number or not config.hotel_service_sequence:
|
|
continue
|
|
number = Sequence.get_id(config.hotel_service_sequence.id)
|
|
cls.write([service], {'number': number})
|
|
|
|
|
|
class ServiceLine(Workflow, ModelSQL, ModelView):
|
|
'Service Line'
|
|
__name__ = 'hotel.service.line'
|
|
service = fields.Many2One('hotel.service', 'Service', required=True)
|
|
room = fields.Many2One('hotel.room', 'Room', select=True, required=True, states=STATES_LINE)
|
|
time_service = fields.Time('Time Service', select=True, states=STATES_LINE)
|
|
product = fields.Many2One('product.product', 'Product',
|
|
domain=[
|
|
('salable', '=', True),
|
|
('active', '=', True),
|
|
], required=True)
|
|
# ('template.kind', '!=', 'accommodation'),
|
|
quantity = fields.Integer('Quantity', required=True, states=STATES_LINE)
|
|
description = fields.Char('Description', states=STATES_LINE)
|
|
order = fields.Char('Order', select=True, states=STATES_LINE)
|
|
sale_line = fields.Many2One('sale.line', 'Sale Line', states={
|
|
'readonly': True,
|
|
})
|
|
guest = fields.Many2One('hotel.operation.guest', 'Guest', states=STATES_LINE,
|
|
domain=[
|
|
('operation.room', '=', Eval('room')),
|
|
# ('operation.start_date', '<=', Eval('')),
|
|
('id', 'in', Eval('current_guests')),
|
|
], depends=['current_guests'])
|
|
operation_line = fields.Many2One('hotel.operation.line', 'Operation Line',
|
|
states={'readonly': True})
|
|
current_guests = fields.Function(fields.Many2Many(
|
|
'hotel.operation.guest', None, None, 'Current Guests'),
|
|
'on_change_with_current_guests')
|
|
operation = fields.Function(fields.Many2One('hotel.operation',
|
|
'Operation'), 'get_operation')
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('loaded', 'Loaded'),
|
|
('deleted', 'Deleted'),
|
|
('done', 'Done'),
|
|
], 'State')
|
|
state_string = state.translated('state')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(ServiceLine, cls).__setup__()
|
|
cls._transitions |= set((
|
|
('draft', 'invoiced'),
|
|
('draft', 'loaded'),
|
|
('draft', 'done'),
|
|
('loaded', 'draft'),
|
|
))
|
|
cls._buttons.update({
|
|
'draft': {
|
|
'invisible': ~Eval('state').in_(['invoiced']),
|
|
},
|
|
'load': {
|
|
'invisible': Eval('state') != 'draft',
|
|
},
|
|
'done': {
|
|
'invisible': Eval('state') != 'draft',
|
|
},
|
|
})
|
|
|
|
@staticmethod
|
|
def default_quantity():
|
|
return 1
|
|
|
|
@staticmethod
|
|
def default_state():
|
|
return 'draft'
|
|
|
|
@staticmethod
|
|
def default_time_service():
|
|
Company = Pool().get('company.company')
|
|
local_date = Company.convert_timezone(datetime.now())
|
|
return local_date.time()
|
|
|
|
@fields.depends('product', 'description')
|
|
def on_change_product(self):
|
|
if self.product:
|
|
self.description = self.product.name
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('loaded')
|
|
def load(cls, records):
|
|
for record in records:
|
|
record.add_product_to_room()
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
def draft(cls, records):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('done')
|
|
def done(cls, records):
|
|
pass
|
|
|
|
@fields.depends('room')
|
|
def on_change_with_current_guests(self, name=None):
|
|
Operation = Pool().get('hotel.operation')
|
|
if self.room:
|
|
operations = Operation.search([
|
|
('room', '=', self.room.id),
|
|
('state', '=', 'check_in'),
|
|
])
|
|
guests = []
|
|
for op in operations:
|
|
guests.extend([g.id for g in op.guests])
|
|
|
|
return guests
|
|
|
|
def get_operation(self, name=None):
|
|
if self.operation_line:
|
|
return self.operation_line.operation.id
|
|
|
|
def add_product_to_room(self):
|
|
pool = Pool()
|
|
Operation = pool.get('hotel.operation')
|
|
OperationLine = pool.get('hotel.operation.line')
|
|
if self.sale_line:
|
|
return
|
|
|
|
dom = [
|
|
('room', '=', self.room.id),
|
|
('state', '=', 'open'),
|
|
('start_date', '<=', self.service.service_date),
|
|
('end_date', '>=', self.service.service_date),
|
|
]
|
|
if self.guest:
|
|
dom.append(('guests', '=', self.guest.id))
|
|
|
|
operations = Operation.search(dom)
|
|
if not operations:
|
|
self.raise_user_error('room_not_occupied', self.room.name)
|
|
elif len(operations) > 1:
|
|
self.raise_user_error('multiple_rooms_active')
|
|
|
|
operation = operations[0]
|
|
new_line = {
|
|
'operation': operation.id,
|
|
'date_service': self.service.service_date,
|
|
'order': self.order,
|
|
'description': self.description,
|
|
# FIXME: not use int
|
|
'quantity': int(self.quantity),
|
|
'invoice_to': operation.main_guest.id,
|
|
'unit_price': self.product.template.list_price,
|
|
'product': self.product.id,
|
|
}
|
|
|
|
line, = OperationLine.create([new_line])
|
|
self.write([self], {'operation_line': line.id})
|
|
|
|
|
|
class ServiceReport(CompanyReport):
|
|
__name__ = 'hotel.service'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, header, data):
|
|
report_context = super().get_context(records, header, data)
|
|
return report_context
|
|
|
|
|
|
class CreateDailyServicesStart(ModelView):
|
|
'Create Daily Services Start'
|
|
__name__ = 'hotel.daily_services.start'
|
|
kind = fields.Many2One('hotel.service.kind', 'Kind', required=True)
|
|
date = fields.Date('Date', required=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
description = fields.Char('Description')
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
class CreateDailyServices(Wizard):
|
|
'Create Daily Services'
|
|
__name__ = 'hotel.daily_services'
|
|
start = StateView('hotel.daily_services.start',
|
|
'hotel.create_daily_services_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Accept', 'accept_', 'tryton-ok', default=True),
|
|
])
|
|
accept_ = StateTransition()
|
|
|
|
def transition_accept_(self):
|
|
pool = Pool()
|
|
Service = pool.get('hotel.service')
|
|
Operation = pool.get('hotel.operation')
|
|
|
|
operations = Operation.search([
|
|
('start_date', '<', self.start.date),
|
|
('state', 'in', ['check_in']),
|
|
])
|
|
|
|
lines_to_create = []
|
|
|
|
service, = Service.create([{
|
|
'service_date': self.start.date,
|
|
'state': 'draft',
|
|
'kind': self.start.kind.id,
|
|
}])
|
|
Service.open([service])
|
|
|
|
if self.start.kind.product:
|
|
for op in operations:
|
|
for guest in op.guests:
|
|
lines_to_create.append({
|
|
'service': service.id,
|
|
'room': op.room.id,
|
|
'guest': guest.id,
|
|
'product': self.start.kind.product.id,
|
|
'quantity': 1,
|
|
})
|
|
if lines_to_create:
|
|
ServiceLine.create(lines_to_create)
|
|
return 'end'
|