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
|
|
|
|
from trytond.wizard import Wizard, StateView, Button, StateTransition, StateReport
|
|
|
|
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 06:18:30 +02:00
|
|
|
MEDIA, PLAN, SOURCE, INVOICE_METHOD, COMPLEMENTARY, INVOICE_STATES,
|
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',
|
|
|
|
}
|
|
|
|
|
2021-09-13 19:24:22 +02:00
|
|
|
STATES_CHECKIN = {
|
2021-10-08 22:43:48 +02:00
|
|
|
'readonly': Eval('registration_state').in_(
|
|
|
|
['check_in', 'no_show', 'cancelled']
|
|
|
|
),
|
2021-09-13 19:24:22 +02:00
|
|
|
'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
|
|
|
})
|
2021-09-13 19:24:22 +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',
|
2021-09-13 19:24:22 +02:00
|
|
|
'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',
|
2021-09-13 19:24:22 +02:00
|
|
|
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',
|
2021-09-13 19:24:22 +02:00
|
|
|
'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',
|
2021-09-13 19:24:22 +02:00
|
|
|
'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')
|
2021-09-13 19:24:22 +02:00
|
|
|
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')
|
2021-09-13 19:24:22 +02:00
|
|
|
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',
|
2021-09-13 19:24:22 +02:00
|
|
|
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')
|
2021-09-13 19:24:22 +02:00
|
|
|
comments = fields.Text('Comments', states=STATES_CHECKIN)
|
2020-04-16 14:45:13 +02:00
|
|
|
segment = fields.Selection(SEGMENT, 'Tourism Segment',
|
2021-09-13 19:24:22 +02:00
|
|
|
states=STATES_CHECKIN)
|
2020-04-16 14:45:13 +02:00
|
|
|
segment_string = segment.translated('segment')
|
|
|
|
guarantee = fields.Selection(GUARANTEE, 'Guarantee',
|
2021-09-13 19:24:22 +02:00
|
|
|
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',
|
2021-09-13 19:24:22 +02:00
|
|
|
'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-07-31 00:44:32 +02:00
|
|
|
total_advance = fields.Function(fields.Numeric('Total Advance'),
|
|
|
|
'get_total_advance')
|
|
|
|
pending_to_pay = fields.Function(fields.Numeric('Pending to Pay'),
|
|
|
|
'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'),
|
2020-07-13 21:48:21 +02:00
|
|
|
('confirmed', 'offer'),
|
2020-06-28 04:35:07 +02:00
|
|
|
('offer', 'not_confirmed'),
|
|
|
|
('offer', 'cancelled'),
|
|
|
|
('cancelled', 'offer'),
|
2020-04-16 14:45:13 +02:00
|
|
|
('confirmed', 'cancelled'),
|
2020-06-28 04:35:07 +02:00
|
|
|
('not_confirmed', 'confirmed'),
|
|
|
|
('not_confirmed', 'cancelled'),
|
2020-04-16 14:45:13 +02:00
|
|
|
))
|
|
|
|
cls._buttons.update({
|
|
|
|
'select_rooms': {
|
2020-06-28 04:35:07 +02:00
|
|
|
'invisible': Eval('state') != 'offer',
|
2021-03-08 14:32:20 +01:00
|
|
|
},
|
|
|
|
'create_guest': {
|
2021-09-13 14:13:11 +02:00
|
|
|
'invisible': Eval('state') != 'offer',
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
|
|
|
'cancel': {
|
2021-02-02 18:39:37 +01:00
|
|
|
'invisible': Eval('state').in_(['cancel', '']) and
|
|
|
|
Eval('registration_state') == 'check_out',
|
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'])
|
|
|
|
or Eval('registration_state').in_(['check_in', 'check_out']),
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
|
|
|
'confirm': {
|
2020-07-13 21:48:21 +02:00
|
|
|
'invisible': ~Eval('state').in_(['offer', 'not_confirmed'])
|
2020-06-28 04:35:07 +02:00
|
|
|
},
|
|
|
|
'not_confirm': {
|
|
|
|
'invisible': Eval('state') != 'offer',
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
|
|
|
'check_in': {
|
2020-08-15 18:01:37 +02:00
|
|
|
'invisible': Eval('state') != 'confirmed' and
|
2021-01-23 16:56:32 +01:00
|
|
|
Eval('registration_state').in_(['check_in', 'check_out']),
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
|
|
|
'no_show': {
|
2020-08-15 18:01:37 +02:00
|
|
|
'invisible': Eval('state') != 'confirmed' and
|
2021-01-23 16:56:32 +01:00
|
|
|
Eval('registration_state').in_(['check_in', 'check_out']),
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
2020-06-28 04:35:07 +02:00
|
|
|
'check_out': {
|
2020-08-15 18:01:37 +02:00
|
|
|
'invisible': Eval('state') != 'confirmed' and
|
|
|
|
Eval('registration_state') == 'check_out',
|
2020-06-28 04:35:07 +02:00
|
|
|
},
|
2020-04-16 14:45:13 +02:00
|
|
|
'pay_advance': {
|
2021-07-31 00:44:32 +02:00
|
|
|
'invisible':
|
2021-10-06 05:37:33 +02:00
|
|
|
Eval('registration_state').in_(['check_out', 'offer']),
|
2020-04-16 14:45:13 +02:00
|
|
|
},
|
2021-02-02 18:39:37 +01:00
|
|
|
'send_email': {
|
|
|
|
'invisible': Eval('state') != 'confirmed'
|
2021-03-08 14:32:20 +01:00
|
|
|
},
|
2020-04-16 14:45:13 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def trigger_create(cls, records):
|
|
|
|
cls.set_number(records)
|
|
|
|
|
2021-03-08 14:32:20 +01:00
|
|
|
@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
|
|
|
|
|
2021-03-08 14:32:20 +01:00
|
|
|
@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):
|
2020-04-16 14:45:13 +02:00
|
|
|
for record in records:
|
|
|
|
record.delete_occupancy()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('cancelled')
|
|
|
|
def cancel(cls, records):
|
|
|
|
for record in records:
|
|
|
|
record.cancel_occupancy()
|
|
|
|
|
2020-06-28 04:35:07 +02:00
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('not_confirmed')
|
|
|
|
def not_confirm(cls, records):
|
|
|
|
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})
|
|
|
|
|
|
|
|
def cancel_occupancy(self):
|
|
|
|
Operation = Pool().get('hotel.operation')
|
|
|
|
ops = Operation.search([('origin', '=', str(self))])
|
|
|
|
if ops:
|
|
|
|
Operation.cancel(ops)
|
|
|
|
|
|
|
|
def delete_occupancy(self):
|
|
|
|
Operation = Pool().get('hotel.operation')
|
|
|
|
for l in self.lines:
|
|
|
|
if l.operation:
|
|
|
|
Operation.delete([l.operation])
|
|
|
|
|
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
|
2021-03-08 14:32:20 +01:00
|
|
|
|
2020-04-16 14:45:13 +02:00
|
|
|
|
2021-09-21 06:33:06 +02:00
|
|
|
class Folio(ModelSQL, ModelView):
|
|
|
|
'Folio'
|
2021-09-20 23:35:34 +02:00
|
|
|
__name__ = 'hotel.folio'
|
|
|
|
booking = fields.Many2One('hotel.booking', 'Booking', ondelete='CASCADE',
|
|
|
|
select=True)
|
2021-10-09 06:18:30 +02:00
|
|
|
registration_card = fields.Char('Registration Card', readonly=True,
|
|
|
|
select=True, help="Unique sequence for card guest registration.")
|
2020-04-16 14:45:13 +02:00
|
|
|
room = fields.Many2One('hotel.room', 'Room', select=True, states={
|
2021-09-13 19:24:22 +02:00
|
|
|
'required': Eval('registration_state') == 'check_in',
|
|
|
|
'readonly': Eval('registration_state') == 'check_in',
|
2020-04-16 14:45:13 +02:00
|
|
|
})
|
2021-09-13 19:24:22 +02:00
|
|
|
arrival_date = fields.Date('Arrival Date', required=True,
|
|
|
|
states=STATES_CHECKIN)
|
|
|
|
departure_date = fields.Date('Departure Date', required=True,
|
|
|
|
states=STATES_CHECKIN)
|
2020-04-16 14:45:13 +02:00
|
|
|
product = fields.Many2One('product.product', 'Product',
|
|
|
|
select=True, domain=[
|
|
|
|
('template.type', '=', 'service'),
|
|
|
|
('template.kind', '=', 'accommodation'),
|
2021-09-13 19:24:22 +02:00
|
|
|
], required=True, states=STATES_CHECKIN)
|
2021-10-06 05:37:33 +02:00
|
|
|
unit_price = fields.Numeric('Unit Price', digits=(16, 4),
|
2020-04-16 14:45:13 +02:00
|
|
|
states={
|
|
|
|
'required': Bool(Eval('product')),
|
2021-09-13 19:24:22 +02:00
|
|
|
'readonly': Eval('registration_state') == 'check_in',
|
2020-04-16 14:45:13 +02:00
|
|
|
})
|
|
|
|
uom = fields.Many2One('product.uom', 'UOM', readonly=True)
|
|
|
|
main_guest = fields.Many2One('party.party', 'Main Guest', select=True,
|
|
|
|
states={
|
2021-09-13 19:24:22 +02:00
|
|
|
'required': Eval('registration_state') == 'check_in',
|
|
|
|
'readonly': Eval('registration_state') == 'check_in',
|
2020-04-16 14:45:13 +02:00
|
|
|
}
|
|
|
|
)
|
2021-09-13 19:24:22 +02:00
|
|
|
contact = fields.Char('Contact', states=STATES_CHECKIN)
|
2020-04-16 14:45:13 +02:00
|
|
|
num_children = fields.Function(fields.Integer('No. Children'),
|
2021-09-13 19:24:22 +02:00
|
|
|
'get_num_children')
|
2020-04-16 14:45:13 +02:00
|
|
|
nights_quantity = fields.Function(fields.Integer('Nights'),
|
2021-09-13 19:24:22 +02:00
|
|
|
'get_nights_quantity')
|
|
|
|
host_quantity = fields.Integer('Host', states=STATES_CHECKIN)
|
2020-04-16 14:45:13 +02:00
|
|
|
unit_digits = fields.Function(fields.Integer('Unit Digits'), 'get_unit_digits')
|
|
|
|
notes = fields.Text('Notes')
|
|
|
|
total_amount = fields.Function(fields.Numeric('Total Amount',
|
2021-09-13 19:24:22 +02:00
|
|
|
digits=(16, 2)), 'get_total_amount')
|
2020-04-16 14:45:13 +02:00
|
|
|
total_commission = fields.Function(fields.Numeric('Channel Commission',
|
2021-09-13 19:24:22 +02:00
|
|
|
digits=(16, 2)), 'get_channel_commission')
|
|
|
|
guests = fields.One2Many('hotel.booking.guest', 'booking_line', 'Guests',
|
2021-10-06 02:27:25 +02:00
|
|
|
states={
|
|
|
|
'readonly': ~Eval('registration_state').in_(['check_in']),
|
|
|
|
})
|
|
|
|
nationality = fields.Many2One('party.nationality', 'Nationality',
|
2021-09-13 19:24:22 +02:00
|
|
|
states=STATES_CHECKIN)
|
2021-03-27 15:35:46 +01:00
|
|
|
origin_country = fields.Many2One('party.nationality', 'Origin Country',
|
2021-09-13 19:24:22 +02:00
|
|
|
select=True, states=STATES_CHECKIN)
|
2021-03-27 15:35:46 +01:00
|
|
|
target_country = fields.Many2One('party.nationality', 'Target Country',
|
2021-09-13 19:24:22 +02:00
|
|
|
select=True, states=STATES_CHECKIN)
|
2021-07-31 00:44:32 +02:00
|
|
|
registration_state = fields.Selection(REGISTRATION_STATE,
|
|
|
|
'Registration State', readonly=True)
|
2021-09-21 06:33:06 +02:00
|
|
|
charges = fields.One2Many('hotel.folio.charge', 'folio', 'Charges',
|
|
|
|
states={
|
2021-10-06 02:27:25 +02:00
|
|
|
'readonly': ~Eval('registration_state').in_(['check_in']),
|
|
|
|
})
|
2021-10-06 05:37:33 +02:00
|
|
|
party = fields.Many2One('party.party', 'Party to Bill', select=True,
|
|
|
|
states={
|
|
|
|
'readonly': Eval('registration_state').in_(['check_out']),
|
|
|
|
})
|
2021-10-08 22:43:48 +02:00
|
|
|
invoice_line = fields.Many2One('account.invoice.line', 'Invoice Line')
|
|
|
|
invoice = fields.Function(fields.Many2One('account.invoice', 'Invoice'),
|
|
|
|
'get_invoice')
|
2021-10-09 06:18:30 +02:00
|
|
|
invoice_state = fields.Selection(INVOICE_STATES, 'Invoice State', readonly=True)
|
2020-04-16 14:45:13 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
2021-09-21 06:33:06 +02:00
|
|
|
super(Folio, cls).__setup__()
|
2021-04-07 21:03:51 +02:00
|
|
|
cls._check_modify_exclude = [
|
|
|
|
'nationality', 'origin_country', 'target_country',
|
2021-04-20 23:07:22 +02:00
|
|
|
'registration_state', 'guests'
|
2021-04-07 21:03:51 +02:00
|
|
|
],
|
2021-03-08 14:32:20 +01:00
|
|
|
cls._buttons.update({
|
|
|
|
'check_in': {
|
2021-10-06 05:37:33 +02:00
|
|
|
'invisible': Eval('registration_state').in_(['check_in', 'check_out']),
|
2021-03-08 14:32:20 +01:00
|
|
|
},
|
2021-09-21 06:33:06 +02:00
|
|
|
'check_out': {
|
2021-10-06 05:37:33 +02:00
|
|
|
'invisible': Eval('registration_state').in_(['check_out', 'pending']),
|
2021-10-08 00:10:46 +02:00
|
|
|
},
|
|
|
|
'bill': {
|
|
|
|
'invisible': Eval('registration_state') != 'check_out',
|
2021-09-21 06:33:06 +02:00
|
|
|
}
|
2021-09-13 19:24:22 +02:00
|
|
|
})
|
2020-04-16 14:45:13 +02:00
|
|
|
|
2021-03-08 14:32:20 +01:00
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
def check_in(cls, records):
|
|
|
|
config_party = Pool().get('party.configuration')(1)
|
|
|
|
validate_party = config_party.validate_party
|
2021-04-20 23:07:22 +02:00
|
|
|
|
2021-03-08 14:32:20 +01:00
|
|
|
if not validate_party:
|
|
|
|
config_party.validate_party = True
|
|
|
|
config_party.save()
|
2021-07-31 00:44:32 +02:00
|
|
|
booking = records[0].booking
|
|
|
|
# booking.party.pre_validate()
|
2021-10-08 22:43:48 +02:00
|
|
|
|
|
|
|
for rec in records:
|
|
|
|
rec.set_registration_card_number()
|
2021-03-08 14:32:20 +01:00
|
|
|
line = records[0]
|
2021-10-08 22:43:48 +02:00
|
|
|
# if line.state == 'offer':
|
|
|
|
# raise UserError(gettext('hotel.msg_missing_confirm_booking'))
|
2021-03-08 14:32:20 +01:00
|
|
|
if line.main_guest is None:
|
2021-07-31 00:44:32 +02:00
|
|
|
raise UserError(gettext('hotel.msg_missing_main_guest'))
|
2021-03-08 14:32:20 +01:00
|
|
|
if line.room is None:
|
2021-07-31 00:44:32 +02:00
|
|
|
raise UserError(gettext('hotel.msg_missing_select_room'))
|
|
|
|
|
|
|
|
booking.check_rooms()
|
2021-03-08 14:32:20 +01:00
|
|
|
cls.write([records[0]], {'registration_state': 'check_in'})
|
2021-07-31 00:44:32 +02:00
|
|
|
|
|
|
|
change_state = all(
|
|
|
|
[rl.registration_state == 'check_in' for rl in booking.lines]
|
|
|
|
)
|
|
|
|
print('change_state..', change_state)
|
|
|
|
if change_state:
|
|
|
|
booking.registration_state = 'check_in'
|
|
|
|
booking.save()
|
2021-03-08 14:32:20 +01:00
|
|
|
if config_party.validate_party != validate_party:
|
|
|
|
config_party.validate_party = validate_party
|
|
|
|
config_party.save()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
def check_out(cls, records):
|
|
|
|
for record in records:
|
|
|
|
cls.write([record], {'registration_state': 'check_out'})
|
|
|
|
|
2021-10-08 00:10:46 +02:00
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
def bill(cls, records):
|
|
|
|
for rec in records:
|
|
|
|
# if rec.complementary:
|
|
|
|
# return
|
2021-10-08 22:43:48 +02:00
|
|
|
cls.create_invoice(rec)
|
|
|
|
|
|
|
|
def set_registration_card_number(self):
|
|
|
|
"""
|
|
|
|
Fill the number field for registration card with sequence
|
|
|
|
"""
|
|
|
|
pool = Pool()
|
|
|
|
Config = pool.get('hotel.configuration')
|
|
|
|
config = Config.get_configuration()
|
|
|
|
|
|
|
|
if self.registration_card:
|
|
|
|
return
|
|
|
|
if not config.registration_card_sequence:
|
|
|
|
raise UserError(gettext('hotel.msg_missing_sequence_registration'))
|
|
|
|
number = config.registration_card_sequence.get()
|
|
|
|
self.registration_card = number
|
|
|
|
self.save()
|
2021-10-08 00:10:46 +02:00
|
|
|
|
|
|
|
@classmethod
|
2021-10-08 22:43:48 +02:00
|
|
|
def create_invoice(cls, record):
|
2021-10-08 00:10:46 +02:00
|
|
|
pool = Pool()
|
|
|
|
Date = pool.get('ir.date')
|
|
|
|
InvoiceLine = pool.get('account.invoice.line')
|
|
|
|
FolioCharge = pool.get('hotel.folio.charge')
|
|
|
|
Configuration = pool.get('hotel.configuration')
|
|
|
|
configuration = Configuration.get_configuration()
|
2021-10-08 22:43:48 +02:00
|
|
|
# SaleVoucher = pool.get('sale.sale-account.voucher')
|
|
|
|
# date_ = Date.today()
|
|
|
|
# ctx = {}
|
|
|
|
invoice_to_create = cls.get_grouped_invoices([record])
|
|
|
|
print(invoice_to_create)
|
|
|
|
for rec in invoice_to_create.values():
|
|
|
|
# if rec.get('price_list'):
|
|
|
|
# ctx['price_list'] = rec.get('price_list')
|
|
|
|
# ctx['sale_date'] = date_
|
|
|
|
# ctx['currency'] = rec['currency']
|
|
|
|
# ctx['customer'] = rec['party'].id
|
2021-10-08 00:10:46 +02:00
|
|
|
|
|
|
|
invoice = cls._get_new_invoice(rec)
|
|
|
|
invoice.save()
|
|
|
|
|
|
|
|
# Here add payments to invoice
|
|
|
|
# if rec.get('vouchers'):
|
|
|
|
# for v in rec['vouchers']:
|
|
|
|
# SaleVoucher.create([{
|
|
|
|
# 'voucher': v.id,
|
|
|
|
# 'sale': sale.id
|
|
|
|
# }])
|
|
|
|
|
|
|
|
# Add and create default charges lines if exists
|
|
|
|
if rec.get('guests_qty') and rec.get('add_default_charges'):
|
|
|
|
for product in configuration.default_charges:
|
2021-10-08 22:43:48 +02:00
|
|
|
if rec['party']:
|
|
|
|
taxes_ids = cls.get_taxes(invoice, product, rec)
|
2021-10-08 00:10:46 +02:00
|
|
|
new_line = {
|
|
|
|
'invoice': invoice.id,
|
|
|
|
'type': 'line',
|
|
|
|
'unit': product.template.default_uom.id,
|
|
|
|
'quantity': rec['guests_qty'],
|
|
|
|
'unit_price': product.template.list_price,
|
|
|
|
'product': product.id,
|
|
|
|
'description': product.rec_name,
|
|
|
|
}
|
|
|
|
if taxes_ids:
|
|
|
|
new_line.update({'taxes': [('add', taxes_ids)]})
|
|
|
|
if new_line:
|
|
|
|
InvoiceLine.create([new_line])
|
|
|
|
|
2021-10-08 22:43:48 +02:00
|
|
|
print('XXXXX', rec)
|
|
|
|
for _line in rec['lines']:
|
|
|
|
line, = InvoiceLine.create([
|
|
|
|
cls._get_invoice_line(invoice, _line, rec)
|
|
|
|
])
|
2021-10-08 00:10:46 +02:00
|
|
|
if _line.get('folios'):
|
|
|
|
cls.write(_line.get('folios'), {
|
|
|
|
'invoice_line': line.id,
|
2021-10-08 22:43:48 +02:00
|
|
|
# 'invoice_state': 'in_process'
|
2021-10-08 00:10:46 +02:00
|
|
|
})
|
|
|
|
else:
|
|
|
|
FolioCharge.write([_line.get('charge')], {
|
|
|
|
'invoice_line': line.id,
|
|
|
|
'state': 'invoiced',
|
|
|
|
})
|
|
|
|
|
2021-10-08 22:43:48 +02:00
|
|
|
@classmethod
|
|
|
|
def _get_invoice_line(cls, invoice, line, record):
|
|
|
|
product = line['product']
|
|
|
|
new_line = {
|
|
|
|
# 'invoice_line': invoice.id,
|
|
|
|
'type': 'line',
|
|
|
|
'invoice': invoice.id,
|
|
|
|
'unit': product.template.default_uom.id,
|
|
|
|
'account': product.template.account_category.account_revenue_used.id,
|
|
|
|
'invoice_type': 'out',
|
|
|
|
'operation_center': 1,
|
|
|
|
'quantity': line['quantity'],
|
|
|
|
'unit_price': line['unit_price'],
|
|
|
|
'product': product.id,
|
|
|
|
'party': invoice.party.id,
|
|
|
|
'description': line['description'],
|
|
|
|
}
|
|
|
|
if not line['taxes_exception']:
|
|
|
|
taxes_ids = cls.get_taxes(invoice, line['product'], record)
|
|
|
|
if taxes_ids:
|
|
|
|
new_line.update({'taxes': [('add', taxes_ids)]})
|
|
|
|
return new_line
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_taxes(cls, invoice, product, rec):
|
|
|
|
ctx = cls.get_context_price(invoice, product, rec)
|
|
|
|
return ctx['taxes']
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_context_price(cls, invoice, product, rec):
|
|
|
|
context = {}
|
|
|
|
context['currency'] = rec['currency']
|
|
|
|
context['customer'] = rec['party'].id
|
|
|
|
context['price_list'] = rec['price_list']
|
|
|
|
context['uom'] = product.template.default_uom.id
|
|
|
|
|
|
|
|
# Set taxes before unit_price to have taxes in context of sale price
|
|
|
|
taxes = []
|
|
|
|
pattern = {}
|
|
|
|
for tax in product.customer_taxes_used:
|
|
|
|
if invoice.party and invoice.party.customer_tax_rule:
|
|
|
|
tax_ids = invoice.party.customer_tax_rule.apply(tax, pattern)
|
|
|
|
if tax_ids:
|
|
|
|
taxes.extend(tax_ids)
|
|
|
|
continue
|
|
|
|
taxes.append(tax.id)
|
|
|
|
if invoice.party and invoice.party.customer_tax_rule:
|
|
|
|
tax_ids = invoice.party.customer_tax_rule.apply(None, pattern)
|
|
|
|
if tax_ids:
|
|
|
|
taxes.extend(tax_ids)
|
|
|
|
|
|
|
|
context['taxes'] = taxes
|
|
|
|
return context
|
|
|
|
|
2020-04-16 14:45:13 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_main_guest():
|
|
|
|
party = Transaction().context.get('party')
|
|
|
|
return party
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_host_quantity():
|
|
|
|
return 1
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_accommodation():
|
|
|
|
Configuration = Pool().get('hotel.configuration')
|
|
|
|
configuration = Configuration.get_configuration()
|
|
|
|
|
|
|
|
if configuration.default_accommodation:
|
|
|
|
return configuration.default_accommodation.id
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def validate(cls, lines):
|
2021-09-21 06:33:06 +02:00
|
|
|
super(Folio, cls).validate(lines)
|
2020-04-16 14:45:13 +02:00
|
|
|
for line in lines:
|
2021-04-07 21:03:51 +02:00
|
|
|
# line.check_method()
|
2020-12-04 21:08:40 +01:00
|
|
|
pass
|
2020-04-16 14:45:13 +02:00
|
|
|
|
2021-10-08 22:43:48 +02:00
|
|
|
def get_invoice(self, name=None):
|
|
|
|
if self.invoice_line and self.invoice_line.invoice:
|
|
|
|
self.invoice_line.invoice.id
|
|
|
|
|
|
|
|
# @fields.depends('accommodation', 'product')
|
|
|
|
# def on_change_accommodation(self):
|
|
|
|
# if not self.accommodation:
|
|
|
|
# self.product = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_room_info(cls, fo):
|
|
|
|
description = ' \n'.join([
|
|
|
|
fo.product.rec_name,
|
|
|
|
'Huesped Principal: ' + fo.main_guest.name,
|
|
|
|
'Habitacion: ' + fo.room.name,
|
|
|
|
'Llegada: ' + str(fo.arrival_date),
|
|
|
|
'Salida: ' + str(fo.departure_date),
|
|
|
|
])
|
|
|
|
return description
|
2020-04-16 14:45:13 +02:00
|
|
|
|
2021-10-08 00:10:46 +02:00
|
|
|
@classmethod
|
|
|
|
def get_grouped_invoices(cls, folios):
|
|
|
|
res = {}
|
2021-10-08 22:43:48 +02:00
|
|
|
# transfered = []
|
2021-10-08 00:10:46 +02:00
|
|
|
# for op in folios:
|
|
|
|
# for top in op.transfered_operations:
|
|
|
|
# top.party = op.party
|
|
|
|
# transfered.append(top)
|
|
|
|
#
|
|
|
|
# if transfered:
|
|
|
|
# folios.extend(transfered)
|
2021-10-08 22:43:48 +02:00
|
|
|
ffols = [fol for fol in folios if not fol.invoice_line]
|
|
|
|
for fo in ffols:
|
|
|
|
if fo.party:
|
|
|
|
party = fo.party
|
|
|
|
else:
|
|
|
|
party = fo.main_guest
|
|
|
|
|
|
|
|
booking = fo.booking
|
|
|
|
agent_id = booking.party_seller.party.id if booking.party_seller else None
|
|
|
|
if party.id not in res.keys():
|
2021-10-08 00:10:46 +02:00
|
|
|
# Add room product to sale
|
2021-10-08 22:43:48 +02:00
|
|
|
res[party.id] = {
|
|
|
|
'party': party,
|
|
|
|
'currency': booking.currency.id,
|
2021-10-08 00:10:46 +02:00
|
|
|
'payment_term': None,
|
2021-10-08 22:43:48 +02:00
|
|
|
'guests_qty': len(fo.guests) + 1,
|
|
|
|
'reference': '',
|
|
|
|
'agent': agent_id,
|
|
|
|
'rooms': fo.room.name,
|
|
|
|
'company': booking.company.id,
|
|
|
|
'price_list': booking.price_list.id if booking.price_list else None,
|
|
|
|
'add_default_charges': False,
|
|
|
|
'vouchers': booking.vouchers,
|
|
|
|
'lines': [{
|
|
|
|
'folios': [fo],
|
|
|
|
'description': cls.get_room_info(fo),
|
|
|
|
'quantity': fo.nights_quantity,
|
|
|
|
'product': fo.product,
|
|
|
|
'unit_price': fo.unit_price,
|
|
|
|
'taxes_exception': booking.taxes_exception,
|
2021-10-08 00:10:46 +02:00
|
|
|
}]
|
|
|
|
}
|
|
|
|
else:
|
2021-10-08 22:43:48 +02:00
|
|
|
res[party.id]['rooms'] += ' ' + fo.room.name
|
|
|
|
res[party.id]['lines'].append({
|
|
|
|
'folios': [fo],
|
|
|
|
'description': cls.get_room_info(fo),
|
|
|
|
'quantity': fo.nights_quantity,
|
|
|
|
'product': fo.accommodation,
|
|
|
|
'unit_price': fo.unit_price,
|
|
|
|
'taxes_exception': booking.taxes_exception,
|
2021-10-08 00:10:46 +02:00
|
|
|
})
|
2021-10-08 22:43:48 +02:00
|
|
|
for charge in fo.charges:
|
2021-10-08 00:10:46 +02:00
|
|
|
if charge.invoice_line:
|
|
|
|
continue
|
|
|
|
invoice_party_id = charge.invoice_to.id
|
2021-10-08 22:43:48 +02:00
|
|
|
unit_price = booking.currency.round(charge.unit_price)
|
|
|
|
if invoice_party_id != party.id:
|
2021-10-08 00:10:46 +02:00
|
|
|
if invoice_party_id not in res.keys():
|
|
|
|
res[invoice_party_id] = {
|
|
|
|
'party': charge.invoice_to.id,
|
2021-10-08 22:43:48 +02:00
|
|
|
'currency': booking.currency.id,
|
2021-10-08 00:10:46 +02:00
|
|
|
'payment_term': None,
|
2021-10-08 22:43:48 +02:00
|
|
|
'lines': [],
|
2021-10-08 00:10:46 +02:00
|
|
|
}
|
2021-10-08 22:43:48 +02:00
|
|
|
res[invoice_party_id]['lines'].append({
|
2021-10-08 00:10:46 +02:00
|
|
|
'description': ' | '.join([
|
|
|
|
str(charge.date_service),
|
|
|
|
charge.order or '',
|
|
|
|
charge.description or ''
|
|
|
|
]),
|
|
|
|
'quantity': charge.quantity,
|
|
|
|
'product': charge.product,
|
|
|
|
'unit_price': unit_price,
|
|
|
|
'charge': charge,
|
2021-10-08 22:43:48 +02:00
|
|
|
'taxes_exception': booking.taxes_exception,
|
2021-10-08 00:10:46 +02:00
|
|
|
})
|
|
|
|
return res
|
|
|
|
|
2020-04-16 14:45:13 +02:00
|
|
|
@fields.depends('product', 'unit_price', 'uom', 'booking', 'nights_quantity')
|
|
|
|
def on_change_product(self):
|
|
|
|
Product = Pool().get('product.product')
|
|
|
|
if self.product and self.booking:
|
|
|
|
self.uom = self.product.default_uom.id
|
|
|
|
with Transaction().set_context(self.booking.get_context_price(self.product)):
|
|
|
|
self.unit_price = Product.get_sale_price([self.product],
|
|
|
|
self.nights_quantity or 0)[self.product.id]
|
|
|
|
if self.unit_price:
|
|
|
|
self.unit_price = self.booking.currency.round(self.unit_price)
|
|
|
|
else:
|
|
|
|
self.unit_price = self.product.list_price
|
|
|
|
|
|
|
|
@fields.depends('arrival_date', 'departure_date')
|
|
|
|
def on_change_arrival_date(self):
|
2021-07-31 00:44:32 +02:00
|
|
|
if not self.arrival_date or (
|
|
|
|
self.departure_date and self.departure_date > self.arrival_date):
|
2020-04-16 14:45:13 +02:00
|
|
|
return
|
|
|
|
self.departure_date = self.arrival_date + timedelta(days=1)
|
|
|
|
|
|
|
|
def check_method(self):
|
|
|
|
"""
|
|
|
|
Check the methods.
|
|
|
|
"""
|
2020-07-18 17:18:29 +02:00
|
|
|
Date = Pool().get('ir.date')
|
2021-04-20 23:07:22 +02:00
|
|
|
if self.registration_state in (['check_in', 'check_out']):
|
2021-10-08 22:43:48 +02:00
|
|
|
raise UserError(gettext('hotel.msg_reservation_checkin'))
|
2020-07-18 17:18:29 +02:00
|
|
|
if self.arrival_date < Date.today():
|
2021-10-08 22:43:48 +02:00
|
|
|
raise UserError(gettext('hotel.msg_invalid_arrival_date'))
|
2020-04-16 14:45:13 +02:00
|
|
|
if self.arrival_date >= self.departure_date:
|
2021-10-08 22:43:48 +02:00
|
|
|
raise UserError(gettext('hotel.msg_invalid_date'))
|
2021-09-21 06:33:06 +02:00
|
|
|
# Operation = Pool().get('hotel.operation')
|
|
|
|
# operations = Operation.search([
|
|
|
|
# ('room', '=', self.room.id),
|
|
|
|
# ['AND',
|
|
|
|
# ['OR', [
|
|
|
|
# ('start_date', '>=', self.arrival_date),
|
|
|
|
# ('end_date', '<=', self.arrival_date),
|
|
|
|
# ], [
|
|
|
|
# ('start_date', '>=', self.departure_date),
|
|
|
|
# ('end_date', '<=', self.departure_date),
|
|
|
|
# ]]
|
|
|
|
# ]
|
|
|
|
# ])
|
|
|
|
# if operations:
|
|
|
|
# raise AccessError(gettext('hotel.msg_occupied_room', s=self.departure_date))
|
|
|
|
# config = Pool().get('hotel.configuration')(1)
|
|
|
|
# quarantine_days = config.quarantine_rooms
|
|
|
|
# room_id = self.room.id
|
|
|
|
# if quarantine_days:
|
|
|
|
# delta = timedelta(days=int(quarantine_days))
|
|
|
|
# _date = self.arrival_date - delta
|
|
|
|
# operations = Operation.search([
|
|
|
|
# ['AND',
|
|
|
|
# ['OR', [
|
|
|
|
# ('room', '=', room_id),
|
|
|
|
# ('end_date', '=', _date)
|
|
|
|
# ],
|
|
|
|
# [
|
|
|
|
# ('room', '=', room_id),
|
|
|
|
# ('end_date', '=', self.arrival_date)
|
|
|
|
# ]]]])
|
|
|
|
# if operations:
|
|
|
|
# raise AccessError(gettext('hotel.msg_restring_room', s=self.room.name))
|
2020-07-18 17:18:29 +02:00
|
|
|
|
2020-04-16 14:45:13 +02:00
|
|
|
def get_state(self, name):
|
|
|
|
if self.booking:
|
|
|
|
return self.booking.state
|
|
|
|
|
|
|
|
def get_host_quantity(self, name):
|
|
|
|
res = 0
|
|
|
|
if self.num_adult:
|
|
|
|
res += self.num_adult
|
|
|
|
if self.num_children:
|
|
|
|
res += self.num_children
|
|
|
|
return res
|
|
|
|
|
2021-10-05 05:16:20 +02:00
|
|
|
@classmethod
|
|
|
|
def get_available_rooms(cls, start_date, end_date, rooms_ids=[], oper=None):
|
|
|
|
"""
|
|
|
|
Look for available rooms.
|
|
|
|
|
|
|
|
given the date interval, return a list of room ids.
|
|
|
|
|
|
|
|
a room is available if it has no operation that overlaps
|
|
|
|
with the given date interval.
|
|
|
|
|
|
|
|
the optional 'rooms' list is a list of room instance, is an
|
|
|
|
additional filter, specifying the ids of the desirable rooms.
|
|
|
|
|
|
|
|
the optional 'oper' is an operation object that has to be
|
|
|
|
filtered out of the test. it is useful for validating an already
|
|
|
|
existing operation.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if start_date >= end_date:
|
2021-10-08 22:43:48 +02:00
|
|
|
raise UserError(gettext('hotel.msg_invalid_date_range'))
|
2021-10-05 05:16:20 +02:00
|
|
|
|
|
|
|
# define the domain of the operations that find a
|
|
|
|
# room to be available
|
|
|
|
dom = ['AND', ['OR',
|
|
|
|
[
|
|
|
|
('arrival_date', '>=', start_date),
|
|
|
|
('arrival_date', '<', end_date),
|
|
|
|
], [
|
|
|
|
('departure_date', '<=', end_date),
|
|
|
|
('departure_date', '>', start_date),
|
|
|
|
], [
|
|
|
|
('arrival_date', '<=', start_date),
|
|
|
|
('departure_date', '>=', end_date),
|
|
|
|
],
|
|
|
|
]]
|
|
|
|
|
|
|
|
## If oper was specified, do not compare the operations with it
|
|
|
|
if oper is not None:
|
|
|
|
dom.append(('id', '!=', oper.id))
|
|
|
|
|
|
|
|
if rooms_ids:
|
|
|
|
dom.append(('room', 'in', rooms_ids))
|
|
|
|
|
|
|
|
folios = cls.search(dom)
|
|
|
|
rooms_not_available_ids = [folio.room.id for folio in folios]
|
|
|
|
rooms_available_ids = set(rooms_ids) - set(rooms_not_available_ids)
|
|
|
|
return list(rooms_available_ids)
|
|
|
|
|
2021-07-25 18:00:02 +02:00
|
|
|
@fields.depends('arrival_date', 'departure_date')
|
2020-04-16 14:45:13 +02:00
|
|
|
def get_nights_quantity(self, name=None):
|
|
|
|
"""
|
|
|
|
Compute nights between start and end
|
|
|
|
return a integer the mean days of occupancy.
|
|
|
|
"""
|
|
|
|
nights = 0
|
|
|
|
if not self.arrival_date or not self.departure_date:
|
|
|
|
return nights
|
|
|
|
nights = (self.departure_date - self.arrival_date).days
|
|
|
|
return nights
|
|
|
|
|
|
|
|
def get_total_amount(self, name):
|
|
|
|
"""
|
|
|
|
The total amount of booking based on room flat price.
|
|
|
|
TODO: If room fee is applied should be used for total price calculation
|
|
|
|
instead of flat price. Fee is linked to channel management.
|
|
|
|
"""
|
|
|
|
res = _ZERO
|
|
|
|
if self.nights_quantity and self.unit_price:
|
|
|
|
res = self.nights_quantity * self.unit_price
|
|
|
|
return res
|
|
|
|
|
|
|
|
def get_channel_commission(self, name):
|
|
|
|
"""
|
|
|
|
Calculation of sale commission for channel based on booking total amount
|
|
|
|
"""
|
|
|
|
res = Decimal(0)
|
|
|
|
if self.total_amount and self.booking.party_seller and \
|
|
|
|
self.booking.party_seller.party.sale_commission:
|
|
|
|
res = self.total_amount * self.booking.party_seller.sale_commission / 100
|
|
|
|
return res
|
|
|
|
|
2021-10-08 00:10:46 +02:00
|
|
|
@classmethod
|
|
|
|
def _get_new_invoice(cls, data):
|
|
|
|
pool = Pool()
|
|
|
|
Invoice = pool.get('account.invoice')
|
|
|
|
Party = pool.get('party.party')
|
|
|
|
Agent = pool.get('commission.agent')
|
2021-10-08 22:43:48 +02:00
|
|
|
Journal = pool.get('account.journal')
|
|
|
|
PaymentTerm = pool.get('account.invoice.payment_term')
|
2021-10-08 00:10:46 +02:00
|
|
|
Date = pool.get('ir.date')
|
|
|
|
date_ = Date.today()
|
|
|
|
|
|
|
|
price_list_id = None
|
|
|
|
if data.get('price_list'):
|
|
|
|
price_list_id = data['price_list']
|
|
|
|
|
|
|
|
company_id = Transaction().context.get('company')
|
2021-10-08 22:43:48 +02:00
|
|
|
party = data['party']
|
2021-10-08 00:10:46 +02:00
|
|
|
description = data.get('rooms')
|
|
|
|
reference = data.get('reference')
|
|
|
|
agent = None
|
|
|
|
if data.get('agent'):
|
|
|
|
agent = Agent(data['agent'])
|
|
|
|
|
2021-10-08 22:43:48 +02:00
|
|
|
journal, = Journal.search([
|
|
|
|
('type', '=', 'revenue'),
|
|
|
|
], limit=1)
|
|
|
|
|
|
|
|
address = Party.address_get(party, type='invoice')
|
|
|
|
payment_term = data.get('payment_term', None)
|
|
|
|
if not payment_term:
|
|
|
|
payment_terms = PaymentTerm.search([])
|
|
|
|
payment_term = payment_terms[0]
|
2021-10-08 00:10:46 +02:00
|
|
|
return Invoice(
|
|
|
|
company=company_id,
|
2021-10-08 22:43:48 +02:00
|
|
|
payment_term=payment_term.id,
|
2021-10-08 00:10:46 +02:00
|
|
|
party=party.id,
|
2021-10-08 22:43:48 +02:00
|
|
|
account=party.account_receivable_used.id,
|
2021-10-08 00:10:46 +02:00
|
|
|
invoice_date=date_,
|
|
|
|
description=description,
|
|
|
|
state='draft',
|
|
|
|
reference=reference,
|
|
|
|
agent=agent,
|
2021-10-08 22:43:48 +02:00
|
|
|
journal=journal,
|
|
|
|
type='out',
|
|
|
|
invoice_type='1',
|
|
|
|
invoice_address=address.id,
|
2021-10-08 00:10:46 +02:00
|
|
|
)
|
|
|
|
|
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 Guest(ModelSQL, ModelView):
|
|
|
|
'Guest'
|
|
|
|
__name__ = 'hotel.booking.guest'
|
|
|
|
_rec_name = 'party'
|
2021-09-20 23:35:34 +02:00
|
|
|
booking_line = fields.Many2One('hotel.folio', 'Booking Line',
|
2020-04-16 14:45:13 +02:00
|
|
|
required=True, ondelete='CASCADE')
|
|
|
|
party = fields.Many2One('party.party', 'Party', required=False)
|
|
|
|
type_guest = fields.Selection([
|
|
|
|
('adult', 'Adult'),
|
|
|
|
('child', 'Child'),
|
|
|
|
], 'Type Guest')
|
|
|
|
type_guest_string = type_guest.translated('type_guest')
|
|
|
|
nationality = fields.Many2One('party.nationality', 'Nationality')
|
2021-03-27 15:35:46 +01:00
|
|
|
origin_country = fields.Many2One('party.nationality', 'Origin Country',
|
2020-04-16 14:45:13 +02:00
|
|
|
select=True)
|
2021-03-27 15:35:46 +01:00
|
|
|
target_country = fields.Many2One('party.nationality', 'Target Country',
|
2020-04-16 14:45:13 +02:00
|
|
|
select=True)
|
|
|
|
|
|
|
|
# New fields for speed reason
|
|
|
|
type_document = fields.Selection([
|
|
|
|
('12', 'Tarjeta de Identidad'),
|
|
|
|
('13', 'Cedula de Ciudadania'),
|
|
|
|
('21', 'Tarjeta de Extranjeria'),
|
|
|
|
('22', 'Cedula de Extranjeria'),
|
|
|
|
('41', 'Pasaporte'),
|
|
|
|
], 'Document Type')
|
|
|
|
doc_number = fields.Char('Doc. Id', select=True)
|
|
|
|
name = fields.Char('Name', select=True)
|
|
|
|
mobile = fields.Char('Mobile', select=True)
|
|
|
|
email = fields.Char('Email', select=True)
|
|
|
|
birthday = fields.Date('Birthday', select=True)
|
|
|
|
sex = fields.Selection([
|
|
|
|
('female', 'Female'),
|
|
|
|
('male', 'Male'),
|
|
|
|
('', ''),
|
|
|
|
], 'Sex')
|
2021-03-27 15:35:46 +01:00
|
|
|
first_name = fields.Char('First Name')
|
|
|
|
second_name = fields.Char('Second Name')
|
|
|
|
first_family_name = fields.Char('First Family Name')
|
|
|
|
second_family_name = fields.Char('Second Family Name')
|
|
|
|
type_person = fields.Selection([
|
|
|
|
('persona_natural', 'Persona Natural'),
|
|
|
|
('persona_juridica', 'Persona Juridica'),
|
|
|
|
], 'Type Person')
|
|
|
|
|
|
|
|
@fields.depends('name', 'first_name', 'second_name',
|
|
|
|
'first_family_name', 'second_family_name', 'type_person')
|
|
|
|
def on_change_name(self):
|
|
|
|
second_family_name = None
|
|
|
|
first_family_name = None
|
|
|
|
second_name = None
|
|
|
|
first_name = None
|
|
|
|
if self.name and self.type_person == 'persona_natural':
|
|
|
|
names = self.name.split(' ')
|
|
|
|
first_name = names[0]
|
|
|
|
second_family_name = names[-1]
|
|
|
|
if len(names) > 1:
|
|
|
|
first_family_name = names[-2]
|
|
|
|
if len(names) == 2:
|
|
|
|
second_family_name = None
|
|
|
|
first_family_name = names[1]
|
|
|
|
elif len(names) == 5:
|
|
|
|
second_name = names[1] + ' ' + names[2]
|
|
|
|
elif len(names) == 4:
|
|
|
|
second_name = names[1]
|
|
|
|
|
|
|
|
self.second_family_name = second_family_name
|
|
|
|
self.first_family_name = first_family_name
|
|
|
|
self.second_name = second_name
|
|
|
|
self.first_name = first_name
|
2020-04-16 14:45:13 +02:00
|
|
|
|
|
|
|
def get_rec_name(self, name):
|
|
|
|
if self.party:
|
|
|
|
return self.party.name
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_type_guest():
|
|
|
|
return 'adult'
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_sex():
|
|
|
|
return 'male'
|
|
|
|
|
2021-03-27 15:35:46 +01:00
|
|
|
@staticmethod
|
|
|
|
def default_type_person():
|
|
|
|
return 'persona_natural'
|
|
|
|
|
2020-04-16 14:45:13 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_type_document():
|
|
|
|
return '13'
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_principal_guest():
|
|
|
|
return False
|
|
|
|
|
2021-03-27 15:35:46 +01:00
|
|
|
@fields.depends('nationality', 'origin_country', 'target_country')
|
|
|
|
def on_change_nationality(self):
|
|
|
|
if self.nationality:
|
|
|
|
self.target_country = self.nationality.id
|
|
|
|
self.origin_country = self.nationality.id
|
2020-04-16 14:45:13 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def create(cls, vlist):
|
|
|
|
Party = Pool().get('party.party')
|
|
|
|
new_values = []
|
|
|
|
for v in vlist:
|
|
|
|
# if not v.get('doc_number'):
|
|
|
|
# continue
|
|
|
|
party = v.get('party')
|
|
|
|
if not party:
|
|
|
|
parties = Party.search([
|
|
|
|
('id_number', '=', v['doc_number']),
|
|
|
|
])
|
|
|
|
if parties:
|
|
|
|
party = parties[0]
|
|
|
|
else:
|
|
|
|
party, = Party.create([{
|
|
|
|
'name': v['name'],
|
|
|
|
'id_number': v['doc_number'],
|
|
|
|
'type_document': v['type_document'],
|
|
|
|
'birthday': v['birthday'],
|
|
|
|
'sex': v['sex'],
|
2021-03-27 15:35:46 +01:00
|
|
|
'first_name': v['first_name'],
|
|
|
|
'second_name': v['second_name'],
|
|
|
|
'first_family_name': v['first_family_name'],
|
|
|
|
'second_family_name': v['second_family_name'],
|
|
|
|
'type_person': v['type_person'],
|
2020-04-16 14:45:13 +02:00
|
|
|
'contact_mechanisms': [
|
|
|
|
('create', [
|
|
|
|
{'type': 'email', 'value': v['email']},
|
|
|
|
{'type': 'mobile', 'value': v['mobile']},
|
|
|
|
])
|
|
|
|
]
|
|
|
|
}])
|
|
|
|
v['party'] = party.id
|
|
|
|
new_values.append(v)
|
|
|
|
|
|
|
|
super(Guest, cls).create(new_values)
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
dom = [['OR',
|
|
|
|
[
|
|
|
|
('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 RegistrationCardReport(Report):
|
|
|
|
__name__ = 'hotel.occupancy.registration_card'
|
|
|
|
|
|
|
|
@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 GuestsListStart(ModelView):
|
|
|
|
'Guests List Start'
|
|
|
|
__name__ = 'hotel.print_guests_list.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 GuestsList(Wizard):
|
|
|
|
'Guests List'
|
|
|
|
__name__ = 'hotel.print_guests_list'
|
|
|
|
start = StateView('hotel.print_guests_list.start',
|
|
|
|
'hotel.print_guests_list_start_view_form', [
|
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Open', 'print_', 'tryton-print', default=True),
|
|
|
|
])
|
|
|
|
print_ = StateReport('hotel.guests_list.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 GuestsListReport(Report):
|
|
|
|
__name__ = 'hotel.guests_list.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-09-21 06:33:06 +02:00
|
|
|
# Operation = pool.get('hotel.operation')
|
2020-04-16 14:45:13 +02:00
|
|
|
Room = pool.get('hotel.room')
|
|
|
|
User = pool.get('res.user')
|
|
|
|
user_id = Transaction().user
|
|
|
|
|
|
|
|
user = User(user_id)
|
|
|
|
start_date = data['date']
|
2021-09-21 06:33:06 +02:00
|
|
|
# operations = Operation.search([
|
|
|
|
# ('start_date', '<=', start_date),
|
|
|
|
# ('kind', '=', 'occupancy'),
|
|
|
|
# ('state', '=', 'open'),
|
|
|
|
# ], order=[('room.code', 'ASC')])
|
|
|
|
#
|
|
|
|
# total_guests = []
|
|
|
|
# rooms_occupied = []
|
|
|
|
# for op in operations:
|
|
|
|
# total_guests.append(len(op.guests))
|
|
|
|
# rooms_occupied.append(op.room.id)
|
|
|
|
#
|
|
|
|
# rooms = Room.search_read([])
|
|
|
|
#
|
|
|
|
# _rooms_occupied = len(set(rooms_occupied))
|
|
|
|
# num_rooms = len(rooms)
|
|
|
|
# if num_rooms:
|
|
|
|
# occupancy_rate = round(_rooms_occupied / num_rooms, 2)
|
|
|
|
# else:
|
|
|
|
# occupancy_rate = 0
|
|
|
|
#
|
|
|
|
# operations_ = []
|
|
|
|
# for op in operations:
|
|
|
|
# for guest in op.guests:
|
|
|
|
# operations_.append({
|
|
|
|
# 'room': op.room.code,
|
|
|
|
# 'guest': guest.party.name,
|
|
|
|
# 'party': op.party.code,
|
|
|
|
# 'start_date': op.start_date,
|
|
|
|
# 'end_date': op.end_date,
|
|
|
|
# 'nights_quantity': op.nights_quantity,
|
|
|
|
# })
|
|
|
|
# report_context['records'] = operations_
|
|
|
|
# report_context['total_rooms'] = _rooms_occupied
|
|
|
|
# report_context['total_guests'] = sum(total_guests)
|
|
|
|
# report_context['occupancy_rate'] = occupancy_rate
|
|
|
|
# report_context['company'] = Company(data['company']).party.name
|
|
|
|
# report_context['date'] = data['date']
|
|
|
|
# report_context['print_date'] = datetime.now()
|
|
|
|
# report_context['user'] = user.name
|
2020-04-16 14:45:13 +02:00
|
|
|
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
|
2021-09-20 23:35:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
class HotelCharge(Workflow, ModelSQL, ModelView):
|
|
|
|
'Hotel Charge'
|
|
|
|
__name__ = 'hotel.charge'
|
|
|
|
|
|
|
|
booking_line = fields.Many2One('', 'Booking Line',
|
|
|
|
required=True)
|
|
|
|
service_date = fields.Date('Service Date', select=True, required=True)
|
|
|
|
product = fields.Many2One('product.product', 'Product',
|
|
|
|
domain=[('salable', '=', True)], required=True)
|
|
|
|
quantity = fields.Integer('Quantity', required=True)
|
|
|
|
invoice_to = fields.Many2One('party.party', 'Invoice To', required=True)
|
|
|
|
unit_price = fields.Numeric('Unit Price', required=True)
|
|
|
|
unit_price_w_tax = fields.Function(fields.Numeric('Unit Price'),
|
|
|
|
'get_unit_price_w_tax')
|
|
|
|
order = fields.Char('Order', select=True)
|
|
|
|
description = fields.Char('Description', select=True)
|
|
|
|
state = fields.Selection(INVOICE_STATES, 'State', readonly=True)
|
|
|
|
state_string = state.translated('state')
|
|
|
|
sale_line = fields.Many2One('sale.line', 'Sale Line', readonly=True)
|
|
|
|
amount = fields.Function(fields.Numeric('Amount',
|
|
|
|
digits=(16, 2)), 'get_amount')
|
|
|
|
taxed_amount = fields.Function(fields.Numeric('Amount with Tax',
|
|
|
|
digits=(16, 2)), 'get_taxed_amount')
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
2021-10-08 22:43:48 +02:00
|
|
|
super(HotelCharge, cls).__setup__()
|
2021-09-20 23:35:34 +02:00
|
|
|
cls._buttons.update({
|
|
|
|
'transfer': {
|
|
|
|
'invisible': True,
|
|
|
|
},
|
2021-10-08 22:43:48 +02:00
|
|
|
# 'bill': {
|
|
|
|
# 'invisible': Eval('invoice_state') is not None,
|
|
|
|
# },
|
2021-09-20 23:35:34 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_quantity():
|
|
|
|
return 1
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_date_service():
|
|
|
|
today = Pool().get('ir.date').today()
|
|
|
|
return today
|
|
|
|
|
|
|
|
def get_amount(self, name=None):
|
|
|
|
if self.quantity and self.unit_price:
|
|
|
|
return self.quantity * self.unit_price_w_tax
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_unit_price_w_tax(self, name=None):
|
|
|
|
Tax = Pool().get('account.tax')
|
|
|
|
res = self.unit_price or 0
|
|
|
|
if self.unit_price:
|
|
|
|
values = Tax.compute(
|
|
|
|
self.product.template.customer_taxes_used,
|
|
|
|
self.unit_price, 1)
|
|
|
|
if values:
|
|
|
|
value = values[0]
|
|
|
|
res = value['base'] + value['amount']
|
|
|
|
return res
|
|
|
|
|
|
|
|
def get_taxed_amount(self, name=None):
|
|
|
|
if self.quantity and self.unit_price:
|
|
|
|
return self.quantity * self.unit_price
|
|
|
|
|
|
|
|
# def get_sale(self, name=None):
|
|
|
|
# if self.sale_line:
|
|
|
|
# return self.sale_line.sale.id
|
|
|
|
|
|
|
|
# def compute_amount_with_tax(line):
|
|
|
|
# tax_amount = _ZERO
|
|
|
|
# amount = _ZERO
|
|
|
|
# if line.taxes:
|
|
|
|
# tax_list = Tax.compute(line.taxes, line.unit_price or _ZERO,
|
|
|
|
# line.quantity or 0.0)
|
|
|
|
#
|
|
|
|
# tax_amount = sum([t['amount'] for t in tax_list], _ZERO)
|
|
|
|
#
|
|
|
|
# if line.unit_price:
|
|
|
|
# amount = line.unit_price * Decimal(line.quantity)
|
|
|
|
# return amount + tax_amount
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
def bill(cls, records):
|
|
|
|
cls.create_sales(records)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button_action('hotel.wizard_operation_line_transfer')
|
|
|
|
def transfer(cls, records):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@fields.depends('unit_price', 'product')
|
|
|
|
def on_change_product(self):
|
|
|
|
if self.product:
|
|
|
|
self.unit_price = self.product.template.list_price
|
|
|
|
self.description = self.product.description
|