trytonpsk-hotel/booking.py

829 lines
29 KiB
Python
Raw Normal View History

2020-04-16 14:45:13 +02:00
# 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 __future__ import with_statement
from datetime import datetime, timedelta
from decimal import Decimal
2021-09-20 23:35:34 +02:00
2020-04-16 14:45:13 +02:00
from trytond.model import Workflow, ModelView, ModelSQL, fields
2021-10-09 19:49:43 +02:00
from trytond.wizard import (
Wizard, StateView, Button, StateTransition, StateReport
)
2020-04-16 14:45:13 +02:00
from trytond.report import Report
from trytond.pyson import Eval, If, In, Get, Not, Or, Equal, Bool
from trytond.transaction import Transaction
from trytond.pool import Pool
2021-07-31 00:44:32 +02:00
from trytond.exceptions import UserError
2021-10-08 22:43:48 +02:00
from trytond.i18n import gettext
from constants import (
STATE_BOOKING, REGISTRATION_STATE, SEGMENT, GUARANTEE, SATISFACTION,
2021-10-09 19:49:43 +02:00
MEDIA, PLAN, SOURCE, INVOICE_METHOD, COMPLEMENTARY,
2021-10-08 22:43:48 +02:00
)
2020-06-28 04:35:07 +02:00
2020-04-16 14:45:13 +02:00
STATES = {
2020-06-28 04:35:07 +02:00
'readonly': Eval('state') != 'offer',
2020-04-16 14:45:13 +02:00
}
STATES_CONFIRMED = {
2020-06-28 04:35:07 +02:00
'readonly': Eval('state') != 'offer',
2020-04-16 14:45:13 +02:00
'required': Eval('state') == 'confirmed',
}
STATES_CHECKIN = {
2021-10-08 22:43:48 +02:00
'readonly': Eval('registration_state').in_(
['check_in', 'no_show', 'cancelled']
),
'required': Eval('registration_state') == 'check_in',
2020-04-16 14:45:13 +02:00
}
_ZERO = Decimal('0.0')
class Booking(Workflow, ModelSQL, ModelView):
'Booking'
__name__ = 'hotel.booking'
_rec_name = 'number'
number = fields.Char('Number', readonly=True, select=True,
help="Sequence of reservation.")
party = fields.Many2One('party.party', 'Party', required=False,
select=True, help="Person or company owner of the booking.",
states={
'required': Eval('state') == 'check_in',
2020-06-28 04:35:07 +02:00
'readonly': Not(In(Eval('state'), ['offer', 'confirmed'])),
2020-04-16 14:45:13 +02:00
})
contact = fields.Char('Contact', states=STATES_CHECKIN)
2020-04-16 14:45:13 +02:00
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term', states=STATES_CHECKIN)
2020-12-04 21:05:03 +01:00
booking_date = fields.DateTime('Booking Date', readonly=False, states=STATES)
2020-04-16 14:45:13 +02:00
person_num = fields.Integer('Person Number', states=STATES)
group = fields.Boolean('Group', states=STATES)
2020-07-18 17:18:29 +02:00
complementary = fields.Boolean('Complementary', states=STATES)
type_complementary = fields.Selection(COMPLEMENTARY, 'Type Complementary', states={
'invisible': ~Bool(Eval('complementary')),
'required': Bool(Eval('complementary')),
})
2020-04-16 14:45:13 +02:00
party_seller = fields.Many2One('hotel.channel', 'Channel',
states=STATES_CHECKIN, help="Agency or channel that do reservation.")
2021-10-08 22:43:48 +02:00
state = fields.Selection(STATE_BOOKING, 'State', readonly=True, required=True)
2020-06-28 04:35:07 +02:00
registration_state = fields.Selection(REGISTRATION_STATE, 'State Registration',
2021-07-31 00:44:32 +02:00
readonly=True, depends=['lines'])
2020-04-16 14:45:13 +02:00
state_string = state.translated('state')
price_list = fields.Many2One('product.price_list', 'Price List',
states={
2020-06-28 04:35:07 +02:00
'readonly': Or(Not(Equal(Eval('state'), 'offer')),
2020-04-16 14:45:13 +02:00
Bool(Eval('lines'))),
}, depends=['state', 'company', 'lines'])
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES, domain=[('id', If(In('company',
Eval('context', {})), '=', '!='), Get(Eval('context', {}),
'company', 0))], readonly=True)
2021-09-20 23:35:34 +02:00
lines = fields.One2Many('hotel.folio', 'booking', 'Lines',
2020-04-16 14:45:13 +02:00
states={
'required': Eval('state') == 'confirmed',
'readonly': Eval('registration_state').in_(['check_in', 'check_out']),
2020-04-16 14:45:13 +02:00
}, depends=['state', 'party'], context={'party': Eval('party')})
cancellation_policy = fields.Many2One('hotel.policy.cancellation',
'Cancellation Policy', states=STATES_CHECKIN)
2020-04-16 14:45:13 +02:00
currency = fields.Many2One('currency.currency', 'Currency',
required=True, states={
2020-06-28 04:35:07 +02:00
'readonly': (Eval('state') != 'offer') |
2020-04-16 14:45:13 +02:00
(Eval('lines', [0]) & Eval('currency', 0)),
}, depends=['state'])
invoice_method = fields.Selection(INVOICE_METHOD, 'Invoice Method',
required=False, states=STATES_CONFIRMED)
satisfaction = fields.Selection(SATISFACTION, 'Satisfaction')
media = fields.Selection(MEDIA, 'Media', states=STATES_CHECKIN,
2020-04-16 14:45:13 +02:00
help="Media from booking coming from.")
media_string = media.translated('media')
plan = fields.Selection(PLAN, 'Commercial Plan', states=STATES_CHECKIN,
2020-04-16 14:45:13 +02:00
help="Plans offered by hotel and selected by guest for booking.")
plan_string = plan.translated('plan')
source_contact = fields.Selection(SOURCE, 'Source Contact',
states=STATES_CHECKIN,
2020-04-16 14:45:13 +02:00
help="Advertising source that create booking opportunity by guest.")
source_contact_string = source_contact.translated('source_contact')
comments = fields.Text('Comments', states=STATES_CHECKIN)
2020-04-16 14:45:13 +02:00
segment = fields.Selection(SEGMENT, 'Tourism Segment',
states=STATES_CHECKIN)
2020-04-16 14:45:13 +02:00
segment_string = segment.translated('segment')
guarantee = fields.Selection(GUARANTEE, 'Guarantee',
states=STATES_CHECKIN)
2020-04-16 14:45:13 +02:00
guarantee_string = guarantee.translated('guarantee')
untaxed_amount = fields.Function(fields.Numeric('Untaxed Amount',
digits=(16, 2), depends=['lines']), 'get_untaxed_amount')
tax_amount = fields.Function(fields.Numeric('Tax Amount',
digits=(16, 2), depends=['lines']), 'get_tax_amount')
total_amount = fields.Function(fields.Numeric('Total Amount',
digits=(16, 2), depends=['lines']), 'get_total_amount')
code = fields.Char('Code', states={'readonly': True})
booker = fields.Many2One('party.party', 'Booker', states={'readonly': True})
created_channel = fields.DateTime('Created Channel', states={'readonly': True})
vouchers = fields.Many2Many('hotel.booking-account.voucher', 'booking',
'voucher', 'Vouchers', states=STATES_CHECKIN, domain=[
2020-04-16 14:45:13 +02:00
], depends=['party'])
vip = fields.Boolean('V.I.P. Customer', states=STATES)
vehicles_num = fields.Integer('Vehicles Number', states=STATES,
help="Number of vehicles that bring with guests.")
vehicle_plate = fields.Integer('Vehicle Plate', states=STATES)
travel_cause = fields.Char('Travel Cause', states=STATES)
taxes_exception = fields.Boolean('Taxes Exception', states=STATES)
2021-10-09 19:00:26 +02:00
total_advance = fields.Function(fields.Numeric('Total Advance',
digits=(16, 2)), 'get_total_advance')
pending_to_pay = fields.Function(fields.Numeric('Pending to Pay',
digits=(16, 2)),'get_pending_to_pay')
2021-09-16 19:47:03 +02:00
breakfast_included = fields.Boolean('Breakfast Included')
2021-02-02 18:39:37 +01:00
2020-04-16 14:45:13 +02:00
@classmethod
def __setup__(cls):
super(Booking, cls).__setup__()
cls._order.insert(0, ('create_date', 'DESC'))
cls._transitions |= set((
2020-06-28 04:35:07 +02:00
('offer', 'confirmed'),
('offer', 'cancelled'),
2021-10-09 19:00:26 +02:00
('confirmed', 'offer'),
2020-06-28 04:35:07 +02:00
('cancelled', 'offer'),
2020-04-16 14:45:13 +02:00
('confirmed', 'cancelled'),
2021-10-09 19:00:26 +02:00
('confirmed', 'not_show'),
2020-04-16 14:45:13 +02:00
))
cls._buttons.update({
'select_rooms': {
2021-10-09 19:49:43 +02:00
'invisible': Eval('state').in_(['finished', 'cancelled', 'not_show']),
},
'create_guest': {
2021-09-13 14:13:11 +02:00
'invisible': Eval('state') != 'offer',
2020-04-16 14:45:13 +02:00
},
'cancel': {
2021-10-09 19:49:43 +02:00
'invisible': Eval('state').in_(
['cancelled', 'not_show', 'finished'])
2020-04-16 14:45:13 +02:00
},
2020-06-28 04:35:07 +02:00
'offer': {
2021-10-05 05:16:20 +02:00
'invisible': Eval('state').in_(['offer', 'confirmed'])
2020-04-16 14:45:13 +02:00
},
'confirm': {
2021-10-09 19:00:26 +02:00
'invisible': ~Eval('state').in_(['offer', 'not_show'])
2020-04-16 14:45:13 +02:00
},
2021-10-09 19:00:26 +02:00
'not_show': {
'invisible': Eval('state') != 'confirmed',
2020-06-28 04:35:07 +02:00
},
2020-04-16 14:45:13 +02:00
'pay_advance': {
2021-10-09 19:49:43 +02:00
'invisible': Eval('state').in_(['finished', 'cancelled']),
2020-04-16 14:45:13 +02:00
},
2021-02-02 18:39:37 +01:00
'send_email': {
'invisible': Eval('state') != 'confirmed'
},
2020-04-16 14:45:13 +02:00
})
@classmethod
def trigger_create(cls, records):
cls.set_number(records)
@classmethod
def copy(cls, bookings, default=None):
if default is None:
default = {}
default = default.copy()
default['number'] = None
default['state'] = 'offer'
default['booking_date'] = datetime.now()
return super(Booking, cls).copy(bookings, default=default)
2021-02-02 18:39:37 +01:00
2020-04-16 14:45:13 +02:00
@staticmethod
def default_currency():
Company = Pool().get('company.company')
company = Transaction().context.get('company')
if company:
return Company(company).currency.id
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@staticmethod
def default_state():
2020-06-28 04:35:07 +02:00
return 'offer'
2020-04-16 14:45:13 +02:00
@staticmethod
def default_invoice_method():
return 'by_booking'
@staticmethod
def default_booking_date():
now = datetime.now()
return now
def get_person_num(self, name):
res = 0
for line in self.lines:
if line.num_adult:
res += line.num_adult
if line.num_children:
res += line.num_children
return res
@fields.depends('party', 'price_list')
def on_change_party(self):
if self.party and self.party.sale_price_list:
self.price_list = self.party.sale_price_list.id
self.price_list.rec_name = self.party.sale_price_list.rec_name
2021-02-02 18:39:37 +01:00
@fields.depends('party_seller')
def on_change_party_seller(self):
if self.party_seller and self.party_seller.price_list:
self.price_list = self.party_seller.price_list
2020-04-16 14:45:13 +02:00
@classmethod
@ModelView.button_action('hotel.wizard_select_rooms')
def select_rooms(cls, bookings):
pass
@classmethod
@ModelView.button_action('hotel.wizard_party_guest')
def create_guest(cls, bookings):
pass
2020-04-16 14:45:13 +02:00
@classmethod
@ModelView.button_action('hotel.wizard_booking_advance_voucher')
def pay_advance(cls, bookings):
pass
@classmethod
@ModelView.button
2020-06-28 04:35:07 +02:00
@Workflow.transition('offer')
def offer(cls, records):
2021-10-09 19:49:43 +02:00
pass
2020-04-16 14:45:13 +02:00
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, records):
2021-10-09 19:49:43 +02:00
pass
2020-04-16 14:45:13 +02:00
2020-06-28 04:35:07 +02:00
@classmethod
@ModelView.button
2021-10-09 19:00:26 +02:00
@Workflow.transition('not_show')
def not_show(cls, records):
2020-06-28 04:35:07 +02:00
pass
2020-04-16 14:45:13 +02:00
@classmethod
@ModelView.button
@Workflow.transition('confirmed')
def confirm(cls, records):
cls.set_number(records)
for rec in records:
# FIXME check if does not exist previous occupancy if exist update state
2021-10-06 02:27:25 +02:00
rec.update_folio('pending')
2020-04-16 14:45:13 +02:00
@classmethod
@ModelView.button
def no_show(cls, records):
for record in records:
2020-08-15 18:01:37 +02:00
cls.write([record], {'registration_state': 'no_show'})
2020-04-16 14:45:13 +02:00
record.cancel_occupancy()
2021-02-02 18:39:37 +01:00
@classmethod
@ModelView.button
def send_email(cls, records):
for reserve in records:
if reserve.state == 'confirmed':
reserve.send_email_to()
2020-04-16 14:45:13 +02:00
@classmethod
def set_number(cls, bookings):
"""
Fill the number field with the booking sequence
"""
pool = Pool()
Config = pool.get('hotel.configuration')
config = Config.get_configuration()
for booking in bookings:
if booking.number or not config.booking_sequence:
continue
2021-07-21 14:56:53 +02:00
number = config.booking_sequence.get()
2020-04-16 14:45:13 +02:00
cls.write([booking], {'number': number})
2021-10-06 02:27:25 +02:00
def update_folio(self, state):
Line = Pool().get('hotel.folio')
Line.write(list(self.lines), {'registration_state': state})
2020-04-16 14:45:13 +02:00
def check_rooms(self):
2020-07-13 21:48:21 +02:00
pool = Pool()
rooms_ids = []
for line in self.lines:
2020-07-18 17:18:29 +02:00
rooms_ids.append(line.room.id)
2020-07-13 21:48:21 +02:00
Housekeeping = pool.get('hotel.housekeeping')
2020-04-16 14:45:13 +02:00
housekeepings = Housekeeping.search([
('state', '!=', 'clean')
])
for hk in housekeepings:
if hk.room.id in rooms_ids:
2021-10-08 22:43:48 +02:00
raise UserError(gettext('hotel.msg_room_no_clean', s=hk.room.name))
2020-04-16 14:45:13 +02:00
def get_context_price(self, product):
context = {}
if getattr(self, 'currency', None):
context['currency'] = self.currency.id
if getattr(self, 'party', None):
context['customer'] = self.party.id
if getattr(self, 'booking_date', None):
context['sale_date'] = self.booking_date
if getattr(self, 'price_list', None):
context['price_list'] = self.price_list.id
context['uom'] = product.template.default_uom.id
# Set taxes before unit_price to have taxes in context of sale price
taxes = []
pattern = {}
if not self.taxes_exception:
for tax in product.customer_taxes_used:
if self.party and self.party.customer_tax_rule:
tax_ids = self.party.customer_tax_rule.apply(tax, pattern)
if tax_ids:
taxes.extend(tax_ids)
continue
taxes.append(tax.id)
if self.party and self.party.customer_tax_rule:
tax_ids = self.party.customer_tax_rule.apply(None, pattern)
if tax_ids:
taxes.extend(tax_ids)
context['taxes'] = taxes
return context
2021-02-02 18:39:37 +01:00
def get_total_advance(self, name):
Advance = Pool().get('hotel.booking-account.voucher')
2021-07-31 00:44:32 +02:00
vouchers = Advance.search([('booking', '=', self.id)])
res = sum([voucher.voucher.amount_to_pay for voucher in vouchers])
2021-02-02 18:39:37 +01:00
return res
2021-07-31 00:44:32 +02:00
def get_pending_to_pay(self, name):
if self.total_amount:
return self.total_amount - (self.total_advance or 0)
2020-04-16 14:45:13 +02:00
def get_total_amount(self, name):
res = 0
if self.tax_amount or self.untaxed_amount:
res = self.tax_amount + self.untaxed_amount
return res
def get_taxes(self, product):
ctx = self.get_context_price(product)
return ctx['taxes']
def get_tax_amount(self, name):
Tax = Pool().get('account.tax')
res = _ZERO
for line in self.lines:
taxes_ids = self.get_taxes(line.product)
if taxes_ids:
taxes = Tax.browse(taxes_ids)
tax_list = Tax.compute(
taxes, line.unit_price or _ZERO, line.nights_quantity or 0
)
tax_amount = sum([t['amount'] for t in tax_list], _ZERO)
res += tax_amount
res = Decimal(round(res, 2))
return res
def get_untaxed_amount(self, name):
res = _ZERO
for line in self.lines:
res += line.total_amount
return res
def get_occupancies(self, name):
occupancies = set()
for line in self.lines:
if line.occupancy_line:
occupancies.add(line.occupancy_line.id)
return list(occupancies)
2021-02-02 18:39:37 +01:00
def send_email_to(self):
pool = Pool()
config = pool.get('hotel.configuration')(1)
Template = pool.get('email.template')
email = self.party.email
if email:
Template.send(config.booking_email_template, self, email)
else:
2021-10-08 22:43:48 +02:00
raise UserError(gettext('El cliente no tiene un correo asociado'))
2021-02-02 18:39:37 +01:00
2021-09-16 19:47:03 +02:00
@fields.depends('price_list', 'breakfast_included')
def on_change_price_list(self):
if self.price_list:
self.breakfast_included = self.price_list.breakfast_included
2020-04-16 14:45:13 +02:00
class BookingReport(Report):
__name__ = 'hotel.booking'
@classmethod
2021-07-21 14:56:53 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-16 14:45:13 +02:00
user = Pool().get('res.user')(Transaction().user)
report_context['company'] = user.company
return report_context
class SelectRoomsAsk(ModelView):
2021-10-05 05:16:20 +02:00
'Seelct Rooms Assistant'
2020-04-16 14:45:13 +02:00
__name__ = 'hotel.booking.select_rooms.ask'
arrival_date = fields.Date('Arrival Date', required=True)
departure_date = fields.Date('Departure Date', required=True)
accommodation = fields.Many2One('product.product',
'Accommodation', domain=[
('template.kind', '=', 'accommodation'),
])
rooms = fields.Many2Many('hotel.room', None, None,
'Rooms', domain=[
('id', 'in', Eval('targets')),
])
overbooking = fields.Boolean('Overbooking')
targets = fields.Function(fields.Many2Many('hotel.room', None, None,
'Targets'), 'on_change_with_targets')
@staticmethod
def default_accommodation():
Configuration = Pool().get('hotel.configuration')
2021-10-05 05:16:20 +02:00
config = Configuration.get_configuration()
if config.default_accommodation:
return config.default_accommodation.id
2020-04-16 14:45:13 +02:00
@fields.depends('arrival_date', 'departure_date', 'accommodation', 'overbooking')
def on_change_with_targets(self, name=None):
pool = Pool()
RoomTemplate = pool.get('hotel.room-product.template')
Room = pool.get('hotel.room')
2021-10-05 05:16:20 +02:00
Folio = pool.get('hotel.folio')
2020-04-16 14:45:13 +02:00
res = []
if not self.accommodation or not self.arrival_date or not self.departure_date:
return res
if self.overbooking:
return [r.id for r in Room.search([])]
room_templates = RoomTemplate.search([
('template.accommodation_capacity', '>=', self.accommodation.accommodation_capacity)
])
rooms_ids = [t.room.id for t in room_templates]
2021-10-05 05:16:20 +02:00
rooms_available_ids = Folio.get_available_rooms(
self.arrival_date,
self.departure_date,
rooms_ids=rooms_ids
)
return rooms_available_ids
2020-04-16 14:45:13 +02:00
class SelectRooms(Wizard):
'Select Rooms'
__name__ = 'hotel.booking.select_rooms'
"""
this is the wizard that allows the front desk employee to select
rooms, based on the requirements listed by the customer.
"""
start = StateView('hotel.booking.select_rooms.ask',
'hotel.view_select_rooms_form', [
Button('Exit', 'end', 'tryton-cancel'),
Button('Add and Continue', 'add_continue', 'tryton-forward'),
Button('Add', 'add_rooms', 'tryton-ok'),
]
)
add_rooms = StateTransition()
add_continue = StateTransition()
def transition_add_rooms(self):
self._add_rooms()
return 'end'
def transition_add_continue(self):
self._add_rooms()
return 'start'
def _add_rooms(self):
pool = Pool()
2021-09-20 23:35:34 +02:00
Line = pool.get('hotel.folio')
2020-04-16 14:45:13 +02:00
Booking = pool.get('hotel.booking')
booking = Booking(Transaction().context.get('active_id'))
lines_to_create = []
ctx = {}
if booking.price_list:
ctx['price_list'] = booking.price_list
ctx['sale_date'] = self.start.arrival_date
ctx['currency'] = booking.currency.id
if booking.party:
ctx['customer'] = booking.party.id
product = self.start.accommodation
quantity = (self.start.departure_date - self.start.arrival_date).days
unit_price = product.template.list_price
if booking.price_list:
with Transaction().set_context(ctx):
unit_price = booking.price_list.compute(
booking.party, product, unit_price,
quantity, product.default_uom)
unit_price = booking.currency.round(unit_price)
for room in self.start.rooms:
values = {
'booking': booking.id,
'product': product.id,
'room': room.id,
'arrival_date': self.start.arrival_date,
'departure_date': self.start.departure_date,
'unit_price': unit_price,
}
if booking.party:
values['main_guest'] = booking.party.id
values.update({'product': product.id})
lines_to_create.append(values)
Line.create(lines_to_create)
booking.save()
class BookingVoucher(ModelSQL):
'Booking - Voucher'
__name__ = 'hotel.booking-account.voucher'
_table = 'booking_vouchers_rel'
booking = fields.Many2One('hotel.booking', 'Booking',
ondelete='CASCADE', select=True, required=True)
voucher = fields.Many2One('account.voucher', 'Voucher', select=True,
domain=[('voucher_type', '=', 'receipt')], ondelete='RESTRICT',
required=True)
@classmethod
def set_voucher_origin(cls, voucher_id, booking_id):
cls.create([{
'voucher': voucher_id,
'booking': booking_id,
}])
class BookingForecastStart(ModelView):
'Booking Forecast Start'
__name__ = 'hotel.print_booking_forecast.start'
date = fields.Date('Start Date', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
@staticmethod
def default_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@staticmethod
def default_company():
return Transaction().context.get('company')
class BookingForecast(Wizard):
'Booking Forecast'
__name__ = 'hotel.print_booking_forecast'
start = StateView('hotel.print_booking_forecast.start',
'hotel.print_booking_forecast_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-print', default=True),
])
print_ = StateReport('hotel.booking_forecast.report')
def do_print_(self, action):
company = self.start.company
data = {
'date': self.start.date,
'company': company.id,
}
return action, data
def transition_print_(self):
return 'end'
class BookingForecastReport(Report):
__name__ = 'hotel.booking_forecast.report'
@classmethod
2021-07-21 14:56:53 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-16 14:45:13 +02:00
MAX_DAYS = 30
pool = Pool()
Company = pool.get('company.company')
Room = pool.get('hotel.room')
2021-09-21 06:33:06 +02:00
BookingFolio = pool.get('hotel.folio')
2020-04-16 14:45:13 +02:00
rooms = Room.search([])
alldays = {}
alldays_convert = {}
for nd in range(MAX_DAYS):
day_n = 'day' + str((nd + 1))
tdate = data['date'] + timedelta(nd)
data[day_n] = tdate
data['total_' + day_n] = 0
data[('revenue_' + day_n)] = 0
data[('rate_' + day_n)] = 0
alldays[day_n] = ''
alldays_convert[tdate] = day_n
date_init = data['date']
date_limit = data['date'] + timedelta(MAX_DAYS)
2021-10-11 19:03:43 +02:00
dom = [['OR', [
2020-04-16 14:45:13 +02:00
('arrival_date', '<=', date_init),
('departure_date', '>=', date_init),
], [
('arrival_date', '>=', date_init),
('arrival_date', '<=', date_limit),
],
], ('booking.state', 'not in', ['no_show', 'cancelled'])]
2021-09-21 06:33:06 +02:00
lines = BookingFolio.search(dom)
2020-04-16 14:45:13 +02:00
drooms = {}
for room in rooms:
drooms[room.id] = {'name': room.name}
drooms[room.id].update(alldays.copy())
for line in lines:
_delta = (line.departure_date - line.arrival_date).days
for i in range(_delta):
dt = line.arrival_date + timedelta(i)
if dt >= date_init and dt < date_limit \
and dt >= data['date']:
dayn = alldays_convert[dt]
drooms[line.room.id][dayn] = "X"
data['total_' + dayn] += 1
data['revenue_' + dayn] += float(line.unit_price) / 1000000
for i in range(MAX_DAYS):
day_n = 'day' + str((i + 1))
data['rate_' + day_n] = (data['total_' + day_n] * 100.0) / len(rooms)
report_context['records'] = list(drooms.values())
report_context['date'] = data['date']
report_context['company'] = Company(data['company']).party.name
return report_context
class RoomsOccupancyStart(ModelView):
'Rooms Occupancy Start'
__name__ = 'hotel.print_rooms_occupancy.start'
date = fields.Date('Date', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
@staticmethod
def default_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@staticmethod
def default_company():
return Transaction().context.get('company')
class RoomsOccupancy(Wizard):
'Rooms Occupancy'
__name__ = 'hotel.print_rooms_occupancy'
start = StateView('hotel.print_rooms_occupancy.start',
'hotel.print_rooms_occupancy_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Open', 'print_', 'tryton-print', default=True),
])
print_ = StateReport('hotel.rooms_occupancy.report')
def do_print_(self, action):
company = self.start.company
data = {
'date': self.start.date,
'company': company.id,
}
return action, data
def transition_print_(self):
return 'end'
class RoomsOccupancyReport(Report):
__name__ = 'hotel.rooms_occupancy.report'
@classmethod
2021-07-21 14:56:53 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-16 14:45:13 +02:00
pool = Pool()
Company = pool.get('company.company')
Room = pool.get('hotel.room')
2021-09-21 06:33:06 +02:00
Folio = pool.get('hotel.folio')
2020-04-16 14:45:13 +02:00
start_date = data['date']
all_rooms = Room.search([], order=[('code', 'ASC')])
2021-09-21 06:33:06 +02:00
operations = Folio.search([
2020-04-16 14:45:13 +02:00
('start_date', '<=', start_date),
2021-08-03 18:51:43 +02:00
('state', '=', 'open'),
2020-04-16 14:45:13 +02:00
('kind', '=', 'occupancy'),
])
def _get_default_room(r):
res = {
'room': r.name,
'guest': None,
'num_guest': None,
'party': None,
'arrival': None,
'departure': None,
'booking': None,
'reference': None,
'amount': None,
'balance': None,
'state': None,
}
return res
rooms_map = {room.id: _get_default_room(room) for room in all_rooms}
occupancy_rooms = 0
for op in operations:
2021-07-31 17:13:49 +02:00
name = op.main_guest.name if op.main_guest else op.party.name
2020-04-16 14:45:13 +02:00
rooms_map[op.room.id].update({
2021-08-03 18:51:43 +02:00
'guest': name,
2020-04-16 14:45:13 +02:00
'num_guest': len(op.guests),
'party': op.party.name,
'arrival': op.start_date,
'departure': op.end_date,
'reference': op.reference,
'amount': op.total_amount,
'balance': op.total_amount_today,
'state': op.state,
})
occupancy_rooms += 1
if all_rooms:
occupancy_rate = (float(len(operations)) / len(all_rooms)) * 100
else:
occupancy_rate = 0
user = Pool().get('res.user')(Transaction().user)
2021-07-31 17:13:49 +02:00
recs = rooms_map.values()
2020-04-16 14:45:13 +02:00
report_context['records'] = rooms_map
report_context['occupancy_rate'] = occupancy_rate
report_context['occupancy_rooms'] = occupancy_rooms
report_context['company'] = Company(data['company']).party.name
report_context['date'] = data['date']
report_context['user'] = user.rec_name
return report_context
class BookingDailyStart(ModelView):
'Booking Daily Start'
__name__ = 'hotel.print_booking_daily.start'
date = fields.Date('Date', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
@staticmethod
def default_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@staticmethod
def default_company():
return Transaction().context.get('company')
class BookingDaily(Wizard):
'Rooms Occupancy'
__name__ = 'hotel.booking_daily'
start = StateView('hotel.print_booking_daily.start',
'hotel.print_booking_daily_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Open', 'print_', 'tryton-print', default=True),
])
print_ = StateReport('hotel.booking_daily.report')
def do_print_(self, action):
company = self.start.company
data = {
'date': self.start.date,
'company': company.id,
}
return action, data
def transition_print_(self):
return 'end'
class BookingDailyReport(Report):
__name__ = 'hotel.booking_daily.report'
@classmethod
2021-07-21 14:56:53 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-16 14:45:13 +02:00
pool = Pool()
Company = pool.get('company.company')
2021-10-09 06:18:30 +02:00
Folio = pool.get('hotel.folio')
records = Folio.search([
2020-04-16 14:45:13 +02:00
('arrival_date', '=', data['date']),
], order=[('room.code', 'ASC')])
report_context['records'] = records
report_context['company'] = Company(data['company']).party.name
report_context['date'] = data['date']
return report_context